2021年7月23日 星期五

Python 內建 GUI 模組 tkinter 測試 (五) : Label 元件

經過複習暖身後, 準備打鐵趁熱繼續 tkinter 的學習. 本篇測試 GUI 視窗應用程式中最常見的 Label 元件 (widget).

本系列之前的文章參考 :

Python 內建 GUI 模組 tkinter 測試 (一) : 建立視窗
Python 內建 GUI 模組 tkinter 測試 (二) : 對話框
Python 內建 GUI 模組 tkinter 測試 (三) : 版面管理員


1. Lebel (標籤) 元件 : 

Label 物件用來建立文字或圖片標籤, 主要功能是作為引導說明之用, 例如表單欄位名稱標示, 或顯示狀態結果等, 只能看而無法互動. 

其語法如下 :

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

參考 :


Label 元件常用參數如下表 : 


 Label 元件常用屬性 說明
 text 標籤文字內容, 多行文字可用 "\n" 跳行
 width 寬度 (單位=字元數)
 height 高度 (單位=字元數)
 bg/background 背景色, 可用標準顏色名稱字串例如 'blue' 或 "#0000ff"
 fg/foreground 前景色 (文字顏色), 可用標準顏色名稱字串例如 'blue' 或 "#0000ff"
 font 字型與尺寸 (px), 粗體 (bold) 或斜體 (italic), 底線或刪除線等 
 padx 元件與容器的水平間距 (px)
 pady 元件與容器的垂直間距 (px)
 textvariable 綁定標籤文字內容的 StringVar 物件之變數名稱
 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) 換行


這些參數可以在建立 Label 物件時傳入, 也可以在建立物件後呼叫其 config() 方法設定. 其中最重要的是 text 參數, 用來設定標籤文字. 其次是 image 參數, 用來設定圖片標籤, 其值為一個 PhotoImage 物件, 可呼叫 tkinter 的 PhotoImage() 函數並傳入圖檔名稱 file 來建立此物件 :

image=tk.PhotoImage(file="myphoto.png")   

tkinter 僅支持四種圖片格式 :
  • gif
  • pgm
  • ppm
  • png (8.6 板以上)
如果要使用 jpg 或 bmp, 則需要用例如 Pillow 等套件進行轉換. 

除了可用 image 參數建立圖片標籤外, 也可以用 bitmap 參數設定位元圖示, 以下是在各作業系統都可用的通用 bitmap 名稱 (字串) :


 bitmap 位元圖示名稱 說明
 "error" 錯誤
 "hourglass" 沙漏
 "info" 訊息 (i)
 "questhead" 問號與人頭
 "question" 問號
 "warning" 驚嘆號
 "gray12" 淡灰階
 "gray25" 淺灰階
 "gray50" 次暗灰階
 "gray75" 暗灰階


注意, ttk 不支援 bitmap 參數, 只有 tk 可用 bitmap, 其次, bitmap 與 image 這兩個參數不能同時設定. 

Label 元件的常用方法如下表 : 


 Label 元件常用方法 說明
 config(**options) 設定物件屬性 options
 keys() 傳回 Label 物件的屬性串列 (元件共通方法)


其中 keys() 是所有 tkinter 元件都有的共通方法, 它會傳回物件的屬性串列, 如下例所示 : 


測試 1 : 比較 tk 與 ttk 的 Label 物件屬性 [看原始碼]

import tkinter as tk
from tkinter import ttk

win=tk.Tk()    
tk_label=tk.Label(win, text="Hello World!", bg="ivory")    # 建立 tk 的標籤文字物件
tk_label.pack()
ttk_label=ttk.Label(win, text="你是在說哈囉嗎?", background="aqua")   # 建立 ttk 的Label 物件
ttk_label.pack()
print(tk_label.keys())          # 呼叫 keys() 傳回 tk 的 Label 物件屬性串列
print(ttk_label.keys())         # 呼叫 keys() 傳回 ttk 的 Label 物件屬性串列
win.mainloop()

