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

PySimpleGUI ProgressBar如何通过函数内变量实时更新?

解决PySimpleGUI进度条实时更新问题

问题核心原因

你的进度条只在加载完成后更新,是因为read_species函数是同步执行的,它会阻塞PySimpleGUI的主事件循环——主循环被卡住时,无法处理任何UI更新操作,只有等函数跑完才会执行UpdateBar。

解决方案:线程分离+进度回调

把文件读取任务放到子线程执行,同时让读取函数定期通知主循环更新进度,这样主UI循环不会被阻塞,进度条就能实时更新。

步骤1:重构读取函数,添加进度回调

修改read_species,去掉全局变量,改用回调函数传递进度,同时修复文件指针重置的bug:

import re
from re import sub

def read_species(data_path, progress_callback):
    key_list = []  # 存储所有化学式
    time_list = []  # 存储所有时间序列
    current_line = 0

    # 先获取文件总行数,用于进度条的最大值
    with open(data_path) as f:
        total_lines = sum(1 for _ in f)

    with open(data_path) as f:
        # 第一遍遍历:提取化学式和时间序列
        for i in f:
            current_line += 1
            if i[0] == '#':
                for j in i[1:].split():
                    j = sub(r'[\x00-\x00]', r'F', j)
                    if j not in key_list:
                        key_list.append(j)
            else:
                time_list.append(i.split()[0])
            # 调用回调更新当前进度
            progress_callback(current_line, total_lines)
        
        # 重置文件指针到开头,准备第二遍遍历
        f.seek(0)
        var_list, dict_data = [], {
            i: {k: 0 for k in time_list} for i in key_list
        }
        # 第二遍遍历:构建时间-化学式-数值的字典
        for i in f:
            current_line += 1
            if i[0] == '#':
                var_list.append(i[1:].split())
            else:
                var_list.append(i.split())
                for k, v in zip(*var_list):
                    k = sub(r'[\x00-\x00]', r'F', k)
                    if k == 'Timestep':
                        time = v
                        continue
                    dict_data[k][time] = v
                var_list.clear()
            # 调用回调更新当前进度
            progress_callback(current_line, total_lines)
        
        del dict_data['Timestep']
    return {k: v for k, v in sorted(dict_data.items())}

步骤2:修改主UI循环,用线程执行读取任务

主循环添加timeout定时检查进度,用子线程执行文件读取,避免阻塞UI:

import PySimpleGUI as sg
import threading

def main():
    layout = [
        [sg.Input(key='-INPUT-'), sg.FileBrowse(), sg.Button('Load')],
        [sg.Text('Loading Progress'), sg.ProgressBar(1, orientation='h', size=(40, 10), key='-PROGRESS-')],
        [sg.Listbox([], size=(40, 10), key='-CHEMICALS-')]
    ]

    window = sg.Window('File Loader', layout)
    progress_value = 0
    total_lines = 0
    species_data = None
    loading_thread = None

    while True:
        # 添加timeout让主循环定时检查进度
        event, values = window.read(timeout=100)
        if event == sg.WIN_CLOSED:
            break
        
        if event == 'Load':
            data_path = values['-INPUT-']
            if not data_path:
                sg.popup('请先选择文件!')
                continue
            
            # 获取文件总行数,设置进度条最大值
            with open(data_path) as f:
                total_lines = sum(1 for _ in f)
            window['-PROGRESS-'].update(max=total_lines)
            
            # 定义进度回调函数,更新外部的进度值
            def update_progress(current, total):
                nonlocal progress_value
                progress_value = current
            
            # 创建子线程执行读取任务,设置daemon=True避免程序退出残留线程
            loading_thread = threading.Thread(
                target=lambda: nonlocal species_data; species_data = read_species(data_path, update_progress),
                daemon=True
            )
            loading_thread.start()
            sg.popup_auto_close('开始加载文件!', title='加载中', button_type=5)
        
        # 实时更新进度条
        if progress_value > 0:
            window['-PROGRESS-'].UpdateBar(progress_value)
        
        # 检查线程是否完成,完成后更新UI
        if loading_thread and not loading_thread.is_alive() and species_data is not None:
            window['-CHEMICALS-'].update(species_data.keys())
            sg.popup_auto_close('文件加载完成!', title='加载完成', button_type=5)
            # 重置状态,准备下一次加载
            progress_value = 0
            species_data = None
            loading_thread = None

    window.close()

if __name__ == '__main__':
    main()

关键说明

  • 线程分离:文件读取在子线程执行,主UI循环可以正常处理事件和更新进度条。
  • 进度回调:读取函数每处理一行就触发回调,把当前行数传递给主循环。
  • 定时检查:主循环通过timeout=100每隔100ms检查一次进度值,更新进度条。
  • 修复文件指针bug:第一次遍历完文件后,用f.seek(0)重置指针,保证第二次遍历能正常执行。

内容的提问来源于stack exchange,提问作者Xiao Zhao

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 03:25:35