Search code examples
pythonkivykivymdkivy-recycleview

Kivy recycleview not updating on Clock call function


My App have a Timer as a data in recycleview that will be updating to change the text of the timer each second. So I used Clock to call timer_update function every one second to update the time.

The issue is that the Timer text is updated when print on a terminal, but is not updating on the App screen.

Here is my code:

import datetime
from kivy.clock import Clock
from kivy.lang import Builder
from kivymd.app import MDApp
from kivy.properties import StringProperty
from kivymd.uix.boxlayout import MDBoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen


Builder.load_string( '''
<Timer>:
    orientarion: 'vertical'
    md_bg_color: 0, 0, 1, 1
        
    MDLabel:
        text: root.text
        color: 1,1,1,1
        halign: 'center'
            
<MyLayout>:
    MDBoxLayout:
        orientation: 'vertical'
    
        RecycleView:
            id: rv
            key_viewclass: 'viewclass'
            key_size: 'height'
            effect_cls: 'ScrollEffect'
            
            RecycleBoxLayout:
                padding: dp(10)
                size_hint_y: None
                default_size: None, dp(60)
                default_size_hint: 1, None
                height: self.minimum_height
                orientation: 'vertical'
                   
''')
class Sm(ScreenManager):
    pass
    
    
class Timer(MDBoxLayout):
    text = StringProperty()
    

class MyLayout(Screen):
    
    def on_enter(self):
        self.add_timer()
        Clock.schedule_interval(self.update_timer, 1)

    def update_timer(self, dt):
        self.ids.rv.data[0]['text'] = str(datetime.datetime.now())
        print(self.ids.rv.data[0]['text'])
        
    def add_timer(self):
        self.ids.rv.data.append(
            {
                "viewclass": "Timer",
                "text": str(datetime.datetime.now()),
                "callback": lambda x: x,
            }
        )
        

class MyApp(MDApp):
    
    def build(self):
        sm = Sm()
        sm.add_widget(MyLayout())
        return sm
        
MyApp().run()


Solution

  • if you call self.ids.rv.refresh_from_data() after updating your data should do the trick.

    Full example:

    import datetime
    from kivy.clock import Clock
    from kivy.lang import Builder
    from kivymd.app import MDApp
    from kivy.properties import StringProperty
    from kivymd.uix.boxlayout import MDBoxLayout
    from kivy.uix.screenmanager import ScreenManager, Screen
    
    
    Builder.load_string( '''
    <Timer>:
        orientarion: 'vertical'
        md_bg_color: 0, 0, 1, 1
            
        MDLabel:
            text: root.text
            color: 1,1,1,1
            halign: 'center'
                
    <MyLayout>:
        MDBoxLayout:
            orientation: 'vertical'
        
            RecycleView:
                id: rv
                key_viewclass: 'viewclass'
                key_size: 'height'
                effect_cls: 'ScrollEffect'
                
                RecycleBoxLayout:
                    padding: dp(10)
                    size_hint_y: None
                    default_size: None, dp(60)
                    default_size_hint: 1, None
                    height: self.minimum_height
                    orientation: 'vertical'
                       
    ''')
    class Sm(ScreenManager):
        pass
        
        
    class Timer(MDBoxLayout):
        text = StringProperty()
        
    
    class MyLayout(Screen):
        
        def on_enter(self):
            self.add_timer()
            Clock.schedule_interval(self.update_timer, 1)
    
        def update_timer(self, dt):
            self.ids.rv.data[0]['text'] = str(datetime.datetime.now())
            self.ids.rv.refresh_from_data()
            print(self.ids.rv.data[0]['text'])
            
        def add_timer(self):
            self.ids.rv.data.append(
                {
                    "viewclass": "Timer",
                    "text": str(datetime.datetime.now()),
                    "callback": lambda x: x,
                }
            )
    
    class MyApp(MDApp):
        
        def build(self):
            sm = Sm()
            sm.add_widget(MyLayout())
            return sm
            
    MyApp().run()