2021年8月19日 星期四

Python 內建 GUI 模組 tkinter 測試 (六) : Button 元件

忙完樹莓派架設 WordPress 網站的任務後, 終於可以回到 Tkinter 的學習了. 本篇記錄 Button 元件之測試, 本系列之前的文章參考 :




按鈕元件是 GUI 程式中最常用到的元件, 主要用來觸發一個事件以執行, 中斷, 或終止一個程序. 建立按鈕元件之語法如下 : 

Button(父容器, **參數列)


按鈕元件常用參數如下表 (部分參數 ttk 不支援) : 


 Button 元件常用屬性 說明
 command 按下按鈕時要執行的函式名稱 (不可加括號)
 text 按鈕上的文字, 多行文字可用 "\n" 跳行
 width 寬度 (單位=字元數)
 height 高度 (單位=字元數)
 bg/background 背景色, 可用標準顏色名稱字串例如 'blue' 或 "#0000ff"
 fg/foreground 前景色 (文字顏色), 可用標準顏色名稱字串例如 'blue' 或 "#0000ff"
 activebackground 按下按鈕時的背景色, 例如 'blue' 或 "#0000ff"
 activeforeground 按下按鈕時的前景色, 例如 'blue' 或 "#0000ff"
 highlightbackground 按鈕取得焦點時的背景色
 highlightcolor 按鈕取得焦點時的前景色
 font 設定字型與尺寸 (px), 粗體 (bold) 或斜體 (italic), 底線或刪除線等 
 padx 元件與容器的水平間距 (px)
 pady 元件與容器的垂直間距 (px)
 textvariable 綁定按鈕文字內容的類別變數物件之名稱
 bitmap 設定預設之 Bitmap 圖示作為標籤內容
 relief 外框型式, sunken  flat (預設), groove, raised, ridge, solid
 bd/borderwith 邊框寬度 (預設 1px)
 underline 設定第幾個字元加上底線 (0 起始, 預設 -1 都不加底線)
 justify 設定按鈕文字的對齊方式, LEFT/CENTER (預設)/RIGHT
 anchor 設定錨定位置="e", "w", "s", "n", "es","en", "ws", "wn","center" (預設)
 compound 設定疊圖方式, TEXT/IMAGE/CENTER/TOP/BOTTOM/LEFT/RIGHT
 image 設定按鈕上的圖片 (PhotoImage 圖片物件名稱)
 wraplength 設定字串超過多少寬度 (單位 px) 換行
 state 設定按鈕狀態=tk.NORMAL (預設可用)/tk.DISABLED (不可用)


這些參數可以在建立 Button 物件時傳入, 也可以在建立物件後呼叫其 config() 方法設定. Button 元件最重要的參數中有兩個 : text (用來顯示按鈕的作用) 與 command (用來綁定按下按鈕後要執行的操作函式). 注意, 以上參數有些 ttk 不支援, 例如 background. 

綁定按鈕操作函式的方式有兩種, 其一是使用 def 定義函式一個函式, 並將其名稱賦值給 command 參數 (只要名稱即可, 不可加括弧, 雖然不會出現錯誤訊息, 但不會動作). 其次是使用 lambda 匿名函式, 這只適用於操作敘述只有一列的情況, 例如 : 


測試 1 : 綁定操作函式的兩種做法 [看原始碼]

import tkinter as tk

def click_count():
    var.set(var.get() + 1)                             # 取得 var 變數現值增量 1 後回存

win=tk.Tk()
win.geometry("200x150") 
var=tk.IntVar()                                          # 定義整數類別變數
var.set(0)                                                   # 設定初始值
label=tk.Label(win, textvariable=var)     # 將整數類別變數綁定到標籤內容
label.pack()
button_1=tk.Button(text="增量", command=click_count)    # 綁定指名函式 
button_1.pack()
button_2=tk.Button(text="歸零", command=lambda: var.set(0))    # 綁定匿名函式
button_2.pack()
win.mainloop()

此例在視窗中放置了一個標籤元件以及兩個按鈕, 標籤用來顯示按鈕次數, 增量按鈕用來使整數類別變數 var 由 0 開始增量, 用 command 參數綁定指名函式 click_count(); 而歸零按鈕則用來將此變數值歸零, 綁定 lambda 匿名操作函式.

