实战wxPython系列-012
一、wxPython中的事件
在wxPython中,事件(Event)是每个GUI应用程序不可分割的一部分,所有GUI应用程序都是事件驱动的。应用程序对在其生命周期内生成的不同事件类型做出反应。事件主要由使用应用程序的用户生成。但它们也可以通过其他方式产生,例如Internet连接、窗口管理器或计时器都会生成相应的事件。当我们调用MainLoop()方法时,我们的应用程序等待事件生成。在退出应用程序时结束MainLoop()方法。
在wxPython中,事件是来自底层框架(通常是GUI工具包)的应用程序级信息,在一个程序中,通过事件循环来等待和分发事件或消息。
事件对象(Event Object)是与事件相关联的对象,它通常表现为一个窗口,一个事件类型是一个已生成的独一无二的事件,通过将事件类型和事件处理程序进行绑定来实现对相应的事件的处理。
下面我们以一个移动事件wx.MoveEvent来演示当窗口移动到一个新的位置的时候,产生移动事件,通过事件绑定,在移动事件处理程序中将新的位置坐标显示出来。演示代码如下:
# 一个简单的事件演示样例
import wx
class SampleSimpleEvent(wx.Frame):
def __init__(self, *args, **kw):
super(SampleSimpleEvent, self).__init__(*args, **kw)
self.SetTitle("实战wxPython: 移动事件")
self.InitUi()
def InitUi(self):
wx.StaticText(self, label="x:", pos=(10, 10))
wx.StaticText(self, label="y:", pos=(10, 30))
self.stx = wx.StaticText(self, label="", pos=(30, 10))
self.sty = wx.StaticText(self, label="", pos=(30, 30))
self.Bind(wx.EVT_MOVE, self.OnMove)
self.SetSize((320, 240))
self.Centre()
def OnMove(self, evt):
x,y = evt.GetPosition()
self.stx.SetLabel(str(x))
self.sty.SetLabel(str(y))
def main():
app = wx.App()
sample = SampleSimpleEvent(None)
sample.Show()
app.MainLoop()
if __name__ == "__main__":
main()
运行效果如图1所示:
当我们拖动窗口的时候,在窗口上显示的x,y 坐标显示值就会发生改变,其实现就在方法OnMove中,它和wx.EVT_MOVE事件绑定,显示窗口移动后新的坐标值。
二、wxPython中的事件绑定
在wxPyton中使用事件的三个步骤是:
- 标识事件绑定器的名称, 如wx.EVT_SIZE, wx.EVT_CLOSE等;
- 创建一个事件处理程序,在事件生成时调用该方法来处理事件;
- 将事件和事件处理程序相绑定。
通过上述三个步骤,就可以在wxPython中处理一个事件,通过调用bind()方法,就可以实现将一个方法绑定到一个事件。
Bind(event, handler, source=None, id=wx.ID_ANY, id2=wx.ID_ANY)
参数 event是一个EVT_*对象,它用于指定事件类型;
handler为事件处理程序,就是程序绑定到事件的方法;
source 当我们希望区分来自不同控件的相同事件类型时,这时候需要使用source参数。
id参数在有多个按钮、菜单项等时使用。id用于区分它们。
id2 当需要将一个处理程序绑定到一个id范围时,例如EVT_MENU_RANGE,则使用id2。
注意:Bind()方法在类EvtHandler中定义,EvtHandler是wx.Window的基类,而wx.Window又是wxPython中绝大多数控件的基类。如果要解除事件绑定,可调用方法Unbind()来实现。
三、否决事件(Vetoing events)
在某些时候,我们需要终止事件处理,在这种情况下,我们可以调用方法Veto()来实现这个功能。
# 否决事件演示
import wx
class SampleVetoEvent(wx.Frame):
def __init__(self, *args, **kw):
super(SampleVetoEvent, self).__init__(*args, **kw)
self.InitUi()
def InitUi(self):
self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)
self.SetTitle("实战wxPython: 否决事件演示")
self.Centre()
def OnCloseWindow(self, e):
dlg = wx.MessageDialog(None, "确定要退出?", "问题", wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
ret = dlg.ShowModal()
if(ret == wx.ID_YES):
self.Destroy()
else:
e.Veto()
def main():
app = wx.App()
sample = SampleVetoEvent(None)
sample.Show()
app.MainLoop()
if __name__ == "__main__":
main()
在这个例子中,当我们处理wx.CloseEvent时,OnCloseWindow方法被调用。在许多应用程序中,我们希望防止在进行一些更改时意外关闭窗口。要做到这一点,我们可以绑定wx.EVT_CLOSE事件绑定器 来实现。通过在关闭窗口前,弹出一个消息对话框,根据消息对话框的返回值,来决定关闭应用,还是取消关闭应用。
上述样例的运行效果如图2所示:
四、wxPython事件传播
在wxPython中,有两种类型的事件,基本事件和命令事件,这两者的传播方式是不同的,命令事件可以传播,它沿子控件向父控件进行传播,基本事件则不会传播到父控件,比如对于wx.CloseEvent, 这是一个基本事件,将其传播到父控件是没有意义的。
在默认情况下,在事件处理程序中捕获事件后,事件将停止传播。为了继续传播,可以调用Skip()方法使事件继续传播。
下面的代码演示了事件是如何传播的。
#事件传播演示
import wx
class MyPanel(wx.Panel):
def __init__(self, *args, **kw):
super(MyPanel, self).__init__(*args, **kw)
self.Bind(wx.EVT_BUTTON, self.OnButtonClicked)
def OnButtonClicked(self, e):
print("event reached panel class")
e.Skip()
class MyButton(wx.Button):
def __init__(self, *args, **kw):
super(MyButton, self).__init__(*args, **kw)
self.Bind(wx.EVT_BUTTON, self.OnButtonClicked)
def OnButtonClicked(self, e):
print("event reached button class")
e.Skip()
class SampleEventPropagation(wx.Frame):
def __init__(self, *args, **kw):
super(SampleEventPropagation, self).__init__(*args, **kw)
self.InitUi()
def InitUi(self):
my_panel = MyPanel(self)
MyButton(my_panel, label="OK", pos=(15, 15))
self.Bind(wx.EVT_BUTTON, self.OnButtonClicked)
self.SetTitle("实战wxPython: 事件传播演示")
self.Centre()
def OnButtonClicked(self, e):
print("event reached frame class")
e.Skip()
def main():
app = wx.App()
sample = SampleEventPropagation(None)
sample.Show()
app.MainLoop()
if __name__ == "__main__":
main()
运行上述代码,点击OK按钮,在终端窗口中会输出如下信息:
event reached button class
event reached panel class
event reached frame class
可以看出,按钮事件从MyButton类传递到父类MyPanel,再传递到MyPanel父类Frame .
五、本文知识点
- 了解wxPython中的事件,及事件绑定;
- 了解否决事件;
- 了解事件的传播方法。
前一篇: wxPython - 布局管理之网格布局
请多多关注,评论,收藏,点赞,和转发。