正确使用wxPython的线程

时间:2015-05-07 00:34:12

标签: python multithreading wxpython

我使用wxPython来构建我的程序的GUI,我在运行一个使用线程从文件中获取数据并在ObjectListView中显示它的函数时遇到了麻烦,从文件中读取数据运行很好,但显示线程中的数据有时会崩溃整个程序。 gui.ProgressDialog是一个wx.Frame,它将显示进度条直到线程完成。 self.dv是一个ObjectListView,一个派生自wx.ListCtrl的对象,set_data是我编写的一个函数,它迭代数据并将其添加到列表中。

def set_data(self,data):
    """set the data on the ObjectListView"""
    pd = gui.ProgressDialog("Setting Data..")
    def set_thread():
        self.dv.set_data(data) #Takes time
        pd.Close()

    #opens a thread that sets the data
    t = Thread(target = set_thread,name="SetDataThread")
    t.start()

我已经阅读了一些关于wx.CallAfter的内容,我应该使用它,但我并不了解如何。我试着像这样调用set_data - wx.CallAfter(self.dv.set_data,data)但它也没有用。有人可以解释函数wx.CallAfterwx.CallLaterwx.PostEvent,它们如何在使用wxPython线程时有用,我应该使用哪一个?或另一种解决方案?

1 个答案:

答案 0 :(得分:1)

您无法从辅助线程进行任何GUI调用。这在wx.Thread文档中有解释,但它也适用于普通的Python threading.Thread线程:

  

GUI调用(例如wxWindow或wxBitmap的调用)在辅助线程中显然是不安全的,并且可能会过早地终止您的应用程序。

因此,您需要做的是向主线程发送消息,要求它为您进行GUI调用。

PostEvent是低级解决方案。您创建了一种新的事件。您在主线程上为该事件类型编写了一个处理程序,它根据事件中的信息执行一些GUI工作。然后在后台线程上,使用适当的信息构造其中一个事件,并PostEvent将它放在主线程的消息队列中。

其他选项基本上是更高级别的包装器。 CallAfter可能就是你想要的那个。 wxPyWiki上的CallAfter页面有一个很好的解释和完整的示例,并且在文档页面本身也有一个很好的例子,所以我不会尝试与它们竞争;相反,我会尝试涵盖您的具体问题。

我认为您缺少的是将 GUI 调用推送到主线程。所有GUI调用,没有其他调用。

我不知道self.dv是什么,但它看起来不像wx GUI对象(它有一个Python风格的set_data方法,而不是{{1}风格wx)。当然,如果它在多个线程之间共享,并且它不是自我同步的,那么您几乎肯定需要一个Lock或其他同步对象来保护它。但是你不需要或想要SetData这里。

另一方面,CallAfter是一个GUI调用。 (好吧,如果你记住了括号,那就好了。)你肯定需要pd.Close

所以:

CallAfter

您还询问了CallLater。如果希望在X毫秒之后在主线程上运行某些代码,而不是尽快运行,则使用此方法。虽然您可以使用def set_data(self,data): """set the data on the ObjectListView""" pd = gui.ProgressDialog("Setting Data..") def set_thread(): data = takes_time() with self.dv_lock: self.dv.set_data(data) wx.CallAfter(pd.Close) #opens a thread that sets the data t = Thread(target = set_thread,name="SetDataThread") t.start() 代替CallLater(0, pd.Close),但没有充分理由这样做。