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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:41:48