wxPython之事件

事件是每个GUI应用程序必不可少的部分。所有的GUI应用程序都是事件驱动。应用程序响应在其生命周期里生成的不同类型的事件。事件主要由应用程序的用户生成。但是事件也有其他方式生成,比如网络连接,窗口管理器,定时器。所以当调用MainLoop()方法,应用程序等待事件生成。退出应用程序时,MainLoop()方法终止。

定义
事件(Event)是一个来自底层框架,特别是GUI工具包的应用层信息。事件循环是一种编程结构,用来在一个程序里等待和调度事件或消息。事件循环不断寻找事件去处理。调度器是一个过程,将事件映射给事件处理器(Event Handlders)。事件处理器是一群响应事件的方法。
事件对象是与事件相关联的对象。它通常是一个窗口。事件类型(Event Type)是已经发生的独特事件。事件绑定是一个对象,将一个事件类型和一个事件处理器绑在一起。

简单的事件例子
下面的部分将描述一个简单的事件。我们以一个move事件为例。当我们移动窗口到一个新位置,产生一个move事件。该事件类型是wx.MoveEvent。事件的绑定是wx.EVT_MOVE。
# -*- coding: utf-8 -*-

import wx

class Example(wx.Frame):

def __init__(self, *args, **kw):
super(Example, self).__init__(*args, **kw)

self.InitUI()

def InitUI(self):

wx.StaticText(self, label='x:', pos=(10,10))
wx.StaticText(self, label='y:', pos=(10,30))

self.st1 = wx.StaticText(self, label='', pos=(30, 10))
self.st2 = wx.StaticText(self, label='', pos=(30, 30))

self.Bind(wx.EVT_MOVE, self.OnMove)

self.SetSize((250, 180))
self.SetTitle('Move event')
self.Centre()
self.Show(True)

def OnMove(self, event):

x, y = e.GetPosition()
self.st1.SetLabel(str(x))
self.st2.SetLabel(str(y))

def main():

ex = wx.App()
Example(None)
ex.MainLoop()

if __name__ == '__main__':
main()
这个例子显示了窗口的当前位置。
self.Bind(wx.EVT_MOVE, self.OnMove)
这里我们把wx.EVT_MOVE事件binder绑定到OnMove()方法。
def OnMove(self, event):
      x, y = e.GetPosition()
     self.st1.SetLabel(str(x))
     self.st2.SetLabel(str(y))
OnMove()方法的event 参数是一个具体到某一特定事件类型的对象。在我们例子里,它是类wx.MoveEvent的实例。这个对象持有关于这个事件的信息。比如事件对象或者窗口的位置。在我们的例子里,事件对象是wx.Frame的部件。我们通过调用事件的GetPosition()方法来知道当前位置。
事件绑定
绑定事件在wxPython里是很容易的。分为三个步骤:
  • 定义event binder 名字:wx.EVT_SIZE,wx.EVT_CLOSE等
  • 创建事件处理器,即方法。当一个事件产生时,该方法被调用。
  • 把事件绑定到事件处理器上。
对于wxPython,可以通过调用Bind()方法绑定一个事件。这个方法有如下形参:
其中,event是 EVT_*对象的一种,它具体了事件的类型。handler是被调用的对象,也就是程序员绑定给事件的方法。source是用来区分来自不同部件的相同事件类型。id是用来区分多重按钮、菜单各项等。id2是用于可以绑定一个处理器给一系列id,比如EVT_MENU_RANGE。
方法Bind()定义在EvtHandler类里。wx.Window继承于EVTHandler。而在wxPython里,wx.Window是大多数部件类的基类。同时也有相反的过程。如果你想把一个方法与事件解除绑定,可以调用Unbind()方法。它和Bind()有相同的参数。

否决事件
有时候我们需要停止处理某个事件,可以调用Veto()方法来实现。
# -*- coding: utf-8 -*-
import wx

class Example(wx.Frame):
           
    def __init__(self, *args, **kw):
        super(Example, self).__init__(*args, **kw)
       
        self.InitUI()
               
    def InitUI(self):

        self.Bind(wx.EVT_CLOSE, self.OnCloseWindow)

        self.SetTitle('Event veto')
        self.Centre()
        self.Show(True)

    def OnCloseWindow(self, e):

        dial = wx.MessageDialog(None, 'Are you sure to quit?', 'Question',
            wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
           
        ret = dial.ShowModal()
       
        if ret == wx.ID_YES:
            self.Destroy()
        else:
            e.Veto()

def main():
   
    ex = wx.App()
    Example(None)
    ex.MainLoop()

if __name__ == '__main__':
    main()
在上面的例子里,我们产生一个wx.CloseEvent。当点击标题栏上的X按钮、按快捷键Alt+F4或者从系统菜单里选择关闭时,这个事件被调用。在很多应用程序里,如果我们做了一些变化,我们想阻止意外关闭窗口。为了实现这个,我们必须绑定wx.EVT_CLOSE event binder。
dial = wx.MessageDialog(None, 'Are you sure to quit?', 'Question',
wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION)
ret = dial.ShowModal()
在处理关闭事件的处理过程中,我们弹出一个消息框。
if ret == wx.ID_YES:
      self.Destroy()
else:
      event.Veto()
依赖对话框的返回值,我们销毁这个窗口,或者否决关闭事件。我们必须调用Destroy()方法来关闭窗口。通过调用Close()方法,我们结束不断循环。

事件传播
有两种类型事件:基本事件和命令事件。它们不同于传播方式。事件传播是指事件从子部件传到父部件和父窗口的父窗口等。基本事件不传播,命令事件传播。比如wx.CloseEvent是一个基本事件。它没有传到父窗口的一样。默认情况下, 这种事件在一个事件处理器里就停止传播。如果想继续传播,我们必须调用Skip()方法。
# -*- coding: utf-8 -*-
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 Example(wx.Frame):
           
    def __init__(self, *args, **kw):
        super(Example, self).__init__(*args, **kw)
        self.InitUI()
                
    def InitUI(self):

        mpnl = MyPanel(self)

        MyButton(mpnl, label='Ok', pos=(15, 15))

        self.Bind(wx.EVT_BUTTON, self.OnButtonClicked)

        self.SetTitle('Propagate event')
        self.Centre()
        self.Show(True)

    def OnButtonClicked(self, e):
       
        print 'event reached frame class'
        e.Skip()

def main():
   
    ex = wx.App()
    Example(None)
    ex.MainLoop()

if __name__ == '__main__':
    main()
在上面的例子,在panel上有一个按钮。这个panel被放置在一个窗口部件上。我们为所有的部件都定义了一个处理器。
def OnButtonClicked(self, e):

print 'event reached button class'
e.Skip()
在我们定制的按钮类处理按钮点击事件。Skip()事件把事件进一步传给panel类。
上面是运行结果。当我们点击按钮时,事件从按钮传到了panel,在传到frame。
可以尝试删掉一些Skip()方法,会发生什么。

窗口ID
窗口ID是整数,在事件系统里唯一决定窗口的标识符。有三种方法创建窗口ID。
  • 系统自动创建
  • 使用标准ID
  • 创建自己的ID
每个部件类都有一个id参数。这个数字在事件系统里是独一无二的。如果我们处理多重部件,必须区分它们。
如果设置id参数为-1或wx.ID_ANY,wxPyhton会自动创建id。这些自动创建的id通常是负数。然后用户创建的id通常必须为正。当我们不必修改部件的状态,常常使用这个选项。举例,在程序生命周期里,静态文本永远不会变化。我们也能够得到id,如果我们想。GetId()方法能够为我们获取id。
# -*- coding: utf-8 -*-

import wx

class Example(wx.Frame):

def __init__(self, *args, **kw):
super(Example, self).__init__(*args, **kw)

self.InitUI()

def InitUI(self):

pnl = wx.Panel(self)
exitButton = wx.Button(pnl, wx.ID_ANY, 'Exit', (10, 10))

self.Bind(wx.EVT_BUTTON, self.OnExit, id=exitButton.GetId())

self.SetTitle("Automatic id")
self.Centre()
self.Show(True)

def OnExit(self, event):

self.Close()

def main():

ex = wx.App()
Example(None)
ex.MainLoop()

if __name__ == '__main__':
main()
这个例子里,我们不关心id的实际数值。
通过调用GetId()方法得到自动生成的id。
推荐使用标准标识符。这些ID能够提供一些标准图形或行为在一些平台上。
# -*- coding: utf-8 -*-

import wx

class Example(wx.Frame):

def __init__(self, *args, **kw):
super(Example, self).__init__(*args, **kw)

self.InitUI()

def InitUI(self):

pnl = wx.Panel(self)
grid = wx.GridSizer(3, 2)

grid.AddMany([(wx.Button(pnl, wx.ID_CANCEL), 0, wx.TOP | wx.LEFT, 9),
(wx.Button(pnl, wx.ID_DELETE), 0, wx.TOP, 9),
(wx.Button(pnl, wx.ID_SAVE), 0, wx.LEFT, 9),
(wx.Button(pnl, wx.ID_EXIT)),
(wx.Button(pnl, wx.ID_STOP), 0, wx.LEFT, 9),
(wx.Button(pnl, wx.ID_NEW))])

self.Bind(wx.EVT_BUTTON, self.OnQuitApp, id=wx.ID_EXIT)

pnl.SetSizer(grid)

self.SetSize((220, 180))
self.SetTitle("Standard ids")
self.Centre()
self.Show(True)

def OnQuitApp(self, event):

self.Close()

def main():

ex = wx.App()
Example(None)
ex.MainLoop()

if __name__ == '__main__':
main()
在这个例子里按钮都是使用标准标识符。在Linux上,这些按钮都图标。
我们添加六个按钮到网格管理器。wx.ID_CANCEL,wx.ID_DELETE,wx.ID_SAVE,wx.ID_EXIT,wx.ID_STOP和wx.ID_NEW都是标准ID。
我们将按钮点击事件绑定到OnQuitApp()事件处理器上。这个ID用来与其他按钮区分开来。我们能够独一无二地标识这件事件源。
最后一个按钮使用自己定义的ID。
我们定义自己的全局ID。
# -*- coding: utf-8 -*-

import wx

ID_MENU_NEW = wx.NewId()
ID_MENU_OPEN = wx.NewId()
ID_MENU_SAVE = wx.NewId()

class Example(wx.Frame):

def __init__(self, *args, **kw):
super(Example, self).__init__(*args, **kw)

self.InitUI()

def InitUI(self):

self.CreateMenuBar()
self.CreateStatusBar()

self.SetSize((250, 180))
self.SetTitle('Global ids')
self.Centre()
self.Show(True)

def CreateMenuBar(self):

mb = wx.MenuBar()

fMenu = wx.Menu()
fMenu.Append(ID_MENU_NEW, 'New')
fMenu.Append(ID_MENU_OPEN, 'Open')
fMenu.Append(ID_MENU_SAVE, 'Save')

mb.Append(fMenu, '&File')
self.SetMenuBar(mb)

self.Bind(wx.EVT_MENU, self.DisplayMessage, id=ID_MENU_NEW)
self.Bind(wx.EVT_MENU, self.DisplayMessage, id=ID_MENU_OPEN)
self.Bind(wx.EVT_MENU, self.DisplayMessage, id=ID_MENU_SAVE)

def DisplayMessage(self, e):

sb = self.GetStatusBar()

eid = e.GetId()

if eid == ID_MENU_NEW:
msg = 'New menu item selected'
elif eid == ID_MENU_OPEN:
msg = 'Open menu item selected'
elif eid == ID_MENU_SAVE:
msg = 'Save menu item selected'

sb.SetStatusText(msg)

def main():

ex = wx.App()
Example(None)
ex.MainLoop()

if __name__ == '__main__':
main()
在这个例子,我们创建了三个菜单子项的菜单。这些菜单子项的ID都是全局的。
方法wx.NewId()生成一个新的独一无二地id。
这三个菜单子项都被它们的id唯一标识。
根据事件对象,我们重新得到id。根据id的数值,我们准备显示在程序状态栏的信息。

绘制事件
绘制事件在窗口被重绘的时候产生。当我们重新设置窗口尺寸或者最大化时窗口重新绘制。一个绘制绘制事件也可以编程产生。比如,当我们调用方法SetLabel()来改变wx.StaticText部件。注意最小化窗口时,没有绘制事件产生。
# -*- coding: utf-8 -*-

import wx

class Example(wx.Frame):

def __init__(self, *args, **kw):
super(Example, self).__init__(*args, **kw)

self.InitUI()

def InitUI(self):

self.count = 0
self.Bind(wx.EVT_PAINT, self.OnPaint)

self.SetSize((250, 180))
self.Centre()
self.Show(True)

def OnPaint(self, e):

self.count += 1
self.SetTitle(str(self.count))

def main():

ex = wx.App()
Example(None)
ex.MainLoop()

if __name__ == '__main__':
main()
在上面例子里,我们计算绘制事件的个数,把当前产生的事件总数显示在主窗口的标题上。
将EVT_PAINT事件绑定到OnPaint()方法。
在OnPaint()事件里,增加统计数并设置到主窗口的标题上。
焦点事件
焦点指示程序中当前被选中的部件。来自键盘或者剪切板的文本会传给具有焦点的部件。有两种与 焦点相关的事件类型。wx.EVT_SET_FOCUS事件当部件获得焦点时产生。当部件失去焦点时,wx.EVT_KILL_FOCUS时间产生。焦点被鼠标点击或者按键盘键,通常是Tal 或者 Shift + Tab。
# -*- coding: utf-8 -*-

import wx

class MyWindow(wx.Panel):

def __init__(self, parent):
super(MyWindow, self).__init__(parent)

self.color = '#b3b3b3'

self.Bind(wx.EVT_PAINT, self.OnPaint)
self.Bind(wx.EVT_SIZE, self.OnSize)
self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)

def OnPaint(self, e):

dc = wx.PaintDC(self)

dc.SetPen(wx.Pen(self.color))
x, y = self.GetSize()
dc.DrawRectangle(0, 0, x, y)

def OnSize(self, e):

self.Refresh()

def OnSetFocus(self, e):

self.color = '#0099f7'
self.Refresh()

def OnKillFocus(self, e):

self.color = '#b3b3b3'
self.Refresh()

class Example(wx.Frame):

def __init__(self, *args, **kw):
super(Example, self).__init__(*args, **kw)

self.InitUI()

def InitUI(self):

grid = wx.GridSizer(2, 2, 10, 10)
grid.AddMany([(MyWindow(self), 0, wx.EXPAND|wx.TOP|wx.LEFT, 9),
(MyWindow(self), 0, wx.EXPAND|wx.TOP|wx.RIGHT, 9),
(MyWindow(self), 0, wx.EXPAND|wx.BOTTOM|wx.LEFT, 9),
(MyWindow(self), 0, wx.EXPAND|wx.BOTTOM|wx.RIGHT, 9)])

self.SetSizer(grid)

self.SetSize((350, 250))
self.SetTitle('Focus event')
self.Centre()
self.Show(True)

def OnMove(self, e):

print e.GetEventObject()
x, y = e.GetPosition()
self.st1.SetLabel(str(x))
self.st2.SetLabel(str(y))

