Kivy以ScreenManager为根时自定义重启后控件重复堆叠问题求解
问题成因
- 你当前使用的重启逻辑存在缺陷:
App.stop()仅停止App的事件循环调度,不会销毁已挂载到系统窗口的旧控件树,也不会释放Kivy全局缓存的kv规则、控件纹理资源。在旧App实例未完全销毁的情况下直接启动新App实例,新的控件树会被挂载到同一个窗口上,和旧控件树叠加渲染。 - 该问题仅在根控件为ScreenManager时触发的原因:普通容器(如FloatLayout)的控件渲染直接绑定到父容器,
clear_widgets可以直接移除所有子控件的渲染引用;但ScreenManager自带屏幕过渡动画缓存、离屏渲染缓冲,clear_widgets仅移除了子Screen的逻辑引用,没有清空缓存的纹理资源,旧的Screen纹理还会继续被绘制到窗口上,最终出现堆叠效果。 - 额外隐患:你没有自定义
build()方法,依赖Kivy自动加载同名kv文件的机制,首次加载kv时会自动创建顶层ScreenManager实例,后续重启时虽然kv不会重复加载,但旧ScreenManager实例的资源未被GC回收,新旧实例的渲染同时生效。
解决方案
方案1(推荐,无需重启App)
你的需求是刷新控件、重读存档,完全不需要重启整个App,直接重置ScreenManager的子控件即可,性能更高且不会有资源残留问题:
- 先在kv文件中给Screen补充name属性,方便切换:
<First>: name: 'first' # 其余原有内容不变 <Second>: name: 'second'
- 修改App的restart方法:
def restart(self): # 清空原有所有屏幕 self.root.clear_widgets() # 重新添加新的屏幕实例,实现控件完全刷新 self.root.add_widget(First()) self.root.add_widget(Second()) # 切回默认首屏 self.root.current = 'first' # 这里可以加你自己的重读存档逻辑
方案2(适用于必须重启整个App的场景)
如果需要重载kv文件、重置全局Kivy配置,就需要完全退出旧进程再启动新进程,避免同一进程内的资源残留:
import sys import os from multiprocessing import Process from kivymd.app import MDApp from kivy.uix.screenmanager import Screen from kivy.config import Config Config.set("kivy", "exit_on_escape", 0) class First(Screen): pass class Second(Screen): pass class Test(MDApp): def restart(self): self.stop() # 启动新进程运行当前脚本 Process(target=os.execv, args=(sys.executable, [sys.executable] + sys.argv)).start() # 退出旧进程 sys.exit() Test().run()
内容的提问来源于stack exchange,提问作者Weebify
相关产品推荐
相关产品推荐

