在 kivy/kivymd 应用中,`app.root.ids` 返回空字典通常是因为 `ids` 并非全局注入到 `app.root`,而是仅存在于定义它们的 kv 规则所对应的**直接父级 widget 实例**中;错误地假设所有 `id` 都会自动挂载到 `app.get_running_app().root.ids` 是常见误区。
Kivy 的 ids 机制本质上是 KV 语言在解析时,将同级(即同一层级 KV 块内)声明的 id: 属性
注入到该 KV 块所创建的根 widget 的 ids 字典中
,而非应用根节点(App.root)本身。这意味着:
✅ 如果你在 Screen: 或 BoxLayout: 等容器的 KV 定义块中写了 id: my_box,那么该 id 会被注入到这个 BoxLayout 实例的 ids 字典里;
❌ 即使这个 BoxLayout 是 App.root 的子组件,它的 id
也不会自动“冒泡”到 App.root.ids
;
❌ 同样,自定义组件(如 MenuButton)内部定义的 id(如 id: Menu_Button),若未在该组件自身的 KV 模板中声明,或未被正确加载为子控件,也不会出现在任何 ids 字典中。
正确获取 ids 的方法
假设你的 KV 文件结构如下:
而 Python 中 MenuButton.press() 需要访问 container,你
不能
写:
Trae win
Trae Windows 官方版本现已全面支持 Win10 和 Win11 系统(64位)。国内开发者可直接访问 Trae 中文官网(trae.cn 或 trae.com.cn),一键下载官方正版 .exe 安装包,享受国内直连的高速稳定体验。安装过程极其简便,双击即可运行,并支持一键无缝导入 VS Code 或 Cursor 的原有主题、快捷键及插件配置。登录支持手机号、微信或稀土掘金账号。内置免费的豆包与 DeepSeek 等顶级大模型,让你开箱即用,轻松开启高效的 AI 智能编程之旅。
下载
而应通过
向上查找父级 widget
来定位目标 ids:
关键注意事项
? self.id 在运行时为空字符串?—— 这说明该 widget
未被 KV 解析器赋予 id
。id 只在 KV 中显式声明且该 widget 是 KV 块中
直接子项
时才生效;在 Python 中手动实例化的 widget 不会自动拥有 id。
? ids 是只读字典,且
仅在 KV 加载完成后可用
。务必在 on_start() 或之后访问(build() 中可能尚未完成解析)。
? 推荐实践:为关键容器(如 Screen、BoxLayout)显式设 id,并在逻辑中通过 self.parent.ids 或 self.parent.parent.ids 定位,避免硬
编码
层级;也可使用 ObjectProperty 在 Python 类中绑定引用,提升可维护性。
总结
ids 不是全局
注册表
,而是 KV
作用域
的局部映射。理解 “ids 属于定义它们的那个 widget 实例” 是解决此类问题的核心。始终检查 id 是否在正确的 KV 块中声明,并通过合理的父子关系导航获取目标 widget,而非依赖 App.root.ids。
# main.kv
MDScreen:
BoxLayout:
id: container
orientation: "vertical"
MenuButton:
id: menu_btn
text: "Open Menu"# ❌ 错误:App.root 是 MDScreen,但 container 和 menu_btn 都不是它直接定义的 id
print(MDApp.get_running_app().root.ids) # → {}class MenuButton(MDFloatingActionButton):
def press(self):
app = MDApp.get_running_app()
# ✅ 正确:从当前按钮出发,逐级向上找含目标 id 的父容器
container = self.parent.parent # 根据实际层级调整(此处为 MDScreen → BoxLayout)
if hasattr(container, 'ids') and 'container' in container.ids:
print("Found container:", container.ids['container'])
# ✅ 更健壮的方式:使用 `get_parent_window()` 或按类型查找
screen = self.get_root_window().children[0] # 若 MDScreen 是顶层窗口子项
if hasattr(screen, 'ids') and 'container' in screen.ids:
print("Via root window:", screen.ids['container'])