简体   繁体   English

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

[英]wxPython: Threading GUI --> Using Custom Event Handler

I am trying to learn how to run a thread off the main GUI app to do my serial port sending/receiving while keeping my GUI alive. 我正在尝试学习如何在主GUI应用程序上运行线程以进行串行端口发送/接收,同时保持GUI处于活动状态。 My best Googling attempts have landed me at the wxpython wiki on: http://wiki.wxpython.org/LongRunningTasks which provides several examples. 我最好的Google搜索尝试使我进入了以下网址的wxpython Wiki: http ://wiki.wxpython.org/LongRunningTasks,其中提供了一些示例。 I have settled on learning the first example, involving starting a worker thread when the particular button is selected. 我决定学习第一个示例,涉及选择特定按钮时启动工作线程。

I am having trouble understanding the custom-event-definition: 我在理解自定义事件定义时遇到了麻烦:

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

Primarily the 主要是

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

I think EVT_RESULT is placed outside the classes so as to make it call-able by both classes (making it global?) 我认为EVT_RESULT放置在类之外,以使其可以被两个类调用(使其成为全局的?)

And.. the main GUI app monitors the thread's progress via: 并且..主GUI应用程序通过以下方式监视线程的进度:

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

I also notice that in a lot of examples, when the writer uses 我还注意到,在许多示例中,当作者使用

from wx import *

they simply bind things by 他们只是通过捆绑事物

EVT_SOME_NEW_EVENT(self, self.handler)

as opposed to 而不是

wx.Bind(EVT_SOME_NEW_EVENT, self.handler)

Which doesn't help me understand it any faster. 这并不能帮助我更快地理解它。 Thanks, 谢谢,

That's the old style of defining custom events. 那是定义自定义事件的旧样式。 See the migration guide for more information. 有关更多信息,请参见迁移指南

Taken from the migration guide: 摘自迁移指南:

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. 如果您创建自己的自定义事件类型和EVT_ *函数,并且希望能够将它们与上述Bind方法一起使用,则应将EVT_ *更改为wx.PyEventBinder的实例,而不是函数。 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) 

Here is another post that I made with a couple of example programs that do exactly what you are looking for. 这是我用几个示例程序编写的,它们确实可以满足您的需求。

You can define events like this: 您可以定义如下事件:

from wx.lib.newevent import NewEvent

ResultEvent, EVT_RESULT = NewEvent()

You post the event like this: 您可以这样发布事件:

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

Bind it like this: 像这样绑定它:

def OnResult(event):
    event.data

handler.Bind(EVT_RESULT, OnResult)

But if you just need to make a call from a non-main thread in the main thread you can use wx.CallAfter , here is an example. 但是,如果您只需要从主线程中的非主线程进行调用,则可以使用wx.CallAfter是一个示例。

Custom events are useful when you don't want to hard code who is responsible for what (see the observer design pattern ). 当您不想硬编码谁负责什么时,自定义事件很有用(请参阅观察者设计模式 )。 For example, lets say you have a main window and a couple of child windows. 例如,假设您有一个主窗口和几个子窗口。 Suppose that some of the child windows need to be refreshed when a certain change occurs in the main window. 假设当主窗口中发生某些更改时,需要刷新某些子窗口。 The main window could directly refresh those child windows in such a case but a more elegant approach would be to define a custom event and have the main window post it to itself (and not bother who needs to react to it). 在这种情况下,主窗口可以直接刷新那些子窗口,但是更优雅的方法是定义一个自定义事件,并让主窗口将其发布到自身(而不必理会需要对此做出反应的人)。 Then the children that need to react to that event can do it them selves by binding to it (and if there is more than one it is important that they call event.Skip() so that all of the bound methods get called). 然后,需要对该事件做出反应的子代可以通过绑定到自身来完成它(如果有多个event.Skip() ,则必须调用event.Skip()以便调用所有绑定的方法,这一点很重要)。

You may want to use Python threads and queues and not custom events. 您可能要使用Python线程和队列,而不要使用自定义事件。 I have a wxPython program ( OpenSTV ) that loads large files that caused the gui to freeze during the loading. 我有一个wxPython程序( OpenSTV ),该程序会加载导致gui在加载过程中冻结的大文件。 To prevent the freezing, I dispatch a thread to load the file and use a queue to communicate between the gui and the thread (eg, to communicate an exception to the 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())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM