如何在Kivy中正确实现EventDispatcher?求示例指导
Kivy EventDispatcher 实现示例
这里是符合你需求的完整实现,解决事件发布与UI组件订阅的问题:
from kivy.app import App from kivy.uix.button import Button from kivy.uix.boxlayout import BoxLayout from kivy.event import EventDispatcher from kivy.clock import mainthread import random import threading # 任务类:作为事件发布者,执行任务后分发结果事件 class Task(EventDispatcher): def __init__(self, **kwargs): super().__init__(**kwargs) # 注册自定义事件类型,命名需以on_开头 self.register_event_type('on_process_task_result') def execute(self): # 启动子线程执行耗时任务,避免阻塞UI线程 threading.Thread(target=self._run_task).start() def _run_task(self): # 模拟任务执行过程 result = random.choice(["ok", "fail", "repeat"]) print(f"Task执行完成,结果: {result}") # 回到主线程分发事件(Kivy UI操作必须在主线程) self._dispatch_result(result) @mainthread def _dispatch_result(self, result): # 分发事件,传递结果参数 self.dispatch('on_process_task_result', result) # 必须定义事件的默认处理函数(即使是空实现) def on_process_task_result(self, result): pass # 自定义接收器按钮:订阅Task的事件,接收结果并更新UI class ResultReceiver(Button): def __init__(self, **kwargs): super().__init__(**kwargs) # 事件处理函数:参数为事件源(Task实例)和结果 def handle_task_result(self, sender, result): self.text = f"收到结果: {result}" print(f"{self} 收到结果: {result}") # 主布局:管理Task和UI组件的绑定 class MainLayout(BoxLayout): def __init__(self, **kwargs): super().__init__(**kwargs) self.orientation = 'vertical' # 创建Task实例 self.task = Task() # 创建两个接收器按钮 self.receiver1 = ResultReceiver(text="接收器1") self.receiver2 = ResultReceiver(text="接收器2") # 将Task的事件绑定到接收器的处理函数 self.task.bind(on_process_task_result=self.receiver1.handle_task_result) self.task.bind(on_process_task_result=self.receiver2.handle_task_result) # 添加触发任务的按钮 exec_btn = Button(text="执行任务") exec_btn.bind(on_press=lambda x: self.task.execute()) self.add_widget(exec_btn) self.add_widget(self.receiver1) self.add_widget(self.receiver2) class EventDispatcherDemoApp(App): def build(self): return MainLayout() if __name__ == '__main__': EventDispatcherDemoApp().run()
关键要点说明
事件发布者(Task)
- 必须用
register_event_type注册自定义事件,事件名需以on_开头 - 必须实现事件对应的默认处理函数(即使是空函数,Kivy要求事件存在默认实现)
- 耗时任务放在子线程执行,通过
mainthread装饰器回到主线程分发事件,避免UI卡顿或异常
- 必须用
事件订阅者(UI组件)
- 通过
bind方法将Task的事件绑定到组件的处理函数 - 处理函数的第一个参数是事件源(Task实例),第二个是自定义的结果参数,参数顺序不能错
- 通过
你的原代码问题
- Task没有注册目标事件类型,导致无法分发
- 接收器错误地自身注册事件,应该是订阅Task的事件
- 直接在子线程分发事件会导致Kivy UI操作异常,必须回到主线程
dispatch方法需传递位置参数,而非关键字参数
内容的提问来源于stack exchange,提问作者mister_kanister
相关产品推荐
相关产品推荐

