更新於 2022/04/26閱讀時間約 33 分鐘

不間斷 Python 挑戰 Day 31 - 使用tkinter開發GUI程式:視窗元件配置

上一篇文章介紹了多種tkinter的視窗元件後,這篇我們再來討論三種視窗元件的配置方法以及優缺點,以適當移動各個元件到視窗中指定的位置,讓整個視窗版面的配置看起來更合理且美觀。
這三種配置方法包括:
  • pack()方法
  • place()方法
  • grid()方法
以下就一一來說明。

pack()方法

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

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()
執行結果:
設定絕對x/y座標

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()
執行結果:
設定相對x/y座標
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開始,但也可以從其它數字開始計數。
grid()版面配置

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()
執行結果:
grid()視窗配置

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()
執行結果:
BMI計算機

總結

在這篇文章中,我們介紹了三種使用tkinter模組設計GUI程式時,元件的版面配置方式,place()方法可直接定位元件的位置,但當元件一多,或是位置需要調整,座標的計算就會變得很繁瑣;pack()方法使用上最簡便,只要將元件由上(下)而下(上)或由左(右)而右(左)依序包裝起來,但使用上便少了一些彈性,無法任意指定位置;grid()算是一種折衷的方式,將視窗分成多個小格子,不用精算到座標,也可以將元件任意插入空的格子,適合用來處理較複雜的頁面。一般而言,pack()和grid()方法用到的機會是比較多的,但還是得根據程式的應用與複雜度來選擇適合的配置方式。

程式範例

分享至
成為作者繼續創作的動力吧!
© 2024 vocus All rights reserved.