From 1224daa929f3d78c54302ff5c1bccc8282fa7128 Mon Sep 17 00:00:00 2001 From: VALLONGOL Date: Mon, 12 May 2025 14:56:09 +0200 Subject: [PATCH] Chore: Stop tracking files based on .gitignore update. Untracked files matching the following rules: - Rule "!.vscode/launch.json": 1 file --- .gitignore | 5 +- GUI_g_reconverter.ico | Bin 0 -> 31959 bytes gui_g_converter.spec | 39 -------- gui_g_reconverter.spec | 46 +++++++++ gui_g_reconverter/__main__.py | 2 +- gui_g_reconverter/_version.py | 90 ++++++++++++++++++ gui_g_reconverter/core/core.py | 0 gui_g_reconverter/gui/application_gui.py | 113 +++++++++++++++++++++-- 8 files changed, 245 insertions(+), 50 deletions(-) delete mode 100644 gui_g_converter.spec create mode 100644 gui_g_reconverter.spec create mode 100644 gui_g_reconverter/_version.py delete mode 100644 gui_g_reconverter/core/core.py diff --git a/.gitignore b/.gitignore index 587c1b5..a129a78 100644 --- a/.gitignore +++ b/.gitignore @@ -149,4 +149,7 @@ dmypy.json *.swp *~ *.txt -*.txt \ No newline at end of file + + +_dist/ +_build/ \ No newline at end of file diff --git a/GUI_g_reconverter.ico b/GUI_g_reconverter.ico index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..327b4c47b6907597588a248c4974690597d4e9b8 100644 GIT binary patch literal 31959 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 NR!aTg|Ld&D{{@)4Jd6MU literal 0 HcmV?d00001 diff --git a/gui_g_converter.spec b/gui_g_converter.spec deleted file mode 100644 index 9597beb..0000000 --- a/gui_g_converter.spec +++ /dev/null @@ -1,39 +0,0 @@ -# -*- mode: python ; coding: utf-8 -*- - -block_cipher = None - -a = Analysis( - ['gui_g_converter/__main__.py'], - pathex=['.'], - binaries=[], - # Usa project_icon_filename nella sezione datas - datas=[('GUI_g_converter.ico', '.')], - hiddenimports=[], - hookspath=[], - runtime_hooks=[], - excludes=[], - win_no_prefer_redirects=False, - win_private_assemblies=False, - cipher=block_cipher, - noarchive=False -) -pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher) - -exe = EXE( - pyz, - a.scripts, - a.binaries, - a.zipfiles, - a.datas, - [], - name='GUI_g_converter', - debug=False, - bootloader_ignore_signals=False, - strip=False, - upx=True, - upx_exclude=[], - runtime_tmpdir=None, - console=True, - # Usa project_icon_filename per l'opzione icon - icon='GUI_g_converter.ico' -) diff --git a/gui_g_reconverter.spec b/gui_g_reconverter.spec new file mode 100644 index 0000000..206683e --- /dev/null +++ b/gui_g_reconverter.spec @@ -0,0 +1,46 @@ +# -*- mode: python ; coding: utf-8 -*- + +block_cipher = None + +import os +a = Analysis(scripts=['gui_g_reconverter\\__main__.py'], + pathex=['gui_g_reconverter'], + binaries=[], + datas=[('GUI_g_reconverter.ico', '.')], + hiddenimports=[], + hookspath=[], + hooksconfig={}, + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=None, + noarchive=False) + +pyz = PYZ(a.pure, a.zipped_data, cipher=None) + +exe = EXE(pyz, + a.scripts, + [], # Binaries/Datas usually handled by Analysis/COLLECT + exclude_binaries=True, # Let COLLECT handle binaries in one-dir + name='GUI_g_converter', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, # Use UPX based on config + runtime_tmpdir=None, + console=False, # Set console based on GUI checkbox + disable_windowed_traceback=False, + target_arch=None, + codesign_identity=None, + entitlements_file=None, + icon='GUI_g_reconverter.ico') + +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, # Match UPX setting + upx_exclude=[], + name='GUI_g_converter') diff --git a/gui_g_reconverter/__main__.py b/gui_g_reconverter/__main__.py index 992ec0a..eaf8611 100644 --- a/gui_g_reconverter/__main__.py +++ b/gui_g_reconverter/__main__.py @@ -10,7 +10,7 @@ import sys # Import the main GUI class from the gui subpackage -from .gui.application_gui import CppConverterGUI +from gui_g_reconverter.gui.application_gui import CppConverterGUI if __name__ == "__main__": root = tk.Tk() diff --git a/gui_g_reconverter/_version.py b/gui_g_reconverter/_version.py new file mode 100644 index 0000000..014cfea --- /dev/null +++ b/gui_g_reconverter/_version.py @@ -0,0 +1,90 @@ +# -*- coding: utf-8 -*- +# File generated by PyInstaller GUI Wrapper. DO NOT EDIT MANUALLY. +# Contains build-time information scraped from Git (if available) +# and a helper function to format version strings. + + +import re + +# --- Version Data (Generated) --- +# This section is automatically generated by the build process. +__version__ = "v.0.0.0.1-2-g537930d-dirty" +GIT_COMMIT_HASH = "537930d65d7e7733d5b39df1e491b58cfeff5126" +GIT_BRANCH = "master" +BUILD_TIMESTAMP = "2025-05-12T12:53:06Z" +IS_GIT_REPO = True + +# --- Default Values (for comparison or fallback) --- +DEFAULT_VERSION = "0.0.0+unknown" +DEFAULT_COMMIT = "Unknown" +DEFAULT_BRANCH = "Unknown" + +# --- Helper Function --- +def get_version_string(format_string=None): + """ + Returns a formatted string based on the build version information. + + Args: + format_string (str, optional): A format string using placeholders. + Defaults to "{{version}} ({{branch}}/{{commit_short}})" if None. + Placeholders: + {{version}}: Full version string (e.g., 'v1.0.0-5-gabcdef-dirty') + {{tag}}: Clean tag part if exists (e.g., 'v1.0.0'), else DEFAULT_VERSION. + {{commit}}: Full Git commit hash. + {{commit_short}}: Short Git commit hash (7 chars). + {{branch}}: Git branch name. + {{dirty}}: '-dirty' if the repo was dirty, empty otherwise. + {{timestamp}}: Full build timestamp (ISO 8601 UTC). + {{timestamp_short}}: Build date only (YYYY-MM-DD). + {{is_git}}: 'Git' if IS_GIT_REPO is True, 'Unknown' otherwise. + + Returns: + str: The formatted version string, or an error message if formatting fails. + """ + if format_string is None: + format_string = "{version} ({branch}/{commit_short})" # Sensible default + + replacements = {} + try: + # Prepare data dictionary for substitution + replacements['version'] = __version__ if __version__ else DEFAULT_VERSION + replacements['commit'] = GIT_COMMIT_HASH if GIT_COMMIT_HASH else DEFAULT_COMMIT + replacements['commit_short'] = GIT_COMMIT_HASH[:7] if GIT_COMMIT_HASH and len(GIT_COMMIT_HASH) >= 7 else DEFAULT_COMMIT + replacements['branch'] = GIT_BRANCH if GIT_BRANCH else DEFAULT_BRANCH + replacements['timestamp'] = BUILD_TIMESTAMP if BUILD_TIMESTAMP else "Unknown" + replacements['timestamp_short'] = BUILD_TIMESTAMP.split('T')[0] if BUILD_TIMESTAMP and 'T' in BUILD_TIMESTAMP else "Unknown" + replacements['is_git'] = "Git" if IS_GIT_REPO else "Unknown" + replacements['dirty'] = "-dirty" if __version__ and __version__.endswith('-dirty') else "" + + # Extract clean tag using regex (handles versions like v1.0.0, 1.0.0) + tag = DEFAULT_VERSION + if __version__ and IS_GIT_REPO: + # Match optional 'v' prefix, then major.minor.patch + match = re.match(r'^(v?([0-9]+)\.([0-9]+)\.([0-9]+))', __version__) + if match: + tag = match.group(1) # Get the full tag (e.g., 'v1.0.0') + replacements['tag'] = tag + + # Perform substitution using regex to find placeholders {placeholder} + output_string = format_string + # Iterate through placeholders and replace them in the format string + for placeholder, value in replacements.items(): + # Compile regex pattern for {placeholder}, allowing for whitespace inside braces + pattern = re.compile(r'{\s*' + re.escape(placeholder) + r'\s*}') + # Substitute found patterns with the corresponding string value + output_string = pattern.sub(str(value), output_string) + + # Optional: Check if any placeholders remain unsubstituted (could indicate typo) + if re.search(r'{\s*[\w_]+\s*}', output_string): + # You might want to log this or handle it, for now, we return the string as is + # print(f"Warning: Unsubstituted placeholders remain in version string: {output_string}") + pass + + return output_string + + except Exception as e: + # Return a simple error message in case of unexpected formatting issues + # Avoid printing directly from this generated function + return f"[Formatting Error: {e}]" + + diff --git a/gui_g_reconverter/core/core.py b/gui_g_reconverter/core/core.py deleted file mode 100644 index e69de29..0000000 diff --git a/gui_g_reconverter/gui/application_gui.py b/gui_g_reconverter/gui/application_gui.py index 4076c4a..9710611 100644 --- a/gui_g_reconverter/gui/application_gui.py +++ b/gui_g_reconverter/gui/application_gui.py @@ -1,3 +1,4 @@ + import tkinter as tk from tkinter import scrolledtext, filedialog, messagebox, ttk import subprocess @@ -9,7 +10,7 @@ import datetime import sys # Needed for sys.platform in open_gui_log_file # Import the runner function from the core module -from ..core.g_reconvert_runner import run_g_reconvert +from gui_g_reconverter.core.g_reconvert_runner import run_g_reconvert # --- Constants --- DEFAULT_GUI_LOG_FILE = "gui_execution_log.txt" @@ -22,6 +23,28 @@ DEFAULT_PROFILE_FILE = "default_launch_profile.json" BIN_FILE_EXTENSION = ".rec" BIN_FILETYPES = [("REC files", f"*{BIN_FILE_EXTENSION}"), ("All files", "*.*")] +# Polling interval for the output queue in milliseconds +OUTPUT_QUEUE_POLLING_INTERVAL = 50 # Reduced from 100ms for potentially faster updates + +# --- Import Version Info FOR THE WRAPPER ITSELF --- +try: + # Use absolute import based on package name + from gui_g_reconverter import _version as wrapper_version + WRAPPER_APP_VERSION_STRING = f"{wrapper_version.__version__} ({wrapper_version.GIT_BRANCH}/{wrapper_version.GIT_COMMIT_HASH[:7]})" + WRAPPER_BUILD_INFO = f"Wrapper Built: {wrapper_version.BUILD_TIMESTAMP}" +except ImportError: + # This might happen if you run the wrapper directly from source + # without generating its _version.py first (if you use that approach for the wrapper itself) + WRAPPER_APP_VERSION_STRING = "(Dev Wrapper)" + WRAPPER_BUILD_INFO = "Wrapper build time unknown" +# --- End Import Version Info --- + +# --- Constants for Version Generation --- +DEFAULT_VERSION = "0.0.0+unknown" +DEFAULT_COMMIT = "Unknown" +DEFAULT_BRANCH = "Unknown" +# --- End Constants --- + class CppConverterGUI: """ @@ -37,7 +60,7 @@ class CppConverterGUI: master_window: The main Tkinter window. """ self.master_window = master_window - self.master_window.title("g_reconvert.exe Interface") + self.master_window.title(f"g_reconvert.exe Interface - {WRAPPER_APP_VERSION_STRING}") # Adjusted initial window size - Further reduced height for more compact layout self.master_window.geometry("950x650") # Adjusted height, might need tweaking based on content @@ -129,6 +152,11 @@ class CppConverterGUI: # Ensure generated paths are updated after potential profile load self._update_generated_file_paths() + # Trace changes to output_dir_var to update button state + self.output_dir_var.trace_add("write", lambda *args: self._update_go_to_output_button_state()) + # Initial update of the button state + self._update_go_to_output_button_state() + def _load_default_profile(self): """Attempts to load the default profile file on application startup.""" @@ -507,6 +535,50 @@ class CppConverterGUI: self._generated_log_file_path = "" self.g_reconvert_log_file_var.set("Generated log file path will appear here.") + # Update the state of the "Go to Output Folder" button + self._update_go_to_output_button_state() + + + def _update_go_to_output_button_state(self): + """Enables or disables the 'Go to Output Folder' button.""" + output_dir = self.output_dir_var.get().strip() + # Enable the button only if output_dir is not empty AND it's a valid directory path + if output_dir and os.path.isdir(output_dir): + if hasattr(self, 'go_to_output_button'): # Check if button exists + self.go_to_output_button.config(state=tk.NORMAL) + else: + if hasattr(self, 'go_to_output_button'): # Check if button exists + self.go_to_output_button.config(state=tk.DISABLED) + + + def _open_output_folder(self): + """Opens the selected output directory in the system's file explorer.""" + output_dir = self.output_dir_var.get().strip() + if not output_dir or not os.path.isdir(output_dir): + messagebox.showwarning("Invalid Output Directory", "The specified output directory is empty or does not exist.") + self._update_status_bar("Warning: Cannot open invalid output directory.") + return + + try: + # Use os.startfile on Windows + if os.name == 'nt': + os.startfile(output_dir) + # Use subprocess for macOS and Linux + elif sys.platform == 'darwin': # macOS + subprocess.run(['open', output_dir], check=True) + else: # Linux and other Unix-like + subprocess.run(['xdg-open', output_dir], check=True) + self._log_message_to_gui_log(f"Opened output directory: {output_dir}") + self._update_status_bar(f"Opened output directory: {os.path.basename(output_dir)}") + except FileNotFoundError: + messagebox.showerror("Error", f"Could not find an application to open the directory.\nPlease open it manually: {output_dir}") + self._log_message_to_gui_log(f"Error opening output directory with system default (app not found): {output_dir}") + self._update_status_bar("Error opening output directory.") + except Exception as e: + messagebox.showerror("Error", f"Could not open output directory: {e}\nPath: {output_dir}") + self._log_message_to_gui_log(f"Error opening output directory: {e}. Path: {output_dir}") + self._update_status_bar("Error opening output directory.") + def _select_bin_file(self): """Open a dialog to select the source binary file (binfile), filtered for .rec.""" @@ -515,12 +587,23 @@ class CppConverterGUI: def _setup_control_buttons(self, parent_frame): """Create and arrange control buttons within a frame using grid.""" - # Use grid for centering the button within the frame - parent_frame.columnconfigure(0, weight=1) # Make the column expandable to center + # Configure grid for two columns to place buttons side-by-side + parent_frame.columnconfigure(0, weight=1) # Column for Run button (will still center it) + parent_frame.columnconfigure(1, weight=1) # Column for Go to Output button - # Run button centered + # Run button self.run_button = tk.Button(parent_frame, text="Run g_reconvert.exe", command=self.run_cpp_application, width=25, height=2, bg="#4CAF50", fg="white", font=('Helvetica', 10, 'bold')) - self.run_button.grid(row=0, column=0, padx=10, pady=10) + # Use sticky='E' to push it to the right in its column (relative to the center if column weight > 0) + # Or just use a fixed column and place it. Let's keep it simple and centered in its column for now. + self.run_button.grid(row=0, column=0, padx=10, pady=10, sticky=tk.E) # Sticky East + + # Go to Output Folder button + self.go_to_output_button = tk.Button(parent_frame, text="Go to Output Folder", command=self._open_output_folder, width=25, height=2, bg="#FFD700", fg="black", font=('Helvetica', 10, 'bold')) + # Place it in the second column, sticky='W' to push it to the left + self.go_to_output_button.grid(row=0, column=1, padx=10, pady=10, sticky=tk.W) + + # Ensure the button is initially disabled until a valid directory is selected + self._update_go_to_output_button_state() def _setup_output_area(self, parent_frame): @@ -736,6 +819,7 @@ class CppConverterGUI: self._update_status_bar(f"Running g_reconvert.exe...") self.run_button.config(state=tk.DISABLED, bg="#cccccc") # Disable button while running + self.go_to_output_button.config(state=tk.DISABLED) # Disable Go To button while running self.process_running = True # Run the core runner function in a separate thread @@ -750,7 +834,7 @@ class CppConverterGUI: # Start polling the output queue # Check queue every 100 milliseconds (adjust as needed) - self.master_window.after(100, self._process_output_queue) + self.master_window.after(OUTPUT_QUEUE_POLLING_INTERVAL, self._process_output_queue) def _process_output_queue(self): @@ -758,8 +842,13 @@ class CppConverterGUI: Checks the output queue for messages from the core runner thread and updates the GUI. This runs in the main GUI thread. """ + # Process a limited number of messages at a time to avoid blocking the GUI + MAX_MESSAGES_TO_PROCESS = 10 # Adjust this number as needed for responsiveness vs update frequency + messages_processed = 0 + try: - while True: # Process all messages currently in the queue + # Process up to MAX_MESSAGES_TO_PROCESS from the queue + while messages_processed < MAX_MESSAGES_TO_PROCESS: # Use get_nowait() to avoid blocking the GUI thread item = self.output_queue.get_nowait() @@ -769,6 +858,8 @@ class CppConverterGUI: self.run_button.config(state=tk.NORMAL, bg="#4CAF50") # Re-enable button self.process_running = False self._update_status_bar("g_reconvert.exe finished. Ready.") + # Re-enable Go To Output button if directory is valid + self._update_go_to_output_button_state() return # Stop polling the `after` loop else: # It's a message dictionary {'text': ..., 'type': ...} @@ -776,14 +867,18 @@ class CppConverterGUI: # Decide whether to log the message to the GUI log based on its source or content # For now, log everything coming from the runner thread self._log_message_to_gui_log(item['text'].strip(), include_timestamp=False) # Log to file + messages_processed += 1 # Increment counter + except queue.Empty: # No new messages in queue, just continue polling if process is still running pass # If process is still marked as running, schedule the next check + # Schedule the next check regardless of whether messages were processed this time if self.process_running: - self.master_window.after(100, self._process_output_queue) + self.master_window.after(OUTPUT_QUEUE_POLLING_INTERVAL, self._process_output_queue) + # If process_running is False, the 'None' signal would have caused the function to return def _insert_output_text(self, message, tag='INFO'):