switch screenmanager inside layout

1.7k Views Asked by At

I'm trying to make a ScreenManager inside a BoxLayout work, so I can have a fixed toolbox below everyScreen the Screen. I managed to show the first screen (thanks to this question), but, when I try to swicth to the other Screen, the app crashes saying that there's no other Screen.

Actually, there really is no other Screen: both prints inside the ScreenManagement's init shows nothing. And I don't know why.

Without the toolbar (only with the ScreeManager(ment) and the necessary tweaks in the code, of course) everything works fine.

I tried to add_widget to the ScreenManagement and the screen_names was populated, but I couldn't switch between the Screens.

What I am missing?

The last part of the error:

screen = self.get_screen(value)
   File "C:\Python27\lib\site-packages\kivy\uix\screenmanager.py", line 944, in get_screen
     raise ScreenManagerException('No Screen with name "%s".' % name)
 ScreenManagerException: No Screen with name "init".

Windows 7, Python 2.7, Kivy 1.9.1

Here is the ClassApp.py:

import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
from kivy.clock import Clock

#just solving my weak GPU issue
from kivy import Config
Config.set('graphics', 'multisamples', '0')

kivy.require('1.9.1')

class Init(Screen):
    pass

class Menu(Screen):
    pass

class ScreenManagement(ScreenManager):

    def __init__(self,**kwargs):
        print('before super: ', self.screen_names)
        super(ScreenManagement,self).__init__(**kwargs)
        print('after super: ', self.screen_names)

    def switch_to_menu(self):
        self.current = 'menu'
        print('going to menu')

    def switch_to_init(self):
        self.current = 'init'
        print('going to init')

class ClassAllScreen(BoxLayout):
    sm = ScreenManagement()
    sm.transition = NoTransition()
    pass

class ClassApp(App):

    def build(self):
        self.root = ClassAllScreen()
        return self.root

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

And here the Class.kv:

<Init>: #first Screen
    name: 'init'
    BoxLayout:
        orientation:'vertical'
        padding:20
        spacing:10
        Button:
            text:'uppest'
        GridLayout:
            spacing:10
            cols:2

            Button:
                text:'upper left'
            Button:
                text:'upper right'
            Button:
                text:'bottom left'
            Button:
                text:'bottom right'
        Button:
            text:'bottomest: go to menu'
            on_press:app.root.sm.switch_to_menu()       

<Menu>: #second Screen
    name: 'menu'

    BoxLayout:
        orientation:'vertical'
        padding:20
        spacing:10
        GridLayout:
            spacing:10
            cols:2

            Button:
                text:'upper left'
            Button:
                text:'upper right'
            Button:
                text:'bottom left'
            Button:
                text:'bottom right'
        Button:
            text:'bottomy'
        Button:
            text:'bottomest: go to init'
            on_press:app.root.sm.switch_to_init()

<ScreenManagement>: #including both the Screens in ScreenManager
    Menu:
    Init:

<ClassAllScreen>: #all the display with ScreenManager and "Toolbar"
    orientation:'vertical'
    ScreenManagement:
    BoxLayout:
        size_hint_y: None
        height: 60
        spacing: 5
        padding: 5,5,0,5

        canvas:
            Color:
                rgba: .1,.1,.1,1
            Rectangle:
                pos: self.pos
                size: self.size
        Button:
            text:'1'
            size_hint_x: None
            width: 60
        Button:
            text:'2'
            size_hint_x: None
            width: 60
        Button:
            text:'3'
            size_hint_x: None
            width: 60
2

There are 2 best solutions below

5
On BEST ANSWER

First you need to add the screens to your screenmanager.
To do that, you can do something like this in your ScreenManager class.

def __init__(self,**kwargs):
    super(ScreenManagement,self).__init__(**kwargs)
    self.add_widget(Init(name="init"))
    self.add_widget(Menu(name="menu"))

Now the two screens will have one manager, which is ScreenManagement
So in your kv code you call the manager like this:

Button:
    text:'bottomest: go to menu'
    on_press:root.manager.switch_to_menu()  

