在并发 future 运行长任务时如何防止 wxpython 中的 UI 冻结

问题描述 投票:0回答:1

我想在 wxpython UI 中执行一个长任务,而又不让 UI 失去响应能力。

我认为使用 ThreadPoolExecutor 的并发 future 可以让我做到这一点,但 UI 仍然冻结。

这是重现该问题的简单代码。 UI 冻结 5 秒。

为什么会出现这种情况以及如何解决?

import time
import wx
from concurrent.futures import ThreadPoolExecutor


class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        super().__init__(*args, **kwds)

        self.panel = wx.Panel(self, wx.ID_ANY)
        self.longtask_button = wx.Button(self, label="Long Task")
        self.longtask_button.Bind(wx.EVT_BUTTON, on_long_task)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.longtask_button, 0, wx.ALIGN_RIGHT)
        self.SetSizer(sizer)
        self.Layout()


def on_long_task(event):
    with ThreadPoolExecutor() as executor:
        executor.submit(block5)


def block5():
    time.sleep(5)
    return 1


if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame(None, wx.ID_ANY, "")
    frame.Show()
    app.MainLoop()
python concurrency wxpython
1个回答
0
投票

这个问题很难回答,因为你没有说任务是什么。

它是默默地运行计算还是需要执行一系列步骤。

假设这是一系列步骤,这就是 wx.GetApp().Yield() 的用途,暂时将控制权传递回 wx 主循环,以便它可以保持响应。

在这种情况下:

import time
import wx
#from concurrent.futures import ThreadPoolExecutor


class MyFrame(wx.Frame):
    def __init__(self, *args, **kwds):
        super().__init__(*args, **kwds)

        self.panel = wx.Panel(self, wx.ID_ANY)
        self.longtask_button = wx.Button(self, label="Long Task")
        self.longtask_button.Bind(wx.EVT_BUTTON, self.on_long_task)
        sizer = wx.BoxSizer(wx.VERTICAL)
        self.other_button = wx.Button(self, label="Push Me!")
        self.other_button.Bind(wx.EVT_BUTTON, self.other_task)
        sizer.Add(self.longtask_button)
        sizer.Add(self.other_button)
        self.SetSizer(sizer)
        self.Layout()


    def on_long_task(self, event):
#        with ThreadPoolExecutor() as executor:
#            executor.submit(self.block5)
        self.block5()

    def block5(self):
        print("sleeping")
        for i in range(11):
            time.sleep(0.5)
            print("yielding")
            wx.GetApp().Yield()
        print("Awoke")
        return 1

    def other_task(self, event):
        print("other pushed")

if __name__ == "__main__":
    app = wx.App()
    frame = MyFrame(None, wx.ID_ANY, "")
    frame.Show()
    app.MainLoop()

可以完成这项工作,而不用担心 ThreadPool,它似乎不会放弃控制直到它结束。

注意:我从未使用过 ThreadPool,并且对它了解甚少。

© www.soinside.com 2019 - 2024. All rights reserved.