[Python] How to implement a GUI that works with threads?

Discussão em 'Python' iniciado por Stack, Março 28, 2025.

  1. Stack

    Stack Membro Participativo

    I've been working on a download manager that automatically opens a GUI menu whenever a new file is downloaded, allowing me to specify a save location.

    Currently, I use the following Python script to detect new downloads and trigger the menu:

    from watchdog.observers import Observer
    import time
    from globals import DOWNLOADS_FOLDER
    import os
    import threading
    import menu
    from collections import deque

    TEMP_FILE_EXTENSIONS = {'.tmp', '.crdownload', '.download', '.part', '.swp', '.temp'}

    class DownloadHandler(FileSystemEventHandler):
    def __init__(self):
    """Initialize the handler."""
    self.open_threads = True
    self.events_to_handle = deque()
    self.menu_thread_lock = threading.Lock()
    self.menu_thread = threading.Thread(target=self.loop_through_menus, daemon=True)
    self.menu_thread.start()

    def on_created(self, event: FileSystemEvent):
    """Handle file created events."""
    file_path = event.src_path
    self.events_to_handle.append(file_path)

    def on_moved(self, event: FileSystemEvent):
    """Handle file moved events."""
    original_path = event.src_path
    file_path = event.dest_path

    if self.is_download_folder(original_path) and file_path.endswith(tuple(TEMP_FILE_EXTENSIONS)):
    self.events_to_handle.append(file_path)

    def loop_through_menus(self):
    """Loop through events and handle them."""
    while self.open_threads:
    if self.events_to_handle:
    with self.menu_thread_lock: # Ensure thread safety when modifying the deque
    file_path = self.events_to_handle.popleft()
    self.new_download_file(file_path)
    else:
    time.sleep(0.1)

    def new_download_file(self, file_path):
    """Check if the file is in Downloads and move it if not temporary."""
    if self.is_download_folder(file_path):
    if not file_path.endswith(tuple(TEMP_FILE_EXTENSIONS)) and not os.path.basename(file_path).startswith("."):
    print(file_path)
    self.create_and_run_menu(file_path)

    def create_and_run_menu(self, file_path):
    """Create and run the file move menu."""
    menu.run(file_path)

    def is_download_folder(self, file_path):
    """Check if the file is in the Downloads folder."""
    return os.path.dirname(file_path) == DOWNLOADS_FOLDER

    def close_thread(self):
    """Gracefully stop the background thread."""
    self.open_threads = False
    self.menu_thread.join()

    if __name__ == "__main__":
    event_handler = DownloadHandler()
    observer = Observer()

    # Set observer to monitor the Downloads folder (use absolute path if needed)
    observer.schedule(event_handler, DOWNLOADS_FOLDER, recursive=True)
    observer.start()

    try:
    while True:
    time.sleep(1)
    finally:
    observer.stop()
    observer.join()
    event_handler.close_thread()


    However, I've encountered an issue: Most Python GUI frameworks don't work well with threads. I've tried both PyQt6 and Tkinter, but neither worked. Instead, I get the following error:


    libc++abi: terminating due to uncaught exception of type NSException
    ​

    Since threading seems to be an issue, I’m looking for alternative solutions: Are there any lightweight GUI frameworks that handle this better? Any way to handle my current implementation without using threading?

    Continue reading...

Compartilhe esta Página