我想在 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()
这个问题很难回答,因为你没有说任务是什么。
它是默默地运行计算还是需要执行一系列步骤。
假设这是一系列步骤,这就是 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,并且对它了解甚少。