在一个事件驱动系统(event-driven system)中,事件(event)是用来连接框架内的动作和与事件相关的回调函数(callback function)的。基于事件驱动框架的应用通过利用事件来了解何时来对用户或系统触发的动作来做出反应。在一个用户接口中(user interface),事件是一个来了解何时按钮被按下,何时菜单被选择或用户与应用交互过程中其它广泛的动作。
正如你所看到的那样,在创建一个应用的过程中了解如何应对应用生命周期内的事件是十分重要的。
处理事件
wxPython是一个事件驱动系统。该系统的应用是十分直接和普遍的。处理事件的基本模式是相似的,而无论你的应用与之互动的控件及事件类型。这一部分我们将介绍在wxPython中处理事件的基础。
How to do it?
import wx
class MyFrame(wx.Frame):
def __init__(self, parent, *args, **kwargs):
super(MyFrame, self).__init__(parent, *args, **kwargs)
self.panel = wx.Panel(self)
self.btn1 = wx.Button(self.panel, label = "Push Me")
self.btn2 = wx.Button(self.panel, label = "Push Me too")
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.btn1, 0, wx.ALL, 10)
sizer.Add(self.btn2, 0, wx.ALL, 10)
self.panel.SetSizer(sizer)
self.Bind(wx.EVT_BUTTON, self.OnButton, self.btn1)
self.Bind(wx.EVT_BUTTON, lambda event: self.btn1.Enable(not self.btn1.Enabled), self.btn2)
sizer.Fit(self)
def OnButton(self, event):
event_id = event.GetId()
event_obj = event.GetEventObject()
print "Button 1 Clicked: "
print "ID=%d" % event_id
print "object=%s" % event_obj.GetLabel()
if __name__ == "__main__":
app = wx.PySimpleApp(False)
MyFrame(None).Show()
app.MainLoop()
How it works?
在上面代码中值得留意的是那两个Bind函数调用。Bind方法是用来连接一个事件处理函数(event handler)和一个可能发送去控件的事件。事件通常在窗口层级中向上传播而非向下。在这个例子中,我们把按钮事件(button event)绑定在框架上(Frame),但是事件实际上是由面板(panel)的子控件按钮(Button)产生的。框架对象(Frame)是在层级的顶部,其包含了面板(Panel),而面板则是两个按钮(button)的容器。正因如此,所以当事件没有被按钮或面板处理时,它将传递到框架上,在这里我们OnButton处理函数被调用。
Bind方法有两个必要的参数
- 事件对象(EVT_FOO)
- 一个以事件对象为第一个参数的可调用对象。这个事件处理函数将在事件发生时被调用
对于我们第二个按钮,我们用lambada函数来作为一个简便的事件处理函数,而非重新定义一个函数。这是一个吃力简单动作的便捷方法。
理解事件传递
在wxPython中有两类行为不同的事件类型(event object):
How to do it?
How it works?
事件处理函数链是从事件触发的控件开始被调用的。在这次例子中,这将是我们的两个按钮。窗口的每个层级都绑定这一个处理函数,它将接受任意一个按钮的事件。当点击第一个按钮,将展示出所有被调用的处理函数。这是因为对于第一个按钮我们调用了Skip方法。Skip方法将把事件传播到层级中更高的一级。从打印在终端上的三条语句我们就可以验证这一点。与之对比,在点击第二个按钮的时候由于没有调用Skip方法,所以只有一个处理函数被调用。
在wxPython中有两类行为不同的事件类型(event object):
- 基本事件(Events)
- 命令事件(Command Events)
How to do it?
import wx
ID_BUTTON1 = wx.NewId()
ID_BUTTON2 = wx.NewId()
class MyApp(wx.App):
def OnInit(self):
self.frame = MyFrame(None, title = "Event Propagation")
self.SetTopWindow(self.frame)
self.frame.Show()
self.Bind(wx.EVT_BUTTON, self.OnButtonApp)
return True
def OnButtonApp(self, event):
event_id = event.GetId()
if event_id == ID_BUTTON1:
print "BUTTON ONE Event reached the App Object"
class MyFrame(wx.Frame):
def __init__(self, parent, *args, **kwargs):
super(MyFrame, self).__init__(parent, *args, **kwargs)
self.panel = MyPanel(self)
self.btn1 = wx.Button(self.panel, ID_BUTTON1, "Propagates")
self.btn2 = wx.Button(self.panel, ID_BUTTON2, "Doesn't Propagates")
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.btn1, 0, wx.ALL, 10)
sizer.Add(self.btn2, 0, wx.ALL, 10)
self.panel.SetSizer(sizer)
sizer.Fit(self)
self.Bind(wx.EVT_BUTTON, self.OnButtonFrame)
def OnButtonFrame(self, event):
event_id = event.GetId()
if event_id == ID_BUTTON1:
print "BUTTON One event reached the Frame"
event.Skip()
elif event_id == ID_BUTTON2:
print "BUTTON TWO event reached the Frame"
class MyPanel(wx.Panel):
def __init__(self, parent):
super(MyPanel, self).__init__(parent)
self.Bind(wx.EVT_BUTTON, self.OnPanelButton)
def OnPanelButton(self, event):
event_id = event.GetId()
if event_id == ID_BUTTON1:
print "BUTTON One event reached the Panel"
event.Skip()
elif event_id == ID_BUTTON2:
print "BUTTON Two event reached the Panel"
if __name__ == "__main__":
app = MyApp(False)
app.MainLoop()
How it works?
事件处理函数链是从事件触发的控件开始被调用的。在这次例子中,这将是我们的两个按钮。窗口的每个层级都绑定这一个处理函数,它将接受任意一个按钮的事件。当点击第一个按钮,将展示出所有被调用的处理函数。这是因为对于第一个按钮我们调用了Skip方法。Skip方法将把事件传播到层级中更高的一级。从打印在终端上的三条语句我们就可以验证这一点。与之对比,在点击第二个按钮的时候由于没有调用Skip方法,所以只有一个处理函数被调用。