此例分別建立一個 tk 與 ttk 的 Label 元件, 並呼叫其 keys() 方法來檢視屬性串列, 結果如下 :




>>> %Run Label_1.py
['activebackground', 'activeforeground', 'anchor', 'background', 'bd', 'bg', 'bitmap', 'borderwidth', 'compound', 'cursor', 'disabledforeground', 'fg', 'font', 'foreground', 'height', 'highlightbackground', 'highlightcolor', 'highlightthickness', 'image', 'justify', 'padx', 'pady', 'relief', 'state', 'takefocus', 'text', 'textvariable', 'underline', 'width', 'wraplength']
['background', 'foreground', 'font', 'borderwidth', 'relief', 'anchor', 'justify', 'wraplength', 'takefocus', 'text', 'textvariable', 'underline', 'width', 'image', 'compound', 'padding', 'state', 'cursor', 'style', 'class']

上方是 tk 的 Label 物件屬性, 下方則是 ttk 的 Label 物件屬性, 可見 ttk 可用的屬性參數比 tk 少很多, 例如它沒有 bg, padx, pady 等參數 (ttk 的背景色要用 background, 不能用 bg). 由於 Label 元件外觀在 tk 與 ttk 沒有明顯差別, 因此對於 Label 元件而言, 其實只要使用 tkinter 版的即可. 

其次, 標籤文字外圍預設沒有框邊 (因為 relief 參數預設為 "flat"), 如果要讓 Label 有框邊, 則在建立物件時要傳入 relief 參數, 如下面範例所示 : 


測試 2 : 建立標籤文字並設定參數 [看原始碼]

import tkinter as tk
win=tk.Tk()                                                      
win.title("tkinter GUI 測試")                           
win.geometry("400x200")                                
label1=tk.Label(win, text="Hello World")
label1.pack()
label2=tk.Label(win, text="你是在說哈囉嗎?", underline=3)
label2.pack()
label3=tk.Label(win, text="洪車瑛(全汝斌)", padx=5, pady=5, relief="solid")
label3.pack()
label4=tk.Label(win, text="文森佐(宋仲基)", bg="aqua", fg="blue")
label4.pack()
label5=tk.Label(win, text="黑道律師文森佐", font="微軟正黑體 16 bold")
label5.config(bd=3, relief="solid")
label5.pack()
win.mainloop()

此例測試了一些 Label 元件的參數, 例如 relief (邊框樣式), underline (底線字元索引), bg (背景), fg (前景), padx (水平間距), pady (垂直間距), 以及 font (字型樣式) 等, 結果如下 :




注意 label2 元件將參數 underline 設為 3 (字元索引), 表示要將第 4 個字元 "說" 加上底線. 由上面的範例可知, Label 元件預設無邊框 (relief="flat"), 下面範例測試 6 種 relief 參數值 :


測試 3 : tk 的 relief 邊框參數測試 [看原始碼]

import tkinter as tk

win=tk.Tk()                                                      
win.title("tkinter GUI 測試")                           
win.geometry("400x200")                                
label1=tk.Label(win, text="relief='flat'", relief="flat")                # 預設無邊框
label1.pack()
label2=tk.Label(win, text="relief='sunken'", relief="sunken")    # 標籤凹下
label2.pack()
label3=tk.Label(win, text="relief='groove'", relief="groove")    # 邊框凹下
label3.pack()
label4=tk.Label(win, text="relief='raised'", relief="raised")       # 標籤凸起
label4.pack()
label5=tk.Label(win, text="relief='ridge'", relief="ridge")          # 邊框凸起
label5.pack()
label6=tk.Label(win, text="relief='solid'", relief="solid")           # 實線邊框
label6.pack()

win.mainloop()

結果如下 : 




下面範例改用 ttk 來測試 6 種邊框效果 : 


測試 4 : ttk 的 relief 邊框參數測試 [看原始碼]

import tkinter as tk
from tkinter import ttk

