【问题标题】:wxPython: Threading GUI --> Using Custom Event HandlerwxPython:线程 GUI --> 使用自定义事件处理程序
【发布时间】:2011-01-21 16:23:29
【问题描述】:

我正在尝试学习如何在主 GUI 应用程序之外运行一个线程来执行我的串行端口发送/接收,同时保持我的 GUI 活动。我最好的谷歌搜索让我在 wxpython wiki 上找到了我:http://wiki.wxpython.org/LongRunningTasks,它提供了几个示例。我已经决定学习第一个示例,涉及在选择特定按钮时启动工作线程。

我无法理解自定义事件定义:

def EVT_RESULT(win, func):
    """Define Result Event."""
    win.Connect(-1, -1, EVT_RESULT_ID, func)

class ResultEvent(wx.PyEvent):
    """Simple event to carry arbitrary result data."""
    def __init__(self, data):
        """Init Result Event."""
        wx.PyEvent.__init__(self)
        self.SetEventType(EVT_RESULT_ID)
        self.data = data

主要是

def EVT_RESULT(win, func):
    """Define Result Event."""
    win.Connect(-1, -1, EVT_RESULT_ID, func)

我认为 EVT_RESULT 放置在类之外,以便两个类都可以调用它(使其成为全局?)

而且.. 主 GUI 应用通过以下方式监控线程的进度:

# Set up event handler for any worker thread results
EVT_RESULT(self,self.OnResult)

我还注意到,在很多例子中,当作者使用

from wx import *

他们只是通过

来绑定东西
EVT_SOME_NEW_EVENT(self, self.handler)

相对于

wx.Bind(EVT_SOME_NEW_EVENT, self.handler)

这并不能帮助我更快地理解它。 谢谢,

【问题讨论】:

    标签: python multithreading wxpython custom-events


    【解决方案1】:

    这是定义自定义事件的旧方式。请参阅the migration guide 了解更多信息。

    取自迁移指南:

    如果您创建自己的自定义事件 类型和 EVT_* 函数,你 希望能够将它们与 上面的绑定方法那么你应该 将您的 EVT_* 更改为 wx.PyEventBinder 的实例而不是 功能。例如,如果你以前 有这样的东西:

    myCustomEventType = wxNewEventType()
    def EVT_MY_CUSTOM_EVENT(win, id, func):
        win.Connect(id, -1, myCustomEventType, func)
    

    改成这样:

    myCustomEventType = wx.NewEventType()
    EVT_MY_CUSTOM_EVENT = wx.PyEventBinder(myCustomEventType, 1)
    

    Here is another post 是我用几个示例程序制作的,它们完全符合您的要求。

    【讨论】:

    • 谢谢,我去看看
    • 干得好,您将页面的那部分存档。它似乎是动态的和非版本化的,所以文本不再存在!相关阅读:wiki.wxpython.org/CustomEventClasses
    【解决方案2】:

    您可以这样定义事件:

    from wx.lib.newevent import NewEvent
    
    ResultEvent, EVT_RESULT = NewEvent()
    

    您这样发布活动:

    wx.PostEvent(handler, ResultEvent(data=data))
    

    这样绑定:

    def OnResult(event):
        event.data
    
    handler.Bind(EVT_RESULT, OnResult)
    

    但如果你只需要在主线程中从非主线程调用你可以使用wx.CallAfterhere就是一个例子。

    当您不想硬编码谁负责什么时,自定义事件很有用(请参阅observer design pattern)。例如,假设您有一个主窗口和几个子窗口。假设当主窗口发生某种变化时,需要刷新一些子窗口。在这种情况下,主窗口可以直接刷新这些子窗口,但更优雅的方法是定义一个自定义事件并让主窗口将其发布给自己(而不用打扰谁需要对其做出反应)。然后,需要对该事件做出反应的孩子可以通过绑定到它自己来完成(如果有多个,重要的是他们调用event.Skip() 以便调用所有绑定的方法)。

    【讨论】:

    • 感谢您的回答,我会在处理它时提及它。
    【解决方案3】:

    您可能希望使用 Python 线程和队列,而不是自定义事件。我有一个 wxPython 程序 (OpenSTV),它加载导致 gui 在加载过程中冻结的大文件。为了防止冻结,我调度了一个线程来加载文件并使用队列在 gui 和线程之间进行通信(例如,向 GUI 传达异常)。

      def loadBallots(self):
        self.dirtyBallots = Ballots()
        self.dirtyBallots.exceptionQueue = Queue(1)
        loadThread = Thread(target=self.dirtyBallots.loadUnknown, args=(self.filename,))
        loadThread.start()
    
        # Display a progress dialog
        dlg = wx.ProgressDialog(\
          "Loading ballots",
          "Loading ballots from %s\nNumber of ballots: %d" % 
          (os.path.basename(self.filename), self.dirtyBallots.numBallots),
          parent=self.frame, style = wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME
        )
        while loadThread.isAlive():
          sleep(0.1)
          dlg.Pulse("Loading ballots from %s\nNumber of ballots: %d" %
                    (os.path.basename(self.filename), self.dirtyBallots.numBallots))
        dlg.Destroy()
    
    if not self.dirtyBallots.exceptionQueue.empty():
      raise RuntimeError(self.dirtyBallots.exceptionQueue.get())
    

    【讨论】:

    • 我猜队列是多线程的标准方式;但是如果我只需要一个简单的“完成”信号,传递标志是否安全?我没有太多使用队列;我需要为每个方向创建一个,并且基本上让接收端定期轮询队列?
    • 您不需要队列来确定线程何时完成。您可以使用上面示例中使用的 isAlive()。您确实需要一个队列来进行各个方向的通信,您可以在 runElection() 中看到一个示例 (code.google.com/p/stv/source/browse/trunk/openstv/OpenSTV.py)。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-11
    相关资源
    最近更新 更多