tkinter --- Tcl/Tk 的 Python 介面¶
tkinter 套件(「Tk 介面」)是 Tcl/Tk GUI 工具集的標準 Python 介面。Tk 和 tkinter 在大多數 Unix 平台(包含 macOS)以及 Windows 系統上都可以使用。
從命令列執行 python -m tkinter 應該會開啟一個展示簡單 Tk 介面的視窗,讓你知道 tkinter 已正確安裝在你的系統上,並且也會顯示已安裝的 Tcl/Tk 版本,這樣你就可以閱讀該版本專屬的 Tcl/Tk 文件。
Tkinter 支援一系列的 Tcl/Tk 版本,這些版本可以是有或沒有執行緒支援的建構版本。官方的 Python 二進位發行版捆綁了有執行緒支援的 Tcl/Tk 8.6。關於支援版本的更多資訊,請參閱 _tkinter 模組的原始碼。
Tkinter 並非一個輕薄的包裝器,而是加入了相當多自身的邏輯,使其體驗更具 Python 風格(pythonic)。本文件將著重於這些新增和變更,對於未變更的細節,則會引導讀者參閱官方的 Tcl/Tk 文件。
備註
Tcl/Tk 8.5(2007)引入了一套現代化的主題式使用者介面元件,以及一個用以操作它們的新 API。新舊 API 目前都還能使用。你在網路上找到的大多數文件仍使用舊版 API,而且可能已經嚴重過時。
這是一個可選模組。如果你的 CPython 副本中缺少它,請查閱你的發行者(即提供 Python 給你的人)的文件。如果你就是發行者,請參閱 可選模組的需求。
也參考
- TkDocs
關於使用 Tkinter 建立使用者介面的詳盡教學。內容解釋了關鍵概念,並使用現代 API 來說明建議的方法。
- Tkinter 8.5 reference: a GUI for Python
Tkinter 8.5 的參考文件,詳細說明了可用的類別、方法和選項。
Tcl/Tk 相關資源:
書籍:
- Modern Tkinter for Busy Python Developers
由 Mark Roseman 所著。(ISBN 978-1999149567)
- Python GUI programming with Tkinter
由 Alan D. Moore 所著。(ISBN 978-1788835886)
- Programming Python
由 Mark Lutz 所著;對 Tkinter 有極佳的涵蓋。(ISBN 978-0596158101)
- Tcl and the Tk Toolkit (2nd edition)
由 Tcl/Tk 發明者 John Ousterhout 與 Ken Jones 所著;不包含 Tkinter。(ISBN 978-0321336330)
架構¶
Tcl/Tk 並非單一函式庫,而是由幾個不同的模組所組成,每個模組都有獨立的功能和官方文件。Python 的二進位發行版也隨附了一個附加模組。
- Tcl
Tcl 是一個動態直譯的程式語言,就像 Python 一樣。雖然它可以作為通用程式語言獨立使用,但它最常被嵌入到 C 應用程式中,作為腳本引擎或 Tk 工具集的介面。Tcl 函式庫有一個 C 介面,可用於建立和管理一個或多個 Tcl 直譯器的實例,在這些實例中執行 Tcl 指令和腳本,並新增以 Tcl 或 C 實作的自訂指令。每個直譯器都有一個事件佇列,並有設施可以向其傳送事件並加以處理。與 Python 不同,Tcl 的執行模型是圍繞著協同多工(cooperative multitasking)設計的,而 Tkinter 則彌合了這種差異(詳情請參閱 Threading model)。
- Tk
Tk 是一個以 C 實作的 Tcl 套件,它新增了自訂指令來建立和操作 GUI 元件。每個
Tk物件都嵌入了自己載入 Tk 的 Tcl 直譯器實例。Tk 的元件非常可自訂,但代價是外觀較為過時。Tk 使用 Tcl 的事件佇列來產生和處理 GUI 事件。- Ttk
主題式 Tk(Ttk)是較新的一族 Tk 元件,相較於許多傳統的 Tk 元件,它在不同平台上提供了更好的外觀。從 Tk 8.5 版開始,Ttk 作為 Tk 的一部分發行。Python 的繫結(bindings)則在一個獨立的模組
tkinter.ttk中提供。
在內部,Tk 和 Ttk 使用底層作業系統的設施,即 Unix/X11 上的 Xlib、macOS 上的 Cocoa、Windows 上的 GDI。
當你的 Python 應用程式使用 Tkinter 中的類別(例如,建立一個元件)時,tkinter 模組會先組合一個 Tcl/Tk 指令字串。它將該 Tcl 指令字串傳遞給一個內部的 _tkinter 二進位模組,後者再呼叫 Tcl 直譯器來對其進行計算。Tcl 直譯器接著會呼叫 Tk 和/或 Ttk 套件,而這些套件又會再呼叫 Xlib、Cocoa 或 GDI。
Tkinter 模組¶
對 Tkinter 的支援分散在數個模組中。大多數應用程式將需要主要的 tkinter 模組,以及 tkinter.ttk 模組,後者提供了現代主題式元件集和 API:
from tkinter import *
from tkinter import ttk
- class tkinter.Tk(screenName=None, baseName=None, className='Tk', useTk=True, sync=False, use=None)¶
建構一個頂層 Tk 元件,它通常是應用程式的主視窗,並為此元件初始化一個 Tcl 直譯器。每個實例都有其自己關聯的 Tcl 直譯器。
Tk類別通常使用所有預設值來實例化。然而,目前可辨識下列關鍵字引數:- screenName
當給定(作為字串)時,設定
DISPLAY環境變數。(僅限 X11)- baseName
設定檔的名稱。預設情況下,baseName 是從程式名稱(
sys.argv[0])衍生而來。- className
元件類別的名稱。用作設定檔,也用作呼叫 Tcl 時的名稱(interp 中的 argv0)。
- useTk
若為
True,則初始化 Tk 子系統。tkinter.Tcl()函式會將此設定為False。- sync
若為
True,則同步執行所有 X 伺服器指令,以便立即回報錯誤。可用於偵錯。(僅限 X11)- use
指定要嵌入應用程式的視窗 id,而不是將其建立為獨立的頂層視窗。id 必須以與頂層元件的 -use 選項值相同的方式指定(也就是說,其形式類似
winfo_id()回傳的形式)。請注意,在某些平台上,只有當 id 指向一個已啟用 -container 選項的 Tk 框架或頂層視窗時,此功能才能正常運作。
Tk會讀取名為.className.tcl和.baseName.tcl的設定檔,並在 Tcl 直譯器中進行直譯,並對.className.py和.baseName.py的內容呼叫exec()。設定檔的路徑是HOME環境變數,如果未定義,則是os.curdir。
- tkinter.Tcl(screenName=None, baseName=None, className='Tk', useTk=False)¶
Tcl()函式是一個工廠函式,它建立的物件與Tk類別建立的物件非常相似,只是它不會初始化 Tk 子系統。這在驅動 Tcl 直譯器時最為有用,尤其是在不希望建立額外頂層視窗的環境中,或者在無法建立的環境中(例如沒有 X 伺服器的 Unix/Linux 系統)。由Tcl()物件建立的物件可以透過呼叫其loadtk()方法來建立一個頂層視窗(並初始化 Tk 子系統)。
提供 Tk 支援的模組包括:
tkinter主要的 Tkinter 模組。
tkinter.colorchooser讓使用者選擇顏色的對話方塊。
tkinter.commondialog此處列出的其他模組中定義的對話方塊的基底類別。
tkinter.filedialog讓使用者指定要開啟或儲存的檔案的通用對話方塊。
tkinter.font協助處理字型的工具程式。
tkinter.messagebox存取標準的 Tk 對話方塊。
tkinter.scrolledtext內建垂直捲軸的文字元件。
tkinter.simpledialog基本的對話方塊和便利函式。
tkinter.ttk在 Tk 8.5 中引入的主題式元件集,為主
tkinter模組中的許多傳統元件提供了現代化的替代方案。
額外模組:
_tkinter一個包含 Tcl/Tk 低階介面的二進位模組。它由主
tkinter模組自動引入,應用程式開發者不應直接使用它。它通常是一個共享函式庫(或 DLL),但在某些情況下可能會與 Python 直譯器靜態連結。idlelibPython 的整合開發與學習環境(IDLE)。基於
tkinter。tkinter.constants在將各種參數傳遞給 Tkinter 呼叫時,可用於替代字串的符號常數。由主
tkinter模組自動引入。tkinter.dnd(實驗性)對
tkinter的拖放支援。當它被 Tk DND 取代時,將會被棄用。turtle在 Tk 視窗中的海龜繪圖。
Tkinter 應急指南¶
本節並非旨在成為 Tk 或 Tkinter 的詳盡教學。為此,請參閱前面提到的外部資源之一。相反地,本節提供了一個快速導覽,介紹 Tkinter 應用程式的外觀、識別基礎的 Tk 概念,並解釋 Tkinter 包裝器的結構。
本節的其餘部分將幫助你識別在 Tkinter 應用程式中所需的類別、方法和選項,以及在哪裡可以找到關於它們的更詳細文件,包括在官方的 Tcl/Tk 參考手冊中。
一個 Hello World 程式¶
我們將從一個 Tkinter 的 "Hello World" 應用程式開始。這不是我們能寫出的最小程式,但足以說明一些你需要知道的關鍵概念。
from tkinter import *
from tkinter import ttk
root = Tk()
frm = ttk.Frame(root, padding=10)
frm.grid()
ttk.Label(frm, text="Hello World!").grid(column=0, row=0)
ttk.Button(frm, text="Quit", command=root.destroy).grid(column=1, row=0)
root.mainloop()
在引入之後,下一行建立了一個 Tk 類別的實例,它會初始化 Tk 並建立其關聯的 Tcl 直譯器。它還會建立一個頂層視窗,稱為根視窗,作為應用程式的主視窗。
接下來的一行建立了一個框架元件,在本例中,它將包含我們接下來要建立的一個標籤和一個按鈕。該框架被放置在根視窗內。
下一行建立了一個包含靜態文字字串的標籤元件。grid() 方法用於指定標籤在其包含的框架元件中的相對佈局(位置),類似於 HTML 中表格的運作方式。
然後建立一個按鈕元件,並放置在標籤的右側。當按下時,它將呼叫根視窗的 destroy() 方法。
最後,mainloop() 方法將所有內容顯示在螢幕上,並回應使用者輸入,直到程式終止。
重要的 Tk 概念¶
即使是這個簡單的程式也說明了以下關鍵的 Tk 概念:
- 元件(widgets)
一個 Tkinter 使用者介面是由個別的元件所組成。每個元件都表示為一個 Python 物件,從
ttk.Frame、ttk.Label和ttk.Button等類別實例化而來。- 元件階層(widget hierarchy)
元件被安排在一個階層中。標籤和按鈕被包含在一個框架內,而框架又被包含在根視窗內。在建立每個子元件時,其父元件會作為第一個引數傳遞給元件的建構函式。
- 設定選項(configuration options)
元件具有設定選項,可以修改其外觀和行為,例如在標籤或按鈕中顯示的文字。不同類別的元件會有不同的選項集。
- 佈局管理(geometry management)
元件在建立時不會自動新增到使用者介面中。像
grid這樣的佈局管理器(geometry manager)會控制它們在使用者介面中的放置位置。- 事件迴圈(event loop)
Tkinter 只有在主動執行事件迴圈時,才會對使用者輸入、程式的變更,甚至螢幕刷新做出反應。如果你的程式沒有執行事件迴圈,你的使用者介面將不會更新。
了解 Tkinter 如何包裝 Tcl/Tk¶
當你的應用程式使用 Tkinter 的類別和方法時,Tkinter 內部正在組合代表 Tcl/Tk 指令的字串,並在附加到你應用程式 Tk 實例的 Tcl 直譯器中執行這些指令。
無論是試圖瀏覽參考文件、尋找正確的方法或選項、改寫一些現有程式碼,還是偵錯你的 Tkinter 應用程式,在某些時候,了解那些底層 Tcl/Tk 指令的樣貌會很有幫助。
為了說明,以下是上面 Tkinter 腳本主要部分的 Tcl/Tk 對等程式碼。
ttk::frame .frm -padding 10
grid .frm
grid [ttk::label .frm.lbl -text "Hello World!"] -column 0 -row 0
grid [ttk::button .frm.btn -text "Quit" -command "destroy ."] -column 1 -row 0
Tcl 的語法類似於許多 shell 語言,第一個字是要執行的指令,後面跟著該指令的引數,以空格分隔。在不深入太多細節的情況下,請注意以下幾點:
用於建立元件的指令(如
ttk::frame)對應於 Tkinter 中的元件類別。Tcl 元件選項(如
-text)對應於 Tkinter 中的關鍵字引數。在 Tcl 中,元件是透過路徑名稱(如
.frm.btn)來參照,而 Tkinter 不使用名稱,而是使用物件參照。元件在元件階層中的位置被編碼在其(階層式)路徑名稱中,該路徑名稱使用
.(點)作為路徑分隔符。根視窗的路徑名稱就是.(點)。在 Tkinter 中,階層不是由路徑名稱定義的,而是在建立每個子元件時指定父元件來定義的。在 Tcl 中實作為獨立指令的操作(如
grid或destroy),在 Tkinter 中則表示為元件物件上的方法。正如你稍後會看到的,在其他時候,Tcl 使用的看起來像是對元件物件的方法呼叫,這更接近 Tkinter 中的用法。
我該如何...?哪個選項可以...?¶
如果你不確定如何在 Tkinter 中做某件事,並且無法立即在你正在使用的教學或參考文件中找到它,這裡有一些可能有幫助的策略。
首先,請記住,個別元件的運作細節可能會因 Tkinter 和 Tcl/Tk 的不同版本而異。如果你正在搜尋文件,請確保它對應於你系統上安裝的 Python 和 Tcl/Tk 版本。
在搜尋如何使用 API 時,知道你正在使用的類別、選項或方法的確切名稱會很有幫助。自省(Introspection),無論是在互動式 Python shell 中還是使用 print(),都可以幫助你識別所需內容。
要找出任何元件上有哪些可用的設定選項,請呼叫其 configure() 方法,該方法會回傳一個字典,其中包含有關每個物件的各種資訊,包括其預設值和目前值。使用 keys() 僅取得每個選項的名稱。
btn = ttk.Button(frm, ...)
print(btn.configure().keys())
由於大多數元件有許多共通的設定選項,找出特定於某個元件類別的選項會很有用。將選項串列與像框架這樣較簡單的元件的選項串列進行比較,是做到這一點的一種方法。
print(set(btn.configure().keys()) - set(frm.configure().keys()))
同樣地,你可以使用標準的 dir() 函式來尋找元件物件可用的方法。如果你試一下,你會看到有超過 200 個常見的元件方法,所以再次強調,識別特定於某個元件類別的方法是很有幫助的。
print(dir(btn))
print(set(dir(btn)) - set(dir(frm)))