Kivy中Screen的__init__方法访问ScreenManager引发AttributeError求助
问题分析与解决方案
错误根源
你遇到的AttributeError: 'NoneType' object has no attribute 'add_widget'确实是因为Screen还未被添加到ScreenManager时,self.manager属性为None:
- 初始
__init__方法执行时,Screen还没被关联到ScreenManager,直接调用self.manager.add_widget自然报错。 - 后续修改中,你在MainApp里先调用
list_view_screen.load_tasks,再把Screen添加到ScreenManager,此时self.manager仍未被赋值,所以打印self.parent为None,报错依旧。 - 循环中使用lambda绑定事件时还存在变量捕获问题——最终所有列表项的点击事件都会指向最后一个item的文本,这是你后续可能遇到的隐性bug。
解决方案
方案一:调整load_tasks的调用时机
将load_tasks的调用放到Screen被添加到ScreenManager之后,此时self.manager已正常赋值:
修改MainApp的build方法
class MainApp(MDApp): def build(self): self.theme_cls.theme_style = "Dark" Builder.load_file("styling.kv") sm = ScreenManager() list_view_screen = ToDoListView(name = "ListView") # 先把Screen添加到ScreenManager sm.add_widget(list_view_screen) # 此时self.manager已可用,再调用加载逻辑 list_view_screen.load_tasks(list_view_screen.loaded_items) return sm
修正load_tasks方法
修复笔误并解决lambda变量捕获问题:
def load_tasks(self, loaded_objects): for i in loaded_objects: todo_page = ToDoListPage(name=str(i.text)) self.manager.add_widget(todo_page) # 使用默认参数固定当前item的文本,避免循环变量捕获问题 i.bind(on_release=lambda x, item_text=i.text: self.change_screen(item_text))
方案二:使用Screen生命周期事件(更符合Kivy设计)
利用Screen的on_pre_enter事件(该事件在Screen被添加到ScreenManager且即将显示时触发),自动执行初始化逻辑,无需手动调用load_tasks:
修改ToDoListView类
def __init__(self, **kw): super().__init__(**kw) json_data_object = JsonData("data.json") self.loaded_items = [] # 先只添加列表项到MDList,不处理页面绑定 for i in json_data_object.data["lists"]: loaded_object = ListItemWithoutCheckbox(text="[b]" + i["list_name"] + "[/b]") self.ids["Container"].add_widget(loaded_object) self.loaded_items.append(loaded_object) def on_pre_enter(self, *args): # 此事件触发时,self.manager已被正确赋值 for i in self.loaded_items: todo_page = ToDoListPage(name=str(i.text)) self.manager.add_widget(todo_page) i.bind(on_release=lambda x, item_text=i.text: self.change_screen(item_text))
简化MainApp的build方法
class MainApp(MDApp): def build(self): self.theme_cls.theme_style = "Dark" Builder.load_file("styling.kv") sm = ScreenManager() sm.add_widget(ToDoListView(name = "ListView")) return sm
补充:确保change_screen方法正确实现
最后确认你的页面跳转方法逻辑正确:
def change_screen(self, screen_name): self.manager.current = screen_name
内容的提问来源于stack exchange,提问作者Dean Williams
相关产品推荐
相关产品推荐

