核心 ==== 要使用 tkouter,我們通常都會從 tkouter 中匯入所有的元件: :: from tkouter import * 當然我們也可以只匯入需要用的部分,例如: :: from tkouter import TkOutWidget 繼承 TkOutWidget 來實作自訂元件 ------------------------------- ``tkouter.core.TkOutWidget`` 是 tkouter 最核心的 class,我們實作的元件都必需繼承自該類別。 一個最基本的 ``TkOutWidget`` 可能長成下面這個樣子: :: class HelloWorld(TkOutWidget): layout = """ hello world """ def hello(self): messagebox.showinfo('welcome to tkouter', 'hello world') 我們透過設定類別變數 ``layout``,來給予該元件一個 html 佈局,tkouter 會自動根據該 html 來生成相應的佈局。 我們也可以將這段 html 放到一個單獨的文件 ``hw.html`` 裡面,然後改成指定文件名的方式來讓 tkouter 找到 html 佈局: :: class HelloWorld(TkOutWidget): layout = "hw.html" def hello(self): messagebox.showinfo('welcome to tkouter', 'hello world') 寫完了 ``TkOutWidget`` 類別之後,我們只要將之實例化,便能將該元件生出來並且做好佈局,最後呼叫 tkinter root 的 mainloop 來啟動 GUI 應用。 >>> from tkinter import Tk >>> root = Tk() >>> hw = HelloWorld(root) >>> hw.pack() >>> root.mainloop() 下面簡列該類別中,五個可設定的 class variable,我們將在各個章節介紹他們: ============= ========= name 說明 ============= ========= widgets 這是一個 dictionay,key 是可使用的標籤名,value 是該標籤對應生成的 tk 元件。 loader html 佈局和 css 等靜態文件的載入器,使用者可以將之設定為任何一個可用的 Jinja2 載入器。 layout 描述佈局的 **html 字串** 或是 **html 文件名稱**,目前僅支援 ``.html`` 和 ``.xml`` 兩種文件類型。 context 這是一個 dictionay,用來進行 html 佈局的 **渲染 (render)**。 data_context 這是一個 dictionay,用來進行 html 佈局的 **綁定 (bind)**。 ============= ========= 接下來,我們會陸續介紹 tkouter 如何實現佈局的,同時也會指出一些設定上要注意的事項。 原理 ---- 整個佈局的原理是這樣子的: 1. ``TkOutWidget`` 本身是 ``Frame`` 元件的子類別,當我們實體化一個 ``TkOutWidget`` 物件的時候,我們會生成一個 ``Frame`` 元件。而在 ```` 標籤其下的元件會 pack 或 grid 到這個 ``Frame`` 底下。 2. ````、```` 和 ```` 標籤不會做任何事情,他們只是用來界定範圍用的。 3. ```` 標籤底下的內容都是用來設置頂層資訊或菜單的,詳見之後的介紹。 4. ```` 標籤底下只允許元件標籤和佈局標籤的存在,tkouter 首先根據元件標籤產生對應的 tk 元件並將之賦值給對應的變數,再根據佈局標籤,呼叫對應的 ``pack`` 和 ``grid`` 方法來做佈局。 .. note:: 元件在生成的時候會以其上層標籤對應的元件為 parent (master),而 TkOutWidget 元件本身的 parent 在其被實例化時指定。 標籤 ---- 接著我們來談佈局上的標籤。 標籤的結構與屬性 ~~~~~~~~~~~~~~~~ 由於 tkouter 的核心引擎是一個 XML parser,所以能夠使用的標籤只有: 1. 成對出現的 start tag 和 end tag,前者如 ````,後者如 ````。 2. 單獨出現的 startend tag,也就是所謂的封閉標籤,如 `` 如同標準的 html,一個 tkouter 標籤具有下列成分: **標籤名稱** 在標籤最一開頭的單一字串,標籤名稱必須是 tkouter 預設支援的標籤或是使用者自行註冊的元件標籤。 **標籤屬性** 以 ``屬性名="屬性值"`` 的形式出現,各屬性間由 *空白* 隔開。 其中 ``class`` 和 ``id`` 屬性是所有標籤皆可設定的屬性,其作用是能讓我們使用 CSS selector 去選取元件。 而名稱中有 ``-`` 字元的屬性我們稱為 **特殊屬性** 或 **元件方法屬性**,我們會在 `元件標籤 <元件標籤_>`_ 中介紹到。 **標籤內文** 夾在成對標籤中的純文字。 對於元件標籤而言,標籤內文會成為其 ``text`` 屬性的值,而對於菜單標籤而言,該文字會成為 ``label`` 屬性的值。 也就是說 ```` 基本上等價於 `` .. note:: 更多的佈局概念請參見 `佈局 <佈局_>`_ 菜單標籤 ~~~~~~~~ 若想要為 tk 元件設定菜單,可使用以下相關的菜單標籤: ```` 用以產生一層的菜單,其下可以有巢狀的菜單結構,tkouter 會自動將之轉為多級菜單。 此標籤必須成對出現,並放置於 ```` 標籤之內。 最外層的 ```` 我們稱為 **頂層菜單**,其下的菜單我們稱為 **子菜單**,一個 tkouter 元件中只允許出現一個頂層菜單。 此標籤下只允許出現 ````、````、````、```` 和 ```` 五種標籤。 ```` 菜單命令標籤,通常會綁定一個元件方法,當其被選取的時候,呼叫對應的方法。 ```` 會產生一條分隔線。 ```` 會產生菜單單選按鈕。 ````: 會產生核取方塊按鈕。 .. note:: 1. 當 tkouter 元件的 parent 是 top-level 元件的時候,才能為其設置菜單。 2. ```` 和 ```` 是唯二可以同時用在 ```` 和 ```` 之下的標籤。 被使用在菜單時,不會真的產生 tk 元件,但是出現在 ```` 標籤之下,則會產生真正的元件。 3. ````、```` 和 ```` 標籤中的 **標籤內文** 會成為菜單的 ``label`` 屬性。 元件標籤 ~~~~~~~~ 元件標籤具有下列標籤屬性: **name** 生成元件會被指派的變數名字,基本上要符合 python 的變數命名法則。 當此項屬性未設定時,tkouter 預設會以 ``_`` 作為該元件標籤的 ``name`` 屬性。 比如說,第一個未給定 ``name`` 屬性的 `` 這個元件的 ``name = "button_1"``、``type = "button"`` 且 ``text = "My Button"``,以下是等價的 tkinter 代碼: :: # self is tkouter widget, instance of class "TkOutWidget" self.button_1 = Button(text="My Button") .. note:: 1. 元件標籤中的 **標籤內文** 會成為元件的 ``text`` 屬性。 2. tkouter 不單單只能進行一次性的轉換佈局,其自動生成的元件也會賦值到指定的 ``name`` 屬性, 使用者可以在 python 代碼中用一般的方式來調整和操作元件。 佈局 ---- tkouter 目前支援兩種佈局方法,分別是 **pack 佈局** 和 **grid 佈局**。 pack 佈局 ~~~~~~~~~~ pack 佈局能做出相對複雜的佈局,且在佈局的語法上相對簡潔,是比較推薦的佈局方法。 我們用下面這個例子來說明: :: 他對應 tkouter 一律對沒有使用 grid 佈局的元件進行 pack 的動作,而且會根據元件的 parent 決定好 pack side。 **pack** 生成元件會被指派的變數名字,基本上要符合 python 的變數命名法則。 .. note:: 就如同一般的 web design 會使用 ``
`` 來組織垂直的排列佈局、使用 `` 來組織水平的排列佈局一樣, tkouter 使用 ```` 和 ```` 實作垂直的排列佈局、使用 ```` 和 ```` 來實現水平的排列佈局。 grid 佈局 ~~~~~~~~~~ 渲染 ---- 綁定 ---- 透過 `渲染 <渲染_>`_ 可以讓我們在佈局模板載入的當下進行靜態的作圖具象,而綁定可以讓我們進行動態的連結, 這對於有綁定 GUI 事件的元件尤其重要。 因為渲染是一次性的,且所有的物件只能被轉為文字輸出,這對於需要綁定非字串物件的 GUI 程式而言,顯然不敷使用。 在 tkouter 的佈局中,我們可以利用單層的大括號 ``{}`` 並使用類別變數 ``data_context`` 所提供的實例來與元件進行綁定。 .. note:: ``data_context`` 是一個 dictionary,其 key 為能在大括號 ``{}`` 中使用的變數,其值為該變數對應的物件。 若 ``data_context`` 未被設定,預設值為 ``{'self': self}``,也就是我們能在佈局中使用 ``self`` 來代稱 tkouter widget。 同時也能夠透過 ``.`` 來存取其屬性和成員。 舉例來說,如果 tkouter widget 有一個方法成員 ``hello`` 而我們想要將其綁定到一個按鈕元件上,可以這樣做: :: class MyWidget(TkOutWidget): layout = """ """ def hello(self): print("Hello") 這個佈局可能的 tkinter 等價代碼是: :: self.button_0 = Button(text="Print Hello", command=self.hello) 又或者 ``hello`` 是一個獨立的全域函數,則我們可以自行調整 ``data_context`` 來綁定該函數: :: def hello(): print("Hello") class MyWidget(TkOutWidget): data_context = {'hello': hello} layout = """ """