Tkinter 教程 - 佈局管理

Jinku Hu 2023年1月30日 2018年2月27日
  1. Tkinter pack 佈局方法
  2. Tkinter grid 佈局方法
  3. Tkinter place 方法
Tkinter 教程 - 佈局管理

我們在前面的幾節中介紹了 Tkinter 的幾種控制元件型別,比如標籤按鈕,下拉條等。在介紹這些控制元件的同時,也多少的提到了如果在程式視窗中來佈局這些小控制元件,這也是你將要在這一節裡所學到的重點-Tkinter 的佈局管理方法。

Tkinter 有三種佈局方法,packgrid 以及 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()

程式執行後,你得到的是這樣的一個視窗介面。

Tkinter pack 佈局方法的 side 相對位置

可以看到,按鈕 buttonWest 緊貼著視窗左側,按鈕 buttonEast 緊貼著視窗右側。你可以試著縮放下視窗的大小,你會發現,它們仍然會緊貼在視窗兩側,相對位置不會改變。

buttonW.pack(side='left')

side 有四個選項,topbottomleftright。它會將控制元件放在視窗的 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,使得控制元件之間以及控制元件文字跟控制元件邊界間不是那麼的擁擠。這時候,你需要 ipadxipady 以及 ipadxipadx 選項的配置。

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()

Tkinter pack 佈局方法增加控制元件 padding

兩個按鈕都增加了內部 20 個單位和外部 30 個單位的 x 方向上的 padding,這裡 ipadpad 的單位是畫素,而不是一個字元的寬度。

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()

Tkinter pack 佈局方法在 X,Y 方向上填充

butonX.pack(fill='x') 意味著控制元件 buttonX 的寬度會填充滿整個視窗的寬度,同樣的,如果 fill='y'就將控制元件的高度延伸至填滿整個視窗的高度,fill='both'就是在 X 和 Y 方向的結合,會自動在寬度和高度方向上填滿整個視窗。

Tkinter pack 佈局 expand 選項-自動展開控制元件

上面 fill= 選項是如果將視窗拖放時候,自動將控制元件在 xy 方向上填充。跟它類似的一個需求就是假如一個控制元件有很多的內容,比如說一個列表項,如果把它的內容自動全部展示出來呢?

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()

Tkinter pack 佈局方法在 expand 選項開啟

expand=True or 1 時,列表框就會鋪開列表中所有的元素,比如例子中,顯示了從 JanuaryDecember

假如 expand 選項沒有選中的話,那麼列表框就預設的只顯示了前十個元素,後續的元素需要你選中列表框後通過滑鼠或者方向鍵移動才能夠顯示出來。

listboxA.pack(fill='both', expand=0)

通過 expand=0 禁止了列表框自動的鋪開所有元素。

Tkinter pack 佈局方法在 expand 選項關閉禁止

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 佈局方法

Tkinter grid columnrow 選項

labelWidth.grid(column=0, row=0, ipadx=5, pady=5, sticky=tk.W+tk.N)

在網格佈局中,每一個控制元件都要放在固定的單元格中,每個單元格的座標由行列也就是 columnrow 來確定。

labelWidth 控制元件就放在座標為 (0, 0) 的單元格中,座標是以視窗的左上角為座標系的原點。

ipadxipadypadx 以及 pady 同前面介紹的 pack 佈局方法中的同名選項意義是一樣的,是控制元件在內部和外部的填充間距。

Tkinter grid sticky 選項

sticky 的作用是當所產生的單元格大小比控制元件本身要大的時候,如何進行擴充套件。

sticky 選項 意義
W 左對齊
E 右對齊
N 上對齊
S 下對齊

sticky 的預設選項是居中,也就是 W+E+N+S

Tkinter columnspanrowspan 選項

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=2rowspan=2 就是表示控制元件以所設定的座標為起點,在 XY 方向上都有兩個單元格大小的跨度。

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 佈局方法

Tkinter place 絕對位置

labelA.place(x=0, y=0)
labelB.place(x=20, y=20)

placex=y= 選項確定了控制元件的絕對位置,它們的單位是畫素。比如 lableB.place(x=20, y=20) 的意思就是將控制元件放置在座標為 (20, 20) 的位置上。

Tkinter place 相對位置

絕對位置的弊端即使假如當視窗縮放的時候,假如視窗中還有其他的控制元件是用相對位置來佈局的,那麼使用絕對位置佈局的控制元件和其他的控制元件可能就會有重疊,這是你不希望看到的。

place 方法中,也有相對位置的方法,這就是

labelD.place(relx=0.5, rely=0.5)

其中,relxrely 的數值是 0.0~1.0。它們代表了控制元件位置和視窗大小的相對大小關係。

比如 relx=0.5, rely=0.5 的意思即使控制元件位置就在視窗水平尺寸的一半,也在視窗垂直尺寸的一半。

relx=1.0 的位置是視窗的右邊框,rely=1.0 的位置是視窗的下邊框。

Author: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn