这个问题解决了使用wxPython发布事件所需的EvtHandler
的非常具体的问题。
我使用Python 2.7。在下面的例子中,我有两种类型的事件:
-
StartMeausuringEvent
在wx.Panel
派生对象(DisplayPanel
)中触发,因此我使用self.GetEventHandler()
,这是有效的,即使绑定在父对象中。 -
NewResultEvent
是从threading.Thread
派生对象(MeasurementThread
)触发的,它没有事件处理程序,因此我被迫发送一个事件处理程序,我选择了wx.Frame
派生对象(MeasurementFrame
)的事件处理程序,因为这也是最终"捕获"事件的对象。
为什么第一个工作,因为对象?更一般地说,事件处理程序和"捕获"事件的对象之间的连接必须有多紧密?
代码范例import wx
import time
import threading
import numpy as np
import wx.lib.newevent
# Create two new event types
StartMeasuringEvent, EVT_START_MEASURING = wx.lib.newevent.NewCommandEvent()
NewResultEvent, EVT_NEW_RESULT = wx.lib.newevent.NewEvent()
class MeasurementFrame(wx.Frame):
def __init__(self, parent):
wx.Frame.__init__(self, parent, title="Lets measure!", size=(300, 300))
# Layout
self.view = DisplayPanel(self)
sizer = wx.BoxSizer(wx.HORIZONTAL)
sizer.Add(self.view, 1, wx.ALIGN_CENTER)
self.SetSizer(sizer)
self.SetMinSize((300, 300))
self.CreateStatusBar()
# Create a new measuring device object to embody a physical measuring device
self.device = MeasuringDevice(self.GetEventHandler(), amplification=10)
# Bind events to the proper handlers
self.Bind(EVT_START_MEASURING, self.OnStartMeasurement)
self.Bind(EVT_NEW_RESULT, self.OnNewResult)
def OnStartMeasurement(self, evt):
self.view.SetStatus("Measuring")
self.device.start_measurement()
def OnNewResult(self, evt):
self.view.SetStatus("New Result!")
print evt.result_data
class DisplayPanel(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
# Attributes
self._result_display = wx.StaticText(self, label="0")
self._result_display.SetFont(wx.Font(16, wx.MODERN, wx.NORMAL, wx.NORMAL))
self._status_display = wx.StaticText(self, label="Ready!")
self._status_display.SetFont(wx.Font(8, wx.MODERN, wx.NORMAL, wx.NORMAL))
# Layout
sizer = wx.BoxSizer(wx.VERTICAL)
button1 = wx.Button(self, wx.NewId(), "Increment Counter")
# button2 = wx.Button(self, wx.NewId(), "Decrease Counter")
sizer.AddMany([(button1, 0, wx.ALIGN_CENTER),
# (button2, 0, wx.ALIGN_CENTER),
((15, 15), 0),
(self._result_display, 0, wx.ALIGN_CENTER),
(self._status_display, 0, wx.ALIGN_LEFT)])
self.SetSizer(sizer)
# Event Handlers
button1.Bind(wx.EVT_BUTTON, self.OnButton)
def OnButton(self, evt):
""" Send an event ... but to where? """
wx.PostEvent(self.GetEventHandler(), StartMeasuringEvent(self.GetId()))
def SetStatus(self, status=""):
""" Set status text in the window"""
self._status_display.SetLabel(status)
class MeasuringDevice:
def __init__(self, event_handler, amplification=10):
self.amplification = amplification
self.event_handler = event_handler # The object to which all event are sent
def start_measurement(self, repetitions=1):
""" Start a thread that takes care of obtaining a measurement """
for n in range(repetitions):
worker = MeasurementThread(self.event_handler, self.amplification)
worker.start()
class MeasurementThread(threading.Thread):
def __init__(self, event_handler, amplification):
threading.Thread.__init__(self)
self.event_handler = event_handler
self.amplification = amplification
def run(self):
print("Beginning simulated measurement")
time.sleep(1) # My simulated calculation time
result = np.random.randn()*self.amplification
evt = NewResultEvent(result_data=result)
wx.PostEvent(self.event_handler, evt)
print("Simulated Measurement done!")
if __name__ == '__main__':
my_app = wx.App(False)
my_frame = MeasurementFrame(None)
my_frame.Show()
my_app.MainLoop()
第一个是有效的,因为命令事件自动沿包含层次结构(也称为窗口父/子连接)向上传播,直到找到匹配的绑定,或者直到它到达顶级父窗口(如框架或对话框)。请参阅http://wiki.wxpython.org/self.Bind%20vs.%20self.button.Bind和http://wxpython.org/OSCON2006/wxPython-intro-OSCON2006.pdf,从幻灯片53开始了解更多说明。
在寻找匹配的事件绑定时,事件处理器将搜索一个特定的路径。简而言之:如上所述,直接或间接从wx.CommandEvent
派生的事件类型将继续在父窗口中搜索,直到找到匹配,对于其他类型的事件,它将只在事件发送到的窗口中查找绑定,而不会传播到父窗口。如果处理程序调用event.Skip()
,那么当处理程序返回事件处理程序时,处理程序将继续寻找另一个匹配的处理程序(对于非命令事件,仍然限于同一窗口)。还有很多要比这更多,(参见上面链接的PDF中的幻灯片52)但是如果你了解了这些,那么它将足以应付你可能遇到的几乎所有事件绑定/处理情况。
顺便说一句,wx.Window
类派生自wx.EvtHandler
,所以除非你做了PushEventHandler
之类的事情,否则window.GetEventHandler()
将返回窗口本身。因此,除非您需要推送新的事件处理程序实例(大多数Python程序不需要,它在c++中更常用),否则您可以通过使用window
而不是window.GetEventHandler()
来节省一些输入。