Search code examples
pythonkivypython-multiprocessing

Issue when importing kivy.core.window and using the multiprocessing library


First of all, I'd like to clarify that I have absolutely 0 background as a software engineer and this is the first time I'm using python for something besides API's and creating excel/plots.

My issue is that I was trying to create an app using kivy and then I imported the kivy.core.window library a blank screen appears.

I've seen that this issue is fairly common when using kivy and multiprocessing, but none of the information I found fixed my problem. Result of running my code without the kivy.core.window library:

enter image description here

Result when adding said library:

enter image description here

Also, if I wait like 10 seconds the blank screen disappears and kills the count_time process.

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
import time
from datetime import datetime
import multiprocessing as mp
from multiprocessing import Process, Queue, freeze_support

Builder.load_string('''
<TestScreen>:
    BoxLayout:
        orientation: "vertical" # place object vertically
        Label:
            size_hint: 1, 1
            text: 'TITLE'
            font_size: 150
            color: 1, 1, 1, 1
        Button:
            text: 'start'
            on_press: root.start_mp()
        Button:
            text: 'stop'
            on_press: root.stop_time()
''')

def count_time(x):
    for i in range(x):
        print('count={}'.format(i))
        time.sleep(1)

class Test(App):

    def build(self):
        sm.add_widget(TestScreen(name='test'))
        return sm

class TestScreen(Screen):
    def __init__(self, **kwargs):
        super(TestScreen, self).__init__(**kwargs)
        
    def start_mp(self):
        self.p1 = mp.Process(target=count_time, args=(10, ))
        self.p1.start()

    def stop_time(self):
        pass


if __name__ == '__main__':
    mp.freeze_support()
    sm = ScreenManager(transition=NoTransition())
    Test().run()

Solution

  • The problem is that when you run Process, it starts a new python proces and imports the current .py file. On most platforms, the line:

    if __name__ == '__main__':
    

    protects the Test().run() from being executed and starting another App and is enough to protect from another window being opened. However, it appears that importing kivy.core.window is enough to open another window (on Windows platform) when the original .py file is imported for multiprocessing. You can read about it here.

    A really ugly work around is to protect all kivy imports (and as a result, all the kivy code) with a if __name__ == '__main__': line. Like this:

    import time
    import multiprocessing as mp
    
    def count_time(x):
        for i in range(x):
            print('count={}'.format(i))
            time.sleep(1)
    
    
    if __name__ == '__main__':
        mp.freeze_support()
        from kivy.core.window import Window
        from kivy.app import App
        from kivy.lang import Builder
        from kivy.uix.screenmanager import ScreenManager, Screen, NoTransition
    
        Builder.load_string('''
    <TestScreen>:
        BoxLayout:
            orientation: "vertical" # place object vertically
            Label:
                size_hint: 1, 1
                text: 'TITLE'
                font_size: 150
                color: 1, 1, 1, 1
            Button:
                text: 'start'
                on_press: root.start_mp()
            Button:
                text: 'stop'
                on_press: root.stop_time()
        ''')
    
    
        class Test(App):
    
            def build(self):
                sm = ScreenManager(transition=NoTransition())
                sm.add_widget(TestScreen(name='test'))
                return sm
    
    
        class TestScreen(Screen):
            def __init__(self, **kwargs):
                super(TestScreen, self).__init__(**kwargs)
    
            def start_mp(self):
                self.p1 = mp.Process(target=count_time, args=(10,))
                self.p1.start()
    
            def stop_time(self):
                pass
    
    
        Test().run()