了解 Kivy 的创建顺序,更改根小部件会导致崩溃

Understand Kivy order-of-creation, changing root widget results in crash

使用 Screenmanager 作为根小部件,我有一些简单的代码来(可选)显示 Splashscreen,然后是 Mainscreen。它工作正常。但我想改变它,所以我有一个 Boxlayout 作为根小部件 insted(所以我可以在 ScreenManager 上方显示一个永远存在的状态栏)。但是,当我添加 Boxlayout 时,它崩溃了,我怀疑是由于我无法理解的创建顺序问题。

此代码有效:

class MainScreenManager(ScreenManager):
    def __init__(self, **kwargs):
        super(MainScreenManager, self).__init__(**kwargs)
        if not SHOW_SPLASH_SCREEN
            self.transition = NoTransition()
            self.current = 'screen_main'
            self.transition = FadeTransition()
        else:
            #close the splash screen after 3 seconds
            Clock.schedule_once(partial(self.changescreen, 'screen_main'),3)
    
    def changescreen(self, newscreen, *largs):
        self.current = newscreen
    
class Screen_Splash(Screen):
    pass

class Screen_Main(Screen):
    pass

class TestApp(App):
    def build(self):
        mainprogram=MainScreenManager()
        return mainprogram

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

test.kv

<MainScreenManager>:
    id: MainScreenManager
    Screen_Splash:
    Screen_Main:

<Screen_Splash>:
    name: 'screen_splash'
    FloatLayout:
        Image:
            source: 'resources/splashscreen.png'
            keep_ratio: True

<Screen_Main>:
    name: 'screen_main'
    Image:
        source: 'resources/1_mainscreen.png'
        keep_ratio: True

但是,如果我尝试添加一个 BoxWidget 作为 ScreenManager 的父级,只有当我允许显示 SplashScreen 时它才会工作,但是如果我设置 SHOW_SPLASH_SCREEN=False,那么它将在第 self.current = 'screen_main' 行出现以下错误:

self.current = 'screen_main'

  File "kivy/properties.pyx", line 520, in kivy.properties.Property.__set__
  File "kivy/properties.pyx", line 567, in kivy.properties.Property.set
  File "kivy/properties.pyx", line 606, in kivy.properties.Property._dispatch
  File "kivy/_event.pyx", line 1307, in kivy._event.EventObservers.dispatch
  File "kivy/_event.pyx", line 1213, in kivy._event.EventObservers._dispatch
  File "/usr/lib/python3/dist-packages/kivy/uix/screenmanager.py", line 1052, in on_current
    screen = self.get_screen(value)

  File "/usr/lib/python3/dist-packages/kivy/uix/screenmanager.py", line 1078, in get_screen
    raise ScreenManagerException('No Screen with name "%s".' % name)

我添加了一些调试,我可以看到创建了“screen_main”这个错误发生之前 - 所以我怀疑可能已经创建了 Screen,但没有但实际添加到 ScreenManager?也许这就是错误的原因?如果是这样,我该如何解决?

这里是导致崩溃的代码:(现在 MasterBoxLayout 是新的根节点)

class MasterBoxLayout(BoxLayout):
    #this is now the new Root Node
    pass

class MainScreenManager(ScreenManager):
    def __init__(self, **kwargs):
        super(MainScreenManager, self).__init__(**kwargs)
        if not SHOW_SPLASH_SCREEN
            self.transition = NoTransition()
            self.current = 'screen_main'
            self.transition = FadeTransition()
        else:
            Clock.schedule_once(partial(self.changescreen, 'screen_main'),3)
    
    def changescreen(self, newscreen, *largs):
        self.current = newscreen
    
class Screen_Splash(Screen):
    pass

class Screen_Main(Screen):
    pass

class TestApp(App):
    def build(self):
        mainprogram=MasterBoxLayout()
        return mainprogram

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

test.ky也是一样,只是多了一个BoxLayout:

<MasterBoxLayout>:
    #this should be the new root of the tree
    orientation: 'vertical'
    MainScreenManager:

<MainScreenManager>:
    id: MainScreenManager
    Screen_Splash:
    Screen_Main:

<Screen_Splash>:
    name: 'screen_splash'
    FloatLayout:
        Image:
            source: 'resources/splashscreen.png'
            keep_ratio: True

<Screen_Main>:
    name: 'screen_main'
    Image:
        source: 'resources/1_mainscreen.png'
        keep_ratio: True

编辑:请注意,我是 Kivy 的新手,如果对我这样做的方式有任何一般性评论......以及对更好方法的建议,我们将不胜感激。

问题是您在 MainScreenManager__init__() 方法中执行此操作。此时,Screens 尚未创建。您可以将该逻辑移动到 on_kv_post() 方法中,这样在执行 kv 规则之前代码不会 运行:

class MainScreenManager(ScreenManager):

    def on_kv_post(self, base_widget):
        if not SHOW_SPLASH_SCREEN:
            self.transition = NoTransition()
            self.current = 'screen_main'
            self.transition = FadeTransition()
        else:
            Clock.schedule_once(partial(self.changescreen, 'screen_main'), 3)

    def changescreen(self, newscreen, *largs):
        self.current = newscreen