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
相关产品推荐
相关产品推荐

