wxPython:线程化GUI-->使用自定义事件处理程序

3
我希望学会如何在主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)

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

标签时,它实际上是多余的。如果段落已经结束,下一句自然会开始一个新段落。因此,在写HTML时,请确保只在需要分段的地方使用

标签。

from wx import *

他们只是通过绑定事物来完成。
EVT_SOME_NEW_EVENT(self, self.handler)

与...相反
wx.Bind(EVT_SOME_NEW_EVENT, self.handler)

这并没有帮助我更快地理解它。 谢谢。

3个回答

4

这是定义自定义事件的旧方式。有关更多信息,请参见迁移指南

从迁移指南中摘取:

If you create your own custom event types and EVT_* functions, and you want to be able to use them with the Bind method above then you should change your EVT_* to be an instance of wx.PyEventBinder instead of a function. For example, if you used to have something like this:

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

Change it like so:

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

这里是我发布的另一篇文章,其中包含几个示例程序,可以完全满足您的需求。


谢谢,我会看一下的。 - PPTim
干得好,你完成了页面的那一部分。它似乎是动态的且没有版本记录,所以那段文本不再存在!相关阅读:https://wiki.wxpython.org/CustomEventClasses - Pod

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.CallAfter这里有一个例子。

自定义事件在你不想硬编码谁负责什么时非常有用(参见观察者设计模式)。例如,假设你有一个主窗口和几个子窗口。假设一些子窗口需要在主窗口发生某些更改时进行刷新。在这种情况下,主窗口可以直接刷新那些子窗口,但更优雅的方法是定义一个自定义事件,并让主窗口将其发布给自己(并不必关心谁需要对其做出反应)。然后需要对该事件做出反应的子项可以通过绑定来自行处理它们(如果有多个,则重要的是它们调用event.Skip(),以便调用所有绑定的方法)。


谢谢你的回答,我在工作时会参考它。 - PPTim

0
你可能希望使用Python线程和队列而不是自定义事件。我有一个wxPython程序(OpenSTV),它加载大文件会导致界面在加载期间冻结。为了防止冻结,我发起了一个线程来加载文件,并使用队列在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())

1
我猜队列是多线程的标准方式;但如果我只需要一个简单的“完成”信号,传递标志是否安全?我很少使用队列;我需要为每个方向创建一个队列,并基本上让接收端定期轮询队列吗? - PPTim
你不需要队列来确定线程何时完成。你可以使用上面示例中使用的isAlive()方法。你确实需要一个队列来进行双向通信,你可以在这里看到一个例子(http://code.google.com/p/stv/source/browse/trunk/openstv/OpenSTV.py),它在runElection()函数中实现。 - new name

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接