Search code examples
pythonkivykivy-language

Kivy accessing class attributes from screen manager


I am using python and kivy to make a simple app, but when I create multiple screen for my app, I cannot access the class attributes of some class.

I have problem accessing a method using screen manager.

main.py

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.uix.widget import Widget


class MyGame(Widget):
    def __init__(self, **kwargs):
        super(MyGame, self).__init__(**kwargs)

    def print_a_word(self):
        print('a word')

class OptionWindow(Screen):
    pass


class SecondWindow(Screen):
    pass


class WindowManager(ScreenManager):
    pass

kv = Builder.load_file('screen_manager.kv')

class MyMainApp(App):
    def build(self):
        return kv

if __name__ == "__main__":
    MyMainApp().run()

kivy file (screen_manager.kv)

#:kivy 2.0.0
# File name: screen_manager.kv
#: import MyGame Widget

WindowManager:

    OptionWindow:
    SecondWindow:

<OptionWindow>:
    name: "main"

    GridLayout:
        cols:1
        canvas.before:
            Color:
                rgba: 1, 1, 1, 1
            Rectangle:
                pos: 0, 0
                size: root.width, root.height
        GridLayout:
            rows: 5
            padding: 50, 50
            spacing: 20, 20
            OptionBtn:
                text: 'Americas'
                on_release:
                    app.root.current = "second"
                    root.manager.transition.direction = "left"



<SecondWindow>:
    name: "second"
    MyGame
    Button:
        on_press: root.MyGame.print_a_word()


<OptionBtn@Button>
    background_normal: 'Images/Other/white.png'
    color: 0, 0, 0, 1
    font_size: 20
    canvas.after:
        Color:
            rgba: 0, 0, 0, 1
        Line:
            rectangle: self.x, self.y, self.size[0], self.size[1]

Everything works well until I press on the button of the second window. I cannot access the method from MyGame(Widget). I get'SecondWindow' object has no attribute 'MyGame'

This is part of a bigger issue because I made this file to solve my original issue which is... I have a big program in which I have two files main.py and my.kv and I want to add screens. In this main.py, everything is defined a class that inherits from Widget and the build method returns an instance of that class. This is why I made the files from above... it is to understand how I can access from the Widget class. Thank you


Solution

  • Here I fixed your example code now the print_my_word() method inside MyGame() object can be accessed and executed this should be enough to get you in the way.

    Only the kv file needed change:

    Used include instead of import in "MyGame" object:
    #: include MyGame

    Instantiate it inside <SecondWindow> object.

    Added an id to it, to be able to access it.

    WindowManager:
    
        OptionWindow:
        SecondWindow:
    
    <OptionWindow>:
        name: "main"
    
        GridLayout:
            cols:1
            canvas.before:
                Color:
                    rgba: 1, 1, 1, 1
                Rectangle:
                    pos: 0, 0
                    size: root.width, root.height
            GridLayout:
                rows: 5
                padding: 50, 50
                spacing: 20, 20
                OptionBtn:
                    text: 'Americas'
                    on_release:
                        app.root.current = "second"
                        root.manager.transition.direction = "left"
    
    
    
    <SecondWindow>:
        name: "second"
        MyGame:
            id: my_game
        Button:
            on_press: my_game.print_a_word()
    
    
    <OptionBtn@Button>
        background_normal: 'Images/Other/white.png'
        color: 0, 0, 0, 1
        font_size: 20
        canvas.after:
            Color:
                rgba: 0, 0, 0, 1
            Line:
                rectangle: self.x, self.y, self.size[0], self.size[1]
    

    Ok this next part below is an update to respond your question in the second comment.

    main.py

    from kivy.app import App
    from kivy.lang import Builder
    from kivy.uix.screenmanager import ScreenManager, Screen
    from kivy.uix.widget import Widget
    
    
    class MyGame(Widget):
        def __init__(self, **kwargs):
            super(MyGame, self).__init__(**kwargs)
    
        def print_a_word(self):
            print('My Game: print method')
    
    class OptionWindow(Screen):
        pass
    
    
    class SecondWindow(Screen):
        pass
    
    
    class WindowManager(ScreenManager):
        pass
    
    kv = Builder.load_file('screen_manager.kv')
    
    class MyMainApp(App):
        def build(self):
            self.my_game = MyGame()  # <-- instantiated MyGame in the Main class to access it from any where
            return kv
    
    if __name__ == "__main__":
        MyMainApp().run()
    

    screen_manager.kv

    WindowManager:
        OptionWindow:
        SecondWindow:
    
    
    
    <OptionWindow>:
        name: "main"
    
        GridLayout:
            cols:1
            canvas.before:
                Color:
                    rgba: 1, 1, 1, 1
                Rectangle:
                    pos: 0, 0
                    size: root.width, root.height
            GridLayout:
                rows: 5
                padding: 50, 50
                spacing: 20, 20
                OptionBtn:
                    text: 'Americas'
                    on_release:
                        app.root.current = "second"
                        root.manager.transition.direction = "left"
                # ADDED EXTRA BUTTON TO SAMPLE ACCESSING MyGame OBJECT, FROM THIS CLASS
                Button:
                    text: 'Access My Game Method'
                    on_release:
                        app.my_game.print_a_word()   # <-- app can access main class attributes and methods
    
    
    
    <SecondWindow>:
        name: "second"
        # USE A BOXLAYOUT TO LAYOUT THE BUTTONS
        BoxLayout:
            orientation: 'vertical'
            spacing: 2
            padding: 2
            # ADDED BUTTON TO SAMPLE ACCESSING MyGame OBJECT
            Button:
                text: 'Access method from MyGame'
                on_press: app.my_game.print_a_word()     # <-- app can access main class attributes and methods
            Button:
                text: 'Return to Main'
                on_press: app.root.current = "main"
    
    
    
    <OptionBtn@Button>
        background_normal: 'Images/Other/white.png'
        color: 0, 0, 0, 1
        font_size: 20
        canvas.after:
            Color:
                rgba: 0, 0, 0, 1
            Line:
                rectangle: self.x, self.y, self.size[0], self.size[1]