繼
上一篇文章介紹了多種tkinter的視窗元件後,這篇我們再來討論三種視窗元件的配置方法以及優缺點,以適當移動各個元件到視窗中指定的位置,讓整個視窗版面的配置看起來更合理且美觀。
這三種配置方法包括:
- pack()方法
- place()方法
- grid()方法
以下就一一來說明。
pack()方法在
上一篇文章有出現過,當時是為了讓元件能在視窗上顯示出來,並沒有帶入任何參數,也就是使用預設的參數,由上往下開始排列元件。
side參數
side參數可以設定元件的排列方式,可設定的參數值如下:
TOP:由上往下排列,這是預設的排列方式
BOTTOM:由下往上排列
LEFT:由左往右排列
RIGHT:由右往左排列
例如,以下範例設定由上往下排列,和預設的排列方式相同。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.minsize(width=300, height=0)
label1 = Label(text="label 1", bg="lightyellow")
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
label1.pack(side=LEFT)
label2.pack(side=LEFT)
label3.pack(side=LEFT)
window.mainloop()
執行結果:
以下範例設定由左往右排列。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.minsize(width=300, height=0)
label1 = Label(text="label 1", bg="lightyellow")
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
label1.pack(side=LEFT)
label2.pack(side=LEFT)
label3.pack(side=LEFT)
window.mainloop()
執行結果:
padx/pady參數
padx/pady參數可設定元件和元件之間或是元件和視窗邊界之間的水平/垂直距離,單位為像素。
例如,我們在前一個範例加上水平方向10個像素的間距,注意label 1和label 2之間、以及label 2和label 3之間,因為各自增加了10個像素的間距,所以最後的間距為20,而label 1和視窗邊界的距離為10。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.minsize(width=300, height=0)
label1 = Label(text="label 1", bg="lightyellow")
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
label1.pack(side=LEFT, padx=10)
label2.pack(side=LEFT, padx=10)
label3.pack(side=LEFT, padx=10)
window.mainloop()
執行結果:
ipadx/ipady參數
ipadx/ipady參數可設定元件內文字和元件邊界的水平/垂直距離,單位為像素。
以下範例設定標籤文字和標籤邊界的水平距離為10。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.minsize(width=300, height=0)
label1 = Label(text="label 1", bg="lightyellow")
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
label1.pack(side=LEFT, ipadx=10)
label2.pack(side=LEFT, ipadx=10)
label3.pack(side=LEFT, ipadx=10)
window.mainloop()
執行結果:
fill參數
fill參數可延伸填滿元件被分配的空間,可設定的參數值如下:
none:預設值不延伸
x:水平方向填滿被分配的空間
y:垂直方向填滿被分配的空間
both:水平及垂直方向都填滿被分配的空間
以下範例設定label 3延伸填滿垂直方向到視窗的邊界,但不影響水平方向的長度,因為水平方向被分配的空間是固定的。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.minsize(width=300, height=50)
label1 = Label(text="label 1", bg="lightyellow")
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
label1.pack(side=LEFT, ipadx=10)
label2.pack(side=LEFT, ipadx=10)
label3.pack(side=LEFT, ipadx=10, fill="both")
window.mainloop()
執行結果:
expand參數
expand參數可填滿額外的視窗空間。以下範例設定label 3填滿到水平及垂直方向的視窗邊界。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.minsize(width=300, height=50)
label1 = Label(text="label 1", bg="lightyellow")
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
label1.pack(side=LEFT, ipadx=10)
label2.pack(side=LEFT, ipadx=10)
label3.pack(side=LEFT, ipadx=10, fill="both", expand=True)
window.mainloop()
執行結果:
place()方法可直接設定元件的左上方位置要顯示在視窗上的座標,視窗左上角的座標為x=0、y=0,向右x座標遞增,向下y座標遞增。
x/y參數
x/y參數可設定元件的x/y座標,以下範例將三個標籤的位置分別設定在(x=0, y=0)、(x=100, y=0)、(x=200, y=0)。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.geometry("300x20")
label1 = Label(text="label 1", bg="lightyellow")
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
label1.place(x=0, y=0)
label2.place(x=100, y=0)
label3.place(x=200, y=0)
window.mainloop()
執行結果:
height/width參數
height/width參數可設定元件的高和寬,以下範例標籤擺放的位置不變,將寬度拉長。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.geometry("300x20")
label1 = Label(text="label 1", bg="lightyellow")
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
label1.place(x=0, y=0, height=20, width=90)
label2.place(x=100, y=0, height=20, width=90)
label3.place(x=200, y=0, height=20, width=90)
window.mainloop()
執行結果:
relx/rely參數
relx/rely參數可設定元件在視窗的相對位置,參數值的範圍在0.0到1.0之間,relx=0.0代表視窗的最左邊位置、relx=1.0代表視窗的最右邊位置,rely=0.0代表視窗的最上方位置、rely=1.0代表視窗的最下方位置。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.geometry("300x100")
label1 = Label(text="label 1", bg="lightyellow")
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
label1.place(relx=0.1, rely=0.4)
label2.place(relx=0.4, rely=0.4)
label3.place(relx=0.7, rely=0.4)
window.mainloop()
執行結果:
relx/rely參數和x/y參數可以同時並存,代表先決定了元件在視窗的相對位置之後,再位移x/y的距離到最後的位置。以下延續上一個範例,決定了標籤的相對位置後再往右及往下20個像素。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.geometry("300x100")
label1 = Label(text="label 1", bg="lightyellow")
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
label1.place(relx=0.1, rely=0.4, x=20, y=20)
label2.place(relx=0.4, rely=0.4, x=20, y=20)
label3.place(relx=0.7, rely=0.4, x=20, y=20)
window.mainloop()
執行結果:
順帶一提,因為是設定元件在視窗的相對位置,當視窗大小改變時,元件會隨著視窗的大小改變在視窗上的絕對位置。
relheight/relwidth參數
除了有相對位置的參數以外,也有可調整相對大小的參數,即relheight/relwidth參數。同relx/rely的原理,relheight/relwidth的參數值範圍也在0.0到1.0之間,代表了是高和寬佔整個視窗的比例。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.geometry("300x100")
label1 = Label(text="label 1", bg="lightyellow")
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
label1.place(relx=0.1, rely=0.4, relheight=0.2, relwidth=0.2)
label2.place(relx=0.4, rely=0.4, relheight=0.3, relwidth=0.2)
label3.place(relx=0.7, rely=0.4, relheight=0.4, relwidth=0.2)
window.mainloop()
執行結果:
grid()方法
grid()方法是將視窗分成格狀,依據設定不同的row和column索引值,將元件放入格子中,一個元件可使用一格或多格,索引值預設從0開始,但也可以從其它數字開始計數。
row/column參數
row/column參數可用來設定元件在格狀視窗上擺放的位置。以下範例建立三個標籤和兩個文字方塊,依據不同的row和column設定擺放。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.geometry("300x100")
label1 = Label(text="label 1", bg="lightyellow")
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
entry1 = Entry()
entry2 = Entry()
label1.grid(row=0, column=1)
label2.grid(row=1, column=0)
label3.grid(row=2, column=0)
entry1.grid(row=1, column=1)
entry2.grid(row=2, column=1)
window.mainloop()
執行結果:
sticky參數
當格子比元件還大時,預設會將元件放在格子的正中央,若要將元件靠某一邊對齊或延伸,便可以使用sticky參數。sticky的參數值如下:
N:對齊格子上方
S:對齊格子下方
E:對齊格子右邊
W:對齊格子左邊
在前例中,若想要使label 1對齊格子左邊可以這樣設定:
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.geometry("300x100")
label1 = Label(text="label 1", bg="lightyellow")
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
entry1 = Entry()
entry2 = Entry()
label1.grid(row=0, column=1, sticky=W)
label2.grid(row=1, column=0)
label3.grid(row=2, column=0)
entry1.grid(row=1, column=1)
entry2.grid(row=2, column=1)
window.mainloop()
執行結果:
若想要使label 1同時對齊左邊跟右邊,亦即延伸label 1使其占滿格子的寬度,可以這樣設定:
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.geometry("300x100")
label1 = Label(text="label 1", bg="lightyellow")
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
entry1 = Entry()
entry2 = Entry()
label1.grid(row=0, column=1, sticky=W+E)
label2.grid(row=1, column=0)
label3.grid(row=2, column=0)
entry1.grid(row=1, column=1)
entry2.grid(row=2, column=1)
window.mainloop()
執行結果:
rowspan/columnspan參數
rowspan/columnspan參數可以合併row/column方向的格子以存放一個元件,例如在前例中,row=0的column=0和column=1可以合併,用來放置label 1,column=0的row=1和row=2可以合併,用來放置label 2,當然此時label 3就必須拿掉或移動位置,否則兩個標籤就會有重疊的可能。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.geometry("300x100")
label1 = Label(text="label 1", bg="lightyellow", width=20)
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
entry1 = Entry()
entry2 = Entry()
label1.grid(row=0, column=0, columnspan=2)
label2.grid(row=1, column=0, rowspan=2)
entry1.grid(row=1, column=1)
entry2.grid(row=2, column=1)
window.mainloop()
執行結果:
padx/pady參數
和pack()方法的使用方式相同,padx/pady參數可設定元件和元件之間或是元件和視窗邊界之間的水平/垂直距離,單位為像素。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.geometry("300x100")
label1 = Label(text="label 1", bg="lightyellow", width=20)
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
entry1 = Entry()
entry2 = Entry()
label1.grid(row=0, column=0, columnspan=2, padx=5, pady=5)
label2.grid(row=1, column=0, padx=5, pady=5)
label3.grid(row=2, column=0, padx=5, pady=5)
entry1.grid(row=1, column=1, padx=5, pady=5)
entry2.grid(row=2, column=1, padx=5, pady=5)
window.mainloop()
執行結果:
ipadx/ipady參數
和pack()方法的使用方式相同,ipadx/ipady參數可設定元件內文字和元件邊界的水平/垂直距離,單位為像素。
from tkinter import *
window = Tk()
window.title("視窗元件配置")
window.geometry("300x100")
label1 = Label(text="label 1", bg="lightyellow", width=20)
label2 = Label(text="label 2", bg="yellow")
label3 = Label(text="label 3", bg="lightgreen")
entry1 = Entry()
entry2 = Entry()
label1.grid(row=0, column=0, columnspan=2, padx=5, pady=5, ipadx=1, ipady=1)
label2.grid(row=1, column=0, padx=5, pady=5, ipadx=1, ipady=1)
label3.grid(row=2, column=0, padx=5, pady=5, ipadx=1, ipady=1)
entry1.grid(row=1, column=1, padx=5, pady=5, ipadx=1, ipady=1)
entry2.grid(row=2, column=1, padx=5, pady=5, ipadx=1, ipady=1)
window.mainloop()
執行結果:
專題:BMI計算機
在
if流程控制一文中,我們使用文字介面實作了一個BMI計算機,以展示if判斷式如何應用在實際的程式設計中,一直到這裡談到了GUI程式設計,終於有種從DOS進化到了Windows的感覺,這系列的文章也算完成了一個里程碑。接下來的這段程式碼將BMI計算機改寫成圖形介面,技術或是邏輯上之前都說明過,所以僅附上程式碼以及執行結果。
from tkinter import *
# function
def bmi_button_clicked():
# calculate BMI
weight = int(BMI_entry_weight.get())
height = int(BMI_entry_height.get())
if height == 0:
bmi = 0
else:
bmi = round(weight / (height / 100) ** 2, 1)
# BMI interpretation
if bmi == 0:
BMI_label_bmi_out.config(text=f"{bmi}")
BMI_label_category_out.config(text="Error!")
elif bmi < 18.5:
BMI_label_bmi_out.config(text=f"{bmi}")
BMI_label_category_out.config(text="Underweight")
elif bmi < 24:
BMI_label_bmi_out.config(text=f"{bmi}")
BMI_label_category_out.config(text=f"Normal Weight")
elif bmi < 28:
BMI_label_bmi_out.config(text=f"{bmi}")
BMI_label_category_out.config(text="Overweight")
else:
BMI_label_bmi_out.config(text=f"{bmi}")
BMI_label_category_out.config(text=f"Obese")
# window
BMI_window = Tk()
BMI_window.title("BMI calculator")
BMI_window.minsize(width=300, height=200)
BMI_window.config(padx=20, pady=20)
# label
BMI_label_title = Label(width=28, text="BMI calculator", bg="lightyellow", font=("Arial", 12, "normal"))
BMI_label_title.grid(row=0, column=0, columnspan=2, ipadx=2, ipady=2)
BMI_label_height = Label(width=15, text="Height (cm)", bg="yellow", font=("Arial", 10, "normal"))
BMI_label_height.grid(row=1, column=0, padx=5, pady=5)
BMI_label_weight = Label(width=15, text="Weight (kg)", bg="lightgreen", font=("Arial", 10, "normal"))
BMI_label_weight.grid(row=2, column=0, padx=5, pady=5)
BMI_label_bmi = Label(text="BMI:", font=("Arial", 12, "bold"))
BMI_label_bmi.grid(row=4, column=0, padx=5, pady=5)
BMI_label_bmi_out = Label(font=("Arial", 12, "normal"))
BMI_label_bmi_out.grid(row=4, column=1, padx=5, pady=5)
BMI_label_category = Label(text="Category:", font=("Arial", 12, "bold"))
BMI_label_category.grid(row=5, column=0, padx=5, pady=5)
BMI_label_category_out = Label(font=("Arial", 12, "normal"))
BMI_label_category_out.grid(row=5, column=1, padx=5, pady=5)
# entry
BMI_entry_height = Entry(width=20)
BMI_entry_height.grid(row=1, column=1, padx=5, pady=5)
BMI_entry_weight = Entry(width=20)
BMI_entry_weight.grid(row=2, column=1)
# button
BMI_button = Button(width=20, text="Calculate", command=bmi_button_clicked, font=("Arial", 10, "normal"))
BMI_button.grid(row=3, column=0, columnspan=2, padx=5, pady=5)
BMI_window.mainloop()
執行結果:
總結
在這篇文章中,我們介紹了三種使用tkinter模組設計GUI程式時,元件的版面配置方式,place()方法可直接定位元件的位置,但當元件一多,或是位置需要調整,座標的計算就會變得很繁瑣;pack()方法使用上最簡便,只要將元件由上(下)而下(上)或由左(右)而右(左)依序包裝起來,但使用上便少了一些彈性,無法任意指定位置;grid()算是一種折衷的方式,將視窗分成多個小格子,不用精算到座標,也可以將元件任意插入空的格子,適合用來處理較複雜的頁面。一般而言,pack()和grid()方法用到的機會是比較多的,但還是得根據程式的應用與複雜度來選擇適合的配置方式。
程式範例