# `tkinter` --- Tcl/Tk 的 Python 介面

原始碼：Lib/tkinter/__init__.py

`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 相關資源：

- Tk 指令

    Tkinter 所使用的每個底層 Tcl/Tk 指令的綜合參考資料。

- Tcl/Tk 首頁

    額外的文件，以及連至 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`。

  tk

    透過實例化 `Tk` 建立的 Tk 應用程式物件。這提供了對 Tcl 直譯器的存取。每個附加到同一個 `Tk` 實例的元件，其 `tk` 屬性都具有相同的值。

  master

    包含此元件的元件物件。對於 `Tk`，`master` 是 `None`，因為它是主視窗。master 和 parent 這兩個術語很相似，有時會作為引數名稱互換使用；然而，呼叫 `winfo_parent()` 會回傳元件名稱的字串，而 `master` 則回傳物件。parent/child 反映了樹狀關係，而 master （或 container）/content 反映了容器結構。

  children

    此元件的直接子代，形式為一個 `dict`，其中鍵為子元件的名稱，值為子實例物件。

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 直譯器靜態連結。

`idlelib`

  Python 的整合開發與學習環境（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)))

### 瀏覽 Tcl/Tk 參考手冊

如前所述，官方的 Tk 指令 參考手冊（man pages）通常是對元件上特定操作作用的最準確描述。即使你知道所需選項或方法的名稱，你可能仍有幾個地方需要查看。

雖然 Tkinter 中的所有操作都實作為對元件物件的方法呼叫，但你已經看到許多 Tcl/Tk 操作顯示為指令，這些指令將元件路徑名稱作為其第一個參數，後面跟著可選的參數，例如：

    destroy .
    grid .frm.btn -column 0 -row 0

然而，其他的看起來更像是對元件物件呼叫的方法（事實上，當你在 Tcl/Tk 中建立一個元件時，它會建立一個以元件路徑名稱為名的 Tcl 指令，該指令的第一個參數是要呼叫的方法名稱）。

    .frm.btn invoke
    .frm.lbl configure -text "Goodbye"

在官方的 Tcl/Tk 參考文件中，你會在特定元件的 man page 上找到大多數看起來像方法呼叫的操作（例如，你可以在 ttk::button man page 上找到 `invoke()` 方法），而將元件作為參數的函式通常有自己的 man page（例如 grid）。

你可以在 options 或 ttk::widget man page 中找到許多常見的選項和方法，而其他的則在特定元件類別的 man page 中找到。

你還會發現許多 Tkinter 方法有複合名稱，例如 `winfo_x()`、`winfo_height()`、`winfo_viewable()`。你可以在 winfo man page 中找到所有這些文件的說明。

備註

有點令人困惑的是，所有 Tkinter 元件上還有一些方法，它們實際上並不是對該元件進行操作，而是在全域範圍內操作，獨立於任何元件。例如存取剪貼簿或系統鈴聲的方法。（它們恰好被實作為所有 Tkinter 元件都繼承的基底 `Widget` 類別中的方法）。

## 執行緒模型

Python 和 Tcl/Tk 有非常不同的執行緒模型，`tkinter` 試圖彌合這種差異。如果你使用執行緒，你可能需要意識到這一點。

一個 Python 直譯器可能有多個與之關聯的執行緒。在 Tcl 中，可以建立多個執行緒，但每個執行緒都有一個獨立的 Tcl 直譯器實例與之關聯。執行緒也可以建立多個直譯器實例，但每個直譯器實例只能由建立它的那個執行緒使用。

由 `tkinter` 建立的每個 `Tk` 物件都包含一個 Tcl 直譯器。它還會追蹤是哪個執行緒建立了該直譯器。可以從任何 Python 執行緒呼叫 `tkinter`。在內部，如果呼叫來自建立 `Tk` 物件的執行緒以外的執行緒，則會將一個事件發佈到直譯器的事件佇列中，執行後，結果會回傳給呼叫的 Python 執行緒。

Tcl/Tk 應用程式通常是事件驅動的，這意味著在初始化之後，直譯器會執行一個事件迴圈（即 `Tk.mainloop()`）並回應事件。因為它是單執行緒的，事件處理常式必須快速回應，否則它們會阻塞其他事件的處理。為避免這種情況，任何長時間執行的計算都不應在事件處理常式中執行，而是使用計時器將其分解成更小的部分，或在另一個執行緒中執行。這與許多 GUI 工具集不同，在那些工具集中，GUI 在一個與所有應用程式碼（包括事件處理常式）完全分離的執行緒中執行。

