diff --git a/Launcher.py b/Launcher.py index 146015f4..84bdeeb7 100644 --- a/Launcher.py +++ b/Launcher.py @@ -223,6 +223,12 @@ def run_gui(): else: launch(get_exe(button.component), button.component.cli) + def _stop(self, *largs): + # ran into what appears to be https://groups.google.com/g/kivy-users/c/saWDLoYCSZ4 with PyCharm. + # Closing the window explicitly cleans it up. + self.root_window.close() + super()._stop(*largs) + Launcher().run() @@ -267,3 +273,9 @@ if __name__ == '__main__': help="Pass either a patch file, a generated game or the name of a component to run.") parser.add_argument('args', nargs="*", help="Arguments to pass to component.") main(parser.parse_args()) + + from worlds.LauncherComponents import processes + for process in processes: + # we await all child processes to close before we tear down the process host + # this makes it feel like each one is its own program, as the Launcher is closed now + process.join() diff --git a/worlds/LauncherComponents.py b/worlds/LauncherComponents.py index bde6a319..aeb55cf9 100644 --- a/worlds/LauncherComponents.py +++ b/worlds/LauncherComponents.py @@ -1,3 +1,4 @@ +import weakref from enum import Enum, auto from typing import Optional, Callable, List, Iterable @@ -48,10 +49,14 @@ class Component: def __repr__(self): return f"{self.__class__.__name__}({self.display_name})" +processes = weakref.WeakSet() + def launch_subprocess(func: Callable, name: str = None): + global processes import multiprocessing - process = multiprocessing.Process(target=func, name=name, daemon=True) + process = multiprocessing.Process(target=func, name=name) process.start() + processes.add(process) class SuffixIdentifier: suffixes: Iterable[str]