Kivy ProgressBar更新异常求助:值增长但界面未刷新
问题:Kivy ProgressBar控件值更新但界面未同步
我正在开发一款可生成带图表PDF报告的简易应用,计划添加进度窗口展示程序执行进度。目前功能初步实现,但遇到ProgressBar控件异常:执行任务时progressbar的value值已增长,可界面控件未同步更新。
使用环境:Python 3.10、Kivy 2.2.1
导入的库
import os import re import time from fpdf import FPDF from matplotlib import pyplot as plt import pandas as pd # set window size from kivy.config import Config Config.set('graphics', 'width', '450') Config.set('graphics', 'height', '300') import kivy from kivy.app import App from kivy.uix.progressbar import ProgressBar from kivy.uix.button import Button from kivy.uix.label import Label from kivy.uix.filechooser import FileChooserListView from kivy.uix.popup import Popup from kivy.uix.boxlayout import BoxLayout from kivy.uix.relativelayout import RelativeLayout from kivy.clock import Clock
GeneratePDF类实现
class FileChooserPopup(Popup): pass class GeneratePDF(): def __init__(self, app_instance, **kwargs): super().__init__(**kwargs) self.app_instance = app_instance # --- other methods --- def create_progress_popup(self): # show progress popup content = BoxLayout(orientation='vertical') self.progress_bar = ProgressBar(max=100, value=0, size_hint_x=0.8, pos_hint={'center_x':0.5, 'center_y':0.5}) self.percent_label = Label(text='0%', size_hint=(None, None), size = (40,30), pos_hint ={'center_x': 0.5,'center_y': 0.5}) content.add_widget(self.progress_bar) content.add_widget(self.percent_label) self.progress = Popup(title='', content=content, size_hint=(0.8,0.8), separator_height=0) # dismiss the file chooser popup self.app_instance.file_chooser.dismiss() self.progress.open() def update_progress(self, value): current = self.progress_bar.value current += value self.progress_bar.value = current self.percent_label.text = f'{int(current)}%' # --- other methods --- def generate_raport1(self, instance, selection, *args): # opening file if selection: self.selected_file = selection[0] # Create the progress popup self.create_progress_popup() # start generating raport inthread Clock.schedule_once(lambda dt: self.generate_report2(), 0) else: # dismiss the file chooser popup self.app_instance.file_chooser.dismiss() def generate_report2(self): # drawing plots and updating progressbar self.plot1(self.selected_file) self.update_progress(20) self.plot2() self.update_progress(20) self.plot3() self.update_progress(20) self.plot4() self.update_progress(20) # generate pdf raport self.write_pdf() self.update_progress(20) time.sleep(1) # deleting unnecessary png files for plot in self.plots: os.remove(plot) # showing message self.show_message_box()
App类实现
class RaportApp(App): def build(self): self.layout = RelativeLayout() self.generate_pdf = GeneratePDF(app_instance=self) self.button = Button(text="Choose File", on_release=self.show_file_chooser, size_hint=(None,None), size=(200,50), pos_hint={'center_x':0.5,'center_y':0.5}) self.layout.add_widget(self.button) return self.layout def show_file_chooser(self, *args): username = os.getenv("USERNAME") content = FileChooserListView(path = f'C:\\Users\\{username}\\Desktop', filters=['*.csv']) self.file_chooser = FileChooserPopup(title="Choose a *.csv file", content=content, size_hint=(0.8, 0.8)) content.bind(on_submit=self.generate_pdf.generate_raport1) self.file_chooser.open() def update_progress(self, value): self.generate_pdf.update_progress(value) if __name__ == '__main__': RaportApp().run()
原因分析
Kivy的UI渲染完全依赖主线程,你的generate_report2方法虽然用Clock.schedule_once调度,但本质还是在主线程中执行。而绘图、PDF生成都是耗时任务,会直接阻塞主线程,导致UI无法及时刷新——ProgressBar的数值更新操作会被积压,直到整个任务执行完毕才会一次性显示。另外,time.sleep(1)也会进一步阻塞主线程,加剧卡顿。
解决方案
1. 用线程隔离耗时任务
把所有绘图、PDF生成的耗时操作放到单独线程中执行,避免阻塞主线程。同时,所有UI更新操作必须通过Clock.schedule_once调度回主线程执行(Kivy禁止在子线程中操作UI控件)。
修改核心代码如下:
import threading # 新增导入线程库 # 修改generate_raport1方法 def generate_raport1(self, instance, selection, *args): if selection: self.selected_file = selection[0] self.create_progress_popup() # 启动子线程执行耗时任务 threading.Thread(target=self.generate_report2, daemon=True).start() else: self.app_instance.file_chooser.dismiss() # 修改generate_report2方法 def generate_report2(self): # 绘图操作+调度UI更新 self.plot1(self.selected_file) Clock.schedule_once(lambda dt: self.update_progress(20)) self.plot2() Clock.schedule_once(lambda dt: self.update_progress(20)) self.plot3() Clock.schedule_once(lambda dt: self.update_progress(20)) self.plot4() Clock.schedule_once(lambda dt: self.update_progress(20)) self.write_pdf() Clock.schedule_once(lambda dt: self.update_progress(20)) # 替换线程内的sleep(如果非必须建议直接删除) time.sleep(1) for plot in self.plots: os.remove(plot) # 弹窗显示也必须调度回主线程 Clock.schedule_once(lambda dt: self.show_message_box())
2. 移除不必要的阻塞
如果time.sleep(1)只是为了让用户看到100%的进度,建议用Clock.schedule_once延迟执行后续操作,避免阻塞线程:
# 替换原time.sleep(1)和后续代码 Clock.schedule_once(lambda dt: self.finish_task(), 1) # 新增finish_task方法 def finish_task(self): for plot in self.plots: os.remove(plot) self.show_message_box()
内容的提问来源于stack exchange,提问作者Kacper Kaszuba
相关产品推荐
相关产品推荐

