You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 21:39:53