win=tk.Tk()                                                      
win.title("tkinter GUI 測試")                           
win.geometry("400x200")                                
label1=ttk.Label(win, text="relief='flat'", relief="flat")                # 預設無邊框
label1.pack()
label2=ttk.Label(win, text="relief='sunken'", relief="sunken")    # 標籤凹下
label2.pack()
label3=ttk.Label(win, text="relief='groove'", relief="groove")    # 邊框凹下
label3.pack()
label4=ttk.Label(win, text="relief='raised'", relief="raised")       # 標籤凸起
label4.pack()
label5=ttk.Label(win, text="relief='ridge'", relief="ridge")          # 邊框凸起
label5.pack()
label6=ttk.Label(win, text="relief='solid'", relief="solid")           # 實線邊框
label6.pack()

win.mainloop()

結果如下 : 




與上面的 tk 標籤比較, 主要差別是 ttk 的 solid 邊框較細顏色較淡, 而 tk 的邊框是較粗的黑線, 其餘的邊框效果看來差不多. 

邊框預設粗細為 1px, 可以用 borderwidth 加以設定, 注意, 在 tk 可以用 bd 或 borderwidth, 但在 ttk 只能用 borderwidth. 另外因為預設的垂直與水平間距為 1px, 這讓標籤文字與邊框太過於接近, 下面範例用 config() 方法加以設定. 注意, tk 用 padx 與 pady 設定間距 (整數); 而 ttk 則用 paading 參數, 是格式為 [left, top, right, bottom] 的串列 (元組也可以), 例如 : 
 

測試 5 : 設定邊框粗細與間距 [看原始碼]

import tkinter as tk
from tkinter import ttk

win=tk.Tk()                                                      
win.title("tkinter GUI 測試")                           
win.geometry("400x250")
# tk
tk_label1=tk.Label(win, text="relief='flat'", relief="flat")
tk_label1.config(bd=3, padx=5, pady=5)          # tk 邊框用 bd 或 borderwidth 均可
tk_label1.grid(row=0, column=0)
tk_label2=tk.Label(win, text="relief='sunken'", relief="sunken")
tk_label2.config(bd=3, padx=5, pady=5)
tk_label2.grid(row=1, column=0)
tk_label3=tk.Label(win, text="relief='groove'", relief="groove")
tk_label3.config(bd=3, padx=5, pady=5)
tk_label3.grid(row=2, column=0)
tk_label4=tk.Label(win, text="relief='raised'", relief="raised")
tk_label4.config(bd=3, padx=5, pady=5)
tk_label4.grid(row=3, column=0)
tk_label5=tk.Label(win, text="relief='ridge'", relief="ridge")
tk_label5.config(bd=3, padx=5, pady=5)
tk_label5.grid(row=4, column=0)
tk_label6=tk.Label(win, text="relief='solid'", relief="solid")
tk_label6.config(bd=3, padx=5, pady=5)
tk_label6.grid(row=5, column=0)
# ttk
ttk_label1=ttk.Label(win, text="relief='flat'", relief="flat")
ttk_label1.config(borderwidth=3, padding=[5, 5, 5, 5])    # ttk 邊框只能用 borderwidth 
ttk_label1.grid(row=0, column=1)
ttk_label2=ttk.Label(win, text="relief='sunken'", relief="sunken")
ttk_label2.config(borderwidth=3, padding=[5, 5, 5, 5])
ttk_label2.grid(row=1, column=1)
ttk_label3=ttk.Label(win, text="relief='groove'", relief="groove")
ttk_label3.config(borderwidth=3, padding=[5, 5, 5, 5])
ttk_label3.grid(row=2, column=1)
ttk_label4=ttk.Label(win, text="relief='raised'", relief="raised")
ttk_label4.config(borderwidth=3, padding=[5, 5, 5, 5])
ttk_label4.grid(row=3, column=1)
ttk_label5=ttk.Label(win, text="relief='ridge'", relief="ridge")
ttk_label5.config(borderwidth=3, padding=[5, 5, 5, 5])
ttk_label5.grid(row=4, column=1)
ttk_label6=ttk.Label(win, text="relief='solid'", relief="solid")
ttk_label6.config(borderwidth=3, padding=(5, 5, 5, 5))                 # padding 用元組亦可
ttk_label6.grid(row=5, column=1)