類別變數可用 set() 與 get() 方法存取其值, 在 click_count() 函式中先呼叫 var.get() 取得目前之計數值, 增量後傳給 var.set() 存回去, 標籤元件由於是透過 textvariable 參數與 var 類別變數綁定在一起, 因此每次按增量鈕使 var 變數增量後, 標籤的內容也會同步改變, 結果如下 :





注意, Python 的指名函式需於呼叫前定義, 此例若將 click_count() 放在程式最後面將出現函式未定義錯誤. 其次, lambda  匿名函式只能允許一個敘述 (用分號隔開的一列算兩個敘述), 對於簡單的操作使用 lambda 可使程式更簡潔, 例如用來關閉視窗的指令 win.destroy() 就很適合 :


測試 2 : 按鈕綁定 lambda 函式的範例-關閉視窗 [看原始碼]

import tkinter as tk

def click_count():
    var.set(var.get() + 1)

win=tk.Tk()
win.geometry("300x150") 
var=tk.IntVar()
var.set(0)
label=tk.Label(win, textvariable=var)
label.pack()
button_1=tk.Button(text="增量", command=click_count)
button_1.pack()
button_2=tk.Button(text="關閉", command=lambda: win.destroy())   # 關閉視窗
button_2.pack()
win.mainloop()

結果如下 : 




此例只是將按鈕文字改成關閉, 然後將 command 綁定到呼叫 win.destroy() 的 lambda 函式而已. 

如果要傳參數倒 command 參數所綁定的處理函式也是要借助 lambda 來達成, 例如 : 


測試 2 : 按鈕綁定 lambda 函式的範例-傳遞參數 [看原始碼]

import tkinter as tk

def click_count():
    var.set(var.get() + 1)
    
def set_init(val):
    var.set(val)    

win=tk.Tk()
win.geometry("300x170") 
var=tk.IntVar()
var.set(0)
label=tk.Label(win, textvariable=var)
label.pack()
button_1=tk.Button(text="增量", command=click_count)
button_1.pack()
button_2=tk.Button(text="關閉", command=lambda: win.destroy())   # 關閉視窗
button_2.pack()
button_3=tk.Button(text="設定", command=lambda: set_init(100))
button_3.pack()
win.mainloop()

此例按鈕 3 的 command 參數綁定了一個有傳入參數的 lambda 函式, 呼叫 set_init() 函式, 按 "設定" 鈕會透過 lambda 呼叫 set_init() 函式並傳入初始值參數, 此處為常數, 也可以是變數. 結果如下 : 




