延續昨天設計與 debug的實作心得,這一篇聚焦於介面優化與防呆設計,目的是為了讓沒有程式背景的設計師或初學者來說,能有一個簡單直觀的 GUI 工具。
最常見的輸入檢查與即時提示,可以用在如選擇來源資料夾或匯出目的資料夾時,即時檢查並提示是否正確輸入,並透過紅色提示標籤或訊息框方式,提醒使用者修正輸入錯誤。程式碼範例如下:
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()
圖28.1 檢查有誤與提示標籤文字
圖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()
圖28.3 失敗與成功處理之案例
在Day26曾提到Tkinter 常用的元件表,那要把物件放置在介面當中,Tkinter常用的是 pack 和 grid。
+---------------------+
| [按鈕1] | ← 預設由上往下排列
|---------------------|
| [按鈕2] |
|---------------------|
| [Label] |
+---------------------+
+-------+------------------+---------+
|來源 | [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()
圖28.4 進度條與訊息框之案例
今天簡單示範從輸入檢查、失敗處理、介面排版到進度條的實用設計,良好的輸入檢查與即時提示、完善的失敗處理與例外捕捉,都是讓程式整體操作流程更直觀順暢,更是讓數位工具真正「好用」、「敢用」的關鍵。希望本篇能幫助你在實作上持續精進,設計出讓人安心的智慧自動化工具!