iT邦幫忙

2025 iThome 鐵人賽

DAY 28
0

  延續昨天設計與 debug的實作心得,這一篇聚焦於介面優化與防呆設計,目的是為了讓沒有程式背景的設計師或初學者來說,能有一個簡單直觀的 GUI 工具。

28.1. 輸入檢查與即時提示

  最常見的輸入檢查與即時提示,可以用在如選擇來源資料夾或匯出目的資料夾時,即時檢查並提示是否正確輸入,並透過紅色提示標籤或訊息框方式,提醒使用者修正輸入錯誤。程式碼範例如下:

import tkinter as tk
from tkinter import filedialog, messagebox
import os

def choose_folder(entry, label):
    path = filedialog.askdirectory()
    entry.delete(0, tk.END)
    entry.insert(0, path)
    label.config(text="")

def submit():
    src = entry_src.get()
    dst = entry_dst.get()
    ok = True
    if not (src and os.path.isdir(src)):
        lbl_src.config(text="*請選來源", fg="red")
        ok = False
    else:
        lbl_src.config(text="")
    if not (dst and os.path.isdir(dst)):
        lbl_dst.config(text="*請選目的", fg="red")
        ok = False
    else:
        lbl_dst.config(text="")
    if ok:
        messagebox.showinfo("成功", f"來源:{src}\n目的:{dst}")

root = tk.Tk()
root.title("資料夾選擇")
root.geometry("360x140")

tk.Label(root, text="來源資料夾:").grid(row=0, column=0, sticky="e")
entry_src = tk.Entry(root, width=24); entry_src.grid(row=0, column=1)
tk.Button(root, text="選擇", command=lambda: choose_folder(entry_src, lbl_src)).grid(row=0, column=2)
lbl_src = tk.Label(root, fg="red"); lbl_src.grid(row=1, column=1, sticky="w")

tk.Label(root, text="目的資料夾:").grid(row=2, column=0, sticky="e")
entry_dst = tk.Entry(root, width=24); entry_dst.grid(row=2, column=1)
tk.Button(root, text="選擇", command=lambda: choose_folder(entry_dst, lbl_dst)).grid(row=2, column=2)
lbl_dst = tk.Label(root, fg="red"); lbl_dst.grid(row=3, column=1, sticky="w")

tk.Button(root, text="執行", command=submit).grid(row=4, column=1, pady=12)

root.mainloop()

https://ithelp.ithome.com.tw/upload/images/20250828/20177646DnUvNxfOgJ.png
圖28.1 檢查有誤與提示標籤文字
https://ithelp.ithome.com.tw/upload/images/20250828/20177646lDtCCc9l3t.png
圖28.2 處理成功提示訊息框

28.2. 失敗處理設計

  在實務開發中,軟體流程很難保證每一步都「萬無一失」。就算介面已經設計好輸入檢查,實際運行時還是可能遇到資料內容異常或不可預期的系統問題。
  因此,建議可以多使用 try...except語法,它可以針對不同的錯誤類型採取相對應的處理方式,使程式在遇到異常狀況時不中斷,並能友善提示使用者。
  其設計思路可解釋為:「當遇到A狀況時,採用A處理方式;否則根據不同情境分別採取B、C、D等方法。」

import tkinter as tk
from tkinter import filedialog, messagebox
import os

def choose_folder(entry):
    # 用對話框讓使用者選擇來源資料夾
    path = filedialog.askdirectory()
    entry.delete(0, tk.END)
    entry.insert(0, path)

def process():
    folder = entry_src.get()
    # 檢查是否有選擇有效資料夾(防止常見的人為疏忽)
    if not folder or not os.path.isdir(folder):
        messagebox.showerror("錯誤", "請選擇有效的來源資料夾!")
        return

    # 檢查有無任何 .txt 檔案(防止資料夾空或格式錯誤)
    txt_files = [f for f in os.listdir(folder) if f.endswith('.txt')]
    if not txt_files:
        messagebox.showerror("錯誤", "來源資料夾沒有任何 .txt 檔案!")
        return

    # 嘗試讀取每一個 txt 檔案(這裡最有可能發生未知錯誤,所以用 try-except 包覆)
    try:
        contents = []
        for fname in txt_files:
            fpath = os.path.join(folder, fname)
            with open(fpath, encoding='utf-8') as f:
                contents.append(f.read())
        messagebox.showinfo("成功", f"成功讀取 {len(txt_files)} 個 txt 檔案。")
    except Exception as e:
        # 捕捉檔案損毀、權限、編碼等所有意外,並明確回報用戶
        messagebox.showerror("處理失敗", f"讀取檔案時發生錯誤:{e}")

