kivykivy-languagekivymd

Kivy Widget init


I am new to Kivy and I wrote an App with 3 screens and a screenmanager. I want to use a mvvm (model-view-view-model pattern), so I want to have a ViewModel object that I add to each screen object. The below code gives me the following error:

text: root.view_model.get_some_text()
... AttributeError: 'ShowEmailScreen' object has no attribute 'view_model'

However, when I comment that out, the other directive that also uses view_model "on_release: root.view_model.set_cancel_lunch()" a few lines above in the kv file, is carried out without problem. So the view_model object must be there. I really can't wrap my head around this. Any help is appreciated.

class ShowEmailScreen(MDScreen):

    def __init__(self, view_model,  **kwargs):
        super().__init__(**kwargs)
        self.view_model = view_model

class ScreenManagement(ScreenManager):
 
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        model = DataModel()
        view_model = ViewModel(model)
        
        main_screen = MainScreen(view_model, name='main_screen')
        pick_date_screen = PickDateScreen(view_model, name='pick_date_screen')
        show_email_screen = ShowEmailScreen(view_model, name='show_email_screen')
        self.add_widget(main_screen)
        self.add_widget(pick_date_screen)
        self.add_widget(show_email_screen)

class ViewModel:

    def __init__(self, model):
        self.model = model
        
    def get_some_text(self):
        return('laskdfjfjd')

    def set_cancel_lunch(self):
        if self.model.cancel_lunch:
            self.model.cancel_lunch=False
        else:
            self.model.cancel_lunch=True

class NoteApp(MDApp):
    def build(self):
        Builder.load_file("doctorsnote.kv")
        self.screen_manager = ScreenManagement()
        return self.screen_manager

if __name__=='__main__':
    NoteApp().run()

And in doctorsnote.kv I have:

<ShowEmailScreen>:
    id: PDS
    md_bg_color: self.theme_cls.backgroundColor
    MDBoxLayout:
        orientation:'vertical'            
        MDBoxLayout:
            MDButton:
                on_release: root.view_model.set_cancel_lunch()   
                MDButtonText:
                    text: root.view_model.get_some_text()

Solution

  • The problem is that the line in your kv:

    text: root.view_model.get_some_text()
    

    get executed before the view_model is set in the ShowEmailScreen. One way to handle that situation is to declare the view_model as an ObjectProperty in the ShowEmailScreen:

    class ShowEmailScreen(MDScreen):
        view_model = ObjectProperty(None)
    
        def __init__(self, view_model, **kwargs):
            super().__init__(**kwargs)
            self.view_model = view_model
    

    Then use that Property in the kv file:

                MDButtonText:
                    text: 'No View Model Yet' if not root.view_model else root.view_model.get_some_text()
    

    Now the get_some_text() method is not called until root.view_model is not None.