如果 Tcl 直譯器沒有在執行事件迴圈和處理事件，那麼從執行 Tcl 直譯器的執行緒以外的執行緒發出的任何 `tkinter` 呼叫都將失敗。

存在一些特殊情況：

- Tcl/Tk 函式庫可以被建構成不具備執行緒感知能力。在這種情況下，`tkinter` 會從原始的 Python 執行緒呼叫該函式庫，即使這與建立 Tcl 直譯器的執行緒不同。一個全域鎖確保一次只發生一個呼叫。

- 雖然 `tkinter` 允許你建立多個 `Tk` 物件的實例（每個都有自己的直譯器），但屬於同一個執行緒的所有直譯器共享一個共同的事件佇列，這很快就會變得混亂。在實務上，不要一次建立多個 `Tk` 的實例。否則，最好在不同的執行緒中建立它們，並確保你正在執行一個具備執行緒感知能力的 Tcl/Tk 建構版本。

- 阻塞事件處理常式並不是防止 Tcl 直譯器重新進入事件迴圈的唯一方法。甚至可以執行多個巢狀的事件迴圈或完全放棄事件迴圈。如果你在事件或執行緒方面做任何複雜的操作，請注意這些可能性。

- 目前有少數幾個 `tkinter` 函式只有在從建立 Tcl 直譯器的執行緒中呼叫時才能正常運作。

## 實用參考

### 設定選項

選項控制著諸如元件的顏色和邊框寬度等。選項可以透過三種方式設定：

在物件建立時，使用關鍵字引數

      fred = Button(self, fg="red", bg="blue")

在物件建立後，將選項名稱視為字典索引

      fred["fg"] = "red"
      fred["bg"] = "blue"

在物件建立後，使用 config() 方法更新多個屬性

      fred.config(fg="red", bg="blue")

有關給定選項及其行為的完整解釋，請參閱相關元件的 Tk man pages。

請注意，man pages 為每個元件列出了「標準選項」和「元件特定選項」。前者是許多元件共有的選項串列，後者是該特定元件特有的選項。標準選項記錄在 options(3) man page 中。

本文件中未對標準選項和元件特定選項進行區分。某些選項不適用於某些類型的元件。給定元件是否回應特定選項取決於該元件的類別；按鈕有 `command` 選項，標籤則沒有。

給定元件支援的選項列在該元件的 man page 中，或者可以在 runtime 透過不帶引數呼叫 `config()` 方法，或對該元件呼叫 `keys()` 方法來查詢。這些呼叫的回傳值是一個字典，其鍵是選項名稱的字串（例如 `'relief'`），其值是 5-tuples。

某些選項，如 `bg`，是具有長名稱的常見選項的同義詞（`bg` 是 "background" 的簡寫）。將簡寫選項的名稱傳遞給 `config()` 方法將回傳一個 2-tuple，而不是 5-tuple。回傳的 2-tuple 將包含同義詞的名稱和「真實」選項（例如 `('bg', 'background')`）。

索引 | 含義 | 範例
--- | --- | ---
0 | 選項名稱 | `'relief'`
1 | 用於資料庫查詢的選項名稱 | `'relief'`
2 | 用於資料庫查詢的選項類別 | `'Relief'`
3 | 預設值 | `'raised'`
4 | 目前值 | `'groove'`

範例：

    >>> print(fred.config())
    {'relief': ('relief', 'relief', 'Relief', 'raised', 'groove')}

當然，印出的字典將包含所有可用的選項及其值。這僅作為一個範例。

### Packer 佈局管理器

packer 是 Tk 的佈局管理（geometry manager）機制之一。佈局管理器（geometry manager）用於指定元件在其容器內的相對位置。與較為繁瑣的 placer（較不常用，我們在此不予介紹）相比，packer 採用定性的關係規範——上方、左方、填滿等——並為你計算出確切的放置座標。

任何 master 元件的大小由其內部的「content 元件」的大小決定。packer 用於控制 content 元件在被 packed 的容器內部出現的位置。你可以將元件 pack 到框架中，再將框架 pack 到其他框架中，以達到你想要的佈局。此外，一旦 packed，佈局會動態調整以適應設定的增量變更。

請注意，元件在透過佈局管理器（geometry manager）指定其佈局（geometry）之前是不會出現的。一個常見的早期錯誤是忽略了佈局（geometry）指定，然後在元件被建立但沒有任何東西出現時感到驚訝。一個元件只有在例如 packer 的 `pack()` 方法被應用於其上之後才會出現。

pack() 方法可以與關鍵字選項/值對一起呼叫，以控制元件在其容器中出現的位置，以及當主應用程式視窗調整大小時它的行為方式。以下是一些範例：

    fred.pack()                     # 預設為 side = "top"
    fred.pack(side="left")
    fred.pack(expand=1)

### Packer 選項

有關 packer 及其可接受選項的更詳盡資訊，請參閱 man pages 和 John Ousterhout 書中的第 183 頁。

anchor

  錨點類型。表示 packer 將每個內容放置在其區域中的位置。

expand

  布林值，`0` 或 `1`。

fill

  合法值：`'x'`、`'y'`、`'both'`、`'none'`。

ipadx 和 ipady

  一個距離 —— 指定內容每一側的內部填充。

padx 和 pady

  一個距離 —— 指定內容每一側的外部填充。

side

  合法值為：`'left'`、`'right'`、`'top'`、`'bottom'`。

### 耦合元件變數

某些元件（如文字輸入元件）的目前值設定可以透過使用特殊選項直接連接到應用程式變數。這些選項是 `variable`、`textvariable`、`onvalue`、`offvalue` 和 `value`。這種連接是雙向的：如果變數因任何原因發生變化，與之連接的元件將會更新以反映新值。

不幸的是，在 `tkinter` 的目前實作中，無法透過 `variable` 或 `textvariable` 選項將任意的 Python 變數傳遞給元件。唯一適用此功能的變數類型是從 `tkinter` 中定義的一個名為 Variable 的類別繼承的子類別變數。

已經定義了許多有用的 Variable 子類別：`StringVar`、`IntVar`、`DoubleVar` 和 `BooleanVar`。要讀取此類變數的目前值，請對其呼叫 `get()` 方法，要更改其值，則呼叫 `set()` 方法。如果你遵循此協定，元件將始終追蹤變數的值，無需你進一步干預。

舉例來說：

    import tkinter as tk

    class App(tk.Frame):
        def __init__(self, master):
            super().__init__(master)
            self.pack()

            self.entrythingy = tk.Entry()
            self.entrythingy.pack()

            # 建立應用程式變數。
            self.contents = tk.StringVar()
            # 將其設定為某個值。
            self.contents.set("this is a variable")
            # 告訴 entry 元件監視此變數。
            self.entrythingy["textvariable"] = self.contents

            # 為使用者按下 return 鍵定義一個回呼。
            # 它會印出變數的目前值。
            self.entrythingy.bind('<Key-Return>',
                                 self.print_contents)

        def print_contents(self, event):
            print("Hi. The current entry content is:",
                  self.contents.get())

    root = tk.Tk()
    myapp = App(root)
    myapp.mainloop()

### 視窗管理器

在 Tk 中，有一個工具指令 `wm`，用於與視窗管理器互動。`wm` 指令的選項允許你控制標題、位置、圖示點陣圖等。在 `tkinter` 中，這些指令已實作為 `Wm` 類別上的方法。頂層元件是 `Wm` 類別的子類別，因此可以直接呼叫 `Wm` 方法。

要取得包含給定元件的頂層視窗，你通常可以直接參照該元件的 `master`。當然，如果該元件被 packed 在一個框架內，`master` 將不代表一個頂層視窗。要取得包含任意元件的頂層視窗，你可以呼叫 `_root()` 方法。此方法以底線開頭，表示此函式是實作的一部分，而不是 Tk 功能的介面。

以下是一些常見用法範例：

    import tkinter as tk

    class App(tk.Frame):
        def __init__(self, master=None):
            super().__init__(master)
            self.pack()

    # 建立應用程式
    myapp = App()

    #
    # 以下是對視窗管理器類別的方法呼叫
    #
    myapp.master.title("My Do-Nothing Application")
    myapp.master.maxsize(1000, 400)

    # 啟動程式
    myapp.mainloop()

### Tk 選項資料型別

anchor

  合法值為羅盤方位點：`"n"`、`"ne"`、`"e"`、`"se"`、`"s"`、`"sw"`、`"w"`、`"nw"`，以及 `"center"`。