root = tk.Tk()
root.title("讀取TXT檔案失敗處理案例")
root.geometry("340x120")

tk.Label(root, text="來源資料夾:").grid(row=0, column=0, sticky="e")
entry_src = tk.Entry(root, width=22)
entry_src.grid(row=0, column=1)
tk.Button(root, text="選擇", command=lambda: choose_folder(entry_src)).grid(row=0, column=2)

tk.Button(root, text="執行", command=process).grid(row=2, column=1, pady=18)

root.mainloop()

https://ithelp.ithome.com.tw/upload/images/20250828/20177646EmKPhAme57.png
圖28.3 失敗與成功處理之案例

28.3. 介面排版與優化

在Day26曾提到Tkinter 常用的元件表,那要把物件放置在介面當中,Tkinter常用的是 pack 和 grid。

  • pack(自動堆疊)的排版中,元件一律是由上往下置放。
+---------------------+
|   [按鈕1]           |   ← 預設由上往下排列
|---------------------|
|   [按鈕2]           |   
|---------------------|
|   [Label]           |   
+---------------------+
  • grid(表格對齊)的排版中,元件可按需要行列進行佈局。
+-------+------------------+---------+
|來源   | [Entry]          | [選擇]  |   ← row=0, col=0/1/2
+-------+------------------+---------+
|目的   | [Entry]          | [選擇]  |   ← row=1, col=0/1/2
+-------+------------------+---------+
|       |   [執行]         |         |   ← row=2, col=1
+-------+------------------+---------+
|       | [進度條/狀態]    |         |   ← row=3, col=1
+-------+------------------+---------+

  除了介面的排版美觀性外,在執行時間較長的任務時,可加入進度條(Progressbar)或即時狀態提示,以避免使用者誤以為程式未處理或未知其進度,這樣的設計可讓使用者有更友善的操作體驗。

import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os
import time
import threading

# 主視窗
root = tk.Tk()
root.title("批次處理小工具")

# 1. 基本表單UI
tk.Label(root, text="資料夾路徑:").grid(row=0, column=0, padx=4, pady=6, sticky="e")
entry = tk.Entry(root, width=40)
entry.grid(row=0, column=1)
def choose_folder():
    folder = filedialog.askdirectory()
    if folder:
        entry.delete(0, tk.END)
        entry.insert(0, folder)
btn = tk.Button(root, text="選擇資料夾", command=choose_folder)
btn.grid(row=0, column=2, padx=4)

# 進度條
progress = ttk.Progressbar(root, length=320, mode="determinate")
progress.grid(row=2, column=0, columnspan=3, pady=8)
status_label = tk.Label(root, text="", fg="blue")
status_label.grid(row=3, column=0, columnspan=3)

# 2. 執行處理(多執行緒避免介面卡死)
def batch_process():
    folder = entry.get()
    if not folder or not os.path.isdir(folder):
        messagebox.showerror("錯誤", "請選擇有效的資料夾")
        return
    files = [f for f in os.listdir(folder) if f.endswith('.txt')]
    total = len(files)
    if total == 0:
        messagebox.showinfo("提示", "資料夾內無txt檔案")
        return
    progress["maximum"] = total
    progress["value"] = 0
    status_label.config(text="處理中...")

    for i, fname in enumerate(files, 1):
        time.sleep(0.3)  # 模擬每個檔案處理時間
        progress["value"] = i
        status_label.config(text=f"正在處理:{fname} ({i}/{total})")
        root.update_idletasks()
    status_label.config(text="全部處理完成!")
    messagebox.showinfo("完成", f"成功處理 {total} 個txt檔案")

def run_thread():
    threading.Thread(target=batch_process).start()

run_btn = tk.Button(root, text="執行", command=run_thread)
run_btn.grid(row=1, column=0, columnspan=3, pady=(8, 0))

# 主迴圈
root.mainloop()

https://ithelp.ithome.com.tw/upload/images/20250828/20177646iZVmia0i0k.png
圖28.4 進度條與訊息框之案例

28.4. 結語

  今天簡單示範從輸入檢查、失敗處理、介面排版到進度條的實用設計,良好的輸入檢查與即時提示、完善的失敗處理與例外捕捉,都是讓程式整體操作流程更直觀順暢,更是讓數位工具真正「好用」、「敢用」的關鍵。希望本篇能幫助你在實作上持續精進,設計出讓人安心的智慧自動化工具!


上一篇
Day27:ChatGPT輔助程式設計應用分享
下一篇
Day29: AI建築自動化技術趨勢與未來展望
系列文
AI圖像辨識輔助的BIM資料流自動化流程30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言