PyGTK GUI 编程/信号
正如你在上一章中学到的,信号由小部件发出,以允许你的应用程序响应特定的操作,例如按钮按下。这些响应是通过编写以下形式的函数来创建的
def callback_func(widget, callback_data=None)
并使用小部件的 connect 方法将其注册到特定的小部件和信号
widget.connect(signal_name, callback_function, callback_data)
其中 signal_name 是一个字符串,指示要响应的信号的名称,callback_function 是对你的回调函数的引用(不带括号和参数),callback_data 是一个可选的任意对象,传递给你的回调函数。
如果你在 Python 类中构建你的应用程序,如上一章所示,那么你的回调“方法”将需要一个额外的参数作为对类实例的引用
class App: ... snip ... def callback_method(self, widget, callback_data): ... snip ...
出于组织目的,通常建议将所有回调方法分组在类的末尾,或以 _callback 为后缀。
GTK+ 事件类似于 信号:它们由特定的小部件“发出”,并且可以通过回调函数处理。就 PyGTK 程序员而言,唯一的区别是回调函数的参数及其返回值。举个例子,我们将使用 gtk.Button 小部件发出的“button_pressed_event”,它可以像连接信号一样连接到回调函数
button.connect('button_press_event', callback_func, callback_data)
其中 button 是 gtk.Button 对象的一个实例。callback_data 参数,如上一章所述,是可选的。回调函数的定义包含三个参数:发出信号的小部件的引用、事件和回调数据
def callback_func(widget, event, callback_data=None)
此函数返回的值 必须 是一个布尔值(与信号的回调函数不同)。此返回值是 GTK+ 事件机制中的重要信息
- False 表示事件未完全处理,因此 GTK+ 应该继续执行发生此事件时通常执行的操作,并且信号应该进一步传播。
- True 表示事件已完全处理,GTK+ 不再需要对事件进行任何进一步的处理。
例如,“delete_event”事件是从 gtk.Window 发出的,当用户尝试关闭窗口时;如果我们将回调函数连接到此,并且函数返回 False,GTK+ 将继续关闭窗口并发出“destroy”信号。但是,如果我们的回调函数返回 True,GTK+ 不会自行关闭窗口或发出“destroy”信号。这使我们能够在有人尝试关闭窗口时进行干预,因此我们有机会询问他们是否要保存他们的工作,然后通过从我们的回调函数返回适当的值来指示 GTK+ 关闭窗口或保持打开状态。
GTK+ 工具包被称为“事件驱动”,因为它在一个循环中休眠,直到发出信号或事件,此时它将信号添加到 队列 的末尾。GTK+ 的另一个部分从队列的顶部逐个获取信号,并运行与信号或事件注册的任何回调函数,然后再移动到队列中的下一项。这就是你需要运行 gtk.main() 函数的原因,它用于防止你的应用程序在设置完 GUI 后立即停止,并在发出信号和事件时运行你使用 .connect() 注册的任何回调函数。
但是,需要注意的是,GTK+ 不会在新线程中运行回调函数;如果一个回调函数需要很长时间才能运行,那么 GUI 的其余部分将保持无响应,直到该函数完成。以下是一个快速示例来演示这一点
#!/usr/bin/env python
import pygtk
pygtk.require('2.0')
import gtk
import time
class App:
def __init__(self):
self.window = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
self.hbox = gtk.HBox()
self.button_print = gtk.Button(label='Print something')
self.button_sleep = gtk.Button(label='Hang for 5 seconds')
self.hbox.pack_start(self.button_print)
self.hbox.pack_start(self.button_sleep)
self.button_print.connect('clicked', self.print_hi)
self.button_sleep.connect('clicked', self.sleep)
self.window.connect('destroy', self.quit)
self.window.add(self.hbox)
self.window.show_all()
gtk.main()
def print_hi(self, widget, callback_data=None):
print 'Hi there!'
def sleep(self, widget, callback_data=None):
time.sleep(5)
def quit(self, widget, callback_data=None):
gtk.main_quit()
if __name__ == "__main__":
app = App()
如果你从终端运行上面的程序,你应该看到一个有两个按钮的窗口。按下第一个按钮会导致“Hi there!”在终端上打印出来。如果你按下第二个按钮,回调函数会挂起五秒钟,这意味着通过快速连续按下第二个按钮和第一个按钮,“Hi there!”消息将在 5 秒后才会打印出来,这表明信号是如何添加到队列中,并且这些信号是逐个执行的。