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
相关产品推荐
相关产品推荐

