From a08fa7aef8ef2eeaf187252fb26ada81c19999c1 Mon Sep 17 00:00:00 2001 From: dazedanon Date: Sat, 25 Oct 2025 18:15:12 -0500 Subject: [PATCH] woo --- gui/translation_tab.py | 193 +++++++++++++------------------------- util/subprocess_runner.py | 149 +++++++++++++++++++++++++++++ 2 files changed, 214 insertions(+), 128 deletions(-) create mode 100644 util/subprocess_runner.py diff --git a/gui/translation_tab.py b/gui/translation_tab.py index 66da2eb..8155660 100644 --- a/gui/translation_tab.py +++ b/gui/translation_tab.py @@ -122,80 +122,93 @@ class TranslationWorker(QThread): def run_module_in_process(self, filename, estimate_only): """Run a module handler in a separate process for better control.""" try: - # Create a separate Python process to run the module - # This allows us to terminate it if needed - script_content = f''' -import sys -import os -from pathlib import Path -import io -from contextlib import redirect_stdout, redirect_stderr - -# Set UTF-8 encoding for stdout to handle Unicode characters -sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') -sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') - -# Add project root to path -project_root = Path(r"{self.project_root}") -sys.path.insert(0, str(project_root)) - -try: - # Change to project directory - os.chdir(str(project_root)) - - # Import and run the handler - {self.get_import_statement()} - - # Run the handler and capture the result - handler_result = {self.get_handler_call()}(r"{filename}", {estimate_only}) - - # Print the result - if handler_result: - print(f"RESULT:{{handler_result}}") - else: - print("RESULT:Fail") - -except Exception as e: - import traceback - error_msg = str(e).encode('ascii', 'ignore').decode('ascii') - print(f"ERROR:{{error_msg}}") - sys.exit(1) -''' - - # Write script to temporary file - temp_script = self.project_root / "temp_translation_script.py" - with open(temp_script, 'w', encoding='utf-8') as f: - f.write(script_content) + # Use the external subprocess runner script + runner_script = self.project_root / "util" / "subprocess_runner.py" + if not runner_script.exists(): + self.emit_log(f"❌ Subprocess runner script not found: {runner_script}") + return "Fail" # Run the script in a separate process env = os.environ.copy() env['PYTHONIOENCODING'] = 'utf-8' # Force UTF-8 encoding process = subprocess.Popen( - [sys.executable, str(temp_script)], + [ + sys.executable, + str(runner_script), + str(self.project_root), + self.module_info[0], # module name + filename, + str(estimate_only) + ], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, encoding='utf-8', errors='replace', cwd=str(self.project_root), - env=env + env=env, + bufsize=1 # Line buffered ) # Track the process for potential termination self.running_processes.append(process) - # Wait for completion - stdout, stderr = process.communicate() + # Read output in real-time to capture progress + stdout_lines = [] + stderr_lines = [] + + def read_stdout(): + """Read stdout line by line.""" + for line in iter(process.stdout.readline, ''): + if not line: + break + line = line.strip() + if line.startswith('PROGRESS:'): + # Parse progress: PROGRESS:filename:current:total + try: + parts = line.split(':', 3) + if len(parts) == 4: + _, desc, current, total = parts + self.item_progress_signal.emit(int(current), int(total)) + # Update translating label with filename + if desc: + self.emit_log(f"📝 Translating: {desc} ({current}/{total})") + except Exception: + pass # Ignore malformed progress lines + else: + stdout_lines.append(line) + process.stdout.close() + + def read_stderr(): + """Read stderr line by line.""" + for line in iter(process.stderr.readline, ''): + if not line: + break + stderr_lines.append(line.strip()) + process.stderr.close() + + # Start reader threads + stdout_thread = threading.Thread(target=read_stdout, daemon=True) + stderr_thread = threading.Thread(target=read_stderr, daemon=True) + stdout_thread.start() + stderr_thread.start() + + # Wait for process completion + process.wait() + + # Wait for reader threads to finish + stdout_thread.join(timeout=1.0) + stderr_thread.join(timeout=1.0) + + # Combine output + stdout = '\n'.join(stdout_lines) + stderr = '\n'.join(stderr_lines) # Remove from tracking if process in self.running_processes: self.running_processes.remove(process) - # Clean up temp file - if temp_script.exists(): - temp_script.unlink() - # Check if process was terminated by stop signal if self.should_stop: return "Stopped" @@ -229,82 +242,6 @@ except Exception as e: except Exception as e: self.emit_log(f"❌ Failed to run module process: {str(e)}") return "Fail" - - def get_import_statement(self): - """Get the import statement for the selected module.""" - module_name = self.module_info[0] - if "RPG Maker MV/MZ" in module_name: - return "from modules.rpgmakermvmz import handleMVMZ" - elif "RPG Maker Ace" in module_name: - return "from modules.rpgmakerace import handleACE" - elif "CSV" in module_name: - return "from modules.csv import handleCSV" - elif "Tyrano" in module_name: - return "from modules.tyrano import handleTyrano" - elif "Kirikiri" in module_name: - return "from modules.kirikiri import handleKirikiri" - elif "JSON" in module_name: - return "from modules.json import handleJSON" - elif "Lune" in module_name: - return "from modules.lune import handleLune" - elif "NScript" in module_name: - return "from modules.nscript import handleOnscripter" - elif "Wolf RPG 2" in module_name: - return "from modules.wolf2 import handleWOLF2" - elif "Wolf RPG" in module_name: - return "from modules.wolf import handleWOLF" - elif "Regex" in module_name: - return "from modules.regex import handleRegex" - elif "Text" in module_name: - return "from modules.text import handleText" - elif "RenPy" in module_name: - return "from modules.renpy import handleRenpy" - elif "Unity" in module_name: - return "from modules.unity import handleUnity" - elif "Images" in module_name: - return "from modules.images import handleImages" - elif "Plugin" in module_name: - return "from modules.rpgmakerplugin import handlePlugin" - else: - return "# Unknown module" - - def get_handler_call(self): - """Get the handler function call for the selected module.""" - module_name = self.module_info[0] - if "RPG Maker MV/MZ" in module_name: - return "handleMVMZ" - elif "RPG Maker Ace" in module_name: - return "handleACE" - elif "CSV" in module_name: - return "handleCSV" - elif "Tyrano" in module_name: - return "handleTyrano" - elif "Kirikiri" in module_name: - return "handleKirikiri" - elif "JSON" in module_name: - return "handleJSON" - elif "Lune" in module_name: - return "handleLune" - elif "NScript" in module_name: - return "handleOnscripter" - elif "Wolf RPG 2" in module_name: - return "handleWOLF2" - elif "Wolf RPG" in module_name: - return "handleWOLF" - elif "Regex" in module_name: - return "handleRegex" - elif "Text" in module_name: - return "handleText" - elif "RenPy" in module_name: - return "handleRenpy" - elif "Unity" in module_name: - return "handleUnity" - elif "Images" in module_name: - return "handleImages" - elif "Plugin" in module_name: - return "handlePlugin" - else: - return "lambda f, e: 'Unknown module'" def run(self): """Run the translation process.""" diff --git a/util/subprocess_runner.py b/util/subprocess_runner.py new file mode 100644 index 0000000..85fa4ad --- /dev/null +++ b/util/subprocess_runner.py @@ -0,0 +1,149 @@ +""" +Subprocess runner for translation modules. +This script runs in a separate process to execute translation modules +and reports progress back to the GUI. +""" + +import sys +import os +from pathlib import Path +import io +import threading +import time + +# Set UTF-8 encoding for stdout to handle Unicode characters +sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') +sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') + +# Progress monitoring thread +progress_active = True +last_reported = {'state': None} + + +def monitor_progress(): + """Monitor module PBAR and report progress.""" + global progress_active + while progress_active: + try: + # Try to get PBAR from any loaded module + for module_name in list(sys.modules.keys()): + if module_name.startswith('modules.'): + module = sys.modules[module_name] + if hasattr(module, 'PBAR') and module.PBAR is not None: + pbar = module.PBAR + desc = getattr(pbar, 'desc', '') or '' + n = getattr(pbar, 'n', 0) + total = getattr(pbar, 'total', 0) + + current_state = (desc, n, total) + if current_state != last_reported['state']: + print(f"PROGRESS:{desc}:{n}:{total}", flush=True) + last_reported['state'] = current_state + break + except Exception: + pass + time.sleep(0.1) + + +def run_handler(project_root, module_name, filename, estimate_only): + """Run a translation module handler.""" + global progress_active + + # Add project root to path + project_root = Path(project_root) + sys.path.insert(0, str(project_root)) + + # Start progress monitoring thread + monitor_thread = threading.Thread(target=monitor_progress, daemon=True) + monitor_thread.start() + + try: + # Change to project directory + os.chdir(str(project_root)) + + # Import the appropriate module and get handler + handler = None + if "RPG Maker MV/MZ" in module_name: + from modules.rpgmakermvmz import handleMVMZ + handler = handleMVMZ + elif "RPG Maker Ace" in module_name: + from modules.rpgmakerace import handleACE + handler = handleACE + elif "CSV" in module_name: + from modules.csv import handleCSV + handler = handleCSV + elif "Tyrano" in module_name: + from modules.tyrano import handleTyrano + handler = handleTyrano + elif "Kirikiri" in module_name: + from modules.kirikiri import handleKirikiri + handler = handleKirikiri + elif "JSON" in module_name: + from modules.json import handleJSON + handler = handleJSON + elif "Lune" in module_name: + from modules.lune import handleLune + handler = handleLune + elif "NScript" in module_name: + from modules.nscript import handleOnscripter + handler = handleOnscripter + elif "Wolf RPG 2" in module_name: + from modules.wolf2 import handleWOLF2 + handler = handleWOLF2 + elif "Wolf RPG" in module_name: + from modules.wolf import handleWOLF + handler = handleWOLF + elif "Regex" in module_name: + from modules.regex import handleRegex + handler = handleRegex + elif "Text" in module_name: + from modules.text import handleText + handler = handleText + elif "RenPy" in module_name: + from modules.renpy import handleRenpy + handler = handleRenpy + elif "Unity" in module_name: + from modules.unity import handleUnity + handler = handleUnity + elif "Images" in module_name: + from modules.images import handleImages + handler = handleImages + elif "Plugin" in module_name: + from modules.rpgmakerplugin import handlePlugin + handler = handlePlugin + else: + print(f"ERROR:Unknown module: {module_name}") + sys.exit(1) + + # Run the handler + handler_result = handler(filename, estimate_only) + + # Stop progress monitoring + progress_active = False + + # Print the result + if handler_result: + print(f"RESULT:{handler_result}") + else: + print("RESULT:Fail") + + except Exception as e: + progress_active = False + import traceback + error_msg = str(e).encode('ascii', 'ignore').decode('ascii') + print(f"ERROR:{error_msg}") + traceback.print_exc() + sys.exit(1) + + +if __name__ == "__main__": + if len(sys.argv) != 5: + print("ERROR:Invalid arguments") + sys.exit(1) + + project_root = sys.argv[1] + module_name = sys.argv[2] + filename = sys.argv[3] + estimate_only = sys.argv[4].lower() == 'true' + + run_handler(project_root, module_name, filename, estimate_only)