From 0a31758df1762b95ecf6cf55830d07ab41a2e87a Mon Sep 17 00:00:00 2001 From: Esma Ali Date: Wed, 4 Mar 2026 20:46:34 -0500 Subject: [PATCH] Fix #617: run anonymize in background thread to prevent GUI freeze --- FileShuffler.qml | 20 ++++++++++++++++--- shuffler_api.py | 52 +++++++++++++++++++++++++++++++++++++++++------- 2 files changed, 62 insertions(+), 10 deletions(-) diff --git a/FileShuffler.qml b/FileShuffler.qml index 113214b6..ad842e0d 100644 --- a/FileShuffler.qml +++ b/FileShuffler.qml @@ -282,9 +282,9 @@ Rectangle { onAccepted: { console.log("Selected folder:", fileShufflerDialog.folder) - var output = fileShufflerGui.run_file_shuffler_program(fileShufflerDialog.folder) - fileShufflerView.runLog += output + "\n" - fileShufflerView.ranShuffle = true + fileShufflerView.runLog += "Starting file shuffler...\n" + fileShufflerView.ranShuffle = false + fileShufflerGui.run_file_shuffler_program(fileShufflerDialog.folder) } onRejected: { @@ -326,4 +326,18 @@ Rectangle { console.log("Folder dialog canceled") } } + + Connections { + target: fileShufflerGui + + function onFileShufflerFinished(output) { + fileShufflerView.runLog += output + "\n" + fileShufflerView.ranShuffle = true + } + + function onFileShufflerError(message) { + fileShufflerView.runLog += message + "\n" + fileShufflerView.ranShuffle = false + } + } } diff --git a/shuffler_api.py b/shuffler_api.py index b5b06a30..b925250b 100644 --- a/shuffler_api.py +++ b/shuffler_api.py @@ -2,7 +2,7 @@ import os import subprocess from pathlib import Path -from PySide6.QtCore import QObject, Signal, Slot, Property, QProcess, QUrl, QTimer +from PySide6.QtCore import QObject, Signal, Slot, QThread import io import urllib.parse import contextlib @@ -15,9 +15,32 @@ import run_file_shuffler import remove8channel +class FileShufflerWorker(QObject): + finished = Signal(str) + error = Signal(str) + + def __init__(self, path): + super().__init__() + self.path = path + + @Slot() + def run(self): + try: + response = run_file_shuffler.main(self.path) + if response is None: + response = "File shuffler finished." + self.finished.emit(str(response)) + except Exception as e: + self.error.emit(f"Error during file shuffling: {e}") + class ShufflerAPI(QObject): + fileShufflerFinished = Signal(str) + fileShufflerError = Signal(str) + def __init__(self): super().__init__() + self._thread = None + self._worker = None @Slot() def launch_file_shuffler_gui(self): @@ -25,15 +48,30 @@ def launch_file_shuffler_gui(self): file_shuffler_path = Path(__file__).resolve().parent / "file-shuffler/file-shuffler-gui.py" subprocess.Popen(["python", str(file_shuffler_path)]) - @Slot(str, result=str) + @Slot(str) def run_file_shuffler_program(self, path): # Need to parse the path as the FolderDialog appends file:// in front of the selection - path = path.replace("file://", "") - if path.startswith("/C:"): - path = 'C' + path[2:] + path = urllib.parse.unquote(path.replace("file://", "")) + + if os.name == "nt" and path.startswith("/"): + path = path[1:] + + self._thread = QThread() + self._worker = FileShufflerWorker(path) + self._worker.moveToThread(self._thread) + + self._thread.started.connect(self._worker.run) + self._worker.finished.connect(self.fileShufflerFinished) + self._worker.error.connect(self.fileShufflerError) + + self._worker.finished.connect(self._thread.quit) + self._worker.error.connect(self._thread.quit) + + self._worker.finished.connect(self._worker.deleteLater) + self._worker.error.connect(self._worker.deleteLater) + self._thread.finished.connect(self._thread.deleteLater) - response = run_file_shuffler.main(path) - return response + self._thread.start() # Adding Synthetic Data and Live Data Logic (Row 327 to 355) as part of Ticket 186