How to do a fast-loading splash screen in a kivy python app

35 Views Asked by At

I am trying to come up with an elegant way of displaying a splash screen when I start my app, until the app is initialised and ready to use. The problem with the obvious solution to use ScreenManager is that my app has an extensive GUI with many screens containing many widgets. At start up whilst Kivy builds the GUI, just a blank box is displayed for a time, which does not look pro. To achieve fast-loading splash screen I have split the GUI into two parts - one containing the bare minimum for splash, and the other part containing the rest. I load the former, then add the latter to it. This works (see my minimalist example below), but I am wondering if there is a better way. Is there a way to specify in kv-lang that a subset of the GUI must be built and displayed first?

core.py:

from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button

Builder.load_string('''

<Core>:
    orientation: 'vertical'
    Button:
        size_hint: 1,0.2
        text: 'BANNER'
    BoxLayout:
        id: mainspace
        size_hint: 1,0.8
        Button:
            id: splashscreen
            Image:
                source: 'images/splash.png'
                size: self.parent.size
                pos: self.parent.pos
                allow_stretch: True
                keep_ratio: False      
    Button:
        id: statusbar
        size_hint: 1,0.1
        text: 'STATUS BAR'
''')

class Core(BoxLayout):
    
    def __init__(self, **kwargs): 
        super(Core, self).__init__(**kwargs)
        from kivy.clock import Clock
        Clock.schedule_once(lambda dt: self.init(), 2)
        
    def init(self):
        from mainscreen import MainScreen
        self.ids.mainspace.remove_widget(self.ids.splashscreen)
        self.ids.mainspace.add_widget(MainScreen(self))
                
class MyApp(App):
    def build(self):
        return Core()
      
if __name__ == '__main__':
    MyApp().run()

mainscreen.py:

from kivy.lang import Builder
from kivy.uix.button import Button

Builder.load_string('''

<MainScreen>:
    size_hint: 1, 1
    text: 'MAIN APP'
    on_press:
        self.core.ids.statusbar.text = 'MAIN APP PRESSED'
    on_release:
        self.core.ids.statusbar.text = 'STATUS BAR'
''')

class MainScreen(Button):
    def __init__(self, core):
        super(Button, self).__init__()
        self.core = core 

Edit. Found that it is possible to keep everything in the same file but split the kv builder string into two. Kivy dynamic classes declared in the first kv string are accessible in the other which is helpful (see PinkButton class).

from kivy.uix.boxlayout import BoxLayout
from kivy.app import App
from kivy.lang import Builder
from kivy.uix.button import Button

Builder.load_string('''

<PinkButton@Button>:
    background_color: 2.75, 1.5, 1.25, 3
    
<Core>:
    orientation: 'vertical'
    PinkButton:
        size_hint: 1,0.2
        text: 'BANNER'
    BoxLayout:
        id: mainspace
        size_hint: 1,0.8
        PinkButton:
            id: splashscreen
            Image:
                source: 'images/splash.png'
                size: self.parent.size
                pos: self.parent.pos
                allow_stretch: True
                keep_ratio: False      
    PinkButton:
        id: statusbar
        size_hint: 1,0.1
        text: 'STATUS BAR'
''')

class Core(BoxLayout):

    version = "123"
    
    def __init__(self, **kwargs): 
        super(Core, self).__init__(**kwargs)
        from kivy.clock import Clock
        Clock.schedule_once(lambda dt: self.init(), 2)
        
    def init(self):
        Builder.load_string('''
<MainScreen>:
    core: None
    size_hint: 1, 1
    PinkButton:
        size_hint: None, None
        size: self.parent.size
        pos: self.parent.pos
        text: 'MAIN APP ' + self.parent.core.version if self.parent.core else ''
        on_press:
            self._ = self.parent.core.ids.statusbar.text
            self.parent.core.ids.statusbar.text = 'MAIN APP PRESSED'
        on_release:
            self.parent.core.ids.statusbar.text = self._
        ''')       
        
        class MainScreen(BoxLayout):
            def __init__(self, core):
                super(BoxLayout, self).__init__()
                self.core = core
        
        self.ids.mainspace.remove_widget(self.ids.splashscreen)
        self.ids.mainspace.add_widget(MainScreen(self))
                
class MyApp(App):
    def build(self):
        return Core()
      
if __name__ == '__main__':
    MyApp().run()

            
0

There are 0 best solutions below