0

I am stuck on writing a very simple kivy gui with 2 buttons. button_1 launches the countdown in multiprocessing. It works. button_2 is supposed to end the countdown in multiprocessing. That does not work... Can anyone please point out what I am doing wrong and why?

Thank you kindly in advance.

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
import multiprocessing
import time

class MyApp(App):

    list_of_objects_in_class = []

    def build(self):
        layout = BoxLayout()
        button_1 = Button(text='launch_countdown', on_press=self.launch_countdown)
        button_2 = Button(text='end_the_countdown', on_press=self.end_the_countdown)
        layout.add_widget(button_1)
        layout.add_widget(button_2)
        return layout
    
    @staticmethod
    def actual_countdown_function(*args):
        print('I am launching the countdown!')
        for count in range(5, 0, -1):
            print(count)
            time.sleep(1)
        print('Countdown Finished!!!') # When I press end_the_countdown button, this is NOT supposed to be printed.

    def launch_countdown(self, *args):
        MyApp.list_of_objects_in_class.append(multiprocessing.Process(target=MyApp.actual_countdown_function, args=()))
        MyApp.list_of_objects_in_class[0].start()

    
    def end_the_countdown(self, *args):
        print('I am terminating the countdown early!')
        try:
            MyApp.list_of_objects_in_class[0].terminate()
            MyApp.list_of_objects_in_class.clear()
        except:
            pass 

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

I tested to see if .terminate() works on my system, it works. The script below terminates successfully but the script above does NOT...

import multiprocessing
import time 

def print_one():
    time.sleep(3)
    print('This does NOT get printed because proc.terminate() works on my Linux Mint OS')

def print_two():
    print('This gets printed')

process_to_terminate = multiprocessing.Process(target=print_one, args=())
process_to_terminate.start()
process_to_keep_and_NOT_terminate = multiprocessing.Process(target=print_two, args=())
process_to_keep_and_NOT_terminate.start()
process_to_terminate.terminate()  # Works and kills print_one function

  • Does this answer your question? [Multiprocessing Process terminate fails on Linux](https://stackoverflow.com/questions/10529461/multiprocessing-process-terminate-fails-on-linux) – John Anderson Aug 06 '21 at 12:56
  • Thank you for responding, but no, that does not work for me. .terminate() works on my linux mint system, but does not work in the OOP script I wrote. .terminate() works for me in non OOP python. – Pavel Bogdanov Aug 06 '21 at 19:10

2 Answers2

1

after many different attempts, I was able to solve my own problem by using threading instead of multiprocessing.

Here is the code:

from kivy.app import App
from kivy.uix.button import Button
from kivy.uix.boxlayout import BoxLayout
import threading
import time


class MyApp(App):
    my_var = True

    list_of_objects_in_class = []

    def build(self):
        layout = BoxLayout(size =(300, 300))
        button_1 = Button(text='launch_countdown', on_press=self.launch_countdown)
        button_2 = Button(text='end_the_countdown', on_press=self.end_the_countdown, height = "100", width = 140)
        layout.add_widget(button_1)
        layout.add_widget(button_2)
        return layout
    

    def actual_countdown_function(self, *args):
        print('I am launching the countdown!')
        x = 5
        while x != 0:
            new_var = self.my_var
            if new_var == False:
                break
            time.sleep(1)
            print(x, new_var)
            x -=1
            del(new_var)
        print('Countdown Finished!!!') # When I press end_the_countdown button, this is NOT supposed to be printed.

    def launch_countdown(self, *args):
        t1 = threading.Thread(target=self.actual_countdown_function) 
        t1.start()

    def end_the_countdown(self, *args):
        print('I am terminating the countdown early!')
        self.my_var = not self.my_var
        print(self.my_var)
        self.list_of_objects_in_class.clear()
        

if __name__ == "__main__":
    MyApp().run()
1

It's better to use threads indeed to run such a small tasks. Anyway, I think, the original problem could be solved by setting SIGTERM handler in a child process, because it seems to be ignoring SIGTERM by default:

def actual_countdown_function(self, *args):
+   signal.signal(signal.SIGTERM, lambda *_, **__: sys.exit(0))
    print('I am launching the countdown!')

But, TBH, if you will really need a way to manage child process, I strongly recommend to use Locks/Conditions/Queues instead. They have almost the same API like all the threading primitives, but built to interact over pipes/shared-memory/sockets/etc.

madbird
  • 1,326
  • 7
  • 11