wxPython 中的 PostEvent 使用哪个 EvtHandler

Which EvtHandler to use for PostEvent in wxPython

这个问题解决了使用 wxPython.

post en 事件所需的 EvtHandler 的非常具体的问题

背景

我正在使用 Python 2.7。 在下面的示例中,我有两种事件:

  1. StartMeausuringEventwx.Panel 派生对象 (DisplayPanel) 中触发,因此我使用 self.GetEventHandler(),这有效,即使绑定在父对象中.
  2. NewResultEvent 是由 threading.Thread 派生对象 (MeasurementThread) 触发的,它没有事件处理程序,因此我不得不发送一个事件处理程序,我选择 wx.Frame 派生对象 (MeasurementFrame) 的事件处理程序,因为这也是最终 "cathes" 事件的对象。

问题

为什么第一个有效,因为对象?更一般地说,事件处理程序和事件必须 "catches" 的对象之间的联系有多紧密?

代码示例

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()

第一个有效,因为命令事件自动向上传播包含层次结构(a.k.a window parent/child 连接)直到找到匹配的绑定或直到它到达顶部-level parent window 像框架或对话框。有关详细说明,请参阅 http://wiki.wxpython.org/self.Bind%20vs.%20self.button.Bind and also http://wxpython.org/OSCON2006/wxPython-intro-OSCON2006.pdf 从幻灯片 53 开始。

事件处理器在查找匹配的事件绑定时将搜索特定路径。简而言之:如上所述,直接或间接派生自 wx.CommandEvent 的事件类型将继续通过父级向上搜索,直到找到匹配项,而对于其他类型的事件,它只会在 [= 中查找绑定34=] 事件被发送到并且不会传播到父 windows。如果处理程序调用 event.Skip(),那么当处理程序 return 时,事件处理器将继续寻找另一个匹配的处理程序(对于非命令事件仍然限于相同的 window。)有 比这更多,(请参阅上面链接的 PDF 中的幻灯片 52)但是如果您了解这么多,那么几乎所有事件都足够了 binding/handling 您的情况很可能会遇到。

顺便说一句,wx.Window class 派生自 wx.EvtHandler 所以除非你做了类似 PushEventHandler 的事情,否则 window.GetEventHandler() 将 return window 本身。所以除非你需要推送新的事件处理程序实例(大多数 Python 程序不需要,它在 C++ 中更常用)那么你可以通过使用 window 而不是 [=15= 来节省一些输入].