def main():

ex = wx.App()
Example(None)
ex.MainLoop()

if __name__ == '__main__':
main()
在上面的例子里,我们有四个panel。焦点到上面时 ,panel高亮。
我们绑定了两个焦点事件给事件处理器。
在OnPaint()方法里,我们在窗口上绘制。框边的颜色由窗口是否获得焦点决定。获得焦点的窗口边框被绘制成蓝色。
在OnSetFocus()方法中,将self.color变量设置一些蓝色颜色。当我们刷新主窗口,会为 它所有的子部件产生一个绘制事件。所有窗口被绘制,获得焦点的那个 边框变成新的颜色。
键盘事件
当我们按下键盘上的一个键,wx.KeyEvent被产生。该事件会 传给当前获得焦点的部件。有三种不同的按键处理器:
wx.EVT_KEY_DOWN
wx.EVT_KEY_UP
wx.EVT_CHAR
当Esc键被按下,通常产生一个关闭应用程序的请求。
# -*- coding: utf-8 -*-

import wx

class Example(wx.Frame):

def __init__(self, *args, **kw):
super(Example, self).__init__(*args, **kw)

self.InitUI()

def InitUI(self):

pnl = wx.Panel(self)
pnl.Bind(wx.EVT_KEY_DOWN, self.OnKeyDown)
pnl.SetFocus()

self.SetSize((250, 180))
self.SetTitle('Key event')
self.Centre()
self.Show(True)

def OnKeyDown(self, e):

key = e.GetKeyCode()

if key == wx.WXK_ESCAPE:

ret = wx.MessageBox('Are you sure to quit?', 'Question',
wx.YES_NO | wx.NO_DEFAULT, self)

if ret == wx.YES:
self.Close()

def main():

ex = wx.App()
Example(None)
ex.MainLoop()

if __name__ == '__main__':
main()
在这个例子里,我们处理Esc键被按下。无论我们是否真地关闭应用程序,会显示一个信息对话框让却确认。
我们给EVT_KEY_DOWN事件绑定一个 时间处理器。
上面我们得到被按下键的键码。
我们检查键码。Esc键的键码是wx.WXK_ESCAPE。

英文地址:http://zetcode.com/wxpython/events/
  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值