bitmap

  有八個內建的、命名的點陣圖：`'error'`、`'gray25'`、`'gray50'`、`'hourglass'`、`'info'`、`'questhead'`、`'question'`、`'warning'`。要指定一個 X 點陣圖檔名，請給出檔案的完整路徑，並在前面加上一個 `@`，例如 `"@/usr/contrib/bitmap/gumby.bit"`。

boolean

  你可以傳遞整數 0 或 1，或字串 `"yes"` 或 `"no"`。

callback

  這是任何不帶引數的 Python 函式。例如：

      def print_it():
          print("hi there")
      fred["command"] = print_it

color

  顏色可以以 rgb.txt 檔案中的 X 顏色名稱給出，或以表示 RGB 值的字串給出，範圍可以是 4 位元：`"#RGB"`、8 位元：`"#RRGGBB"`、12 位元：`"#RRRGGGBBB"` 或 16 位元：`"#RRRRGGGGBBBB"`，其中 R、G、B 在此代表任何合法的十六進位數字。詳情請參閱 Ousterhout 書中的第 160 頁。

cursor

  可以使用 `cursorfont.h` 中的標準 X 游標名稱，無需 `XC_` 前綴。例如，要取得手形游標（`XC_hand2`），請使用字串 `"hand2"`。你也可以指定自己的點陣圖和遮罩檔案。請參閱 Ousterhout 書中的第 179 頁。

distance

  螢幕距離可以以像素或絕對距離指定。像素以數字給出，絕對距離以字串給出，尾隨字元表示單位：`c` 代表公分，`i` 代表英寸，`m` 代表毫米，`p` 代表印刷的點。例如，3.5 英寸表示為 `"3.5i"`。

font

  Tk 使用串列字型名稱格式，例如 `{courier 10 bold}`。帶有正數的字型大小以點為單位；帶有負數的大小以像素為單位。

geometry

  這是一個 `widthxheight` 形式的字串，其中 width 和 height 對於大多數元件是以像素為單位（對於顯示文字的元件則以字元為單位）。例如：`fred["geometry"] = "200x100"`。

justify

  合法值為字串：`"left"`、`"center"` 和 `"right"`。

region

  這是一個由四個以空格分隔的元素組成的字串，每個元素都是一個合法的距離（見上文）。例如：`"2 3 4 5"`、`"3i 2i 4.5i 2i"` 和 `"3c 2c 4c 10.43c"` 都是合法的區域。

relief

  決定元件的邊框樣式。合法值為：`"raised"`、`"sunken"`、`"flat"`、`"groove"` 和 `"ridge"`。

scrollcommand

  這幾乎總是某個捲軸元件的 `set()` 方法，但也可以是任何接受單一引數的元件方法。

wrap

  必須是以下之一：`"none"`、`"char"` 或 `"word"`。

### 繫結與事件

來自元件指令的 bind 方法允許你監視某些事件，並在該事件類型發生時觸發一個回呼函式。bind 方法的形式是：

    def bind(self, sequence, func, add=''):

其中：

sequence

  是一個表示目標事件類型的字串。（詳情請參閱 bind(3tk) man page，以及 John Ousterhout 的書 Tcl and the Tk Toolkit (2nd edition) 的第 201 頁）。

func

  是一個 Python 函式，接受一個引數，在事件發生時被呼叫。一個 Event 實例將作為引數傳遞。（以這種方式部署的函式通常稱為回呼。）

add

  是可選的，可以是 `''` 或 `'+'`。傳遞一個空字串表示此繫結將取代與此事件關聯的任何其他繫結。傳遞一個 `'+'` 意味著此函式將被新增到繫結到此事件類型的函式串列中。

舉例來說：

    def turn_red(self, event):
        event.widget["activeforeground"] = "red"

    self.button.bind("<Enter>", self.turn_red)

請注意在 `turn_red()` 回呼中如何存取事件的 widget 欄位。此欄位包含捕獲 X 事件的元件。下表列出了你可以存取的其他事件欄位，以及它們在 Tk 中的表示方式，這在參閱 Tk man pages 時可能很有用。

Tk | Tkinter 事件欄位 | Tk | Tkinter 事件欄位
--- | --- | --- | ---
%f | focus | %A | char
%h | height | %E | send_event
%k | keycode | %K | keysym
%s | state | %N | keysym_num
%t | time | %T | type
%w | width | %W | widget
%x | x | %X | x_root
%y | y | %Y | y_root

