- pack()
- grid()
- place()
注意 : 一個視窗容器中不能同時使用 pack 與 grid 排版, 但 place 卻可以與 pack 或 grid 同時使用.
本系列之前的文章參考 :
# Python 內建 GUI 模組 tkinter 測試 (一) : 建立視窗
# Python 內建 GUI 模組 tkinter 測試 (二) : 對話框
本篇測試參考了下列幾本書 :
# Python GUI 設計活用 tkinter 之路王者歸來 (深石, 洪錦魁)
# Python 程式設計學習經典:工程分析x資料處理x專案開發 (碁峰, 黃立政等, 第 9 章)
# 科學運算 : Python 程式理論與應用 (上奇, 楊珮璐等, 第 12 章)
# Tkinter GUI Application Development Blueprints (Packt, Bhaskar Chaudhary)
# Tkinter GUI Application Development Hotshot (Packt, Bhaskar Chaudhary)
# Python GUI Programming Cookbook (Packt, Burkhard A. Meier)
# Python and Tkinter Programming (Manning, John E. Grayson)
# Python GUI programming with Tkinter (Packt, Alan D Moore)
# Tkinter GUI Programming by Example (Packt, David Love)
tkinter 的說明文件參考 :
# https://pythonspot.com/en/tkinter/
# Graphical User Interfaces with Tk
一. 使用 pack() 管理版面 :
pack() 方法的參數全部都是可有可無的, 沒有傳入參數時, 加入版面的元件以預設值排版, 常用參數如下 :
pack() 參數 | 說明 |
side | 排列方向 : TOP (預設), BOTTOM, LEFT, RIGHT |
fill | 填滿所分配空間之方向 : NONE (預設), X, Y, BOTH |
expand | 填滿容器 : True/False (預設) |
padx/pady | 元件邊框與容器之距離 (px, 預設=0) |
ipadx/ipady | 元件內容 (文字/圖像) 與其邊框之距離 (px, 預設=0) |
anchor | 元件在容器中的錨定位置 : E, W, S, N, CENTER (預設), NE, SE, SW, NW |
參考 :
# The Tkinter Pack Geometry Manager
pack() 的詳細介面可輸入下列指令查詢 :
>>> import tkinter
>>> help(tkinter.Pack)
元件用 pack() 加入版面時預設會由上而下, 由左而右排版, 例如 :
測試 1 : pack() 預設排版
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("pack() 測試")
root.geometry("200x100")
ttk.Button(root, text="南志鉉").pack()
ttk.Button(root, text="都敬秀").pack()
ttk.Button(root, text="百日的郎君").pack()
root.mainloop()
此程式在視窗版面中放了三個按鈕, 在呼叫 pack() 放入按鈕時未傳入參數, 故以預設之 TOP 由上而下排版 :
在呼叫 pack() 若傳入 BOTTOM/LEFT/RIGHT 可改變排版方式, 例如 :
測試 2 : pack() 全部指定排版
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("pack() 測試")
root.geometry("200x100")
ttk.Button(root, text="南志鉉").pack(side=tk.BOTTOM)
ttk.Button(root, text="都敬秀").pack(side=tk.BOTTOM)
ttk.Button(root, text="百日的郎君").pack(side=tk.BOTTOM)
root.mainloop()
注意, 由於 tkinter 是以別名 tk 匯入, 因此排版常數 BOTTOM/LEFT/RIGHT 須加上別名參考 tk, 否則會出現常數未定義錯誤.
由於三個按鈕全部以 BOTTOM 方式排版, 因此依加入順序由下而上排版. 下面是只有部分元件為指定排版, 其他為預設 TOP 排版範例 :
測試 3 : pack() 部分指定排版
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("pack() 測試")
root.geometry("200x100")
ttk.Button(root, text="南志鉉").pack(side=tk.BOTTOM)
ttk.Button(root, text="都敬秀").pack()
ttk.Button(root, text="百日的郎君").pack()
root.mainloop()
此例只有第一個按鈕指定 BOTTON 排版, 因此它被放在最底下; 另兩個是預設 TOP 排版, 故還是由上而下擺放.
下面範例是全部指定由左至右排版 :
測試 4 : pack() 全部指定排版
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("pack() 測試")
root.geometry("220x100")
ttk.Button(root, text="南志鉉").pack(side=tk.LEFT)
ttk.Button(root, text="都敬秀").pack(side=tk.LEFT)
ttk.Button(root, text="百日的郎君").pack(side=tk.LEFT)
root.mainloop()
注意, 此例因由全部左向右排版, 因此版面大小改為 220x100.
可見全部都用 LEFT 就會由左向右排版, 如果版面不夠寬會被截掉按鈕內容.
測試 5 : pack() 混合排版
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("pack() 測試")
root.geometry("300x100")
ttk.Button(root, text="南志鉉").pack()
ttk.Button(root, text="都敬秀").pack(side=tk.LEFT)
ttk.Button(root, text="百日的郎君").pack(side=tk.BOTTOM)
root.mainloop()
應該沒有會這樣惡搞排版吧! 不過奇怪的是, 第三個按鈕竟然沒有跟第一個 TOP 的對齊.
接著測試 anchor 參數, 此參數用來指定元件在父容器中的 9 個錨定方位 : E/W/N/W/NW/SW/NE/SE/CENTER (預設值) :
注意, 若 tkinter 以別名 tk 匯入, 則須加別名參考, 例如 tk.E 等.
測試 6 : pack() 錨定位置
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("pack() 測試")
root.geometry("300x250")
ttk.Button(root, text="東").pack(anchor=tk.E)
ttk.Button(root, text="西").pack(anchor=tk.W)
ttk.Button(root, text="南").pack(anchor=tk.S)
ttk.Button(root, text="北").pack(anchor=tk.N)
ttk.Button(root, text="中").pack(anchor=tk.CENTER)
ttk.Button(root, text="東南").pack(anchor=tk.SE)
ttk.Button(root, text="西北").pack(anchor=tk.NW)
ttk.Button(root, text="西南").pack(anchor=tk.SW)
ttk.Button(root, text="東北").pack(anchor=tk.NE)
root.mainloop()
此例在視窗版面的 9 個錨定方位放置按鈕, 由於 pack() 是流水式排版, 元件是按照先後順序擺放在錨定位置 :
參數 padx/pady 與 ipadx/ipady 都是用來指定間隙用的, padx/pady 指定元件之外部間隙; ipadx/ipady 則指定內部間隙 (元件邊框與內部文字或圖像之距離), 單位為 px.
從上面測試 1 與測試4 可知, 元件之間的間隙 padx 與 pady 預設大約是 1px, 但若取消視窗大小設定, 元件與視窗之間隙 pady 大約是 1px; 而 padx 大約是 20px 左右, 例如 :
測試 6 : 間隙距離 padx/pady
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("pack() 測試")
ttk.Button(root, text="南志鉉").pack()
ttk.Button(root, text="都敬秀").pack()
ttk.Button(root, text="百日的郎君").pack()
root.mainloop()
測試 7 : 間隙距離 padx/pady
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("pack() 測試")
#root.geometry("300x200")
ttk.Button(root, text="南志鉉").pack(padx=20, pady=10)
ttk.Button(root, text="都敬秀").pack(padx=20, pady=10)
ttk.Button(root, text="百日的郎君").pack(padx=20, pady=10)
root.mainloop()
可見與視窗之間隙, pady 如實反映設定, 而 padx 則多了約 20px 之空間. 但如果空間不夠時, 這多出來的 20px 就會消失了, 例如 :
測試 8 : 間隙距離 padx/pady
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("pack() 測試")
ttk.Button(root, text="南志鉉").pack(side=tk.LEFT, pady=10)
ttk.Button(root, text="都敬秀").pack(side=tk.LEFT, pady=10)
ttk.Button(root, text="百日的郎君").pack(side=tk.LEFT, pady=10)
root.mainloop()
參數 ipadx/ipady 用來設定元件內部間隙, 即邊框與內部文字或圖像之距離, 例如 :
測試 9 : 間隙距離 ipadx/ipady
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("pack() 測試")
root.geometry("200x100")
ttk.Button(root, text="南志鉉").pack(ipadx=50, ipady=10)
ttk.Button(root, text="都敬秀").pack(ipadx=50)
ttk.Button(root, text="百日的郎君").pack(ipadx=50)
root.mainloop()
與上面測試 1 相比, 可見三個按鈕的 x 軸內部間隙都放寬為 50px; 第一個按鈕的 y 軸間隙則放寬為 10px.
參數 fill 用來設定元件在 X/Y 軸方向填滿所分配到的空間, 其值為常數 X/Y/BOTH, 若以別名 tk 匯入, 則需用 tk.X/tk.Y/tk.BOTH, 例如 :
測試 10 : 填滿分配的空間 fill
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("pack() 測試")
root.geometry("300x150")
ttk.Button(root, text="南志鉉").pack(side=tk.LEFT, fill=tk.BOTH)
ttk.Button(root, text="都敬秀").pack(fill=tk.X)
ttk.Button(root, text="百日的郎君").pack(fill=tk.Y)
root.mainloop()
此例中第一個按鈕指定為 LEFT 對齊與填滿 XY 軸, 結果在 Y 軸方向它填滿整個視窗高度, 可見在 LEFT 對齊時元件所分配到的高度就是視窗高度. 第二與第三個按鈕為預設 TOP 對齊, 第二個按鈕為 X 軸填滿, 第三個按鈕為 Y 軸填滿, 但實際上第三個按鈕並未填滿視窗高度, 可見在 TOP 對齊時, 元件所獲得的高度並非視窗高度, 而是足夠放元件之高度. 從下面的範例可了解此特性 :
測試 11 : 填滿分配的空間 fill
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("pack() 測試")
root.geometry("300x150")
ttk.Button(root, text="南志鉉").pack(side=tk.LEFT, fill=tk.BOTH)
ttk.Button(root, text="都敬秀").pack(side=tk.LEFT, fill=tk.X)
ttk.Button(root, text="百日的郎君").pack(side=tk.LEFT, fill=tk.BOTH)
root.mainloop()
此例中三個按鈕都是 LEFT 對齊, 因此都被分配了整個視窗高度, 第一與第三個按鈕設定 X/Y 軸填滿故佔據全部視窗高度. 第二個按鈕則僅填滿被分配到的 X 軸寬度.
參數 expand 用來設定元件所分配到的版面是否要擴展到最大, 且當父容器放大或縮小時是否會隨著改變位置, 其值為 True/False (預設), 也可以是 1/0 或 YES/NO (以別名 tk 匯入 tkinter 時要用 tk.YES/tk.NO), 例如上面測試 1 中的三個按鈕會緊貼在一起, 且視窗縮放時還是黏在一起不會分開, 但若將第一個按鈕設為 expand=True 則會擴充自己的版面到最大, 例如 :
測試 12 : 擴充所分配的空間 expand
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("pack() 測試")
root.geometry("300x150")
ttk.Button(root, text="南志鉉").pack(expand=True)
ttk.Button(root, text="都敬秀").pack()
ttk.Button(root, text="百日的郎君").pack()
root.mainloop()
由於第一個按鈕的 expand 設為 True, 因此它所獲得的版面會擴充到最大, 因而將其餘兩個按鈕擠到最底下. 如果縮放視窗的話第一個按鈕的位置會隨之改變.
二. 使用 grid() 管理版面 :
grid 排版是最容易理解也最好用版面管理員, 適合用來處理較複雜的版面, 它使用類似網頁表格或二維陣列的索引方式來放置元件, 每個網格只能放置一個元件, 但多個網格可用 rowspan 與 columnspan 參數合併鄰近的多個網格來放置一個元件, 其介面如下 :
元件.grid([options])
options 為可有可無之參數, 常用參數如下表 :
grid() 參數 | 說明 |
row | 列索引 |
column | 行索引 |
rowspan | 儲存格合併列數 |
columnspan | 儲存格合併行數 |
padx/pady | 元件邊框與容器之距離 (px, 預設=1) |
ipadx/ipady | 元件內容 (文字/圖像) 與其邊框之距離 (px, 預設=1) |
sticky | 元件於網格中的錨定位置 : E, W, S, N, CENTER (預設) |
其中 row 與 column 都是 0 起始的索引. 一般來說元件呼叫 grid() 將自己放進容器時應該傳入 row 與 column 參數指定網格索引位置, 否則預設是以 1 行網格來擺放元件. 參數 padx/pady/ipadx/ipady 用法與上面 pack() 的一樣.
grid() 的詳細介面可輸入下列指令查詢 :
>>> import tkinter
>>> help(tkinter.Grid)
測試 13 : 呼叫 grid() 時不傳參數
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("grid() 測試")
root.geometry("300x150")
ttk.Button(root, text="南志鉉").grid()
ttk.Button(root, text="都敬秀").grid()
ttk.Button(root, text="百日的郎君").grid()
root.mainloop()
可見呼叫 grid() 時若不傳參數, 預設是以 n 列 1 行的網格來依序放置元件.
測試 14 : 呼叫 grid() 時傳入索引參數
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("grid() 測試")
root.geometry("300x150")
ttk.Button(root, text="南志鉉").grid(row=0, column=0)
ttk.Button(root, text="都敬秀").grid(row=0, column=1)
ttk.Button(root, text="百日的郎君").grid(row=1, column=0, columnspan=2)
root.mainloop()
此例在呼叫 grid() 時傳入了 row 與 column 參數指定擺放之索引位置, 形成 2*1 網格. 第三個按鈕傳入 columnspan 參數指定與下一行網格合併, 因此第二列只有一個網格, 預設是 CENTER 對齊, 如果要擴展按鈕所占版面, 需用 sticky 參數, 此參數與 pack() 排版之錨定參數 anchor 類似, 但只能傳入 E/W/S/N 四個常數或其相加組合. 若以 tk 別名匯入 tkinter, 則須使用 tk.E/tk.W/tk.S/tk.N, 例如 :
測試 15 : 呼叫 grid() 時傳入 sticky 參數
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("grid() 測試")
root.geometry("300x150")
ttk.Button(root, text="南志鉉").grid(row=0, column=0)
ttk.Button(root, text="都敬秀").grid(row=0, column=1)
ttk.Button(root, text="百日的郎君").grid(row=1, column=0, columnspan=2,
sticky=tk.E+tk.W)
root.mainloop()
此例第三個按鈕設定 sticky 參數為 E 與 W, 亦即元件擴展至容器東西兩端.
三. 使用 place() 管理版面 :
此排版法是指定以父容器左上角為原點之座標位置來精確放置元件, 但使用的機會較少, 因為只要某個元件的位置更改, 整個版面中的元件可能都要隨之調整, 通常是用來製作客製化的版面管理員之用. 其介面如下 :
元件.place([options])
place() 函數的常用參數如下 :
place() 參數 | 說明 |
x | 相對於視窗左上角之 x 座標 |
y | 相對於視窗左上角之 y 座標 |
width | 指定元件寬度 (px) |
height | 指定元件高度 (px) |
relx | 相對於父容器寬度之比率 x 座標 (0~1) |
rely | 相對於父容器高度之比率 y 座標 (0~1) |
relwidth | 相對於父容器寬度之比率 (0~1) |
relheight | 相對於父容器高度之比率 (0~1) |
anchor | 元件在容器中的錨定位置 : E, W, S, N, CENTER (預設), NE, SE, SW, NW |
其中 (x, y) 或 (relx, rely) 是必須傳入之定位參數, 雖然沒有傳入任何參數也不會出現錯誤訊息, 但因為缺乏定位資訊, 因此元件也不會出現在視窗上. place() 的詳細介面可輸入下列指令查詢 :
>>> import tkinter
>>> help(tkinter.Place)
place() 提供兩種定位法 :
- 絕對定位 :
以 (x, y) 參數指定絕對座標
以 (width, height) 指定絕對大小 - 相對定位 :
以 (relx, rely) 參數指定相對座標
以 (relwidth, relheight) 參數指定相對大小
測試 16 : 絕對定位 vs 相對定位
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("place() 測試")
root.geometry("250x100")
ttk.Button(root, text="絕對定位").place(x=25, y=25)
ttk.Button(root, text="相對定位").place(relx=0.5,rely=0.5)
root.mainloop()
此例相對定位按鈕傳入 (relx=0.5, rely=0.5), 表示其初始左上角座標是視窗寬度與高度的 0.5 倍, 即 250*0.5=125, 100*0.5=50, 因此程式開始執行時相對定位的按鈕左上角位於視窗中央, 而絕對定位的按鈕左上角位於 (50, 50), 縮放視窗時相對定位的按鈕會隨之重新定位, 而絕對定位之按鈕則固定不動.
上例中相對定位之元件僅用 (relx, rely) 來設定相對座標, 還可用參數 (relwidth, relheight) 來控制元件相對於視窗寬度與高度之倍率 (0.0~1.0), 如下例所示 :
測試 17 : 相對定位元件之寬度與高度設定
import tkinter as tk
from tkinter import ttk
root=tk.Tk()
root.title("place() 測試")
root.geometry("250x100")
ttk.Button(root, text="絕對定位").place(x=25, y=25)
ttk.Button(root, text="相對定位").place(relx=0.5, rely=0.5, relwidth=0.5, relheight=0.5)
root.mainloop()
參數 relx=0.5, rely=0.5 表示按鈕的寬度與高度為視窗的一半, 即使視窗縮放相對定位按鈕還是佔據視窗右下角 1/4 空間.
沒有留言:
張貼留言