wxPython线程阻塞

Mor*_*app 11 python multithreading wxpython

这是在wxPython的Phoenix分支中.

为了不阻止GUI,我试图运行一些线程.

我的两个线程工作正常,但另一个线程似乎永远不会达到其绑定结果函数.我可以告诉它正在运行,它似乎没有正确发布事件.

这是主计算线程的结果函数:

def on_status_result(self, event):
    if not self.panel.progress_bar.GetRange():
        self.panel.progress_bar.SetRange(event.data.parcel_count)
    self.panel.progress_bar.SetValue(event.data.current_parcel)
    self.panel.status_label.SetLabel(event.data.message)
Run Code Online (Sandbox Code Playgroud)

这是我绑定他们的方式:

from wx.lib.pubsub.core import Publisher
PUB = Publisher()
Run Code Online (Sandbox Code Playgroud)

这是我绑定方法的方式:

def post_event(message, data):
    wx.CallAfter(lambda *a: Publisher().sendMessage(message, data=data))
Run Code Online (Sandbox Code Playgroud)

这是线程.第一个不起作用,但后两个做:

class PrepareThread(threading.Thread):
    def __init__(self, notify_window):
        threading.Thread.__init__(self)
        self._notify_window = notify_window
        self._want_abort = False

    def run(self):
        while not self._want_abort:
            for status in prepare_collection(DATABASE, self._previous_id, self._current_id, self._year, self._col_type,
                                             self._lock):
                post_event('prepare.running', status)
        post_event('prepare.complete', None)
        return None

    def abort(self):
        self._want_abort = True


class SetupThread(threading.Thread):
    def __init__(self, notify_window):
        threading.Thread.__init__(self)
        self._notify_window = notify_window
        self._want_abort = False

    def run(self):
        while not self._want_abort:
            do_more_stuff_with_the_database()
            return None

    def abort(self):
        self._want_abort = True


class LatestCollectionsThread(threading.Thread):
    def __init__(self, notify_window):
        threading.Thread.__init__(self)
        self._notify_window = notify_window
        self._want_abort = False

    def run(self):
        while not self._want_abort:
            do_stuff_with_my_database()
            return None

    def abort(self):
        self._want_abort = True
Run Code Online (Sandbox Code Playgroud)

prepare_collection是一个产生如下所示Status对象的函数:

class Status:
    def __init__(self, parcel_count, current_parcel, total, message):
        self.parcel_count = parcel_count
        self.current_parcel = current_parcel
        self.total = total
        self.message = message
Run Code Online (Sandbox Code Playgroud)

以下是我创建/启动/订阅PrepareThread的方法:

MainForm(wx.Form):
    prepare_thread = PrepareThread(self)
    prepare_thread.start()

    self.pub = Publisher()
    self.pub.subscribe(self.on_status_result, 'prepare.running')
    self.pub.subscribe(self.on_status_result, 'prepare.complete')

    def on_status_result(self, event):
        if not self.panel.progress_bar.GetRange():
            self.panel.progress_bar.SetRange(event.data.parcel_count)
        self.panel.progress_bar.SetValue(event.data.current_parcel)
        self.panel.status_label.SetLabel(event.data.message)
Run Code Online (Sandbox Code Playgroud)

我已经尝试过prepare_collection使用range(10),但我仍然没有遇到事件处理程序.

J R*_*ape 4

这可能是一个相当复杂的答案,并且很难准确地计算出代码的每个部分中有哪些片段(即它们都位于哪些文件中)。我假设您想保留pubsub执行此操作的方法(我认为这是一个好主意)。如果您的真实项目的结构非常复杂,您可能需要比Publisher我在这里使用的更复杂的管理 - 让我知道......


这里 - 我将先剧透 - 这是一个您似乎想要的单文件解决方案- 一个带有按钮的面板,用于在准备完成时启动准备线程、状态栏和处理程序。使用 wxPython Phoenix 64 位 Python 3 和 Python 2.7 上的老式 wxPython 进行测试。两者都在 Windows 上运行 - 但如果需要的话我可以在 Linux 机器上运行它。

总结该文件的重要(非样板)部分

您需要一个Publisher对象,您的线程向其发送消息并且您的主线程(MainForm我猜在您的示例中)订阅该对象。您可以为每个线程管理一个Publisher,但我认为这里您只需要一个PrepareThread,所以我现在将使用该模型。

在文件的顶部,使用

from wx.lib.pubsub import pub
Run Code Online (Sandbox Code Playgroud)

这可以pubsub管理实例化单个Publisher对象。

在您的线程中,正如您所做的那样,在那里发布消息 - 对您的帮助程序稍作修改post_event

def post_event(message, data):
    wx.CallAfter(lambda *a: pub.sendMessage(message, data=data))
Run Code Online (Sandbox Code Playgroud)

在您的主线程中 - 订阅这些消息。我想说,通常最简单的是每条消息都有一个处理程序,而不是像您一样向同一个处理程序发送两条不同的消息,所以我选择了

pub.subscribe(self.on_status_result, 'prepare.running')
pub.subscribe(self.on_status_finished, 'prepare.complete')
Run Code Online (Sandbox Code Playgroud)

您可以保持on_status_result原样并定义类似的on_status_finished. 在我的示例中,我稍后启用了一个新按钮,让您可以完成一些实际工作。

注意,在命名消息的有效负载时,您需要小心 -pubsub推断出大量有关其期望的信息,这首先吸引了我。


PS 就在准备这个答案的最后 - 我发现了这篇博文。它说的内容与我上面的内容类似,所以我不会重现它,但他们使用另一种实例化方法,Publisher()就像你原来的例子一样——这意味着这也应该有效。您可能更喜欢那里的措辞。同样 - 您可能会发现这个 wxPython wiki页面很有用。