Tkinter 教程 - 佈局管理
我們在前面的幾節中介紹了 Tkinter 的幾種控制元件型別,比如標籤,按鈕,下拉條等。在介紹這些控制元件的同時,也多少的提到了如果在程式視窗中來佈局這些小控制元件,這也是你將要在這一節裡所學到的重點-Tkinter 的佈局管理方法。
Tkinter 有三種佈局方法,pack,grid 以及 place 方法。我們會對它們一一進行介紹。
Tkinter pack 佈局方法
pack 按照字面理解,就是打包的意思,它將剛建立的控制元件通過打包的方法來放置到視窗中。我們在 Tkinter 標籤的學習章節中,第一次使用這種佈局方法,並且列出了 pack 的所有選項。
我們通過幾個例子來看我們如何通過 pack 以及它的選項來佈局 Tkinter 的控制元件。
Tkinter pack 佈局-相對位置
import tkinter as tk
app = tk.Tk()
app.geometry("300x200")
buttonW = tk.Button(app, text="West", width=15)
buttonW.pack(side="left")
buttonE = tk.Button(app, text="East", width=15)
buttonE.pack(side="right")
app.mainloop()
程式執行後,你得到的是這樣的一個視窗介面。

可以看到,按鈕 buttonWest 緊貼著視窗左側,按鈕 buttonEast 緊貼著視窗右側。你可以試著縮放下視窗的大小,你會發現,它們仍然會緊貼在視窗兩側,相對位置不會改變。
buttonW.pack(side="left")
side 有四個選項,top,bottom,left 和 right。它會將控制元件放在視窗的 side 側。就像所舉的例子中,buttonW 放在視窗的左側,因為 side='left',而 buttonE 放在視窗的右側,因為其 side=right。
現在問題來了,如果兩個控制元件,它們具有相同的 side 屬性,那它們又是怎麼佈局的呢?
試著自己回答吧,或者執行下以下的程式碼來實際操作下。
import tkinter as tk
app = tk.Tk()
app.geometry("300x200")
buttonW = tk.Button(app, text="West", width=15)
buttonW.pack(side="left")
buttonE1 = tk.Button(app, text="East 1", width=15)
buttonE1.pack(side="right")
buttonE2 = tk.Button(app, text="East 2", width=15)
buttonE2.pack(side="right")
app.mainloop()
Tkinter pack 增加控制元件內部外部 padding
有些情況下,你需要在控制元件的內部或者外部增加一些 padding,使得控制元件之間以及控制元件文字跟控制元件邊界間不是那麼的擁擠。這時候,你需要 ipadx,ipady 以及 ipadx,ipadx 選項的配置。
import tkinter as tk
app = tk.Tk()
app.geometry("300x200")
buttonW = tk.Button(app, text="West")
buttonW.pack(side="left", ipadx=20, padx=30)
buttonE = tk.Button(app, text="East")
buttonE.pack(side="right", ipadx=20, padx=30)
app.mainloop()

兩個按鈕都增加了內部 20 個單位和外部 30 個單位的 x 方向上的 padding,這裡 ipad 和 pad 的單位是畫素,而不是一個字元的寬度。
Tkinter pack 佈局在 x,y 方向上的填充
下面的程式碼實現的功能就是控制元件的尺寸能夠自動的填充至同視窗等寬或者等高,而且當你縮放視窗的時候,控制元件的尺寸也能夠自動隨著視窗的大小而變化。
import tkinter as tk
app = tk.Tk()
app.geometry("300x200")
buttonX = tk.Button(app, text="Fill X", bg="red", height=5)
buttonX.pack(fill="x")
buttonY = tk.Button(app, text="Fill Y", bg="green", width=10)
buttonY.pack(side="left", fill="y")
app.mainloop()

butonX.pack(fill='x') 意味著控制元件 buttonX 的寬度會填充滿整個視窗的寬度,同樣的,如果 fill='y'就將控制元件的高度延伸至填滿整個視窗的高度,fill='both'就是在 X 和 Y 方向的結合,會自動在寬度和高度方向上填滿整個視窗。
Tkinter pack 佈局 expand 選項-自動展開控制元件
上面 fill= 選項是如果將視窗拖放時候,自動將控制元件在 x 和 y 方向上填充。跟它類似的一個需求就是假如一個控制元件有很多的內容,比如說一個列表項,如果把它的內容自動全部展示出來呢?
import tkinter as tk
import calendar
app = tk.Tk()
buttonX = tk.Button(app, text="Label ", bg="blue", height=5)
buttonX.pack(fill="x")
listboxA = tk.Listbox(app, width=10)
listboxA.pack(fill="both", expand=1)
for i in range(1, 13):
listboxA.insert(tk.END, calendar.month_name[i])
app.mainloop()

當 expand=True or 1 時,列表框就會鋪開列表中所有的元素,比如例子中,顯示了從 January 到 December。
假如 expand 選項沒有選中的話,那麼列表框就預設的只顯示了前十個元素,後續的元素需要你選中列表框後通過滑鼠或者方向鍵移動才能夠顯示出來。
listboxA.pack(fill="both", expand=0)
通過 expand=0 禁止了列表框自動的鋪開所有元素。

