Search code examples
pythonperformanceraspberry-pikivy

Performance issue when using the kivy MDDatatable on RaspberryPi 3+


The Problem: I want to display a table with data on the raspberry, that gets updated after clicking on a button. I use the MDDatatable form kivymd for the table. The app is not lagging on my Windows-PC. When I run it on the Raspberry Pi 3+ moving the cursor causes the app to lag. When I click on the button it takes a few seconds to update the table.

The Windows System is running on Windows 10 Pro. The CPU is a Intel(R) Core(TM) i7-7700. It has 3.60 GHz. The GPU is a Intel(R) HD Graphics 630 and has 7.9 GB GPU memory.

Im using python 3.7.3 and the Buster OS on the Raspberry. The processor is a "Broadcom BCM2837B0 4-Core-Processor ARM Cortex-A53". On a raspberry you can designate how much of this processor should be used as GPU or CPU. Currently I have designated 64 MB as GPU. It has a clock frequency of 1.2 GHz and 1 GB Ram.

What I need help with: Does someone know another module I should try? Can I optimize the code to make it run faster? I think that multi threading wouldn't help, since there isn't any code that could run in parallel. Is that correct?

Python File

from kivymd.app import MDApp
from kivy.uix.widget import Widget
from kivymd.uix.datatables import MDDataTable
from kivy.metrics import dp
from kivy.lang import Builder
from memory_profiler import profile

#Designate .kv design file
Builder.load_file('mdapp.kv')

class MyLayout(Widget):
    pass

class GUI(MDApp):

    def build(self):
        return MyLayout()
    
    @profile    
    def on_start(self):
        #create Datatable
        self.data_tables = MDDataTable(
            pos_hint={'center_x': 10,'center_y': 2.7},
            size_hint=(None, None),
            size = (460, 460),
            use_pagination=True,
            column_data=[("No.", dp(10)),
                ("UID", dp(30)),
                ("Reichweite in cm", dp(30))],
            row_data=[
                ("1", "x100", "30")])
        mylayout = self.root.ids.mylayout
        mylayout.add_widget(self.data_tables)
        pass
        
    def add_data(self):
        self.data_tables.add_row(("1","2","3"))
        pass
    
    @profile
    def Start(self):
        #Logikschicht.drive50cm()
        self.add_data()
        pass
    
if __name__ == '__main__':
    GUI().run()

kv file

<MyLayout>:    
FloatLayout:
    id: mylayout  
        
    Button:
        text: 'Start'
        on_press: app.Start()
        pos: 1050, 650
        size_hint: None, None
        size: 200, 80

    FloatLayout:
        size_hint: None, None
        size: 800,800 
        id: container

What I have tried so far: This thread (Kivy ui is very slow on a RPI) suggests trying a simple app with only one button. This app runs lag free. I tried increasing the GPU Memory from 64 MB to 256 MB which also didn't help. Increasing the GPU Memory to 512 MB seems to help a little bit, but the app is still laggy.

I have added the profiler modul to see the Memory usage. Im not quite sure if 220 MB memory usage is an unnormal amount of memory usage. The output on the Windows System is:

Line #    Mem usage    Increment  Occurrences   Line Contents
=============================================================
    19    153.5 MiB    153.5 MiB           1       @profile    
    20                                             def on_start(self):
    21                                                 #create Datatable
    22    153.5 MiB      0.0 MiB           1           self.data_tables = MDDataTable(
    23    153.5 MiB      0.0 MiB           1               pos_hint={'center_x': 10,'center_y': 2.7},
    24    153.5 MiB      0.0 MiB           1               size_hint=(None, None),
    25    153.5 MiB      0.0 MiB           1               size = (460, 460),
    26    153.5 MiB      0.0 MiB           1               use_pagination=True,
    27    153.5 MiB      0.0 MiB           1               column_data=[("No.", dp(10)),
    28    153.5 MiB      0.0 MiB           1                   ("UID", dp(30)),
    29    153.5 MiB      0.0 MiB           1                   ("Reichweite in cm", dp(30))],
    30                                                     row_data=[
    31    156.2 MiB      2.7 MiB           1                   ("1", "x100", "30")])
    32    156.2 MiB      0.0 MiB           1           mylayout = self.root.ids.mylayout
    33    156.2 MiB      0.0 MiB           1           mylayout.add_widget(self.data_tables)
    34    156.2 MiB      0.0 MiB           1           pass


Filename: i:\sebastian\bachelorarbeit\code\gui\datatableminimalexample\gui_kivymd.py

Line #    Mem usage    Increment  Occurrences   Line Contents
=============================================================
    40    216.2 MiB    216.2 MiB           1       @profile
    41                                             def Start(self):
    42                                                 #Logikschicht.drive50cm()
    43    216.2 MiB      0.0 MiB           1           self.add_data()
    44    216.2 MiB      0.0 MiB           1           pass

Solution

  • Update: I haven't found any other module for creating tabels. I however found, that normaly people use Labels to create tabels.

    I created a seperate file I can import that handels the content of the label to create the table:

    class SimpleTabel:
        
        def __init__(self,header):
            #create header row
            self.header = header
            self.header_row = ""
            self.header_row_length = []
            self.row_nr = 0
            for head in header:
                self.header_row = self.header_row + head + "  "
                self.header_row_length.append(len(head))
            self.header_row = self.header_row[:-2]
            #create tabel
            self.content = f"{self.header_row}\n"
            #add break line
            for i in range(1,len(self.content)):
                self.content = self.content + "_"
            self.content = self.content +"\n"
            pass
        
        def add_row(self, data_row):
            # add data row
            self.row_nr += 1
            data_row.insert(0, str(self.row_nr))
            for data,head_len in zip(data_row,self.header_row_length):
                if len(data) <= head_len:
                    self.content = self.content + data + "  "
                    for i in range (0, head_len- len(data)):
                        self.content = self.content + " "
                else:
                    print("Data is too long")
            self.content = self.content + "\n"
            print(self.content)
            pass
        
        def remove_row(self, row_nr):
            row_nr += 1
            row_counter = 0
            index1 = 0
            index2 = 0
            index = 0
            for char, index in zip(self.content, range(len(self.content))):
                if char == "\n":
                    row_counter += 1
                    pass
                if row_counter == row_nr:
                    if index1 == 0:
                        index1 = index
                        pass
                    pass
                if row_counter > row_nr:
                    if index2 == 0:
                        index2 = index
                        pass
                    pass
                pass
            self.content = self.content[:index1] + self.content[index2:]
            print("index1 " + str(index1))
            print("index2 " + str(index2))
            print(self.content)
            pass