Button 元件與 Label 一樣也可以利用 textvariable 參數綁定類別變數來控制按鈕文字的存取 (不限於綁定 StringVar 類別, 也可以是 IntVar, BoolVar, 或 DoubleVar 類別, 例如 : 


測試 3 : 利用 textvariable 參數綁定類別變數以存取按鈕文字 [看原始碼]

import tkinter as tk

def click_count():
    global count            # 存取全域變數
    count += 1               # 增量
    label_var.set("您按了 " + str(count) + " 次")
    if (button_var.get()=="增量"):       # 透過類別變數變換按鈕文字
        button_var.set("還要增量")
    else:
        button_var.set("增量")

count=0          # 計數器
win=tk.Tk()
win.geometry("300x150") 
label_var=tk.StringVar()
label_var.set("您按了 0 次")    # 設定類別變數之初始值
label=tk.Label(win, textvariable=label_var)     # 標籤綁定類別變數
label.pack()
button_var=tk.StringVar()
button_var.set("增量")             # 設定類別變數之初始值
button=tk.Button(win, textvariable=button_var, command=click_count)
button.pack()
win.mainloop()

此例中的標籤與按鈕都用 textvariable 參數綁定了一個字串類別變數, 用來控制標籤與按鈕上的文字. 然後利用 global 關鍵字在 click_count() 內將全域變數增量, 透過辨別目前與按鈕綁定之類別變數之值來切換按鈕文字內容, 結果如下 :





以下範例測試 Button 元件的參數設定. Tkinter 元件的參數設定方式有三種 : 
  • 建立物件時以關鍵字參數傳入 
  • 建立物件後以 [] 運算子設定物件參數
  • 建立物件後呼叫 config() 或 configure() 方法傳入關鍵字參數設定
注意, Tkinter 物件的 config() 是 configure() 方法的別名, 兩者功能相同.例如 :


測試 4 : tk 設定元件參數的三種方法 [看原始碼]

import tkinter as tk

win=tk.Tk()                                                      
win.title("tkinter GUI 測試")                           
win.geometry("300x150")
button1=tk.Button(win, text="12345", width=10, bg="cyan")   # 建立物件同時設定參數
button1.pack()
button2=tk.Button(win)                  # 建立按鈕物件
button2["text"]="abcde"                 # 設定參數
button2["background"]="ivory"
button2["padx"]=10
button2["pady"]=10
button2["relief"]=tk.SOLID   
button2.pack()
button3=tk.Button(win)                                                 # 建立按鈕物件
button3.config(width=6, fg="blue", relief="ridge")      # 呼叫 config() 設定參數
button3.configure(text="你在說哈囉嗎")                    # 呼叫 configure() 設定參數
button3.pack()
win.mainloop()

此例在視窗中放置了三個按鈕, 分別採用三種方法來設定按鈕元件之參數, button1 於建立物件時傳入關鍵字設定參數; button2 於建立後以 [] 運算子設定. 注意, 例如 relief 之類有固定值的參數, 可以用字串 (例如 "ridge") 或 TK 的常數 (例如 tk.RIDGE) 表示, 結果如下 : 




可見 width 與 height 的單位是 ASCII 字元數不是 px, 因此 button1 寬度設為 10 個字元比其內容 '12345' 大一倍, button3 寬度設為 6 個字元, 雖然其內容 "你在說哈囉嗎" 也是 6 個字元, 但中文一個字元比 ASCII 字元大, 因此無法完全顯示字串內容. 

這三種設定參數的方式同樣適用於 ttk 子套件, 但要注意的是 tk 與 ttk 的部分參數名稱有差異, 例如 tk 按鈕的內文間隔使用 padx 與 pady 參數設定, 但 ttk 則是使用 padding (表示左, 上, 右, 下間距之元組或串列). 

另外 ttk 按鈕不能像 Label 元件那樣用 background 與 foreground 來設定背景與前景色, 因為 ttk 沒有提供這樣的參數, 我參考了下面這篇文章用 Style 去改背景, 結果都被預設的灰色蓋掉, 僅露出邊框顏色而已:


事實上, ttk 的按鈕可用的參數不多, 呼叫 ttk 按鈕物件的 keys() 方法僅列出如下 14 個參數 :

>>> print(button1.keys())  
['command', 'default', 'takefocus', 'text', 'textvariable', 'underline', 'width', 'image', 'compound', 'padding', 'state', 'cursor', 'style', 'class']

可見 ttk 的按鈕在尺寸上僅提供 width 而沒有 height, 顯然 ttk 對元件外觀的彈性較 tk 為少, 例如 : 


測試 5 : ttk 設定元件參數的三種方法 [看原始碼]

import tkinter as tk
from tkinter import ttk

win=tk.Tk()                                                      
win.title("tkinter GUI 測試")                           
win.geometry("300x150")
button1=ttk.Button(win, text="12345", width=10)
print(button1.keys())
button1.pack()
button2=ttk.Button(win)
button2["text"]="abcde"
#button2["background"]="ivory"          #出現 unknown option "-background" 錯誤
button2["padding"]=[10, 10, 10, 10]
# button2["relief"]=tk.SOLID               #出現 unknown option "-relief" 錯誤
button2.pack()
button3=ttk.Button(win)
button3.config(width=6)
#button3.config(foreground="blue")      #出現 unknown option "-foreground" 錯誤
#button3.config(relief="ridge")              #出現 unknown option "-relief" 錯誤
button3.configure(text="你在說哈囉嗎")
button3.pack()
win.mainloop()

此例中被 # 註解掉的敘述就是 ttk 按鈕沒有支援的參數, 存取這些參數會出現 unknown option 的錯誤, 結果如下 : 




可見 ttk 都是以灰色為底色. 

參考 :


沒有留言:

張貼留言