### index 參數

許多元件需要傳遞 "index" 參數。這些參數用於指向 Text 元件中的特定位置、Entry 元件中的特定字元，或 Menu 元件中的特定選單項目。

Entry 元件索引（index、view index 等）

  Entry 元件具有參照所顯示文字中字元位置的選項。你可以使用這些 `tkinter` 函式來存取文字元件中的這些特殊點：

Text 元件索引

  Text 元件的索引表示法非常豐富，在 Tk man pages 中有最好的描述。

選單索引（menu.invoke()、menu.entryconfig() 等）

  選單的某些選項和方法會操作特定的選單項目。每當選項或參數需要選單索引時，你可以傳入：

  - 一個整數，它參照元件中項目的數字位置，從頂部開始計數，從 0 開始；

  - 字串 `"active"`，它參照目前在游標下的選單位置；

  - 字串 `"last"`，它參照最後一個選單項目；

  - 一個以 `@` 為前綴的整數，如 `@6`，其中該整數被解釋為選單座標系統中的 y 像素座標；

  - 字串 `"none"`，表示完全沒有選單項目，最常用於 menu.activate() 以停用所有項目，最後，

  - 一個文字字串，它會與從選單頂部掃描到底部的選單項目標籤進行模式匹配。請注意，此索引類型在所有其他類型之後考慮，這意味著標籤為 `last`、`active` 或 `none` 的選單項目的匹配可能會被解釋為上述的字面值。

### 圖片

可以透過 `tkinter.Image` 的相應子類別來建立不同格式的圖片：

- `BitmapImage` 用於 XBM 格式的圖片。

- `PhotoImage` 用於 PGM、PPM、GIF 和 PNG 格式的圖片。後者從 Tk 8.6 開始支援。

任一類型的圖片都是透過 `file` 或 `data` 選項建立的（也有其他選項可用）。

在 3.13 版的變更: 新增了 `PhotoImage` 方法 `copy_replace()`，用於將一個區域從一個圖片複製到另一個圖片，可能帶有像素縮放和/或子採樣。將 from_coords 參數新增到 `PhotoImage` 方法 `copy()`、`zoom()` 和 `subsample()`。將 zoom 和 subsample 參數新增到 `PhotoImage` 方法 `copy()`。

然後，圖片物件可以在任何支援 `image` 選項的元件（例如標籤、按鈕、選單）中使用。在這些情況下，Tk 將不會保留對圖片的參照。當對圖片物件的最後一個 Python 參照被刪除時，圖片資料也會被刪除，Tk 將在使用該圖片的任何地方顯示一個空框。

也參考

Pillow 套件新增了對 BMP、JPEG、TIFF 和 WebP 等格式的支援。

## 檔案處理常式

Tk 允許你註冊和取消註冊一個回呼函式，當檔案描述器上可以進行 I/O 時，該函式將從 Tk 主迴圈中被呼叫。每個檔案描述器只能註冊一個處理常式。範例程式碼：

    import tkinter
    widget = tkinter.Tk()
    mask = tkinter.READABLE | tkinter.WRITABLE
    widget.tk.createfilehandler(file, mask, callback)
    ...
    widget.tk.deletefilehandler(file)

此功能在 Windows 上不可用。

由於你不知道有多少位元組可供讀取，你可能不想使用 `BufferedIOBase` 或 `TextIOBase` 的 `read()` 或 `readline()` 方法，因為這些方法會堅持讀取預定義數量的位元組。對於 sockets（通訊端），`recv()` 或 `recvfrom()` 方法可以正常運作；對於其他檔案，請使用原始讀取或 `os.read(file.fileno(), maxbytecount)`。

Widget.tk.createfilehandler(file, mask, func)

  註冊檔案處理常式回呼函式 func。file 引數可以是一個具有 `fileno()` 方法的物件（例如檔案或 socket（通訊端）物件），或是一個整數檔案描述器。mask 引數是下面三個常數中任意一個的 OR 組合。回呼函式被呼叫的方式如下：

      callback(file, mask)

Widget.tk.deletefilehandler(file)

  取消註冊檔案處理常式。

_tkinter.READABLE
_tkinter.WRITABLE
_tkinter.EXCEPTION

  用於 mask 引數的常數。