Search code examples
pythonscreenkivykivy-language

(Kivy Python) Switching Screens on Button Press Inside .py file


I understand that it is relatively easy to switch screens in the .kv file using on_release. I want to keep my button creating in the .py file, however, so I do not want to use this method. I have done the following to add a function that occurs when the 14th button is pressed. When the button is pressed in the program nothing happens. Experimenting with other names for the screen to sm.current to threw the error: "kivy.uix.screenmanager.ScreenManagerException: No Screen with name "InputScreen" when the 14th button was pressed."

# Kivy Formatting
kv_text='''\

<MyScreenManager>:
    LandingScreen:
    InputScreen:

<InputScreen@Screen>:
    name: 'input_sc'
    AnchorLayout:
        id: anchor_1

<LandingScreen@Screen>:
    name: 'landing_sc'
    GridLayout:
        id: grid_1
        cols: 5
        height: 480
        width: 800
        spacing: 25, 20
        padding: 25,25

'''
# Screen Manager
class MyScreenManager(ScreenManager):
    pass

# Main screen with button layout
class LandingScreen(Screen):
    def __init__(self, **kwargs):
        super(LandingScreen, self).__init__(**kwargs)
        self.buttons = [] # add references to all buttons here
        Clock.schedule_once(self._finish_init)

    # IDs have to be used here because they cannot be applied until widget initialized
    def _finish_init(self, dt):
        self.ids.grid_1.cols = 5

        # Loop to make 15 different buttons on screen
        for x in range(15):
            self.buttons.append(Button(text='button {}'.format(x)))
            self.ids.grid_1.add_widget(self.buttons[x])
            self.buttons[x].background_normal = 'YOUTUBE.png'

        def SwitchScreen(self,*args):
            sm.current = 'input_sc'

        sm = ScreenManager()
        sm.add_widget(InputScreen(name='input_sc'))
        sm.add_widget(LandingScreen(name='landing'))
        self.buttons[14].bind(on_release=SwitchScreen)


# Input screen
class InputScreen(Screen):
    pass

class MySubApp(App):
    def build(self):
        return MyScreenManager()

def main():
    Builder.load_string(kv_text)
    app = MySubApp()
    app.run()

if __name__ == '__main__':
    main()

If someone could help me understand the hole in my current logic I would appreciate it greatly. Thanks.


Solution

  • Each screen has a manager property that gives you the instance of the ScreenManager used. You only need to use it to refer to the ScreemManager instance and use its current method:

    from kivy.app import App
    from kivy.lang import Builder
    from kivy.uix.screenmanager import ScreenManager, Screen
    from kivy.uix.button import Button
    from kivy.clock import Clock
    
    
    # Kivy Formatting
    kv_text='''\
    
    <MyScreenManager>:
        LandingScreen:
        InputScreen:
    
    <InputScreen@Screen>:
        name: 'input_sc'
        AnchorLayout:
            id: anchor_1
            Button:
                text: 'Hello'
    
    <LandingScreen@Screen>:
        name: 'landing_sc'
        GridLayout:
            id: grid_1
            cols: 5
            height: 480
            width: 800
            spacing: 25, 20
            padding: 25,25
    
    '''
    # Screen Manager
    class MyScreenManager(ScreenManager):
        pass
    
    # Main screen with button layout
    class LandingScreen(Screen):
        def __init__(self, **kwargs):
            super(LandingScreen, self).__init__(**kwargs)
            self.buttons = [] # add references to all buttons here
            Clock.schedule_once(self._finish_init)
    
        # IDs have to be used here because they cannot be applied until widget initialized
        def _finish_init(self, dt):
            self.ids.grid_1.cols = 5
    
            # Loop to make 15 different buttons on screen
            for x in range(15):
                self.buttons.append(Button(text='button {}'.format(x)))
                self.ids.grid_1.add_widget(self.buttons[x])
                self.buttons[x].background_normal = 'YOUTUBE.png'
    
            self.buttons[14].bind(on_release=self.switch_screen)
    
        def switch_screen(self, *args):
            self.manager.current = 'input_sc'
    
    # Input screen
    class InputScreen(Screen):
        pass
    
    class MySubApp(App):
        def build(self):
            return MyScreenManager()
    
    def main():
        Builder.load_string(kv_text)
        app = MySubApp()
        app.run()
    
    if __name__ == '__main__':
        main()