Kivy退出时后台生成Matplotlib图表,无法显示等待提示求助
解决Kivy退出时显示等待提示的问题
你的问题核心在于Kivy的UI主线程被阻塞了,而且exit()命令会直接终止程序,导致你的提示还没来得及渲染就消失了。具体来说:
- 当你调用
p1.join()、p2.join()这些方法时,主线程会被完全卡住,一直等待子进程完成,这时候Kivy根本没时间去更新UI,所以你的Label永远显示不出来。 - 就算主线程没被完全阻塞,最后的
exit()也会直接终止整个应用,UI组件还没完成渲染就被强制关掉了。
下面给你几个可行的解决方案:
方案一:用线程+Kivy Clock监控进程状态
我们可以把等待子进程完成的逻辑放到单独线程里,同时用Kivy的Clock回到主线程更新UI,这样主线程不会被阻塞,提示能正常显示。
修改后的代码示例:
from kivy.clock import Clock import threading from kivy.uix.popup import Popup from kivy.uix.label import Label from kivy.app import App def fermerBanc(self): # 先创建并显示等待弹窗(比直接加Label更直观) self.wait_popup = Popup( title='处理中', content=Label(text="Merci de patienter...", font_size='55sp'), size_hint=(0.6, 0.4), auto_dismiss=False ) self.wait_popup.open() # 图表参数准备部分保持不变 list_argument_graph = [ passerelle.graph1.y_low_min, passerelle.graph1.y_low_max, passerelle.graph1.y_high_min, passerelle.graph1.y_high_max, passerelle.graph1.setTaille ] list_argument_graph_1 = [chemin1] + list_argument_graph list_argument_graph_2 = [chemin2] + list_argument_graph list_argument_graph_3 = [chemin3] + list_argument_graph list_argument_graph_4 = [chemin4] + list_argument_graph processes = [] if passerelle.graph1.flagGraph: process_args = [ (list_argument_graph_1,), (list_argument_graph_2,), (list_argument_graph_3,), (list_argument_graph_4,) ] for idx, args in enumerate(process_args, 1): try: p = Process(target=process_graph, args=args) p.start() processes.append(p) except AttributeError: Logger.warning(f'fermerBanc: impossible de generer un graph {idx}, fichier vide') # 定义线程函数:等待所有进程完成后,回到主线程处理退出 def wait_for_processes(): for p in processes: if p.is_alive(): p.join() # 必须用Clock调度,确保UI操作在主线程执行 Clock.schedule_once(lambda dt: self.finish_exit()) # 启动后台线程,不阻塞主线程 threading.Thread(target=wait_for_processes, daemon=True).start() def finish_exit(self): # 关闭等待弹窗 self.wait_popup.dismiss() # 用Kivy官方的stop方法退出,保证应用正常收尾 App.get_running_app().stop()
关键改进点
- 用Popup替代直接添加Label:弹窗是独立的UI组件,更适合显示等待提示,用户感知更清晰。
- 避免主线程阻塞:把
join()放到后台线程,让主线程能继续处理UI渲染。 - 用
App.stop()替代exit():exit()会直接终止进程,而App.stop()会让Kivy正常关闭应用,确保所有UI操作完成。 - Clock调度UI操作:Kivy的UI更新必须在主线程执行,子线程完成后通过
Clock.schedule_once回到主线程处理收尾。
方案二:简化版(定期检查进程状态)
如果你觉得线程方案有点繁琐,也可以用Clock.schedule_interval定期检查进程是否完成:
from kivy.clock import Clock from kivy.app import App def fermerBanc(self): # 显示等待Label self.msgWarning = Label(text="Merci de patienter...", font_size='55sp', pos_hint={'center_x': .5, 'center_y':.5}) self.add_widget(self.msgWarning) # 进程创建和启动的代码和之前一致... processes = [p1, p2, p3, p4] # 定义检查函数 def check_processes(dt): # 判断所有进程是否都已结束 all_done = all(not p.is_alive() for p in processes if p is not None) if all_done: self.remove_widget(self.msgWarning) App.get_running_app().stop() return False # 停止调度 # 每0.5秒检查一次进程状态 Clock.schedule_interval(check_processes, 0.5)
为什么之前注释掉多进程和exit就正常?
因为去掉join()后,主线程不会被阻塞,能立刻渲染Label;同时没有exit()强制终止程序,所以你能看到Label正常显示出来。
内容的提问来源于stack exchange,提问作者Julie96
相关产品推荐
相关产品推荐

