简体   繁体   中英

Python Kivy Screen manager with predefined Layout doesn't work

My Main class returns a instance of ScreenManager. This ScreenManager has a widget with a Screen class, and I want this Screen class to use a widget which is a Layout that I defined earlier.

When I execute the code, it only show a black screen with no more information. It should show a Button instead.

This is my file minimum.py :

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

class LayoutWithButton(BoxLayout):

    def __init__(self, **kwargs):
        super(LayoutWithButton, self).__init__(**kwargs)

class MainScreenApp(Screen):

    def __init__(self, **kwargs):
        super(MainScreenApp, self).__init__(**kwargs)
        button_layout = LayoutWithButton()
        self.add_widget(button_layout)

screen_manager = ScreenManager()
screen_manager.add_widget(MainScreenApp(name='main'))

class TestMainApp(App):

    def build(self):
        return screen_manager

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

And this is my file testmain.kv :

<LayoutWithButton>:
    Button:
        text: 'hello'

Even so, if I replace the line self.add_widget(button_layout) of the class MainScreenApp with the line self.add_widget(Button()) it works well.

What am I doing wrong?

The problem is that kv file is not loaded until TestMainApp is initialized. As you instanciate MainScreenApp before this, the rules defined within the kv have no effect.

A very simple solution is to move instanciation of MainScreenApp to App subclass:

class MainApp(App):

    def build(self):
        screen_manager = ScreenManager()
        screen_manager.add_widget(MainScreenApp(name='main'))
        return screen_manager

You can also force the kv load before instantiating:

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


class LayoutWithButton(BoxLayout):
    pass


class MainScreenApp(Screen):

    def __init__(self, **kwargs):
        super(MainScreenApp, self).__init__(**kwargs)
        button_layout = LayoutWithButton()
        self.add_widget(button_layout)

Builder.load_file("testmain.kv")
screen_manager = ScreenManager()
screen_manager.add_widget(MainScreenApp(name='main'))


class TestApp(App):

    def build(self):
        return screen_manager


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

Another option is do everything in your kv:

from kivy.uix.screenmanager import ScreenManager
from kivy.app import App


class RootWidget(ScreenManager):
    pass


class MainTestApp(App):
    def build(self):
        return MainWindow()


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

maintest.kv:

<LayoutWithButton@BoxLayout>:
    Button:
        text: 'hello'

<MainScreenApp@Screen>:
    LayoutWithButton:

<RootWidget>:
    MainScreenApp:
        name: "main"

From testmain.kv you should remove that < and > from the class name. You only use it when you are styling it and will also be calling it many many times.

Example how your code should look like:

 LayoutWithButton: Button: text: 'hello' 

When to use < and >:

<LayoutWithButton>:
      pos_hint: 0, .5
      Button:
          text: 'hello'

BoxLayout:
    LayoutWithButton:
         pos_hint: 0, .5

Good luck!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM