能否用Kivy Clock调度StringProperty的set()方法及相关问题
如何延迟Kivy ScreenManager的切换并正确调度属性set()方法
首先明确回答:你完全可以通过Clock调度Kivy属性的set()方法,但需要注意对象引用的问题——也就是你遇到的WeakProxy类型错误的根源。
为什么会遇到WeakProxy错误?
在你的代码里,self.manager是一个WeakProxy对象(Kivy为避免循环引用常用的手段),而property('current').set()方法第一个参数要求的是原始的EventDispatcher实例,不是代理对象,这就是报错的核心原因。
解决方案1:直接调度属性赋值(更简单推荐)
其实没必要纠结set()方法,直接在Clock.schedule_once的回调里给current属性赋值就行,Kivy的属性系统会自动处理通知和UI更新。这也是Kivy开发中的常规写法,直观且不易出错。
比如你想在点击按钮后延迟2秒切换屏幕,直接这么写:
# 在kv语言的按钮事件里 on_press: root.statusmsg = 'Preparing to switch...' Clock.schedule_once(lambda dt: root.manager.current = 'screen_two', 2)
解决方案2:如果一定要用set()方法(处理WeakProxy)
如果你坚持要用属性的set()方法,需要先把WeakProxy转换成原始对象。可以通过WeakProxy的内部属性__target__获取原始实例(在Kivy环境下这个用法是安全的):
from kivy.weakproxy import WeakProxy def deliver_payload(self, dt=0): if not dt: self.statusmsg = 'Busy launching Armageddon' # 获取原始的ScreenManager对象 sm = self.manager.__target__ if isinstance(self.manager, WeakProxy) else self.manager # 用set()方法调度切换 Clock.schedule_once(lambda dt: self.manager.property('current').set(sm, 'screen_one'), 2)
不过还是那句话,这种方法完全没必要,直接赋值更符合Kivy的设计逻辑。
关于BooleanProperty调度无效果的问题
如果你的BooleanProperty调度没反应,大概率也是同样的WeakProxy问题——你传给set()方法的是代理对象而非原始对象。解决思路和上面一致:要么直接赋值,要么先获取原始对象再调用set()。
修改后的完整示例代码
这里给你调整后的代码,把延迟切换逻辑更清晰地封装,同时避免WeakProxy问题:
#!/usr/bin/env python from kivy.app import App from kivy.lang import Builder from kivy.uix.floatlayout import FloatLayout from kivy.properties import StringProperty from kivy.clock import Clock Builder.load_string(''' #: kivy 1.9.2 #: import sm kivy.uix.screenmanager #: set base_font_size 25 <MainWidget>: manager: screen_manager ScreenManager: id: screen_manager transition: sm.NoTransition() Screen: name: 'screen_one' Button: text: 'Screen 1, press to switch to Screen 2 (2s delay)' font_size: base_font_size on_press: root.statusmsg = 'Preparing to switch to Screen 2...' Clock.schedule_once(root.switch_to_screen_two, 2) Screen: name: 'screen_two' Button: text: root.statusmsg font_size: base_font_size disabled: root.statusmsg == 'Busy launching Armageddon' on_press: root.deliver_payload() ''') class MainWidget(FloatLayout): statusmsg = StringProperty() def __init__(self, **kwargs): super(MainWidget, self).__init__(**kwargs) def switch_to_screen_two(self, dt): self.manager.current = 'screen_two' self.statusmsg = 'Screen 2, press to launch Armageddon' def deliver_payload(self, dt=0): if not dt: self.statusmsg = 'Busy launching Armageddon' # 延迟2秒切回screen_one,直接用属性赋值 Clock.schedule_once(self.switch_back_to_one, 2) def switch_back_to_one(self, dt): self.manager.current = 'screen_one' self.statusmsg = '' class MyApp(App): def build(self): mw=MainWidget() return mw if __name__ == '__main__': MyApp().run()
总结
- 优先选择直接在Clock回调里赋值属性的方式,这是Kivy最常规、最简洁的写法。
- 若必须使用
set()方法,一定要确保传给它的是原始EventDispatcher实例,而非WeakProxy对象。 - 所有属性调度的异常,本质都是对象引用的问题,只要找准原始实例就能解决。
内容的提问来源于stack exchange,提问作者Eugene B
相关产品推荐
相关产品推荐

