Python Tkinter 的排版演練
【零】前言
Tkinter ( 簡稱 tk ) 是 Python 的內建套件之一,其功能是用來建立圖形化使用者介面 ( Graphical user interface, GUI )。假設你寫了一個很厲害的程式,像是自動記帳工具,如果想要分享給其他人使用的話,最好要能夠配合美美的圖形化使用者介面,才能讓使用者在視覺上快速理解此程式該如何操作。
在 Python 中能夠製作 GUI 的套件很多,雖然 tkinter 可能不如其他第三方套件像是 PyQT 、 PySide 等等那麼的方便簡易,但它是最基本的,學會它能夠幫助理解 GUI 的機制,若未來要使用其他套件也能更快熟悉!
本文會介紹使用 tkinter 設計 GUI 的過程中,該如何設定主視窗,再進一步規劃視窗的排版,讓視窗能夠拆分出不同的區塊並滿足美觀的需求~
【壹】建立主視窗
一、基本語法
首先,匯入模組
import tkinter as tk
拉出一個空白的主視窗,並命名為root
,
root = tk.Tk()
接著,此名為root
的視窗物件還有許多方法 ( method ) 可以進行調整,例如設定視窗名稱
root.title()
設定視窗大小
root.geometry()
可拖曳改變範圍
root.resizable()
設定icon
root.iconbitmap()
最後,透過以下指令開啟視窗,
root.mainloop()
二、綜合示範
以下建立一個名為 myApp 的視窗,其寬500、高200,並限制視窗大小無法改變,還附上一個 logo 圖案。
import os
import tkinter as tk
fileDir = os.path.dirname(__file__)
pardir = os.path.abspath(os.path.join(fileDir, os.path.pardir))
logoPath = os.path.join(pardir, 'image', 'w.ico')
root = tk.Tk()
root.title('myApp') # 設定視窗名稱
root.geometry('500x200+100+100') # 設定視窗大小及位置(width x height + x + y)
root.resizable(width=0, height=0) # 決定能用滑鼠拖曳來改變多少的視窗大小
root.iconbitmap(logoPath)
root.mainloop() # 啟用視窗
執行後的畫面如下,
【貳】版面設計
學會建立主視窗之後,再來將主視窗進行區塊劃分,來達到版面設計的目的。要使用到LabelFrame
和Frame
這兩種元件 ( widget ),兩者的差異在於LabelFrame
帶有文字標題,而Frame
則無。
一、建立LabelFrame
建立LabelFrame
的基本語法如下,
widgetObj = tk.LabelFrame(master, **kw)
在 tkinter 當中,參數master
是用來指定該Widget
所屬的上層零件,此參數不可省略。其他常用的 keywords 包含:
text
:控制顯示名稱height
:控制高度weight
:控制寬度background
,bg
:控制底色
二、建立Frame
建立Frame
的方式和建立LabelFrame
很像,其語法如下,
widgetObj = tk.Frame(master, **kw)
搭配Frame
常用的 keywords 有:
height
:控制高度weight
:控制寬度background
,bg
:控制底色
三、使用grid擺放位置
以上指令雖建立了LabelFrame
和Frame
,但還要透過擺放指令才能在畫面中呈現。在tkinter當中,所有Widget
的物件都帶有pack
、grid
、place
這三種擺放方法,我個人比較推薦的是grid
,它是以二維陣列的概念來控制元件所要擺放的位置,其語法如下,
widgetObj.grid(**kw)
常用的 keywords 有:
row
,column
:控制Widget
擺放的列數與行數rowspan
,columnspan
:控制Widget
跨越的列數與行數padx
,pady
:控制Widget
「外側」的留白空間ipadx
,ipady
:控制Widget
「內側」的留白空間sticky
:控制Widget
的靠齊方向
當使用grid
擺放元件,tkinter 會依照該Widget
所包含的內容自動調整至最緊密的大小。若希望將長寬固定,那麼需要透過以下指令,
widgetObj.grid_propagate(0)
四、綜合示範
以下程式碼將在主視窗裡建立一個LabelFrame
,且內含四個不同的區塊Frame
,為了方便辨識,每個不同區塊會以不同顏色做區隔。
import os
import tkinter as tk
fileDir = os.path.dirname(__file__)
pardir = os.path.abspath(os.path.join(fileDir, os.path.pardir))
logoPath = os.path.join(pardir, 'image', 'w.ico')
root = tk.Tk()
root.title('myApp')
root.geometry('500x200+100+100')
root.resizable(width=0, height=0)
root.iconbitmap(logoPath)
labelFrame1 = tk.LabelFrame(root, text='labelFrame1', height=200, width=500, bg='skyblue')
labelFrame1.grid()
frame1 = tk.Frame(labelFrame1, height=80, width=240, bg='orange')
frame1.grid(row=0, column=0)
frame2 = tk.Frame(labelFrame1, height=80, width=240, bg='tomato')
frame2.grid(row=0, column=1)
frame3 = tk.Frame(labelFrame1, height=80, width=240, bg='mediumpurple')
frame3.grid(row=1, column=0)
frame4 = tk.Frame(labelFrame1, height=80, width=240, bg='mediumseagreen')
frame4.grid(row=1, column=1)
root.mainloop()
關注上面程式碼的第17行到第27行,使用了grid
搭配row
和column
參數來控制元件的位置,讓這些元件能棋盤式的排列。
接著,以上面的程式碼為基礎,來進行幾種不同的調整測試吧~
測試一:propagate
觀察上圖,淺藍色的labelFrame1
和底下四個Frame
的邊界是貼齊的狀態,也就是說主元件master
的邊界已依照其下層元件的實際範圍而調整,試著加上以下指令使邊界固定,
labelFrame1.propagate(0)
或是
labelFrame1.grid_propagate(0)
執行後可以發現淺藍色labelFrame1
的邊界並未自動貼齊底下的四個Frame
元件,而是維持原先的設定。
測試二 :rowspan
將綠色的frame4
拿掉,再調整番茄色frame2
的設定如下,
frame2 = tk.Frame(labelFrame1, height=160, width=240, bg='tomato')
frame2.grid(row=0, column=1, rowspan=2)
由於新增rowspan=2
,因此frame2
可以跨越兩列的範圍。
測試三: columnspan
若改拿掉番茄色frame2
,並修改橘色frame1
的設定如下,
frame1 = tk.Frame(labelFrame1, height=80, width=480, bg='orange')
frame1.grid(row=0, column=0, columnspan=2)
由於新增columnspan=2
,因此frame1
會跨越兩行的範圍。
測試四:sticky, padx, pady, ipadx, ipady
這次不拿掉任何東西,而是將綠色frame4
縮小至高40、寬120,可以發現frame4
在水平及垂直方向皆自動置中。
frame4 = tk.Frame(labelFrame1, height=40, width=120, ...)
再指定sticky=tk.NW
參數,將其貼齊邊界,
frame4.grid(row=1, column=1, sticky=tk.NW)
以上的tk.NW
指的是西北方,依此類推,可以選擇的值有tk.N
、tk.E
、tk.S
、tk.W
、tk.NE
、tk.NW
、tk.SE
以及tk.SW
。
再加上padx
和pady
參數來控制外側留白範圍,
frame4.grid(row=1, column=1, padx=10, pady=10, sticky=tk.NW)
若將padx
及pady
改為ipadx
及ipady
,則「外側留白」的呈現將改為「內側留白」。
frame4.grid(row=1, column=1, ipadx=10, ipady=10, sticky=tk.NW)
【參】結語
在整理這篇文章以前,常常在寫 tk 的時候發現介面執行的長相和我原先設想根本不一樣啊 XD,所以才下定決心釐清 tk 的排版機制。但除了排版以外,一個完整的 GUI 還要搭配其他更豐富的功能,因此接下來會再寫一篇文章,羅列一些常用的元件,例如Button
、Checkbutton
、Combobox
等等,並示範這些元件如何互動以及觸發其他的事件,學會這些就能輕鬆創造屬於自己的小工具啦!