win.mainloop()

此例改用 grid() 排版以利比較 tk 與 ttk 的差異, 結果如下 : 




奇怪的是, ttk 的 borderwidth 參數似乎無效, 不管設定為多少都沒變, why? 不過 ttk 的 padding 有個好處是設定較靈活, 上下左右都可以不同值, tk 的 padx, pady 使左右間距一樣, 上下間距一樣. 

從以上範例可知, 標籤文字預設是置中 ("center") 對齊, 可用 justify 參數設定向左 ("left") 或向右 ("right") 對齊. 下面範例同時也測試 wraplength 參數, 它可指定標籤文字每幾個 px 強制跳行 : 


測試 6 : 用 justify 參數設定對齊方式 [看原始碼]

import tkinter as tk
from tkinter import ttk

win=tk.Tk()                                                      
win.title("tkinter GUI 測試")                           
win.geometry("400x200")
msg="人生就像騎腳踏車,想保持平衡就得往前走。"
label1=ttk.Label(win, text=msg, wraplength=200)
label1.config(relief="ridge")
label1.pack()
label2=ttk.Label(win, text=msg, wraplength=300, justify="left")
label2.config(relief="ridge")
label2.pack()
label3=ttk.Label(win, text=msg, wraplength=300, justify="right")
label3.config(relief="ridge")
label3.pack()

win.mainloop()

此例利用 wraplength 參數強制每 300px 換行顯示以測試 justify 參數之左中右對齊效果, 同時也設定邊框以利觀察, 可見 label1 預設置中對齊, label2 為向左對齊, 而 label 為向右對齊, 結果如下 :  




Label 元件除了可以利用 text 參數放置標籤文字外, 還可以利用 image 參數放置圖片標籤, 此參數需要一個 PhotoImage 物件, tkinter 提供了 PhotoImage() 函數可將 gif 或 png 等圖檔轉成 PhotoImage 物件 (目前尚不支援 jpeg 檔). 

下面範例使用的圖檔取自維基百科中的 Lenna 圖, 原圖為 300x300 的 jpeg 檔, 下載後用小畫家更改解析度為 200x200 並存成 png 檔 :


測試 7 : 用 image 參數設定圖片標籤 [看原始碼] [200x200 png Lenna 圖]

import tkinter as tk
from tkinter import ttk

win=tk.Tk()                                                      
win.title("tkinter GUI 測試")                           
win.geometry("400x300")
lenna=tk.PhotoImage(file="200x200-Lenna.png")   # 從指定知圖檔建立 PhotoImage 物件
print(type(lenna))                                                        # 輸出 <class 'tkinter.PhotoImage'>
label1=ttk.Label(win, image=lenna)                          # 用 image 參數建立圖片標籤
label1.config(relief="solid", padding=10)
label1.pack()

win.mainloop()

此例呼叫 tk 的 PhotoImage() 函數並傳入 file 參數指定圖檔路徑與名稱來建立一個 PhotoImage 物件, 然後將此 PhotoImage 物件傳入 Label() 函數的 image 參數即可. 注意, PhotoImage() 是放在 tk 底下的函數, ttk 並未重複支援, 不能呼叫 ttk.PhotoImage(). 結果如下 : 




Label 元件的標籤內容也可以同時使用圖片與文字, 這需要用 compound 參數來指定文字與圖片的相對關係 (compound="left"/"center"/"right"/"top"/"bottom", 分別表示圖片靠左, 中, 與右). 例如 : 


測試 8 : 用 image 與 compond 參數 (left/center/right) 設定圖文標籤 [看原始碼]