You can also do like this, if you dont want to make methods:

Button:
    text:'bottomest: go to menu'
    on_press:root.manager.current = 'menu'

Also there is a problem with your code, You add the screenmanager to a boxlayout, and try to return the boxlayout. You cannot do that. You can only show one screen at a a time. So I am guessing you want a third screen here. What you should do is retunr the screen manager in your build method, and only add screens to it.
I wrote an example. I dont know if its something like this you want.

import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen
from kivy.lang import Builder


Builder.load_string("""

<Init>:
    name: 'init'
    BoxLayout:
        orientation:'vertical'
        Button:
            text:'go to menu'
            on_press:root.manager.current = "menu"


<Menu>:
    name: 'menu'

    BoxLayout:
        orientation:'vertical'
        Button:
            text:'go to init'
            on_press:root.manager.current = "init"


<ClassAllScreen>:
    BoxLayout:
        orientation:'vertical'
        Button:
            text:'go to init'
            on_press:root.manager.current = "init"
        Button:
            text:'go to menu'
            on_press:root.manager.current = "menu"

""")


class Init(Screen):
    pass

class Menu(Screen):
    pass

class ClassAllScreen(Screen):
    pass


class ClassApp(App):

    def build(self):
        sm = ScreenManager()
        sm.add_widget(ClassAllScreen(name="main"))
        sm.add_widget(Init(name="init"))
        sm.add_widget(Menu(name="menu"))
        return sm


if __name__ == '__main__':
    ClassApp().run()
0
On

With the great help of @EL3PHANTEN and this, I fixed my code and I guess I know where was my mistake.

I can't use app.root.sm.current='whateverScreenName' inside kv file. I can't explain why, but I guess it has something with the moment of ScreenManagement's instatiation. As I thought from the begin, the python part is very straightforward.

Again: thanks for your help, @EL3PHANTEN :)

Here is the result:

ScreenManager inside a BoxLayout to have a Toolbar

Here is the working fixed python code:

import kivy
from kivy.app import App
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
from kivy.clock import Clock

#just solving my weak GPU issue
from kivy import Config
Config.set('graphics', 'multisamples', '0')

kivy.require('1.9.1')

class ScreenManagement(ScreenManager):
    pass

class Init(Screen):
    pass

class Menu(Screen):
    pass


class ClassAllScreen(BoxLayout):
    pass

class ClassApp(App):

    def build(self):
        self.root = ClassAllScreen()
        return self.root

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

And the Class.kv:

#: import NoTransition kivy.uix.screenmanager.NoTransition
<Init>:
    name: 'init'
    BoxLayout:
        orientation:'vertical'
        padding:20
        spacing:10
        Button:
            text:'uppest'
        GridLayout:
            spacing:10
            cols:2

            Button:
                text:'upper left'
            Button:
                text:'upper right'
            Button:
                text:'bottom left'
            Button:
                text:'bottom right'
        Button:
            text:'bottomest: go to menu'
            on_press: root.manager.current = 'menu'

<Menu>:
    name: 'menu'     
    BoxLayout:
        orientation:'vertical'
        padding:20
        spacing:10
        GridLayout:
            spacing:10
            cols:2

            Button:
                text:'upper left'
            Button:
                text:'upper right'
            Button:
                text:'bottom left'
            Button:
                text:'bottom right'
        Button:
            text:'bottomy'
        Button:
            text:'bottomest: go to init'
            on_press: root.manager.current = 'init'

<ScreenManagement>:
    transition: NoTransition()
    Init:
    Menu:

<ClassAllScreen>:
    orientation:'vertical'
    ScreenManagement:
    BoxLayout:
        size_hint_y: None
        height: 60
        spacing: 5
        padding: 5,5,0,5

        canvas:
            Color:
                rgba: .1,.1,.1,1
            Rectangle:
                pos: self.pos
                size: self.size
        Button:
            text:'1'
            size_hint_x: None
            width: 60
        Button:
            text:'2'
            size_hint_x: None
            width: 60
        Button:
            text:'3'
            size_hint_x: None
            width: 60