Tkinter 是一個可以用 Python 寫出 GUI,並輕鬆串到自己寫的 Python API 的套件。
Tkinter、ttkbootstrap
tkinter.ttk 是風格化的 tk 元件。而 ttkbootstrap 是風格參考至 CSS 框架 bootstrap 的 tkinter 主題插件。ttkbootstrap 提供了許多現代化的主題、簡便的主題設置方式及額外的元件。安裝和使用可參考至官方文件。
製造主視窗
1 | import tkinter as tk |
1 | style = ttk.Style(theme='lumen') |
產生元件
我們需要實例化一個元件出來,並在建構子中指定父元件。利用 elm.grid
或 elm.pack
等方法可以將 elm
顯示於父元件中。
一些基本的元件有:
- Frame:一塊空白的容器,通常用來放入其他元件做分類、排版用。如果要有框框可以用 LabelFrame。
- Button:按鈕。在
comand
參數放入函式,可以在按下按鈕時執行。 - Label:文字
1 | frm_elm = ttk.Labelframe(root) |
在所有元件的最後,使用 root.mainloop()
顯示主視窗。
1 | root.mainloop() |
可以看到,LableFrame 緊貼在視窗左上角,然後 Button 填滿了 LableFrame。
佈局方法
grid 是常用的佈局方法。預先在父元件中分配好每個欄、列的比例,並指定該物件位於父元件的位置、所佔欄列數即可。
rowconfigure/columnconfigure
可以配置每個欄列的比例。 rowconfigure/columnconfigure
的參數:
index
:第一個參數。指定要配置哪些列(可塞int
、list
或tuple
)。weight
:指定這些列的比例。weight=1
為單位比例。
grid 的常用參數:
row/column
:指定該元件的左上角要放在父元件的哪個網格rowspan/columnspan
:該元件要跨幾個欄/列padx/pady
:填充外部,增加與其他元件的空隙ipadx/ipady
:填充內部,增加邊界與文字、圖片內容的空隙sticky
:對齊方式。'n'
會靠上,'ns'
會上下延伸,'nswe'
會填滿四邊,以此類推。
1 | style = ttk.Style(theme='lumen') |
風格
ttkbootstrap 提供了非常方便的風格設置方式。只要在實例化元件時指定 bootstrap
參數即可,並且 ttkbootstrap 還提供了常數使用。
1 | ttk.Button(frm_elm, text='owo', bootstyle=(ttk.DARK,ttk.OUTLINE)).grid(sticky='we', padx=5, pady=5) |
Control variables
control variables
是一種特殊的物件,提供 get()
和 set()
來存取值,就像變數一樣。元件們可以共享同一個 control variables,並在它改變時自動更新顯示。如果將 control variables
指派給輸入元件(如:Radio Buttons
、Entry
)時,元件也可以改變 control variables 的值。
control variables 有: DoubleVar
、IntVar
、 StringVar
、BooleanVar
。
1 | name = ttk.StringVar() |
RadioButton
則是透過共享 control variables 來達成只能選一個的效果。
1 | name = ttk.StringVar() |
更多資料可以參考至:
MVC
MVC 是一種軟體架構,代表 Model、View、Controller。
- Model:資料庫、演算法,不依賴 View 和 Controller,透過 API 就能正常使用。
- View:圖形界面,不含邏輯。
- Controller:界面邏輯、轉發 View 層的請求到 Model、更新 View 層的顯示。
我的日麻點數計算器參考了 MVC 概念並用 Python 和 tkinter 實作。
主要概念是在 view 層裡將按鈕的 command
參數指定為 controller 層中的 handler。當按下按鈕,執行 handler 時 controller 會去查看 view 中的 control variables,把輸入資料打包後傳給 model 層。從 model 層獲得計算結果後,則呼叫 view 層的函數更新顯示。
由於 controller 和 view 會互相參考,我又想把它們寫在不同檔案裡,為了避免循環 import 的問題,我將 controller 和 view 分別打包成 class。當實例化一個 controller 物件時,它也會實例化 view 物件,並將該 view 物件的 contoller 設為 self。
架構如下:
view.py
:
1 | import tkinter as tk |
controller.py
:
1 | import tkinter as tk |
這個專案一開始在寫 API 時一開始完全沒有考慮 GUI 的需求,好像恰好的符合了 MVC 中 Model 的獨立性......