PySimpleGUI执行两个长耗时函数时如何保持动画持续运行?
解决PySimpleGUI里长耗时函数执行时GIF动画卡着不动的问题
为啥会出现这问题?
PySimpleGUI的主事件循环是单线程运行的,你直接在点击事件里调用func_1、func_2这类慢函数时,主线程会被完全阻塞,根本腾不出资源处理GUI刷新——包括GIF的帧切换逻辑,所以GIF就停住不动了。手动调用refresh()也只能临时更新一次显示,没法让GIF持续播放。
最优雅的实现方案:用多线程分离耗时任务与GUI操作
把长耗时函数放到子线程中执行,让主线程专注处理GUI事件和动画自动刷新。这里有两种常用方式,都是官方推荐的思路:
方法一:用官方封装的perform_long_operation(推荐)
这个方法会自动把任务丢到子线程,任务完成后给主线程发送自定义事件,主线程收到事件后再切换动画或更新GUI状态,代码简洁且不易出错。
示例代码:
import PySimpleGUI as sg import time # 模拟长耗时业务函数 def func_1(): time.sleep(5) return "func1执行完成" def func_2(): time.sleep(5) return "func2执行完成" # 构建GUI布局 layout = [ [sg.Image(filename='progressing_1.gif', key='-IMAGE-')], [sg.Button('开始计算', key='-START-')] ] window = sg.Window('带动画的长任务处理', layout, finalize=True) # 启动第一个GIF的循环播放,无需手动刷新 window['-IMAGE-'].start_animation('progressing_1.gif', time_between_frames=100) while True: event, values = window.read() if event == sg.WIN_CLOSED: break elif event == '-START-': # 禁用按钮防止重复触发任务 window['-START-'].update(disabled=True) # 启动第一个耗时任务,完成后发送'-FUNC1_DONE-'事件 window.perform_long_operation(func_1, '-FUNC1_DONE-') elif event == '-FUNC1_DONE-': # 切换到第二个GIF动画 window['-IMAGE-'].update_animation('progressing_2.gif', time_between_frames=100) # 启动第二个耗时任务,完成后发送'-FUNC2_DONE-'事件 window.perform_long_operation(func_2, '-FUNC2_DONE-') elif event == '-FUNC2_DONE-': # 任务全部完成,恢复按钮状态,可切换回空闲GIF window['-START-'].update(disabled=False) window['-IMAGE-'].update_animation('idle.gif', time_between_frames=100) # 假设有空闲状态GIF window.close()
方法二:手动创建线程+事件通信
如果需要更灵活的线程控制,可以手动创建子线程,通过window.write_event_value向主线程发送自定义事件,触发GUI更新操作。
示例代码:
import PySimpleGUI as sg import time import threading def func_1(window): time.sleep(5) # 任务完成后向主线程发送事件通知 window.write_event_value('-FUNC1_DONE-', None) def func_2(window): time.sleep(5) window.write_event_value('-FUNC2_DONE-', None) # 构建GUI布局 layout = [ [sg.Image(filename='progressing_1.gif', key='-IMAGE-')], [sg.Button('开始计算', key='-START-')] ] window = sg.Window('带动画的长任务处理', layout, finalize=True) window['-IMAGE-'].start_animation('progressing_1.gif', time_between_frames=100) while True: event, values = window.read() if event == sg.WIN_CLOSED: break elif event == '-START-': window['-START-'].update(disabled=True) # 启动子线程执行func1,设置daemon=True确保程序退出时线程自动结束 threading.Thread(target=func_1, args=(window,), daemon=True).start() elif event == '-FUNC1_DONE-': window['-IMAGE-'].update_animation('progressing_2.gif', time_between_frames=100) threading.Thread(target=func_2, args=(window,), daemon=True).start() elif event == '-FUNC2_DONE-': window['-START-'].update(disabled=False) window['-IMAGE-'].update_animation('idle.gif', time_between_frames=100) window.close()
关键注意事项
- 绝对不要在主线程里直接跑长耗时函数,必须放到子线程,否则GUI会完全卡住。
- 使用
start_animation()和update_animation()管理GIF播放,这两个方法会自动处理帧刷新,再也不用手动调用window.refresh()。 - 子线程里不要直接操作GUI组件,必须通过事件通知主线程来更新,避免线程安全问题。
内容的提问来源于stack exchange,提问作者Xudong
相关产品推荐
相关产品推荐

