You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Kivy跨类调用类方法实现双按钮触发MDDataTable数据更新

问题原因

代码失效的核心原因非常明确:Test类内按钮绑定事件时写的Example().update_row_data会当场创建一个完全独立的Example实例,这个实例从未被挂载到当前正在渲染的控件树上,修改它的data_tables属性完全不会影响界面上实际显示的那个Example实例的数据表。

Kivy框架下跨类调用控件方法的核心逻辑是:必须先拿到界面上已经挂载渲染的目标实例引用,再调用对应方法,绝对不能凭空新建同类型实例操作。


修复方案

方法1:通过kv的id索引目标实例(推荐)

这是Kivy原生支持的实例索引方式,逻辑最清晰,后续维护成本最低。只需要给kv规则里的Example实例绑定id,在Test类中通过父节点的ids属性拿到实际渲染的实例即可,不需要手动新建Example对象。
修复后的完整可运行代码:

from kivy.metrics import dp

from kivymd.app import MDApp
from kivymd.uix.datatables import MDDataTable
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.button import MDRaisedButton
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import Screen
from kivy.lang import Builder


Builder.load_string("""
<All>:
    Test:
    # 给界面实际渲染的Example实例绑定id
    Example:
        id: example_instance
""")

class All(Screen):
    pass


class Test(BoxLayout):

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.test()
    

    def test(self):
        # 通过父节点All的ids字典,拿到已经挂载到界面的Example实例
        target_example = self.parent.ids.example_instance
        self.add_widget(MDRaisedButton(
                text="Change 2 row",
                pos_hint={"center_x": 0.5},
                # 绑定实际渲染实例的更新方法
                on_press = target_example.update_row_data,
                y=24,
            ))

class Example(BoxLayout):
    data_tables = None

    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.build_()


    def build_(self):
        self.layout = MDFloatLayout()
        self.layout.add_widget(
            MDRaisedButton(
                text="Change 2 row",
                pos_hint={"center_x": 0.5},
                on_release=self.update_row_data,
                y=24,
            )
        )
        self.data_tables = MDDataTable(
            pos_hint={"center_y": 0.5, "center_x": 0.5},
            size_hint=(0.9, 0.6),
            use_pagination=False,
            column_data=[
                ("No.", dp(30)),
                ("Column 1", dp(40)),
                ("Column 2", dp(40)),
                ("Column 3", dp(40)),
            ],
            row_data=[(f"{i + 1}", "1", "2", "3") for i in range(3)],
        )
        self.layout.add_widget(self.data_tables)
        self.add_widget(self.layout)


    def update_row_data(self, instance_button):
        self.data_tables.row_data = [('1','1','1','1')]
        print(self.data_tables.row_data)


class App(MDApp):
    def build(self):
        return All()


App().run()

方法2:通过App全局属性存储实例引用

如果控件嵌套层级太深,逐层找父节点拿id比较麻烦,可以在Example类初始化时把自身实例存到App的全局属性中,其他类直接从App实例取引用即可:

  1. 在Example的__init__方法中添加一行:MDApp.get_running_app().example_ref = self
  2. 在Test类中获取目标实例时写:target_example = MDApp.get_running_app().example_ref
    这种方式不需要修改kv规则,但全局引用过多会提升维护成本,仅适合小型项目快速实现使用。

注意事项
  • 只要是已经挂载到Kivy控件树渲染的实例,不要手动执行类名()新建同类型对象调用方法,新对象和界面实例是内存中完全独立的两个实体,操作不会同步到界面。
  • 如果多个位置需要调用同一个更新逻辑,也可以把更新逻辑抽到全局工具类或者App类的方法中,避免跨控件拿引用的复杂度。

内容的提问来源于stack exchange,提问作者ahmed omran

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 21:39:28