Search code examples
pythonkivykivy-languagekivymd

Kivy - How to access instance of class created by .kv file?


My class OrderManagementScreen is firstly instantiated by Builder.load_file and then by sm.add_widget(OrderManagementScreen(name='order_management')).

I would like to be able to access the instance created by Builder.load_file - how can I do this?

This post is the closest I could find, but when I do MDApp.get_running_app().root it returns None.

app.py: (mixture of attempts in my update_database function)

import atexit
from kivymd.app import MDApp
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen, ScreenManager
from order_management.constants import HELP_TEXT
from order_management.presentation.order_management.order_management_view import OrderManagementScreen
from order_management.presentation.components.dialog import Dialog
from order_management.data.query import Query


class App(MDApp):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)
        self.screen = Builder.load_file(
            "order_management/presentation/main.kv")
        self._query = Query()
        something = MDApp.get_running_app()
        self._dialog = Dialog()

    def build(self):
        self.theme_cls.primary_palette = "Green"
        sm = ScreenManager()
        # Instantiates OrderManagementScreen.py
        sm.add_widget(OrderManagementScreen(name='order_management'))
        return self.screen

    def handle_menu_click(self):
        self._dialog.render_dialog("Usage Guide:", HELP_TEXT, None, None)

    def update_database(self):
        # order_management_screen = MDApp.get_running_app().screen.children[0].manager.get_screen('order_management')
        # order_management_screen = self.screen.get_screen('order_management')
        self.order_management_screen.update_database()


if __name__ == "__main__":
    App().run()
    main_app = App()
    atexit.register(main_app.update_database)  # Calls update_database on exit.

main.kv:

#:kivy 1.11.0
#:include order_management/presentation/order_management/order_management_ui.kv
# Having to use v1.11.0 because of bug relating to MDDataTable

<Toolbar@AnchorLayout>:
    AnchorLayout:
        anchor_x: 'center'
        anchor_y: 'top'
        MDToolbar:
            title: 'Order Management App'
            specific_text_color: app.theme_cls.accent_color
            right_action_items: [['help-circle', lambda x: app.handle_menu_click()]]
            elevation: 9

ScreenManager:
    OrderManagementScreen:
        Toolbar:
 

order_management_ui.kv:

#:kivy 1.11.0
# Having to use v1.11.0 because of bug relating to MDDataTable

<OrderManagementScreen>:
    name: 'order_management'
    id: order_management

    GridLayout:
        cols: 1
        pos_hint: {'top': 0.85}

        ScrollView:
            id: table_container

Solution

  • I guess it goes to show how sometimes you just need to step away from the computer and come back with fresh eyes...

    My solution was to simply store the instance of app when run so that when I called my update_database function is was looking at the correct instance - and therefore could access the instance of a screen class the kivy had created with:

    self.root.children[0].manager.get_screen('order_management')
    

    I use children[0] here because this is the first screen I added to ScreenManager, and 'order_management' is the name of the screen obviously.

    I hope that this by chance does help someone else.

    New app.py:

    import atexit
    from kivymd.app import MDApp
    from kivy.lang import Builder
    from kivy.uix.screenmanager import Screen, ScreenManager
    from order_management.constants import HELP_TEXT
    from order_management.presentation.order_management.order_management_view import OrderManagementScreen
    from order_management.presentation.components.dialog import Dialog
    from order_management.data.query import Query
    
    
    class App(MDApp):
        def __init__(self, **kwargs):
            super().__init__(**kwargs)
            self.screen = Builder.load_file(
                "order_management/presentation/main.kv")
            self._query = Query()
            self._dialog = Dialog()
    
        def build(self):
            self.theme_cls.primary_palette = "Green"
            sm = ScreenManager()
            sm.add_widget(OrderManagementScreen(name='order_management'))
            return self.screen
    
        def handle_menu_click(self):
            self._dialog.render_dialog("Usage Guide:", HELP_TEXT, None, None)
    
        def update_database(self):
            order_management_screen = self.root.children[0].manager.get_screen('order_management')
            order_management_screen.update_database()
    
    
    if __name__ == "__main__":
        app = App()
        app.run()
        atexit.register(app.update_database)  # Calls update_database on exit.