Tkinter grid 佈局方法
Tkinter grid 是另外一種,也是最重要的一種視窗內控制元件佈局的方法,假如要在三種佈局方法裡面只學習一種的話,那非 grid 方法莫屬。
grid 經常用在用在對話方塊中,你可以按照網格的座標位置來安放控制元件,這樣能夠得到穩定的控制元件相對位置。
同以上的舉例方法不一樣,我們下面的例子會建立一個相對複雜的介面,儘可能的能夠用到所有的 grid 選項,然後在後續的說明中再詳細的進行解釋。
import tkinter as tk
app = tk.Tk()
labelWidth = tk.Label(app, text="Width Ratio")
labelWidth.grid(column=0, row=0, ipadx=5, pady=5, sticky=tk.W + tk.N)
labelHeight = tk.Label(app, text="Height Ratio")
labelHeight.grid(column=0, row=1, ipadx=5, pady=5, sticky=tk.W + tk.S)
entryWidth = tk.Entry(app, width=20)
entryHeight = tk.Entry(app, width=20)
entryWidth.grid(column=1, row=0, padx=10, pady=5, sticky=tk.N)
entryHeight.grid(column=1, row=1, padx=10, pady=5, sticky=tk.S)
resultButton = tk.Button(app, text="Get Result")
resultButton.grid(column=0, row=2, pady=10, sticky=tk.W)
logo = tk.PhotoImage(file="python.gif")
labelLogo = tk.Label(app, image=logo)
labelLogo.grid(
row=0,
column=2,
columnspan=2,
rowspan=2,
sticky=tk.W + tk.E + tk.N + tk.S,
padx=5,
pady=5,
)
app.mainloop()

Tkinter grid column 和 row 選項
labelWidth.grid(column=0, row=0, ipadx=5, pady=5, sticky=tk.W + tk.N)
在網格佈局中,每一個控制元件都要放在固定的單元格中,每個單元格的座標由行列也就是 column 和 row 來確定。
labelWidth 控制元件就放在座標為 (0, 0) 的單元格中,座標是以視窗的左上角為座標系的原點。
ipadx,ipady,padx 以及 pady 同前面介紹的 pack 佈局方法中的同名選項意義是一樣的,是控制元件在內部和外部的填充間距。
Tkinter grid sticky 選項
sticky 的作用是當所產生的單元格大小比控制元件本身要大的時候,如何進行擴充套件。
sticky 選項 |
意義 |
|---|---|
W |
左對齊 |
E |
右對齊 |
N |
上對齊 |
S |
下對齊 |
sticky 的預設選項是居中,也就是 W+E+N+S
Tkinter columnspan 和 rowspan 選項
labelLogo.grid(
row=0,
column=2,
columnspan=2,
rowspan=2,
sticky=tk.W + tk.E + tk.N + tk.S,
padx=5,
pady=5,
)
在 logo 控制元件中,單元格的座標是 (column=2, row=0),又因為 logo 的尺寸比較大,我們讓它佔據 2x2 個單元格的大小。columnspan=2 和 rowspan=2 就是表示控制元件以所設定的座標為起點,在 X 和 Y 方向上都有兩個單元格大小的跨度。
Tkinter place 方法
Tkinter place 是將控制元件安放在視窗中的固定或者相對的位置上。我們還是同樣的採用剛才類似的思路,先列出例子和圖形介面來,然後再來解釋具體的選項。
import tkinter as tk
app = tk.Tk()
app.geometry("300x300")
labelA = tk.Label(app, text="Label (0, 0)", fg="blue", bg="#FF0")
labelB = tk.Label(app, text="Label (20, 20)", fg="green", bg="#300")
labelC = tk.Label(app, text="Label (40, 50)", fg="black", bg="#f03")
labelD = tk.Label(app, text="Label (0.5, 0.5)", fg="orange", bg="#0ff")
labelA.place(x=0, y=0)
labelB.place(x=20, y=20)
labelC.place(x=40, y=50)
labelD.place(relx=0.5, rely=0.5)
app.mainloop()

Tkinter place 絕對位置
labelA.place(x=0, y=0)
labelB.place(x=20, y=20)
place 中 x= 和 y= 選項確定了控制元件的絕對位置,它們的單位是畫素。比如 lableB.place(x=20, y=20) 的意思就是將控制元件放置在座標為 (20, 20) 的位置上。
Tkinter place 相對位置
絕對位置的弊端即使假如當視窗縮放的時候,假如視窗中還有其他的控制元件是用相對位置來佈局的,那麼使用絕對位置佈局的控制元件和其他的控制元件可能就會有重疊,這是你不希望看到的。
在 place 方法中,也有相對位置的方法,這就是
labelD.place(relx=0.5, rely=0.5)
其中,relx 和 rely 的數值是 0.0~1.0。它們代表了控制元件位置和視窗大小的相對大小關係。
比如 relx=0.5, rely=0.5 的意思即使控制元件位置就在視窗水平尺寸的一半,也在視窗垂直尺寸的一半。
relx=1.0 的位置是視窗的右邊框,rely=1.0 的位置是視窗的下邊框。
