4

I'm having some trouble adding the kivymd MDDataTable component as a child widget to a Screen in a .kv file. Keep getting a KeyError: 'container' and AttributeError: 'super' object has no attribute '__getattr__ error. I've looked through the docs and multiple sites and everyone seems to be using some variant of the example found in the docs, which starts the component in the build method.

What I'm trying to say is if this works

class Example(MDApp):
    def build(self):
        screen = Screen()
        data_tables = MDDataTable(
            size_hint=(0.9, 0.6),
            column_data=[
                ('Template Id', dp(30)),
                ('Name', dp(30))
            ],
            row_data=[
                ('23lkjk33', 'Ayang Paul'),
                ('28ij399kk', 'Ringwa Justin')
            ]
        )
        screen.add_widget(data_tables)
        return screen
   

Example().run()

then why doesn't this work

KV = '''
Screen:
    MDDataTable:
        size_hint: 0.9, 0.6
        pos_hint: {"center_x": 0.5, "center_y": 0.5}
        column_data: [('Template Id', dp(30)), ('Name', dp(30))]
        row_data: [('23lkjk33', 'Ayang Paul'), ('28ij399kk', 'Ringwa Justin')]
'''
class Example(MDApp):
    def build(self):
        return Builder.load_string(KV)
Example().run()

???

droptop
  • 1,372
  • 13
  • 24

3 Answers3

6

The problem is that the MDDataTable__init__() method references ids, but when used in a kv file, the ids are not yet available. The MDDataTable is a subclass of ModalView, so it is apparently intended to be used like a Popup rather than in a kv file.

Here is a hack that will let it work in a kv file:

from kivymd.uix.datatables import MDDataTable, TableHeader, TableData, TablePagination
from kivymd.uix.dialog import BaseDialog


class MyDataTable(MDDataTable):
    def __init__(self, **kwargs):
        # skip the MDDataTable.__init__() and call its superclass __init__()
        super(BaseDialog, self).__init__(**kwargs)

        # schedule call to MDDataTable.__init__() contents after ids are populated
        Clock.schedule_once(partial(self.delayed_init, **kwargs))

    def delayed_init(self, dt, **kwargs):
        # this is copied from MDDataTable.__init__() with super() call deleted
        self.register_event_type("on_row_press")
        self.register_event_type("on_check_press")
        self.header = TableHeader(column_data=self.column_data, sort=self.sort)
        self.table_data = TableData(
            self.header,
            row_data=self.row_data,
            check=self.check,
            rows_num=self.rows_num,
            _parent=self,
        )
        self.pagination = TablePagination(table_data=self.table_data)
        self.table_data.pagination = self.pagination
        self.header.table_data = self.table_data
        self.table_data.fbind("scroll_x", self._scroll_with_header)
        self.ids.container.add_widget(self.header)
        self.ids.container.add_widget(self.table_data)
        if self.use_pagination:
            self.ids.container.add_widget(self.pagination)
        Clock.schedule_once(self.create_pagination_menu, 0.5)

So use MyDataTable in your kv in place of MDDataTable. The above code delays the execution of the guts of the MDDataTable.__init__() method until the ids are available. If the MDDataTable code is ever updated, this hack may not work.

John Anderson
  • 35,991
  • 4
  • 13
  • 36
1

As previous have commented, this can be achieved via an override. The only change in the override is to remove the super() call from the original class and schedule the call to your new delayed_init. For the sake of copy+paste, as of kivy (2.1.0) and kivymd (0.104.2) the following works:

from kivymd.theming import ThemableBehavior
from kivymd.uix.datatables import MDDataTable
from kivy.clock import Clock

class MyDataTable(MDDataTable):
        def __init__(self, **kwargs):
            # skip the MDDataTable.__init__() and call its superclass __init__()
            super(ThemableBehavior, self).__init__(**kwargs)
    
            # schedule call to MDDataTable.__init__() contents after ids are populated
            Clock.schedule_once(partial(self.delayed_init, **kwargs))
    
        def delayed_init(self, dt, **kwargs):
            # this is copied from MDDataTable.__init__() with super() call deleted
            self.header = TableHeader(
                column_data=self.column_data,
                sorted_on=self.sorted_on,
                sorted_order=self.sorted_order,
            )
            self.table_data = TableData(
                self.header,
                row_data=self.row_data,
                check=self.check,
                rows_num=self.rows_num,
                _parent=self,
            )
            self.register_event_type("on_row_press")
            self.register_event_type("on_check_press")
            self.pagination = TablePagination(table_data=self.table_data)
            self.table_data.pagination = self.pagination
            self.header.table_data = self.table_data
            self.table_data.fbind("scroll_x", self._scroll_with_header)
            self.ids.container.add_widget(self.header)
            self.ids.container.add_widget(self.table_data)
            if self.use_pagination:
                self.ids.container.add_widget(self.pagination)
            Clock.schedule_once(self.create_pagination_menu, 0.5)
            self.bind(row_data=self.update_row_data)

Your KV file:

Screen:
    MDDataTable:
        size_hint: 0.9, 0.6
        pos_hint: {"center_x": 0.5, "center_y": 0.5}
        column_data: [('Template Id', dp(30)), ('Name', dp(30))]
        row_data: [('23lkjk33', 'Ayang Paul'), ('28ij399kk', 'Ringwa Justin')]
badhex
  • 11
  • 1
0

Looks like you have some indentation errors. From the documentation:

The indentation is important and must be consistent. The spacing must be a multiple of the number of spaces used on the first indented line. Spaces are encouraged: mixing tabs and spaces is not recommended.

Try this:

<ModifyTeacherInfo@Screen>:
    name: "modifyTeacherInfo"
    MDDataTable:
        size_hint: 0.9, 0.6
        pos_hint: {"center_x": 0.5, "center_y": 0.5}
        column_data: [('Template Id', dp(30)), ('Name', dp(30))]
        row_data: [('23lkjk33', 'Ayang Paul'), ('28ij399kk', 'Ringwa Justin')]
John Anderson
  • 35,991
  • 4
  • 13
  • 36