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

KivyMD中MDDataTable调用add_row等方法报AttributeError如何解决

问题描述

练习使用KivyMD的MDDataTable组件时,参考官方文档编写代码测试行增删改功能,点击对应按钮触发以下报错:

1) self.data_tables.add_row((str(last_num_row + 1), "1", "2", "3"))  
 AttributeError: 'MDDataTable' object has no attribute 'add_row'  
   
2) self.data_tables.remove_row(self.data_tables.row_data[-1])   
 AttributeError: 'MDDataTable' object has no attribute 'remove_row'

3) self.data_tables.update_row(
  AttributeError: 'MDDataTable' object has no attribute 'update_row'

组件实际表现和官方文档描述的功能不一致,测试用的datatable_operation.py代码如下:

from kivy.metrics import dp
from kivymd.app import MDApp
from kivymd.uix.datatables import MDDataTable
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.button import MDRaisedButton

class Example(MDApp):
    data_tables = None

    def build(self):
        layout = MDFloatLayout()  # root layout
        # Creating control buttons.
        button_box = MDBoxLayout(
            pos_hint={"center_x": 0.5},
            adaptive_size=True,
            padding="24dp",
            spacing="24dp",
        )

        for button_text in ["Add row", "Remove row"]:
            button_box.add_widget(
                MDRaisedButton(
                    text=button_text, on_release=self.on_button_press
                )
            )

       layout = MDFloatLayout()
           layout.add_widget(
              MDRaisedButton(
                  text="Change 2 row",
                  pos_hint={"center_x": 0.5},
                  on_release=self.update_row,
                  y=24,
               )
           )

        # Create a table.
        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=[
                ("1", "1", "2", "3"),
                ("1", "1", "2", "3"),
                ],
        )
        # Adding a table and buttons to the toot layout.
        layout.add_widget(self.data_tables)
        layout.add_widget(button_box)

        return layout

    def add_row(self):
        last_num_row = int(self.data_tables.row_data[-1][0])
        self.data_tables.add_row((str(last_num_row + 1), "1", "2", "3"))

    def remove_row(self):
        if len(self.data_tables.row_data) > 1:
            self.data_tables.remove_row(self.data_tables.row_data[-1])

    def update_row(self, instance_button: MDRaisedButton) -> None:
       self.data_tables.update_row(
           self.data_tables.row_data[1],  # old row data
           ["2", "A", "B", "C"],          # new row data
    )
            
    def on_button_press(self, instance_button: MDRaisedButton) -> None:
        '''Called when a control button is clicked.'''

        try:
            {   "Add row": self.add_row,
                "Remove row": self.remove_row,
            }[instance_button.text]()
        except KeyError:
            pass
           
Example().run() 
问题原因

触发AttributeError的核心原因是本地安装的KivyMD版本与参考的官方文档版本不匹配:

  • add_row、remove_row、update_row是KivyMD 1.2.0及以上版本才为MDDataTable新增的实例方法
  • 本地如果安装的是1.1.1及更早的稳定版本,类本身没有这几个方法,调用时必然抛出属性不存在的错误

除此之外贴出的代码本身存在两处语法问题,就算版本匹配也无法正常运行:

  • build方法内重复声明layout = MDFloatLayout(),第二次声明会覆盖第一次创建的、已经挂载了按钮组的布局实例
  • 第二次声明layout后,添加"Change 2 row"按钮的代码缩进不符合Python语法规范,运行时会直接抛缩进错误
解决方法

方案1:升级KivyMD到匹配文档的版本

直接执行升级命令:

pip install kivymd --upgrade

升级完成后修正代码里的语法错误,修正后的可运行代码如下:

from kivy.metrics import dp
from kivymd.app import MDApp
from kivymd.uix.datatables import MDDataTable
from kivymd.uix.boxlayout import MDBoxLayout
from kivymd.uix.floatlayout import MDFloatLayout
from kivymd.uix.button import MDRaisedButton

class Example(MDApp):
    data_tables = None

    def build(self):
        layout = MDFloatLayout()  # 根布局
        # 创建控制按钮组
        button_box = MDBoxLayout(
            pos_hint={"center_x": 0.5},
            adaptive_size=True,
            padding="24dp",
            spacing="24dp",
        )

        for button_text in ["Add row", "Remove row"]:
            button_box.add_widget(
                MDRaisedButton(
                    text=button_text, on_release=self.on_button_press
                )
            )
        # 移除重复的layout声明,修复缩进,直接在原有布局上加修改行按钮
        layout.add_widget(
            MDRaisedButton(
                text="Change 2 row",
                pos_hint={"center_x": 0.5},
                on_release=self.update_row,
                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=[
                ("1", "1", "2", "3"),
                ("1", "1", "2", "3"),
            ],
        )
        # 把表格和按钮组加到根布局
        layout.add_widget(self.data_tables)
        layout.add_widget(button_box)

        return layout

    def add_row(self):
        last_num_row = int(self.data_tables.row_data[-1][0])
        self.data_tables.add_row((str(last_num_row + 1), "1", "2", "3"))

    def remove_row(self):
        if len(self.data_tables.row_data) > 1:
            self.data_tables.remove_row(self.data_tables.row_data[-1])

    def update_row(self, instance_button: MDRaisedButton) -> None:
        self.data_tables.update_row(
            self.data_tables.row_data[1],  # 旧行数据
            ["2", "A", "B", "C"],          # 新行数据
        )
            
    def on_button_press(self, instance_button: MDRaisedButton) -> None:
        '''按钮点击触发逻辑'''
        try:
            {   
                "Add row": self.add_row,
                "Remove row": self.remove_row,
            }[instance_button.text]()
        except KeyError:
            pass
           
Example().run()

方案2:不升级版本,直接操作row_data属性

如果不想升级KivyMD,旧版本的MDDataTable会自动监听row_data的变化,直接修改这个列表属性就能实现增删改效果,对应三个方法的写法改成:

def add_row(self):
    last_num_row = int(self.data_tables.row_data[-1][0])
    # 直接append新行元组即可
    self.data_tables.row_data.append(
        (str(last_num_row + 1), "1", "2", "3")
    )

def remove_row(self):
    if len(self.data_tables.row_data) > 1:
        # 直接pop最后一行
        self.data_tables.row_data.pop()

def update_row(self, instance_button: MDRaisedButton) -> None:
    # 找到要修改的行的索引,直接替换对应位置的数据
    self.data_tables.row_data[1] = ("2", "A", "B", "C")

使用该方案同样需要先修复前文提到的代码缩进、重复定义layout的语法错误,才能正常运行。

内容的提问来源于stack exchange,提问作者Loh Boon How

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 00:03:33