import tkinter as tk
from tkinter import ttk

win=tk.Tk()                                                      
win.title("tkinter GUI 測試")                           
win.geometry("500x700")
lenna=tk.PhotoImage(file="200x200-Lenna.png")
print(type(lenna))
msg="""影像處理教科書上著名的 Lenna 圖,取自於 1972 年 Playboy 雜誌上
模特兒 Lenna Söderberg 的照片。"""
label1=ttk.Label(win, text=msg, justify="left", image=lenna, compound="right")
label1.config(relief="solid", padding=10, wraplength=200)
label1.pack()
label2=ttk.Label(win, text=msg, justify="left", image=lenna, compound="left")
label2.config(relief="solid", padding=10, wraplength=200)
label2.pack()
label3=ttk.Label(win, text=msg, justify="left", image=lenna, compound="center")
label3.config(relief="solid", padding=10, wraplength=200)
label3.pack()
win.mainloop()

此例在視窗中建立了三個圖文標籤, 利用 compound 參數分別設定圖片靠右, 左, 以及在中央, 文字則利用 wraplength 強制 200px 換行顯示, 結果如下 : 




參數 compound 也可以指定圖片靠上 (="top") 或靠下 (="bottom"), 例如 : 


測試 9 : 用 image 與 compond 參數 (top/bottom) 設定圖文標籤 [看原始碼]

import tkinter as tk
from tkinter import ttk

win=tk.Tk()                                                      
win.title("tkinter GUI 測試")                           
win.geometry("500x400")
lenna=tk.PhotoImage(file="200x200-Lenna.png")
print(type(lenna))
msg="""影像處理教科書上著名的 Lenna 圖,取自於 1972 年 Playboy 雜誌上
模特兒 Lenna Söderberg 的照片。"""
label1=ttk.Label(win, text=msg, justify="left", image=lenna, compound="top")
label1.config(relief="solid", padding=10, wraplength=200)
label1.grid(row=0, column=0)
label2=ttk.Label(win, text=msg, justify="left", image=lenna, compound="bottom")
label2.config(relief="solid", padding=10, wraplength=200)
label2.grid(row=0, column=1)
win.mainloop()

結果如下 :




但是如果同時指定了 text 與 image 參數, 卻沒指定 compound 參數, 則 text 會被忽略, 僅顯示圖片, 換句話說, text 相當於無效, 如下範例所示 :


測試 10 : 指定 text 與 image 卻沒指定 compond 時 text 會被忽略 [看原始碼]

import tkinter as tk
from tkinter import ttk

win=tk.Tk()                                                      
win.title("tkinter GUI 測試")                           
win.geometry("500x700")
lenna=tk.PhotoImage(file="200x200-Lenna.png")
print(type(lenna))
msg="""影像處理教科書上著名的 Lenna 圖,取自於 1972 年 Playboy 雜誌上
模特兒 Lenna Söderberg 的照片。"""
label1=ttk.Label(win, text=msg, justify="left", image=lenna)
label1.config(relief="solid", padding=10, wraplength=200)
label1.pack()
win.mainloop()

此例同時指定了 text 與 image 參數, 卻沒有指定 compound 參數, 結果只顯示圖片 : 




除了可在標籤中放置自訂的圖片外, Tkinter 也內建了 10 個圖示可透過 bitmap 參數放置在 Label 上, 但此功能僅 tk 有, ttk 並沒有 bitmap 參數可用, 例如 : 


測試 11 : 使用內建的位元圖 bitmap 參數 (ttk 無此參數) [看原始碼]

import tkinter as tk

win=tk.Tk()                                                      
win.title("tkinter GUI 測試")                           
win.geometry("300x250")
label1=tk.Label(win, text="訊息", bitmap="info")     # 使用 bitmap 時 text 無效
label1.config(relief="groove", width=200)
label1.pack()
label2=tk.Label(win, bitmap="warning")
label2.config(relief="groove", width=200)
label2.pack()
label3=tk.Label(win, bitmap="error")
label3.config(relief="groove", width=200, height=30)
label3.pack()
label4=tk.Label(win, bitmap="question")
label4.config(relief="groove", width=200)
label4.pack()
label5=tk.Label(win, bitmap="questhead")
label5.config(relief="groove", padx=10, pady=10)      # padx 與 pady 無效
label5.pack()
label6=tk.Label(win, bitmap="gray12")
label6.config(relief="groove", width=200)
label6.pack()
label7=tk.Label(win, bitmap="gray25")
label7.config(relief="groove", width=200)
label7.pack()
label8=tk.Label(win, bitmap="gray25")
label8.config(relief="groove", width=200)
label8.pack()
label9=tk.Label(win, bitmap="gray50")
label9.config(relief="groove", width=200)
label9.pack()
label10=tk.Label(win, bitmap="gray75")
label10.config(relief="groove", width=200)
label10.pack()
win.mainloop()

結果如下 : 




可見 text 與 bimap 參數同時使用時 text 參數無效 (如 label1 所示); 同時 padx 與 pady 對 bitmap 標籤也是無效 (如 label5 所示). 

還有一個元件共通參數 anchor, 當元件的尺寸大於內容 (文字或圖片) 時, 此參數用來定位內容在在元件中的 9 個方位, 即 "n" (北), "e" (東), "s" (南), "w" (西), "nw" (西北), "ne" (東北), "sw" (西南), "se" (東南), "center" (中央) : 




注意, anchor 參數的值除了上述的方位字串外, 也可以用 tk 的常數, 例如 "sw" 可用 tk.SW, 例如 :


測試 12 : 使用內建的位元圖 bitmap 參數 (ttk 無此參數) [看原始碼]

import tkinter as tk

win=tk.Tk()                                                      
win.title("tkinter GUI 測試")                           
win.geometry("600x600")
label1=tk.Label(win, text="Hello", anchor="nw")
label1.config(relief="groove", width=15, height=7)
label1.grid(row=0, column=0)
label2=tk.Label(win, text="Hello", anchor="n")
label2.config(relief="groove", width=15, height=7)
label2.grid(row=0, column=1)
label3=tk.Label(win, text="Hello", anchor="ne")
label3.config(relief="groove", width=15, height=7)
label3.grid(row=0, column=2)
label4=tk.Label(win, text="Hello", anchor="w")
label4.config(relief="groove", width=15, height=7)
label4.grid(row=1, column=0)
label5=tk.Label(win, text="Hello", anchor="center")
label5.config(relief="groove", width=15, height=7)
label5.grid(row=1, column=1)
label6=tk.Label(win, text="Hello", anchor="e")
label6.config(relief="groove", width=15, height=7)
label6.grid(row=1, column=2)
label7=tk.Label(win, text="Hello", anchor="sw")
label7.config(relief="groove", width=15, height=7)
label7.grid(row=2, column=0)
label8=tk.Label(win, text="Hello", anchor=tk.S)
label8.config(relief="groove", width=15, height=7)
label8.grid(row=2, column=1)
label9=tk.Label(win, text="Hello", anchor=tk.SE)
label9.config(relief="groove", width=15, height=7)
label9.grid(row=2, column=2)
win.mainloop()

此例使用 grid 排版了 3x3 的九宮格, 每格放一個大小較文字內容 "Hello" 還大的 Label 元件, 並以 anchor 參數指定了相對應的方位, 結果如下 : 




可見文字內容顯示在 anchor 所指定的方位上. 

參考 :


2 則留言 :

鮮採翠玉芽 提到...

非常詳細的教學
程式碼測試和預覽圖都很貼心
受益良多

小狐狸事務所 提到...

Hi, 感謝您的讚美, 這其實都是為了避免自己忘記, 能快速恢復功力而邊學邊整理, Python 測試總目錄如下:
https://yhhuang1966.blogspot.com/2019/02/python_24.html