Chore: Stop tracking files based on .gitignore update.

Untracked files matching the following rules:
- Rule "!.vscode/launch.json": 1 file
This commit is contained in:
VALLONGOL 2025-05-12 14:56:09 +02:00
parent 00aea53907
commit 1224daa929
8 changed files with 245 additions and 50 deletions

5
.gitignore vendored
View File

@ -149,4 +149,7 @@ dmypy.json
*.swp *.swp
*~ *~
*.txt *.txt
*.txt
_dist/
_build/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 0 B

After

Width:  |  Height:  |  Size: 31 KiB

View File

@ -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'
)

46
gui_g_reconverter.spec Normal file
View File

@ -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')

View File

@ -10,7 +10,7 @@ import sys
# Import the main GUI class from the gui subpackage # 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__": if __name__ == "__main__":
root = tk.Tk() root = tk.Tk()

View File

@ -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}]"

View File

@ -1,3 +1,4 @@
import tkinter as tk import tkinter as tk
from tkinter import scrolledtext, filedialog, messagebox, ttk from tkinter import scrolledtext, filedialog, messagebox, ttk
import subprocess import subprocess
@ -9,7 +10,7 @@ import datetime
import sys # Needed for sys.platform in open_gui_log_file import sys # Needed for sys.platform in open_gui_log_file
# Import the runner function from the core module # 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 --- # --- Constants ---
DEFAULT_GUI_LOG_FILE = "gui_execution_log.txt" DEFAULT_GUI_LOG_FILE = "gui_execution_log.txt"
@ -22,6 +23,28 @@ DEFAULT_PROFILE_FILE = "default_launch_profile.json"
BIN_FILE_EXTENSION = ".rec" BIN_FILE_EXTENSION = ".rec"
BIN_FILETYPES = [("REC files", f"*{BIN_FILE_EXTENSION}"), ("All files", "*.*")] 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: class CppConverterGUI:
""" """
@ -37,7 +60,7 @@ class CppConverterGUI:
master_window: The main Tkinter window. master_window: The main Tkinter window.
""" """
self.master_window = master_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 # Adjusted initial window size - Further reduced height for more compact layout
self.master_window.geometry("950x650") # Adjusted height, might need tweaking based on content 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 # Ensure generated paths are updated after potential profile load
self._update_generated_file_paths() 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): def _load_default_profile(self):
"""Attempts to load the default profile file on application startup.""" """Attempts to load the default profile file on application startup."""
@ -507,6 +535,50 @@ class CppConverterGUI:
self._generated_log_file_path = "" self._generated_log_file_path = ""
self.g_reconvert_log_file_var.set("Generated log file path will appear here.") 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): def _select_bin_file(self):
"""Open a dialog to select the source binary file (binfile), filtered for .rec.""" """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): def _setup_control_buttons(self, parent_frame):
"""Create and arrange control buttons within a frame using grid.""" """Create and arrange control buttons within a frame using grid."""
# Use grid for centering the button within the frame # Configure grid for two columns to place buttons side-by-side
parent_frame.columnconfigure(0, weight=1) # Make the column expandable to center 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 = 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): def _setup_output_area(self, parent_frame):
@ -736,6 +819,7 @@ class CppConverterGUI:
self._update_status_bar(f"Running g_reconvert.exe...") self._update_status_bar(f"Running g_reconvert.exe...")
self.run_button.config(state=tk.DISABLED, bg="#cccccc") # Disable button while running 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 self.process_running = True
# Run the core runner function in a separate thread # Run the core runner function in a separate thread
@ -750,7 +834,7 @@ class CppConverterGUI:
# Start polling the output queue # Start polling the output queue
# Check queue every 100 milliseconds (adjust as needed) # 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): def _process_output_queue(self):
@ -758,8 +842,13 @@ class CppConverterGUI:
Checks the output queue for messages from the core runner thread Checks the output queue for messages from the core runner thread
and updates the GUI. This runs in the main GUI 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: 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 # Use get_nowait() to avoid blocking the GUI thread
item = self.output_queue.get_nowait() 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.run_button.config(state=tk.NORMAL, bg="#4CAF50") # Re-enable button
self.process_running = False self.process_running = False
self._update_status_bar("g_reconvert.exe finished. Ready.") 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 return # Stop polling the `after` loop
else: # It's a message dictionary {'text': ..., 'type': ...} 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 # 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 # For now, log everything coming from the runner thread
self._log_message_to_gui_log(item['text'].strip(), include_timestamp=False) # Log to file self._log_message_to_gui_log(item['text'].strip(), include_timestamp=False) # Log to file
messages_processed += 1 # Increment counter
except queue.Empty: except queue.Empty:
# No new messages in queue, just continue polling if process is still running # No new messages in queue, just continue polling if process is still running
pass pass
# If process is still marked as running, schedule the next check # 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: 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'): def _insert_output_text(self, message, tag='INFO'):