import platform import multiprocessing import threading import time import tkinter as tk import tkinter.ttk as ttk from functools import partial mp = multiprocessing.get_context("spawn") def main(): def on_command(worker): status_label.grid_forget() progressbar.grid(sticky="ew") progressbar.start(10) enable_widgets(False) worker(target=task, args=(worker.__name__, result_queue,), daemon=True).start() def on_result(result): status_label["text"] = str(result) progressbar.stop() progressbar.grid_forget() status_label.grid(sticky="ew") enable_widgets(True) def on_toggle_patch(): if enable_patch.get(): patch.install() else: patch.uninstall() def read_result_queue(): while result := result_queue.get(): root.after_idle(on_result, result) def enable_widgets(enabled): state = "normal" if enabled else "disabled" for b in buttons: b["state"] = state checkbox["state"] = state patch = Patch() root = tk.Tk() progressbar = ttk.Progressbar(root, mode="indeterminate") status_label = tk.Label(root) enable_patch = tk.IntVar() result_queue = mp.SimpleQueue() buttons = [tk.Button(root, text="Run Thread task", command=partial(on_command, threading.Thread)), tk.Button(root, text="Run Process task", command=partial(on_command, mp.Process))] checkbox = tk.Checkbutton(root, text="Enable patch", variable=enable_patch, command=on_toggle_patch) for b in buttons: b.grid(sticky="ew", ipadx=50) checkbox.grid() status_label.grid(sticky="ew") root.columnconfigure(0, weight=1) root.rowconfigure(2, weight=1) root.title("Background task demo") threading.Thread(target=read_result_queue, daemon=True).start() root.mainloop() def task(name, result_queue): time.sleep(10) result = f"{name} task finished!" result_queue.put(result) class Patch: def __init__(self): self._spawn = None self._real_winapi = None def install(self): if platform.system() != "Windows": return if self._real_winapi is None: import subprocess import multiprocessing.popen_spawn_win32 self._spawn = multiprocessing.popen_spawn_win32 self._real_winapi = self._spawn._winapi self._STARTUPINFO = subprocess.STARTUPINFO attrs = dict(__getattr__=self._real_winapi.__getattribute__) proxy_class = type("WinApiProxy", (), attrs) proxy = proxy_class() proxy.CreateProcess = self._create_process self._spawn._winapi = proxy def uninstall(self): if self._real_winapi is not None: self._spawn._winapi = self._real_winapi def _create_process(self, application_name, command_line, proc_attrs, thread_attrs, inherit_handles, creation_flags, env_mapping, current_directory, startup_info): STARTF_FORCEOFFFEEDBACK = 0x80 startup_info = (self._STARTUPINFO() if startup_info is None else startup_info.copy()) startup_info.dwFlags |= STARTF_FORCEOFFFEEDBACK return self._real_winapi.CreateProcess( application_name, command_line, proc_attrs, thread_attrs, inherit_handles, creation_flags, env_mapping, current_directory, startup_info) if __name__ == "__main__": main()