"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，而且可能已經嚴重過時。

也參考:

  * 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*/*slave* 反映了容器結構。

   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）用於指定元件在其容器（它們共同的 *master*）內的相對
位置。與較為繁瑣的 *placer*（較不常用，我們在此不予介紹）相比，packer
採用定性的關係規範——*上方*、*左方*、*填滿*等——並為你計算出確切的放置座
標。

任何 *master* 元件的大小由其內部的「slave 元件」的大小決定。packer 用
於控制 slave 元件在被 packed 的 master 內部出現的位置。你可以將元件
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 將每個 slave 放置在其區域中的位置。

expand
   布林值，"0" 或 "1"。

fill
   合法值："'x'"、"'y'"、"'both'"、"'none'"。

ipadx 和 ipady
   一個距離——指定 slave 元件每側的內部填充。

padx 和 pady
   一個距離——指定 slave 元件每側的外部填充。

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* 引數的常數。
