python - wxPython线程阻塞

标签 python multithreading wxpython

这是在 wxPython 的 Phoenix 分支中。

为了不阻塞 GUI,我正在尝试运行几个线程。

我的两个线程工作正常,但另一个线程似乎从未达到其绑定(bind)结果函数。我可以看出它正在运行,只是似乎没有正确发布事件。

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

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)

这是我绑定(bind)它们的方式:

from wx.lib.pubsub.core import Publisher
PUB = Publisher()

下面是我绑定(bind)方法的方式:

def post_event(message, data):
    wx.CallAfter(lambda *a: Publisher().sendMessage(message, data=data))

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

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

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

这是我创建/启动/订阅 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)

我已经尝试用 range(10) 删除 prepare_collection,但我仍然没有点击事件处理程序。

最佳答案

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


开始 - 我会先剧透 - 这是 one file solution对于您似乎想要的 - 一个带有按钮的面板,用于启动准备线程,状态栏和准备完成时的处理程序。在 Python 2.7 上使用 wxPython Phoenix 64 位 Python 3 和老式的 wxPython 进行了测试。两者都在 Windows 上 - 但如有必要,我可以在 Linux 机器上启动它。

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

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

在你的文件的顶部,使用

from wx.lib.pubsub import pub

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

在您的线程中,正如您所做的那样,在那里发布消息 - 对您的 post_event 助手的一个小修改:

def post_event(message, data):
    wx.CallAfter(lambda *a: pub.sendMessage(message, data=data))

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

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

您可以保留 on_status_result 不变并定义类似的 on_status_finished。在我的例子中,我后来有一个启用新按钮的按钮,让你做一些实际的工作。

N.B. 在命名消息的有效负载时需要小心 - pubsub 推断出相当多的关于它在那里期望的信息,它一开始就吸引了我。


附言就在准备这个答案的最后 - 我找到了 this blog post .它说的内容与我上面的内容类似,所以我不会复制它,但他们使用另一种实例化 Publisher() 的方法,就像您的原始示例一样 - 暗示它也应该有效。您可能更喜欢那里的措辞。 Simlarly - 你可能会发现 this wxPython wiki页面有用。

关于python - wxPython线程阻塞,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32467060/

相关文章:

Python函数返回字典?

python - 为什么这个装饰器调用 "TypeError: make_sandwich() takes exactly 2 arguments (1 given)"

c++ - 类实例线程亲和性对其数据有影响吗?

multithreading - 为什么在线程方面需要 "move"关键字;为什么我永远不想要这种行为?

python - 如何在没有命令提示符窗口的情况下运行 wxPython 对话框?

python - ValueError : Expected 2D array, 在 svm 识别期间得到一维数组

python - 在进度条的计算中实现python多线程

python - 如何通过鼠标单击获取 wx Canvas 中的 x 轴值?

java - wait 和 wait 的时差?

python - 在 wxPython/python 中的 while 无限循环中使用 wx.CallAfter() 导致应用程序崩溃