Search code examples
pythonkivypython-multithreadingkivymd

Python, KivyMD and Threading


I'm trying to develop an application that makes some test on computer to check the components, etc...

I have the first KivyMD screen which prints welcome to the users and when the user clicks on the button "Start the test", I want to switch the screen to LoadingScreen and start the test. At the end of the test, the screen must switch to end screen.

My problem is that the screen loading doesn't appear before the entire test completion... and the application switch immediately to the last screen after the test.

So actually when i click on the start button, the test start, and when it's finished, the laoding screen appear.

I want this: Click on button < Loading Screen < Test < EndScreen

Thanks a lot !

My .py file `

class IconButtonTooltips(MDIconButton, MDTooltip):
    pass

class DeclarativeHardwareScreen(Screen):
    pass

class DeclarativeScreenScreen(Screen):
    pass

class DeclarativeChargerScreen(Screen):
    pass

class DeclarativeKeyBoardScreen(Screen):
    pass

class TitleScreen(Screen):
    
    def startGetAllHardware(self):
        self.manager.current = "LoadingScreen"
        threading.Thread(target=myHardware.getHardware()).start()
            
class LoadingScreen(Screen):
    pass

class EndScreen(Screen):
    pass

class Certifitech(MDApp):
    
    def build(self):
        
        Builder.load_file("main.kv")
        screen_manager = ScreenManager()
        screen_manager.add_widget(TitleScreen(name='TitleScreen'))
        screen_manager.add_widget(LoadingScreen(name='LoadingScreen'))
        screen_manager.add_widget(EndScreen(name='EndScreen'))
        screen_manager.add_widget(DeclarativeHardwareScreen(name='DeclarativeHardwareScreen'))
        screen_manager.add_widget(DeclarativeScreenScreen(name='DeclarativeScreenScreen'))
        screen_manager.add_widget(DeclarativeChargerScreen(name='DeclarativeChargerScreen'))
        screen_manager.add_widget(DeclarativeKeyBoardScreen(name='DeclarativeKeyBoardScreen'))
        

        return screen_manager

if __name__ == "__main__":
    Certifitech().run()

`

My .kv file

`

#: import utils kivy.utils

<TitleScreen>
    MDFloatLayout:
        md_bg_color: kivy.utils.get_color_from_hex("#5B66FF")
        Image:
            source: "ressources/logo.png"
            size_hint: (0.3,0.3)
            pos_hint: {'center_x': 0.5, 'center_y': 0.8}
        Label:
            text: "Bienvenue dans le diagnostic de votre ordinateur !"
            font_size: 33.5
            color: (1,1,1,1)
            pos_hint: {'center_x': 0.5, 'center_y': 0.6}
        Label:
            text: "Manufacturer | Model"
            font_size: 33.5
            color: (1,1,1,1)
            pos_hint: {'center_x': 0.5, 'center_y': 0.4}
        MDRectangleFlatButton:
            text: "Démarrer"
            font_size: 25
            padding: 20
            pos_hint: {'center_x': 0.5, 'center_y': 0.2}
            text_color: "white"
            line_color: "white"
            on_press: 
                root.startGetAllHardware()

<LoadingScreen>
    MDFloatLayout:
        md_bg_color: kivy.utils.get_color_from_hex("#5B66FF")
        Image:
            source: "ressources/logo.png"
            size_hint: (0.3,0.3)
            pos_hint: {'center_x': 0.5, 'center_y': 0.8}
        Label:
            text: "Diagnostic en cours, veuillez patienter..."
            font_size: 33.5
            color: (1,1,1,1)
            pos_hint: {'center_x': 0.5, 'center_y': 0.5}
        MDSpinner:
            size_hint: None, None
            size: dp(46), dp(46)
            pos_hint: {'center_x': .5, 'center_y': .3}
            active: True
            color: (1,1,1,1)

<EndScreen>
    MDFloatLayout:
        md_bg_color: kivy.utils.get_color_from_hex("#5B66FF")
        Image:
            source: "ressources/logo.png"
            size_hint: (0.3,0.3)
            pos_hint: {'center_x': 0.5, 'center_y': 0.8}
        Label:
            text: "Le diagnostic est termine !"
            font_size: 33.5
            color: (1,1,1,1)
            pos_hint: {'center_x': 0.5, 'center_y': 0.6}
        Label:
            text: "Vous allez être redirigé(e) d'ici quelques secondes..."
            font_size: 25
            color: (1,1,1,1)
            pos_hint: {'center_x': 0.5, 'center_y': 0.4}
        MDRectangleFlatButton:
            text: "Télecharger mon rapport de diagnostic"
            font_size: 25
            padding: 20
            pos_hint: {'center_x': 0.5, 'center_y': 0.2}
            text_color: "white"
            line_color: "white"

`


Solution

  • The code for getHardware was not present so I added something to make an example.

    from kivy.clock import mainthread
    class TitleScreen(Screen):
    
        def getHardware(self, delay_seconds):
            for i in range(delay_seconds):
                time.sleep(1.0)
                print(i)
            self.change_screen()
    
        @mainthread
        def change_screen(self):
            self.manager.current = "EndScreen"
    
        def startGetAllHardware(self):
            self.manager.current = "LoadingScreen"
            threading.Thread(target=self.getHardware, args=(5, )).start()
            print("startGetAllHardware has executed")
    

    This will create and start a new Thread and it can continue as long as it needs, freeing the gui to display the loading screen. When the threaded function completes its job it will force changing the screen.

    The imports were missing so I added this chunk to make it run.

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import time
    from kivymd.uix.button import MDIconButton
    from kivymd.uix.tooltip import MDTooltip
    from kivymd.uix.screen import Screen
    import threading
    from kivy.lang import Builder
    from kivymd.app import MDApp
    from kivymd.uix.screenmanager import ScreenManager