PyGTK GUI 程式設計/入門
本章旨在使讀者熟悉 PyGTK 模組的基本類和函式,以及如何將它們連結在一起。首先,開啟一個互動式 Python shell(例如啟動 IDLE,或在終端的命令列中鍵入`python`),並輸入以下內容(區分大小寫)
import gtk
window = gtk.Window()
label = gtk.Label("Hello, world!")
window.add(label)
window.show_all()
|
如果您將上面的程式碼輸入到一個檔案中並執行該檔案(透過使用檔案作為引數呼叫直譯器,或者透過其他一些 IDE 間接呼叫),那麼可能會發生什麼都沒有發生。這是因為在 `window.show_all()` 呼叫之後,直譯器(以及 PyGTK 應用程式)正在終止。為了使這個例子在這種情況下工作,只需在最後追加 `gtk.main()` 即可,更多細節請參見“訊號和回撥”部分。
| PyGTK 的一些版本中存在一個錯誤,會導致 IDLE 在使用 `gtk` 模組時崩潰。如果您啟動 IDLE,開始輸入上面的行,並且突然 IDLE 視窗關閉或消失 - 通常發生在您在一行末尾按 [Enter] 時 - 那麼您就遇到了這個錯誤!開啟一個命令 shell/終端,鍵入“python”命令,按 [Enter],然後再次啟動上面的指令碼。 | |
| 技術說明 |
這段程式碼建立了一個視窗,其中包含“Hello, world!”文字(見截圖)。

- 第 1 行:這將匯入 gtk 庫,以便我們訪問構建 GUI 所需的類和函式。
- 第 3 行:建立一個新的 `Window` 物件,並將其分配給變數 `window`。請注意,預設情況下,這不會立即在螢幕上顯示視窗 - 通常的做法是在所有部件都新增到視窗後才進行。
- 第 4 行:建立一個新的 `gtk.Label` 部件。第一個引數是要在標籤中顯示的字串。
- 第 6 行:`Window` 的 `add()` 方法是將部件附加到它的其中一種方法。
- 第 8 行:`Window` 類的 `show_all()` 方法呼叫其所有子部件(已放置在視窗內部的部件)上的 `show()` 方法,然後呼叫包含視窗本身的 `show()` 方法。
GTK 物件和容器
[edit | edit source]
在上面的示例程式碼中,`gtk.Window()` 物件明顯地表示 GUI 視窗,部件 `gtk.Label` 使用 `add()` 方法新增到該物件。但是,`add` 方法只允許將單個物件新增到視窗。由於您通常希望將多個部件放入一個視窗,因此需要新增某種容器物件,例如 `gtk.VBox`。`gtk.VBox` 物件可以包含和組織多個部件到一個**垂直**列中。類似地,`gtk.HBox` 物件也可用,它將子部件組織到一個**水平**行中。為了建立更復雜的佈局,您可以巢狀容器物件,即在一個 `gtk.VBox` 內部有一個 `gtk.HBox`。
這些物件的用法在右側的插圖中展示。`gtk.WindowGroup` 物件是可選的,但允許您的應用程式顯示多個 `gtk.Window`。本章“多個視窗”解釋瞭如何使用它;目前,我們將堅持使用一個視窗。
這是一個簡單的 PyGTK 程式的示例,它利用 `gtk.VBox` 和 `gtk.HBox` 物件將多個部件放到螢幕上 - 嘗試在互動式 Python 或 IDLE 直譯器中鍵入以下內容
import gtk
window = gtk.Window()
entry = gtk.Entry()
button_ok = gtk.Button("OK")
button_cancel = gtk.Button("Cancel")
vbox = gtk.VBox()
vbox.pack_start(entry)
hbox = gtk.HBox()
hbox.pack_start(button_ok)
hbox.pack_start(button_cancel)
vbox.pack_start(hbox)
window.add(vbox)
window.show_all()
|
上面的程式碼的結果應該是帶有輸入框和兩個按鈕的視窗。

- 第 3 行:建立一個新的 `gtk.Window` 物件。
- 第 4 行:建立一個新的 `gtk.Entry` 物件(輸入框)。
- 第 5 & 6 行:建立兩個新的 `gtk.Button` 物件,文字作為第一個引數。
- 第 8 行:建立一個新的 `gtk.VBox` 物件。稍後將其新增到視窗,並將包含視窗中的所有部件。
- 第 9 行:容器上的 `.pack_start()` 方法將作為其第一個引數傳遞的部件新增到容器的開頭(在頂部)。與視窗的 `.add()` 方法不同,這允許在多次呼叫時新增多個部件。`.pack_end()` 方法執行相同操作,但從容器的末尾(底部)開始放置物件,因此後續呼叫將部件放置在之前的部件之上。
- 第 10 行:建立另一個容器物件。
- 第 11 & 12 行:使用 `.pack_start()` 方法將按鈕部件新增到這個新的 `hbox` 容器中。
- 第 13 行:透過在 `vbox` 容器上呼叫 `.pack_start()` 方法,將 `hbox` 容器巢狀到 `vbox` 容器中。
- 第 15 行:使用視窗的 `.add()` 方法將 `vbox` 容器新增到視窗。
- 第 16 行:顯示所有物件,因此您現在可以在螢幕上看到視窗。
訊號和回撥
[edit | edit source]為了使您的應用程式對 GUI 做出反應,您可以利用發生在部件上的**事件**,並告訴 PyGTK 在該事件發生時執行一個函式(稱為**回撥函式**)。例如,當用戶點選一個按鈕時,該特定部件的“clicked”事件被髮出,如果您已將其連線到一個回撥函式,則該函式就會執行。
要將事件與函式連線,請在將要發出事件的部件上呼叫 `.connect()` 方法。此方法的語法如下:
handler_id = widget.connect(event_name, callback_function[, function_data])
`.connect()` 的返回值可以被捕獲,以便可以撤銷連線。
- `event_name` 引數應該是一個字串,表示要連線的事件的名稱。這在部件之間有所不同;例如,`gtk.Button` 部件在被點選時會發出一個“clicked”事件。
- `callback_function` 應該是一個對您希望 PyGTK 在發出 `event_name` 時執行的回撥函式的引用。不要在名稱末尾新增括號 (),也不需要將其括在字串中。
- 可選的 `function_data` 引數可以包含傳遞給回撥函式的其他引數。
回撥函式應該採用以下形式:
def callback_function(widget, callback_data=None)
函式的名稱及其引數可以呼叫任何內容。
- 第一個引數 `widget` 是對發出訊號的部件物件的引用。
- 第二個引數 `callback_data` 包含作為部件的 `connect()` 方法的最後一個引數指定的 `callback_data`(如果提供)。
最後,為了確保處理您的事件,請在應用程式的末尾新增以下程式碼
gtk.main()
`.gtk.main()` 函式監聽事件,並執行已連線到它們的任何回撥函式。此函式一直執行,直到視窗被作業系統視窗管理器關閉,或者執行 `.gtk.main_quit()`。
下面是上面示例程式碼的修改版本,其中定義了一些回撥函式,並將其連線到按鈕事件。
import gtk
# Define the callback functions first:
def exit(widget, callback_data=None):
window.hide_all()
gtk.main_quit()
window = gtk.Window()
entry = gtk.Entry()
button_ok = gtk.Button("OK")
button_cancel = gtk.Button("Cancel")
# Connect events to callback functions:
button_cancel.connect("clicked", exit)
vbox = gtk.VBox()
vbox.pack_start(entry)
hbox = gtk.HBox()
hbox.pack_start(button_ok)
hbox.pack_start(button_cancel)
vbox.pack_start(hbox)
window.add(vbox)
window.show_all()
# Put gtk.main() last so our callback functions are used.
gtk.main()
|
- 第 4-6 行:這是一個回撥函式,它使用 `.hide()` 方法關閉視窗並停止 `gtk.main()` 迴圈。雖然 `callback_data` 引數沒有被使用,但通常在所有回撥函式中包含它很有幫助,以防它在某些時候需要。
- 第 14 行:`.connect()` 方法捕獲來自 `button_cancel` 部件的“clicked”事件,並將其傳送到我們之前定義的 `exit()` 回撥函式。
- 第 26 行:`.gtk.main()` 函式啟動 `主迴圈`,它將事件/訊號傳送到正確的回撥函式。
整合所有
[edit | edit source]以下程式碼使用上面討論的所有方法來建立一個非常簡單的 GTK 應用程式,它會詢問您的姓名,並將它列印到控制檯中。將以下文字複製並儲存到一個檔案中,然後在命令列中執行 `python example.py`(用您剛建立的檔案的名稱替換 `example.py`)。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import pygtk
pygtk.require('2.0')
import gtk
class Application():
def __init__(self):
self.window = gtk.Window()
self.window.set_title("Example GTK Application")
self.create_widgets()
self.connect_signals()
self.window.show_all()
gtk.main()
def create_widgets(self):
self.vbox = gtk.VBox(spacing=10)
self.hbox_1 = gtk.HBox(spacing=10)
self.label = gtk.Label("Your Name:")
self.hbox_1.pack_start(self.label)
self.entry = gtk.Entry()
self.hbox_1.pack_start(self.entry)
self.hbox_2 = gtk.HBox(spacing=10)
self.button_ok = gtk.Button("OK")
self.hbox_2.pack_start(self.button_ok)
self.button_exit = gtk.Button("Exit")
self.hbox_2.pack_start(self.button_exit)
self.vbox.pack_start(self.hbox_1)
self.vbox.pack_start(self.hbox_2)
self.window.add(self.vbox)
def connect_signals(self):
self.button_ok.connect("clicked", self.callback_ok)
self.button_exit.connect("clicked", self.callback_exit)
def callback_ok(self, widget, callback_data=None):
name = self.entry.get_text()
print name
def callback_exit(self, widget, callback_data=None):
gtk.main_quit()
if __name__ == "__main__":
app = Application()
|
此應用程式的結構在單個類 `Application` 內。這種方法允許 GTK 部件和其他物件的名字駐留在它們自己的名稱空間中,並且還可以讓程式設計師在其他指令碼中匯入該檔案,並由程式設計師決定何時建立和顯示 GUI。
- 第 4 & 5 行:這兩行必須放在 `import gtk` 語句之上。它們處理 GTK+ 工具包在建立 GUI 之前需要執行的所有初始化和版本檢查。雖然我們在之前的示例中省略了它,但您仍然應該在自己的程式中使用它,以避免潛在的重繪問題。
- 第 12 行:`__init__()` 方法在類構造時被呼叫,它執行以下操作來設定應用程式
- 第 13 & 14 行:建立一個 `gtk.Window` 物件,並使用它的 `.set_title()` 方法告訴 GTK 在視窗標題欄中顯示什麼字串。
- 第 16 行:執行 **create_widgets()** 方法,該方法建立所有容器和視窗部件,並將視窗部件打包到正確的容器中。
- 第 17 行:執行 **connect_signals()** 方法,該方法將按鈕上的“單擊”事件連線到幾個回撥方法。
- 第 19 和 20 行:遞迴地顯示視窗中的所有視窗部件,然後執行 **gtk.main()** 迴圈以響應事件。
- 第 49 行:此回撥方法連線到我們“確定”按鈕上的“單擊”事件,使用 **gtk.Entry** 視窗部件的 **get_text()** 方法獲取文字,並將其列印到控制檯。請注意,**回撥函式** 和 **回撥方法**(此處使用)之間的區別在於,回撥方法在開頭有一個額外的引數 **self**,因為它是在類中定義的,因此 Python 直譯器會將該類的屬性和方法傳遞給它。
- 第 55 行:此回撥方法透過停止 **gtk.main()** 迴圈來退出應用程式。請注意,它不必在視窗物件上執行 **.hide_all()** 方法,因為視窗在應用程式停止執行時會銷燬。
