本系列之前的文章參考 :
# Python 內建 GUI 模組 tkinter 測試 (一) : 建立視窗
# Python 內建 GUI 模組 tkinter 測試 (二) : 對話框
# Python 內建 GUI 模組 tkinter 測試 (三) : 版面管理員
tkinter 的說明文件參考 :
# https://pythonspot.com/en/tkinter/
# Graphical User Interfaces with Tk
# Python 內建 GUI 模組 tkinter 測試 (一) : 建立視窗
# Python 內建 GUI 模組 tkinter 測試 (二) : 對話框
# Python 內建 GUI 模組 tkinter 測試 (三) : 版面管理員
tkinter 的說明文件參考 :
# https://pythonspot.com/en/tkinter/
# Graphical User Interfaces with Tk
茲將前三篇筆記摘要整理如下 :
1. 匯入模組 :
這幾年因為機器學習的推波助瀾, Python 3 現在已是主流, 所以學習 GUI 套件應該改用 Python 3 的 tkinter 模組 (注意是小寫的 t), 而非 Python 2 的 Tkinter 模組 (注意是大寫的 T). 使用 tkinter 模組通常即可開發一般的 GUI 程式, 但若要使用更進階的元件也可以同時匯入其子模組 ttk :
>>> import tkinter as tk # 匯入 tkinter 並取簡名 tk
>>> from tkinter import ttk # 匯入 tkinter 的子模組 ttk
>>> tk
<module 'tkinter' from 'C:\\Python37\\lib\\tkinter\\__init__.py'>
>>> ttk
<module 'tkinter.ttk' from 'C:\\Python37\\lib\\tkinter\\ttk.py'>
原始版的 tkinter 提供了 19 種視窗元件 (widget, 或稱控制項), 其外觀依賴於作業系統, 為了美化視窗元件外觀與提供更多好用元件, tkinter 另外發展了主題版 (Themed tkinter) 的子模組 ttk, 它提供了 18 種外觀美化的主題元件 (其中 12 種與 tkinter 相同), 合計有 25 種元件可用 :
tkinter 元件 | ttk 元件 | 說明 |
1. Label | 1. Label | 標籤文字 |
2. Button | 2. Button | 按鈕 |
3. Radiobutton | 3. Radiobutton | 單選圓鈕 |
4. Checkbutton | 4. Checkbutton | 核取方塊 (多選) |
5. Entry | 5. Entry | 文字欄位 (單行) |
6. Text | 文字區域 (多行) | |
7. Scrollbar | 6. Scrollbar | 捲軸 |
8. Listbox | 清單方塊 | |
9. Spinbox | 7. Spinbox | 數值微調器 |
10. Scale | 8. Scale | 尺規 |
11. Frame | 9. Frame | 框架 |
12. LabelFrame | 10. LabelFrame | 標籤框架 |
13. Toplevel | 頂層視窗 | |
14. PanedWindow | 11.PanedWindow | 視窗面板 |
15. Menu | 選單 | |
16. OptionMenu | 功能表 | |
17. Menubutton | 12. MenuButton | 選單按鈕 |
18. Canvas | 畫布 | |
19. Message | 訊息 (可多行) | |
13. Combobox | 下拉式選單 | |
14. Notebook | 頁籤面板 | |
15. Progressbar | 進度條 | |
16. Separator | 分隔線 | |
17. Sizegrip | 尺寸調整器 | |
18. Treeview | 樹狀表格 |
2. 視窗程式基本架構 :
tkinter 模組基本視窗程式結構如下 :
import tkinter as tk # 匯入 tkinter 模組並取簡名為 tk
root=tk.Tk() # 建立根視窗 Tk 物件
root.title("Hello") # 設定標題
label=tk.Lable(root, "Hello!") # 建立標籤元件
label.pack() # 將標籤元件加入視窗
root.mainloop() # 將根視窗加入事件監視迴圈並持續描繪視窗
使用 ttk 的基本架構如下 :
import tkinter as tk # 匯入 tkinter 模組並取簡名為 tk
from tkinter import ttk # 匯入 ttk 子模組
root=tk.Tk() # 建立根視窗 Tk 物件 (仍用 tk)
root.title("Hello") # 設定標題
label=ttk.Lable(root, "Hello!") # 建立標籤元件
label.pack() # 將標籤元件加入視窗
root.mainloop() # 將根視窗加入事件監視迴圈並持續描繪視窗
注意, 建立根視窗物件仍然要用 tk.Tk(), 建立元件才用 ttk.
如果視窗元件物件後續不會再用到就不須建立物件參考, 可以直接用鏈式呼叫 :
ttk.Label(root, "Hello").pack()
視窗預設是可拖曳調整大小, 放到最大或縮小, 也可以呼叫根視窗 Tk 物件的 geometry() 方法設定視窗大小 :
root.geometry("400x300")
呼叫根視窗物件的 resizable() 方法傳入 (0,0) 或 (False, False) 可限制視窗不可調整大小 :
root.resizable(0,0) # 設定視窗不可變更大小
root.resizable(False, False) # 設定視窗不可變更大小3. 建立元件 :
原始版 tkinter 使用模組簡名 tk 呼叫方法, 主題版則用子模組名稱 ttk.
(1). 標籤元件 :
更改標籤內容 :
label.configure("你是在說哈囉嗎?")
(2). 按鈕元件 :
按鈕可用 command 參數綁定一個事件處理函式名稱 (不用括號), 此處為 clickOK, 須在呼叫之前定義. 另外一個方法是呼叫元件的 bind() 方法來綁定指定的事件, 例如按滑鼠右鍵或按下鍵盤等, bind() 的介面如下 :
bind(event, handler)
第一參數 event 是一個代表特定事件的字串, 例如最常用的是 "<Button-1>" 表示按下滑鼠左鍵事件, 而 "<Double-Button-1>" 則表示連續按滑鼠左鍵兩次 (滑鼠左, 中, 右鍵分別以數字 1, 2, 3 表示), 例如 :
button1.bind("<Button-1>", clickOK)
第二參數 handler 為事件處理函式名稱 (不用括號), 但與使用 command 參數綁定不同的是, 定義此 handler 函式時須傳入事件變數 (否則會出現錯誤), 例如 :
def clickOK(e):
pass
利用此事件物件可取得此事件之相關資訊, 例如 e.x 與 e.y 可取得滑鼠左鍵之座標.
關於更多 Tkinter 事件處理細節可參考 "Introduction to Programming Using Python (Python 程式設計入門指南)" 這本書的 9-11 節.
simpledialog 提供了三個方法分別用來讀取字串, 整數, 以及浮點數三種型態之輸入 :
(3). 元件排版 :
label.grid(row=0, column=0)
tkinter 元件提供 pack(流水式), grid(網格式), 以及 place(絕對與相對位置) 三種排版方法, 其中 pack 與 grid 兩種方式互斥, 一個視窗容器中不可同時使用 pack 與 grid, 但 place 排版中則可以同時用 pack 與 grid.
流水式排版 : 呼叫元件的 pack() 方法可將元件如流水般順序放入視窗容器, 預設是由下而上堆疊, 但可以利用可選參數 side (tk.TOP/tk.BOTTOM/tk.LEFT, tk.RIGHT/tk.CENTER) 與 anchor (tk.E/tk.W/tk.S/tk.N/tk.NW/tk.SW/tk.NE/tk.SE/tk.CENTER) 指定元件的放置方位與錨定位置, 預設是 tk.TOP 與 tk.CENTER, 故元件預設會由上而下置中放置 :
label.pack()
button.pack()
button.pack(side=tk.LEFT, anchor=tk.S)
網格式排版 : 呼叫元件的 grid() 方法
button.grid() # 多列單行
button.grid(row=0, column=1)
messagebox.functionName(title, message [, detail])
from tkinter import messagebox as mb
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox as msgbox
def hello():
msgbox.showinfo("Info", "Hello World!")
root=tk.Tk()
root.title("Hello")
button.grid(row=0, column=1)
4. 對話框 :
tkinter 透過三個子模組提供完整的對話框功能 :
tkinter 對話框類別 | 說明 |
messagebox | 提供訊息輸出與確定, 取消, 是, 否等按鈕回應功能 (輸出) |
simpledialog | 提供輸入框與確定, 取消等按鈕回應功能 (輸入) |
filedialog | 提供檔案開啟與儲存對話框功能 |
其中訊息盒 messagebox 透過方法呼叫提供 8 種訊息盒類型, 語法如下 :
參數 message 是主訊息, 而可選的 detail 則是說明訊息.
messagebox 方法 | 說明 | 按鈕 | 圖示 | 回傳值 |
showinfo(title, massage [, detail]) | 通知對話框 | 確定 | i | "ok" |
showwarning(title, massage [, detail]) | 警告對話框 | 確定 | ! | "ok" |
showerror(title, massage [, detail]) | 錯誤對話框 | 確定 | x | "ok" |
askyesno(title, massage [, detail]) | 詢問對話框 | 是/否 | ? | True/False |
askokcancel(title, massage [, detail]) | 詢問對話框 | 是/取消 | ? | True/None |
askyesnocancel(title, massage[, detail]) | 詢問對話框 | 是/否/取消 | ? | True/False/None |
askquestion(title, massage[, detail]) | 詢問對話框 | 是/否 | ? | "yes"/"no" |
askretrycancel(title, massage[, detail]) | 詢問對話框 | 重試/取消 | ? | True/False |
使用 messagebox 要先從 tkinter 匯入此子模組並取個方便的簡名例如 mb :
例如 :
from tkinter import ttk
from tkinter import messagebox as msgbox
def hello():
msgbox.showinfo("Info", "Hello World!")
root=tk.Tk()
root.title("Hello")
root.geometry("300x200")
button=ttk.Button(root, text="Hello", command=hello)
button.pack()
root.mainloop()
from tkinter import simpledialog as sd
button=ttk.Button(root, text="Hello", command=hello)
button.pack()
root.mainloop()
子模組 simpledialog 就是輸入盒的功能, 可讓使用者輸入資料供程式後續運算, 使用前須從 tkinter 匯入此子模組並取個簡名例如 sd, 語法如下 :
simpledialog 的方法 | 說明 | 回傳值 |
askstring(title, message) | 顯示字串輸入對話框 | 字串 |
askinteger(title, message) | 顯示整數輸入對話框 | 整數 |
askfloat(title, message) | 顯示浮點數輸入對話框 | 浮點數 |
這三個方法有 title 與 message 這兩個必要參數, 其實對於數值輸入的 askinteger() 與 askfloat() 還有如下三個可選參數 :
- minvalue : 設定最小值
- maxvalue : 設定最大值
- initialvalue : 設定初始值 (預設值)
若輸入值超出 minvalue~maxvalue 的範圍會出現提示對話框.
5. tkinter 元件的共同屬性與方法 :
每一個 tkinter 元件都是物件, 它們有各自專有之屬性與方法, 也有如下之共同屬性與方法:
tkinter 元件共同屬性 | 說明 |
width | 元件寬度, px 或字元數 (Label) |
height | 元件高度, px 或字元數 (Label) |
bg/background | 背景色, 可用標準顏色名稱字串例如 'blue' 或 "#0000ff" |
fg/foreground | 前景色, 可用標準顏色名稱字串例如 'blue' 或 "#0000ff" |
anchor | 元件的錨點位置, 可用 "n", "e", "s", "w", "nw", "ne","sw","se","center" |
font | 字型, 例如 "Helvetic 20 bold italic" 或 ("Helvetic", 20, "bold", "italic") |
bitmaps | 內建的圖形, 例如 "error","info", "question", "warning", "gray12" 等 |
relief | 元件的邊框, 例如 "groov", "flat", "raised", "ridge", "solid", "sunken" |
cursors | 滑鼠在元件上時的游標型式, 例如 : "heart", "cross", "star" 等 |
tkinter 元件共同方法 | 說明 |
config() | 即時更改元件屬性值 |
keys() | 以串列傳回元件的所有屬性值 |
pack() | 將元件以流水方式放入父容器中 |
grid() | 將元件以網格方式放入父容器中 |
place() | 將元件以絕對或相對定位方式放入父容器中 |
6. tkinter 的物件導向寫法 :
為了程式碼可重用性 (例如協同開發大型程式時), tkinter 程式也可以採用物件導向寫法, 上例之物件導向寫法為 :
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox as msgbox
class Hello():
def __init__(self):
root=tk.Tk()
root.title("Hello")
root.geometry("300x200")
button=ttk.Button(root, text="Hello", command=self.hello)
button.pack()
root.mainloop()
def hello(self):
msgbox.showinfo("Info", "Hello World!")
Hello()
或者標準一些的寫法 :
import tkinter as tk
from tkinter import ttk
from tkinter import messagebox as msgbox
class Hello(tk.Tk):
def __init__(self):
super().__init__()
self.title("tkinter GUI 測試")
self.geometry("200x100")
button=ttk.Button(self, text="Hello", command=self.hello)
button.pack()
def hello(self):
msgbox.showinfo("Info", "Hello World!")
if __name__ == "__main__":
win=Hello()
win.mainloop()
物件導向寫法要注意類別內存取屬性與方法時要用 self, 否則會出現函式未定義之 NameError.
7. 變數類別 :
Tkinter 的元件是一種 Python 物件, 其內容存取並非直接使用指定敘述, 而是透過 tkinter 所提供的四種變數類別 (對應 GUI 使用者介面常用的四種資料型別) :
tkinter 變數類別 | 說明 |
IntVar | 整數變數, 預設值 0 |
DoubleVar | 浮點數變數, 預設值 0.0 |
BooleanVar | 布林值變數, 預設 0 (False) |
StringVar | 字串變數, 預設空字串 "" |
呼叫這些類別的建構子即可建立變數物件, 例如 :
x=IntVar()
x=DoubleVar()
x=BooleanVar()
x=StringVar()
然後將這些變數指定給 tkinter 元件的 textvariable 或 variable 參數, 即可將此變數綁定到元件上, 只要呼叫變數物件的 get() 即可取得元件之內容 (例如取得 Entry 內使用者輸入之值), 呼叫變數物件的 set() 則可設定元件之內容 (例如設定 Entry 內容的預設值), 例如 :
import tkinter as tk
from tkinter import ttk
win=tk.Tk()
win.title("tkinter GUI 測試")
win.geometry("200x150")
i=tk.IntVar()
d=tk.DoubleVar()
b=tk.BooleanVar()
s=tk.StringVar()
tk.Label(win, textvariable=i, bg="ivory", width=5).pack()
tk.Label(win, textvariable=d, bg="cyan", width=5).pack()
tk.Label(win, textvariable=b, bg="pink", width=5).pack()
tk.Label(win, textvariable=s, bg="gold", width=5).pack()
win.mainloop()
當這些變數被更改或元件內容被更改時, 對方的值也會動態地被更改, 因為變數物件與 tkinter 元件已經透過 textvariable 參數互相綁定在一起了.
複習完前三篇的主要內容就可以開始進行下一步的測試學習了.
參考 :
沒有留言 :
張貼留言