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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.26 15:00:01