Python中sleep函数运行时Loader Frame持续加载的实现问题
解决wxPython耗时操作阻塞UI的问题
你的问题核心是wxPython的主事件循环是单线程的,当你在主线程里调用time.sleep()时,会阻塞整个UI更新,导致Loader的加载动画无法正常运行。下面是修改后的代码,用线程实现耗时操作与UI的分离:
import threading import time import wx class MainFrame(wx.Frame): def __init__(self, parent, id, title): wx.Frame.__init__(self, None, id, "My main frame", wx.DefaultPosition, wx.Size(500, 300), style=wx.MINIMIZE_BOX|wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX|wx.STAY_ON_TOP) sizer = wx.BoxSizer(wx.VERTICAL) self.panel = wx.Panel(self,-1) self.panel.Fit() self.panel.Show() # 提前初始化Loader,避免重复创建 self.loader = Loader(self,-1,title) self.button = wx.Button(self.panel,-1,"Go To Sleep") self.Bind(wx.EVT_BUTTON, self.on_sleep_button_click, self.button) self.another_button = wx.Button(self.panel,-1,"Go To Another Sleep") self.Bind(wx.EVT_BUTTON, self.on_another_sleep_button_click, self.another_button) sizer.AddStretchSpacer(1) sizer.Add(self.button, 0, wx.ALIGN_CENTER) sizer.AddSpacer(20) sizer.Add(self.another_button, 0, wx.ALIGN_CENTER) sizer.AddStretchSpacer(1) self.panel.SetSizerAndFit(sizer) def _do_sleep(self, duration, callback): # 子线程执行耗时操作 time.sleep(duration) # 操作完成后,用wx.CallAfter通知主线程更新UI(必须在主线程操作UI) wx.CallAfter(callback) def on_sleep_button_click(self, evt): # 主线程显示Loader self.loader.Show() self.loader.Centre() # 启动子线程执行耗时操作,完成后隐藏Loader threading.Thread(target=self._do_sleep, args=(10, self.loader.Hide)).start() def on_another_sleep_button_click(self, evt): self.loader.Show() self.loader.Centre() threading.Thread(target=self._do_sleep, args=(10, self.loader.Hide)).start() class Loader(wx.Frame): def __init__(self, parent, id, title): wx.Frame.__init__(self, None, id, "Please Wait...", wx.DefaultPosition, wx.Size(500, 300), style=wx.MINIMIZE_BOX|wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX|wx.STAY_ON_TOP) sizer = wx.BoxSizer(wx.VERTICAL) self.panel = wx.Panel(self,-1) self.panel.Fit() self.panel.Show() self.txt = wx.StaticText(self.panel,-1,"Please Wait...") self.spinner = wx.ActivityIndicator(self.panel, size=(30, 30)) sizer.AddStretchSpacer(1) sizer.Add(self.txt, 0, wx.ALIGN_CENTER) sizer.Add(self.spinner, 1, wx.ALIGN_CENTER) sizer.AddStretchSpacer(1) self.panel.SetSizerAndFit(sizer) self.spinner.Start() if __name__ == "__main__": app = wx.App() frame = MainFrame(None,-1,None) frame.Show(True) frame.Centre() app.MainLoop()
关键修改点说明:
- 提前初始化Loader:在MainFrame的
__init__中创建Loader实例,避免每次点击按钮重复创建窗口 - 分离耗时操作到子线程:把
time.sleep()放到子线程中执行,不会阻塞主线程的事件循环 - 用
wx.CallAfter更新UI:子线程不能直接操作wx的UI元素,必须通过wx.CallAfter将UI操作(比如隐藏Loader)交给主线程执行 - 主线程负责UI状态切换:点击按钮时,主线程立即显示Loader,子线程完成后通知主线程隐藏Loader
这样修改后,当点击按钮时,Loader的加载动画会正常转动,主窗口也不会冻结,耗时操作在后台执行。
内容的提问来源于stack exchange,提问作者Garbez François
相关产品推荐
相关产品推荐

