fix edit dialogs and run sequence, add version
This commit is contained in:
parent
e32fe63dd4
commit
326baf3c03
BIN
launchertool.ico
BIN
launchertool.ico
Binary file not shown.
|
Before Width: | Height: | Size: 0 B After Width: | Height: | Size: 46 KiB |
90
launchertool/_version.py
Normal file
90
launchertool/_version.py
Normal 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__ = "0339a04-dirty"
|
||||||
|
GIT_COMMIT_HASH = "0339a048f09f221e17d3d368e5efd52048ef44c7"
|
||||||
|
GIT_BRANCH = "master"
|
||||||
|
BUILD_TIMESTAMP = "2025-05-08T08:43:30Z"
|
||||||
|
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}]"
|
||||||
|
|
||||||
|
|
||||||
@ -248,9 +248,10 @@ class ExecutionHandler:
|
|||||||
|
|
||||||
self._log_and_output(f"Waiting {wait_time_seconds} seconds before next step...")
|
self._log_and_output(f"Waiting {wait_time_seconds} seconds before next step...")
|
||||||
|
|
||||||
|
delay_ms = int(wait_time_seconds * 1000)
|
||||||
# Schedule the next step
|
# Schedule the next step
|
||||||
self.step_delay_callback(
|
self.step_delay_callback(
|
||||||
wait_time_seconds * 1000, # Convert to milliseconds for Tkinter's 'after'
|
delay_ms,
|
||||||
self._execute_step,
|
self._execute_step,
|
||||||
steps,
|
steps,
|
||||||
step_index + 1
|
step_index + 1
|
||||||
|
|||||||
@ -5,12 +5,12 @@ Dialogs for adding and editing applications.
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk, messagebox, filedialog
|
from tkinter import ttk, messagebox, filedialog
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional, List, Dict, Any
|
from typing import Optional, List, Dict, Any, Callable # Aggiunto Callable
|
||||||
|
|
||||||
from launchertool.core.config_manager import ConfigManager
|
from ...core.config_manager import ConfigManager
|
||||||
from launchertool.core.exceptions import DuplicateNameError, NameNotFoundError, ConfigError
|
from ...core.exceptions import DuplicateNameError, NameNotFoundError, ConfigError, ApplicationNotFoundError
|
||||||
from launchertool.gui.utils_gui import GuiUtils
|
from ..utils_gui import GuiUtils
|
||||||
from launchertool.gui.dialogs.parameter_dialog import AddParameterDialog, EditParameterDialog
|
from ...gui.dialogs.parameter_dialog import AddParameterDialog, EditParameterDialog
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -19,28 +19,45 @@ class BaseApplicationDialog(tk.Toplevel):
|
|||||||
Base class for Add and Edit Application dialogs.
|
Base class for Add and Edit Application dialogs.
|
||||||
Provides common widgets and functionality.
|
Provides common widgets and functionality.
|
||||||
"""
|
"""
|
||||||
def __init__(self, parent: tk.Widget, config_manager: ConfigManager, title: str):
|
def __init__(self, parent: tk.Widget, config_manager: ConfigManager, title: str, load_data_method: Optional[Callable[[], None]] = None):
|
||||||
|
logger.debug(f"BaseApplicationDialog __init__ - START - Title: '{title}'")
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.transient(parent)
|
self.transient(parent)
|
||||||
self.title(title)
|
self.title(title)
|
||||||
self.parent_widget = parent # Explicitly store parent for messagebox context
|
self.parent_widget = parent
|
||||||
self.config_manager = config_manager
|
self.config_manager = config_manager
|
||||||
self.result: Optional[Dict[str, Any]] = None
|
self.result: Optional[Dict[str, Any]] = None
|
||||||
self.original_app_name: Optional[str] = None
|
# self.original_app_name è definito e gestito dalle sottoclassi prima di chiamare questo costruttore base,
|
||||||
|
# se necessario per load_data_method.
|
||||||
|
|
||||||
|
logger.debug("BaseApplicationDialog: Calling _setup_widgets()")
|
||||||
|
self._setup_widgets() # Crea tutti i widget
|
||||||
|
logger.debug("BaseApplicationDialog: _setup_widgets() completed.")
|
||||||
|
|
||||||
|
# Carica i dati specifici della sottoclasse (es. per EditDialog) PRIMA di rendere modale e attendere
|
||||||
|
if load_data_method:
|
||||||
|
logger.debug("BaseApplicationDialog: Calling provided load_data_method.")
|
||||||
|
load_data_method()
|
||||||
|
logger.debug("BaseApplicationDialog: load_data_method completed.")
|
||||||
|
else:
|
||||||
|
logger.debug("BaseApplicationDialog: No load_data_method provided or needed.")
|
||||||
|
|
||||||
self._setup_widgets()
|
|
||||||
|
|
||||||
GuiUtils.center_window(self, parent)
|
GuiUtils.center_window(self, parent)
|
||||||
self.protocol("WM_DELETE_WINDOW", self._on_cancel)
|
self.protocol("WM_DELETE_WINDOW", self._on_cancel)
|
||||||
self.grab_set()
|
|
||||||
self.focus_set()
|
self.grab_set() # Rendi modale
|
||||||
self.wait_window(self)
|
self.focus_set() # Dai focus
|
||||||
|
logger.debug(f"BaseApplicationDialog __init__ for '{title}': Now calling wait_window().")
|
||||||
|
self.wait_window(self) # Blocca qui finché il dialogo non viene distrutto
|
||||||
|
logger.debug(f"BaseApplicationDialog __init__ - END - Title: '{title}' (after wait_window)")
|
||||||
|
|
||||||
|
|
||||||
def _setup_widgets(self):
|
def _setup_widgets(self):
|
||||||
main_frame = ttk.Frame(self, padding="10")
|
main_frame = ttk.Frame(self, padding="10")
|
||||||
main_frame.pack(expand=True, fill=tk.BOTH)
|
main_frame.pack(expand=True, fill=tk.BOTH)
|
||||||
|
|
||||||
|
# --- Application Name ---
|
||||||
name_frame = ttk.Frame(main_frame)
|
name_frame = ttk.Frame(main_frame)
|
||||||
name_frame.pack(fill=tk.X, pady=(0, 5))
|
name_frame.pack(fill=tk.X, pady=(0, 5))
|
||||||
ttk.Label(name_frame, text="Application Name:").pack(side=tk.LEFT, padx=(0, 5))
|
ttk.Label(name_frame, text="Application Name:").pack(side=tk.LEFT, padx=(0, 5))
|
||||||
@ -48,6 +65,7 @@ class BaseApplicationDialog(tk.Toplevel):
|
|||||||
self.name_entry = ttk.Entry(name_frame, textvariable=self.name_entry_var, width=50)
|
self.name_entry = ttk.Entry(name_frame, textvariable=self.name_entry_var, width=50)
|
||||||
self.name_entry.pack(side=tk.LEFT, expand=True, fill=tk.X)
|
self.name_entry.pack(side=tk.LEFT, expand=True, fill=tk.X)
|
||||||
|
|
||||||
|
# --- Application Path ---
|
||||||
path_frame = ttk.Frame(main_frame)
|
path_frame = ttk.Frame(main_frame)
|
||||||
path_frame.pack(fill=tk.X, pady=(0, 10))
|
path_frame.pack(fill=tk.X, pady=(0, 10))
|
||||||
ttk.Label(path_frame, text="Application Path:").pack(side=tk.LEFT, padx=(0, 5))
|
ttk.Label(path_frame, text="Application Path:").pack(side=tk.LEFT, padx=(0, 5))
|
||||||
@ -57,10 +75,11 @@ class BaseApplicationDialog(tk.Toplevel):
|
|||||||
self.browse_button = ttk.Button(path_frame, text="Browse...", command=self._browse_file)
|
self.browse_button = ttk.Button(path_frame, text="Browse...", command=self._browse_file)
|
||||||
self.browse_button.pack(side=tk.LEFT)
|
self.browse_button.pack(side=tk.LEFT)
|
||||||
|
|
||||||
|
# --- Parameters ---
|
||||||
params_labelframe = ttk.LabelFrame(main_frame, text="Parameters")
|
params_labelframe = ttk.LabelFrame(main_frame, text="Parameters")
|
||||||
params_labelframe.pack(expand=True, fill=tk.BOTH, pady=(0, 10))
|
params_labelframe.pack(expand=True, fill=tk.BOTH, pady=(0, 10))
|
||||||
|
|
||||||
params_tree_frame = ttk.Frame(params_labelframe)
|
params_tree_frame = ttk.Frame(params_labelframe) # Frame to hold tree and scrollbar
|
||||||
params_tree_frame.pack(expand=True, fill=tk.BOTH, padx=5, pady=5)
|
params_tree_frame.pack(expand=True, fill=tk.BOTH, padx=5, pady=5)
|
||||||
|
|
||||||
param_scrollbar_y = ttk.Scrollbar(params_tree_frame, orient=tk.VERTICAL)
|
param_scrollbar_y = ttk.Scrollbar(params_tree_frame, orient=tk.VERTICAL)
|
||||||
@ -102,11 +121,12 @@ class BaseApplicationDialog(tk.Toplevel):
|
|||||||
self.delete_param_button.pack(side=tk.LEFT)
|
self.delete_param_button.pack(side=tk.LEFT)
|
||||||
|
|
||||||
self.params_tree.bind("<<TreeviewSelect>>", self._on_parameter_select)
|
self.params_tree.bind("<<TreeviewSelect>>", self._on_parameter_select)
|
||||||
self._on_parameter_select()
|
self._on_parameter_select() # Init button states
|
||||||
|
|
||||||
|
# --- Dialog Buttons (Save, Cancel) ---
|
||||||
dialog_buttons_frame = ttk.Frame(main_frame)
|
dialog_buttons_frame = ttk.Frame(main_frame)
|
||||||
dialog_buttons_frame.pack(fill=tk.X, pady=(10, 0))
|
dialog_buttons_frame.pack(fill=tk.X, pady=(10, 0))
|
||||||
ttk.Frame(dialog_buttons_frame).pack(side=tk.LEFT, expand=True)
|
ttk.Frame(dialog_buttons_frame).pack(side=tk.LEFT, expand=True) # Spacer
|
||||||
|
|
||||||
self.save_button = ttk.Button(dialog_buttons_frame, text="Save", command=self._on_save)
|
self.save_button = ttk.Button(dialog_buttons_frame, text="Save", command=self._on_save)
|
||||||
self.save_button.pack(side=tk.LEFT, padx=(0,5))
|
self.save_button.pack(side=tk.LEFT, padx=(0,5))
|
||||||
@ -117,7 +137,7 @@ class BaseApplicationDialog(tk.Toplevel):
|
|||||||
filepath = filedialog.askopenfilename(
|
filepath = filedialog.askopenfilename(
|
||||||
title="Select Application Executable",
|
title="Select Application Executable",
|
||||||
filetypes=(("Executable files", "*.exe"), ("All files", "*.*")),
|
filetypes=(("Executable files", "*.exe"), ("All files", "*.*")),
|
||||||
parent=self # Ensure dialog is modal to this Toplevel
|
parent=self
|
||||||
)
|
)
|
||||||
if filepath:
|
if filepath:
|
||||||
self.path_entry_var.set(filepath)
|
self.path_entry_var.set(filepath)
|
||||||
@ -128,7 +148,7 @@ class BaseApplicationDialog(tk.Toplevel):
|
|||||||
if app_name_suggestion:
|
if app_name_suggestion:
|
||||||
self.name_entry_var.set(app_name_suggestion)
|
self.name_entry_var.set(app_name_suggestion)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass # Ignore errors in suggestion
|
||||||
|
|
||||||
def _get_parameters_from_tree(self) -> List[Dict[str, str]]:
|
def _get_parameters_from_tree(self) -> List[Dict[str, str]]:
|
||||||
parameters = []
|
parameters = []
|
||||||
@ -145,24 +165,29 @@ class BaseApplicationDialog(tk.Toplevel):
|
|||||||
def _populate_parameters_tree(self, parameters: List[Dict[str, str]]):
|
def _populate_parameters_tree(self, parameters: List[Dict[str, str]]):
|
||||||
for item in self.params_tree.get_children():
|
for item in self.params_tree.get_children():
|
||||||
self.params_tree.delete(item)
|
self.params_tree.delete(item)
|
||||||
|
|
||||||
|
logger.debug(f"BaseApplicationDialog: Populating parameters tree with {len(parameters)} parameters.")
|
||||||
|
|
||||||
for i, param in enumerate(parameters):
|
for i, param in enumerate(parameters):
|
||||||
# Use param name for iid if unique, otherwise generate one
|
|
||||||
param_name = param.get('name','')
|
param_name = param.get('name','')
|
||||||
iid = param_name if param_name else f"param_gen_{i}"
|
description = param.get('description','')
|
||||||
# Ensure iid is unique if names can be non-unique (though they shouldn't)
|
default_value = param.get('default_value','')
|
||||||
|
param_type = param.get('type','string')
|
||||||
|
|
||||||
|
iid_base = param_name if param_name and param_name.strip() else f"param_gen_{i}" # Ensure non-empty iid_base
|
||||||
|
|
||||||
|
final_iid = iid_base
|
||||||
counter = 0
|
counter = 0
|
||||||
final_iid = iid
|
# Ensure iid is unique in the tree
|
||||||
while self.params_tree.exists(final_iid):
|
while self.params_tree.exists(final_iid):
|
||||||
counter += 1
|
counter += 1
|
||||||
final_iid = f"{iid}_{counter}"
|
final_iid = f"{iid_base}_{counter}"
|
||||||
|
|
||||||
|
logger.debug(f"BaseApplicationDialog: Inserting parameter: iid='{final_iid}', values=('{param_name}', '{description}', '{default_value}', '{param_type}')")
|
||||||
self.params_tree.insert(
|
self.params_tree.insert(
|
||||||
'', tk.END,
|
'', tk.END,
|
||||||
iid=final_iid,
|
iid=final_iid,
|
||||||
values=(param_name,
|
values=(param_name, description, default_value, param_type)
|
||||||
param.get('description',''),
|
|
||||||
param.get('default_value',''),
|
|
||||||
param.get('type','string'))
|
|
||||||
)
|
)
|
||||||
self._on_parameter_select()
|
self._on_parameter_select()
|
||||||
|
|
||||||
@ -173,11 +198,11 @@ class BaseApplicationDialog(tk.Toplevel):
|
|||||||
self.delete_param_button.config(state=state)
|
self.delete_param_button.config(state=state)
|
||||||
|
|
||||||
def _add_parameter(self):
|
def _add_parameter(self):
|
||||||
logger.debug("Add Parameter button clicked.")
|
logger.debug("BaseApplicationDialog: Add Parameter button clicked.")
|
||||||
dialog = AddParameterDialog(self)
|
dialog = AddParameterDialog(self) # 'self' (BaseApplicationDialog) is the parent
|
||||||
if dialog.result:
|
if dialog.result:
|
||||||
# Check for duplicate parameter name before adding to tree
|
|
||||||
new_param_name = dialog.result['name']
|
new_param_name = dialog.result['name']
|
||||||
|
# Check for duplicate parameter name before adding to tree
|
||||||
for item_id in self.params_tree.get_children():
|
for item_id in self.params_tree.get_children():
|
||||||
if self.params_tree.item(item_id, 'values')[0] == new_param_name:
|
if self.params_tree.item(item_id, 'values')[0] == new_param_name:
|
||||||
messagebox.showerror("Duplicate Parameter",
|
messagebox.showerror("Duplicate Parameter",
|
||||||
@ -185,14 +210,14 @@ class BaseApplicationDialog(tk.Toplevel):
|
|||||||
parent=self)
|
parent=self)
|
||||||
return
|
return
|
||||||
|
|
||||||
param_iid = new_param_name # Use name as iid, assuming unique
|
# Use the (now validated unique) new_param_name as iid
|
||||||
self.params_tree.insert('', tk.END, iid=param_iid, values=(
|
self.params_tree.insert('', tk.END, iid=new_param_name, values=(
|
||||||
new_param_name,
|
new_param_name,
|
||||||
dialog.result['description'],
|
dialog.result['description'],
|
||||||
dialog.result['default_value'],
|
dialog.result['default_value'],
|
||||||
dialog.result['type']
|
dialog.result['type']
|
||||||
))
|
))
|
||||||
logger.info(f"Parameter '{new_param_name}' added to application dialog tree.")
|
logger.info(f"BaseApplicationDialog: Parameter '{new_param_name}' added to this dialog's parameter tree.")
|
||||||
self._on_parameter_select()
|
self._on_parameter_select()
|
||||||
|
|
||||||
def _edit_parameter(self):
|
def _edit_parameter(self):
|
||||||
@ -201,7 +226,7 @@ class BaseApplicationDialog(tk.Toplevel):
|
|||||||
messagebox.showwarning("Edit Parameter", "Please select a parameter to edit.", parent=self)
|
messagebox.showwarning("Edit Parameter", "Please select a parameter to edit.", parent=self)
|
||||||
return
|
return
|
||||||
|
|
||||||
selected_iid = selected_item_ids[0]
|
selected_iid = selected_item_ids[0] # This is the iid of the selected item
|
||||||
item_values = self.params_tree.item(selected_iid, 'values')
|
item_values = self.params_tree.item(selected_iid, 'values')
|
||||||
|
|
||||||
param_data_to_edit = {
|
param_data_to_edit = {
|
||||||
@ -210,39 +235,45 @@ class BaseApplicationDialog(tk.Toplevel):
|
|||||||
"default_value": item_values[2],
|
"default_value": item_values[2],
|
||||||
"type": item_values[3]
|
"type": item_values[3]
|
||||||
}
|
}
|
||||||
original_param_name = param_data_to_edit["name"]
|
original_param_name = param_data_to_edit["name"] # This is the name before editing
|
||||||
logger.debug(f"Edit Parameter button clicked for: {original_param_name}")
|
logger.debug(f"BaseApplicationDialog: Edit Parameter button clicked for: {original_param_name} (iid: {selected_iid})")
|
||||||
|
|
||||||
dialog = EditParameterDialog(self, param_data_to_edit)
|
dialog = EditParameterDialog(self, param_data_to_edit) # 'self' is the parent
|
||||||
if dialog.result:
|
if dialog.result:
|
||||||
new_param_name = dialog.result['name']
|
new_param_name = dialog.result['name']
|
||||||
# Check for duplicate name if name changed
|
|
||||||
|
# Check for duplicate name if the name was changed
|
||||||
if new_param_name != original_param_name:
|
if new_param_name != original_param_name:
|
||||||
for item_id in self.params_tree.get_children():
|
for item_id in self.params_tree.get_children():
|
||||||
|
# Don't compare the item with itself (if its iid hasn't changed yet)
|
||||||
|
# Or, more robustly, check all OTHER items
|
||||||
if item_id != selected_iid and self.params_tree.item(item_id, 'values')[0] == new_param_name:
|
if item_id != selected_iid and self.params_tree.item(item_id, 'values')[0] == new_param_name:
|
||||||
messagebox.showerror("Duplicate Parameter",
|
messagebox.showerror("Duplicate Parameter",
|
||||||
f"A parameter with the name '{new_param_name}' already exists.",
|
f"Another parameter with the name '{new_param_name}' already exists.",
|
||||||
parent=self)
|
parent=self)
|
||||||
return
|
return
|
||||||
|
|
||||||
# If name changed, we need to delete old and insert new, or re-tag. Simpler to update values & iid.
|
new_values = (
|
||||||
self.params_tree.item(selected_iid, values=(
|
|
||||||
new_param_name,
|
new_param_name,
|
||||||
dialog.result['description'],
|
dialog.result['description'],
|
||||||
dialog.result['default_value'],
|
dialog.result['default_value'],
|
||||||
dialog.result['type']
|
dialog.result['type']
|
||||||
))
|
)
|
||||||
# If iid was based on name, and name changes, treeview might get confused.
|
|
||||||
# It's safer if iid is independent or handled carefully on name change.
|
# If name changed AND iid was the original name, we need to re-insert with new iid
|
||||||
# For now, assume iid can be the new name IF it's unique.
|
if new_param_name != original_param_name and selected_iid == original_param_name:
|
||||||
if new_param_name != original_param_name:
|
logger.debug(f"Parameter name changed from '{original_param_name}' to '{new_param_name}'. Re-inserting in tree.")
|
||||||
# Detach and re-insert with new iid if name (used as iid) changed
|
self.params_tree.delete(selected_iid)
|
||||||
values = self.params_tree.item(selected_iid, 'values')
|
self.params_tree.insert('', tk.END, iid=new_param_name, values=new_values)
|
||||||
self.params_tree.delete(selected_iid)
|
self.params_tree.selection_set(new_param_name) # Reselect the (new) item
|
||||||
self.params_tree.insert('', tk.END, iid=new_param_name, values=values)
|
else:
|
||||||
self.params_tree.selection_set(new_param_name) # Reselect
|
# Name didn't change, or iid was not the name, so just update values for the current iid
|
||||||
|
self.params_tree.item(selected_iid, values=new_values)
|
||||||
|
logger.debug(f"Parameter values updated for iid '{selected_iid}'. New name: '{new_param_name}'.")
|
||||||
|
|
||||||
|
logger.info(f"BaseApplicationDialog: Parameter '{new_param_name}' (originally '{original_param_name}') updated.")
|
||||||
|
self._on_parameter_select() # Refresh button states
|
||||||
|
|
||||||
logger.info(f"Parameter '{new_param_name}' updated in application dialog tree.")
|
|
||||||
|
|
||||||
def _delete_parameter(self):
|
def _delete_parameter(self):
|
||||||
selected_item_ids = self.params_tree.selection()
|
selected_item_ids = self.params_tree.selection()
|
||||||
@ -255,7 +286,7 @@ class BaseApplicationDialog(tk.Toplevel):
|
|||||||
f"Are you sure you want to delete parameter '{param_name}'?",
|
f"Are you sure you want to delete parameter '{param_name}'?",
|
||||||
parent=self):
|
parent=self):
|
||||||
self.params_tree.delete(selected_item_ids[0])
|
self.params_tree.delete(selected_item_ids[0])
|
||||||
logger.debug(f"Parameter '{param_name}' deleted from tree.")
|
logger.debug(f"BaseApplicationDialog: Parameter '{param_name}' deleted from this dialog's tree.")
|
||||||
self._on_parameter_select()
|
self._on_parameter_select()
|
||||||
|
|
||||||
|
|
||||||
@ -274,18 +305,23 @@ class BaseApplicationDialog(tk.Toplevel):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def _on_save(self):
|
def _on_save(self):
|
||||||
|
# This method should be implemented by subclasses (AddApplicationDialog, EditApplicationDialog)
|
||||||
raise NotImplementedError("Subclasses must implement _on_save")
|
raise NotImplementedError("Subclasses must implement _on_save")
|
||||||
|
|
||||||
def _on_cancel(self):
|
def _on_cancel(self):
|
||||||
logger.debug(f"{self.title()} cancelled or closed.")
|
logger.debug(f"BaseApplicationDialog: '{self.title()}' cancelled or closed by user.")
|
||||||
self.result = None
|
self.result = None # Explicitly set result to None for cancellation
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
|
|
||||||
class AddApplicationDialog(BaseApplicationDialog):
|
class AddApplicationDialog(BaseApplicationDialog):
|
||||||
"""Dialog for adding a new application."""
|
"""Dialog for adding a new application."""
|
||||||
def __init__(self, parent: tk.Widget, config_manager: ConfigManager):
|
def __init__(self, parent: tk.Widget, config_manager: ConfigManager):
|
||||||
super().__init__(parent, config_manager, title="Add New Application")
|
# AddApplicationDialog does not need to load initial data from an existing app,
|
||||||
|
# so load_data_method is None.
|
||||||
|
super().__init__(parent, config_manager, title="Add New Application", load_data_method=None)
|
||||||
|
logger.debug("AddApplicationDialog __init__ completed.")
|
||||||
|
|
||||||
|
|
||||||
def _on_save(self):
|
def _on_save(self):
|
||||||
if not self._validate_inputs():
|
if not self._validate_inputs():
|
||||||
@ -293,7 +329,7 @@ class AddApplicationDialog(BaseApplicationDialog):
|
|||||||
|
|
||||||
app_name = self.name_entry_var.get().strip()
|
app_name = self.name_entry_var.get().strip()
|
||||||
app_path = self.path_entry_var.get().strip()
|
app_path = self.path_entry_var.get().strip()
|
||||||
parameters = self._get_parameters_from_tree()
|
parameters = self._get_parameters_from_tree() # Gets params currently in this dialog's tree
|
||||||
|
|
||||||
application_data = {
|
application_data = {
|
||||||
"name": app_name,
|
"name": app_name,
|
||||||
@ -303,52 +339,99 @@ class AddApplicationDialog(BaseApplicationDialog):
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
self.config_manager.add_application(application_data)
|
self.config_manager.add_application(application_data)
|
||||||
self.result = application_data
|
self.result = application_data # Set result upon successful save to ConfigManager
|
||||||
logger.info(f"Application '{app_name}' added successfully through dialog.")
|
logger.info(f"AddApplicationDialog: Application '{app_name}' added successfully to config.")
|
||||||
# Message shown by MainWindow after successful dialog.result
|
self.destroy() # Close dialog
|
||||||
# messagebox.showinfo("Success", f"Application '{app_name}' added successfully.", parent=self.parent_widget)
|
|
||||||
self.destroy()
|
|
||||||
except DuplicateNameError as e:
|
except DuplicateNameError as e:
|
||||||
logger.warning(f"Failed to add application: {e}")
|
logger.warning(f"AddApplicationDialog: Failed to add application due to duplicate name: {e}")
|
||||||
messagebox.showerror("Error Adding Application", str(e), parent=self)
|
messagebox.showerror("Error Adding Application", str(e), parent=self)
|
||||||
self.name_entry.focus_set()
|
self.name_entry.focus_set()
|
||||||
except ConfigError as e:
|
except ConfigError as e: # Catch other config related errors (e.g., save failed)
|
||||||
logger.error(f"Configuration error adding application '{app_name}': {e}", exc_info=True)
|
logger.error(f"AddApplicationDialog: Configuration error adding application '{app_name}': {e}", exc_info=True)
|
||||||
messagebox.showerror("Configuration Error", f"Could not save application:\n{e}", parent=self)
|
messagebox.showerror("Configuration Error", f"Could not save application:\n{e}", parent=self)
|
||||||
except Exception as e:
|
except Exception as e: # Catch any other unexpected errors
|
||||||
logger.error(f"Unexpected error adding application '{app_name}': {e}", exc_info=True)
|
logger.error(f"AddApplicationDialog: Unexpected error adding application '{app_name}': {e}", exc_info=True)
|
||||||
messagebox.showerror("Unexpected Error", f"An unexpected error occurred:\n{e}", parent=self)
|
messagebox.showerror("Unexpected Error", f"An unexpected error occurred:\n{e}", parent=self)
|
||||||
|
|
||||||
|
|
||||||
class EditApplicationDialog(BaseApplicationDialog):
|
class EditApplicationDialog(BaseApplicationDialog):
|
||||||
"""Dialog for editing an existing application."""
|
"""Dialog for editing an existing application."""
|
||||||
def __init__(self, parent: tk.Widget, config_manager: ConfigManager, application_name_to_edit: str):
|
def __init__(self, parent: tk.Widget, config_manager: ConfigManager, application_name_to_edit: str):
|
||||||
self.original_app_name = application_name_to_edit
|
logger.debug(f"EditApplicationDialog __init__ - START - App to edit: '{application_name_to_edit}'")
|
||||||
super().__init__(parent, config_manager, title=f"Edit Application: {application_name_to_edit}")
|
self.original_app_name = application_name_to_edit # Must be set BEFORE super().__init__ if load_data_method uses it
|
||||||
self._load_initial_data()
|
|
||||||
|
# Pass its own _load_initial_data method to the base class constructor
|
||||||
|
super().__init__(parent, config_manager, title=f"Edit Application: {self.original_app_name}", load_data_method=self._load_initial_data)
|
||||||
|
|
||||||
|
# Code here (after super call) executes only after the dialog (and wait_window in base) has finished.
|
||||||
|
# Usually, nothing more is needed here as 'result' is checked by the caller (MainWindow).
|
||||||
|
logger.debug(f"EditApplicationDialog __init__ - END - for '{self.original_app_name}' (after super call has completed)")
|
||||||
|
|
||||||
|
|
||||||
def _load_initial_data(self):
|
def _load_initial_data(self):
|
||||||
|
# This method is now called by BaseApplicationDialog's __init__ via load_data_method
|
||||||
|
if not self.original_app_name:
|
||||||
|
# This case should ideally be caught before even calling super() in __init__
|
||||||
|
# or by BaseApplicationDialog if load_data_method is None when it shouldn't be.
|
||||||
|
logger.error("EditApplicationDialog _load_initial_data: original_app_name is None or empty. Dialog should be closing.")
|
||||||
|
# If parent_widget is None (though it shouldn't be), messagebox might fail.
|
||||||
|
if self.parent_widget:
|
||||||
|
messagebox.showerror("Initialization Error",
|
||||||
|
"Cannot edit: No application name specified for editing.",
|
||||||
|
parent=self.parent_widget)
|
||||||
|
self.after_idle(self.destroy) # Ensure dialog closes
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.debug(f"EditApplicationDialog _load_initial_data: Attempting to load for: '{self.original_app_name}'")
|
||||||
try:
|
try:
|
||||||
app_data = self.config_manager.get_application_by_name(self.original_app_name)
|
app_data = self.config_manager.get_application_by_name(self.original_app_name)
|
||||||
|
logger.debug(f"EditApplicationDialog _load_initial_data: Data from ConfigManager for '{self.original_app_name}': {app_data}")
|
||||||
|
|
||||||
|
if not app_data: # Should be caught by NameNotFoundError, but as a safeguard
|
||||||
|
logger.error(f"EditApplicationDialog _load_initial_data: ConfigManager returned None (or empty) for '{self.original_app_name}'.")
|
||||||
|
messagebox.showerror("Load Error",
|
||||||
|
f"Could not retrieve valid data for application '{self.original_app_name}'.",
|
||||||
|
parent=self.parent_widget)
|
||||||
|
self.after_idle(self.destroy)
|
||||||
|
return
|
||||||
|
|
||||||
|
# Populate the dialog's widgets with the loaded data
|
||||||
self.name_entry_var.set(app_data.get("name", ""))
|
self.name_entry_var.set(app_data.get("name", ""))
|
||||||
self.path_entry_var.set(app_data.get("path", ""))
|
self.path_entry_var.set(app_data.get("path", ""))
|
||||||
self._populate_parameters_tree(app_data.get("parameters", []))
|
logger.debug(f"EditApplicationDialog _load_initial_data: Name set to '{self.name_entry_var.get()}', Path to '{self.path_entry_var.get()}'")
|
||||||
|
|
||||||
|
parameters_data = app_data.get("parameters", [])
|
||||||
|
logger.debug(f"EditApplicationDialog _load_initial_data: Parameters to populate: {parameters_data}")
|
||||||
|
self._populate_parameters_tree(parameters_data) # Call base method to fill param tree
|
||||||
|
|
||||||
|
logger.info(f"EditApplicationDialog _load_initial_data: Successfully loaded data for '{self.original_app_name}'.")
|
||||||
|
|
||||||
except NameNotFoundError:
|
except NameNotFoundError:
|
||||||
logger.error(f"Cannot edit application. Name '{self.original_app_name}' not found.")
|
logger.error(f"EditApplicationDialog _load_initial_data: App '{self.original_app_name}' not found (NameNotFoundError).")
|
||||||
messagebox.showerror("Error", f"Application '{self.original_app_name}' not found. Cannot edit.", parent=self.parent_widget)
|
messagebox.showerror("Load Error",
|
||||||
self.destroy()
|
f"Application '{self.original_app_name}' not found. Cannot edit.",
|
||||||
|
parent=self.parent_widget) # parent_widget is MainWindow
|
||||||
|
self.after_idle(self.destroy)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error loading application data for edit: {e}", exc_info=True)
|
logger.error(f"EditApplicationDialog _load_initial_data: Unexpected error for '{self.original_app_name}': {e}", exc_info=True)
|
||||||
messagebox.showerror("Error", f"Could not load application data for editing:\n{e}", parent=self.parent_widget)
|
messagebox.showerror("Load Error",
|
||||||
self.destroy()
|
f"An unexpected error occurred while loading data for '{self.original_app_name}':\n{e}",
|
||||||
|
parent=self.parent_widget)
|
||||||
|
self.after_idle(self.destroy)
|
||||||
|
|
||||||
def _on_save(self):
|
def _on_save(self):
|
||||||
if not self._validate_inputs():
|
# Ensure original_app_name is valid; if _load_initial_data failed, it might not be safe to save.
|
||||||
|
if not self.original_app_name:
|
||||||
|
messagebox.showerror("Save Error", "Cannot save: Critical information about the original application is missing.", parent=self)
|
||||||
|
logger.error("EditApplicationDialog _on_save: Attempted to save without a valid original_app_name.")
|
||||||
|
return
|
||||||
|
|
||||||
|
if not self._validate_inputs(): # Validates current entries in the dialog
|
||||||
return
|
return
|
||||||
|
|
||||||
new_app_name = self.name_entry_var.get().strip()
|
new_app_name = self.name_entry_var.get().strip()
|
||||||
new_app_path = self.path_entry_var.get().strip()
|
new_app_path = self.path_entry_var.get().strip()
|
||||||
new_parameters = self._get_parameters_from_tree()
|
new_parameters = self._get_parameters_from_tree() # Gets params currently in this dialog's tree
|
||||||
|
|
||||||
updated_application_data = {
|
updated_application_data = {
|
||||||
"name": new_app_name,
|
"name": new_app_name,
|
||||||
@ -357,21 +440,21 @@ class EditApplicationDialog(BaseApplicationDialog):
|
|||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# Use self.original_app_name to identify which app to update in ConfigManager
|
||||||
self.config_manager.update_application(self.original_app_name, updated_application_data)
|
self.config_manager.update_application(self.original_app_name, updated_application_data)
|
||||||
self.result = updated_application_data
|
self.result = updated_application_data # Set result to the successfully updated data
|
||||||
logger.info(f"Application '{self.original_app_name}' updated to '{new_app_name}' successfully through dialog.")
|
logger.info(f"EditApplicationDialog: Application '{self.original_app_name}' updated to '{new_app_name}' in config.")
|
||||||
# messagebox.showinfo("Success", f"Application '{new_app_name}' updated successfully.", parent=self.parent_widget)
|
self.destroy() # Close dialog
|
||||||
self.destroy()
|
except NameNotFoundError as e: # If original_app_name is somehow no longer in config
|
||||||
except NameNotFoundError as e:
|
logger.error(f"EditApplicationDialog _on_save: Original app '{self.original_app_name}' no longer found: {e}", exc_info=True)
|
||||||
logger.error(f"Update failed: Original application '{self.original_app_name}' not found: {e}", exc_info=True)
|
|
||||||
messagebox.showerror("Error Updating Application", str(e), parent=self)
|
messagebox.showerror("Error Updating Application", str(e), parent=self)
|
||||||
except DuplicateNameError as e:
|
except DuplicateNameError as e: # If new_app_name conflicts with another existing app
|
||||||
logger.warning(f"Failed to update application: {e}")
|
logger.warning(f"EditApplicationDialog _on_save: Failed to update due to duplicate name: {e}")
|
||||||
messagebox.showerror("Error Updating Application", str(e), parent=self)
|
messagebox.showerror("Error Updating Application", str(e), parent=self)
|
||||||
self.name_entry.focus_set()
|
self.name_entry.focus_set() # Keep dialog open and focus on name field
|
||||||
except ConfigError as e:
|
except ConfigError as e: # Other config errors (e.g., save failed)
|
||||||
logger.error(f"Configuration error updating application '{new_app_name}': {e}", exc_info=True)
|
logger.error(f"EditApplicationDialog _on_save: Config error updating '{new_app_name}': {e}", exc_info=True)
|
||||||
messagebox.showerror("Configuration Error", f"Could not save application updates:\n{e}", parent=self)
|
messagebox.showerror("Configuration Error", f"Could not save application updates:\n{e}", parent=self)
|
||||||
except Exception as e:
|
except Exception as e: # Catch-all for other unexpected errors
|
||||||
logger.error(f"Unexpected error updating application '{new_app_name}': {e}", exc_info=True)
|
logger.error(f"EditApplicationDialog _on_save: Unexpected error updating '{new_app_name}': {e}", exc_info=True)
|
||||||
messagebox.showerror("Unexpected Error", f"An unexpected error occurred:\n{e}", parent=self)
|
messagebox.showerror("Unexpected Error", f"An unexpected error occurred:\n{e}", parent=self)
|
||||||
@ -5,7 +5,7 @@ Dialogs for adding and editing application parameters.
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk, messagebox
|
from tkinter import ttk, messagebox
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional, Dict, Any
|
from typing import Optional, Dict, Any, Callable # Aggiunto Callable
|
||||||
|
|
||||||
from ..utils_gui import GuiUtils
|
from ..utils_gui import GuiUtils
|
||||||
|
|
||||||
@ -15,7 +15,8 @@ class BaseParameterDialog(tk.Toplevel):
|
|||||||
"""
|
"""
|
||||||
Base class for Add and Edit Parameter dialogs.
|
Base class for Add and Edit Parameter dialogs.
|
||||||
"""
|
"""
|
||||||
def __init__(self, parent: tk.Widget, title: str):
|
def __init__(self, parent: tk.Widget, title: str, load_data_method: Optional[Callable[[], None]] = None): # Aggiunto load_data_method
|
||||||
|
logger.debug(f"BaseParameterDialog __init__ - START - Title: '{title}'")
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.transient(parent)
|
self.transient(parent)
|
||||||
self.title(title)
|
self.title(title)
|
||||||
@ -24,18 +25,33 @@ class BaseParameterDialog(tk.Toplevel):
|
|||||||
|
|
||||||
self._parameter_types = ["string", "integer", "boolean", "float", "file", "folder"]
|
self._parameter_types = ["string", "integer", "boolean", "float", "file", "folder"]
|
||||||
|
|
||||||
|
logger.debug("BaseParameterDialog: Calling _setup_widgets()")
|
||||||
self._setup_widgets()
|
self._setup_widgets()
|
||||||
|
logger.debug("BaseParameterDialog: _setup_widgets() completed.")
|
||||||
|
|
||||||
|
# === MODIFICA CHIAVE ===
|
||||||
|
if load_data_method:
|
||||||
|
logger.debug("BaseParameterDialog: Calling provided load_data_method.")
|
||||||
|
load_data_method()
|
||||||
|
logger.debug("BaseParameterDialog: load_data_method completed.")
|
||||||
|
else:
|
||||||
|
logger.debug("BaseParameterDialog: No load_data_method provided.")
|
||||||
|
# =======================
|
||||||
|
|
||||||
GuiUtils.center_window(self, parent)
|
GuiUtils.center_window(self, parent)
|
||||||
self.protocol("WM_DELETE_WINDOW", self._on_cancel)
|
self.protocol("WM_DELETE_WINDOW", self._on_cancel)
|
||||||
|
|
||||||
self.grab_set()
|
self.grab_set()
|
||||||
self.focus_set()
|
self.focus_set()
|
||||||
|
logger.debug(f"BaseParameterDialog __init__ for '{title}': Now calling wait_window().")
|
||||||
self.wait_window(self)
|
self.wait_window(self)
|
||||||
|
logger.debug(f"BaseParameterDialog __init__ - END - Title: '{title}' (after wait_window)")
|
||||||
|
|
||||||
def _setup_widgets(self):
|
def _setup_widgets(self):
|
||||||
main_frame = ttk.Frame(self, padding="10")
|
main_frame = ttk.Frame(self, padding="10")
|
||||||
main_frame.pack(expand=True, fill=tk.BOTH)
|
main_frame.pack(expand=True, fill=tk.BOTH)
|
||||||
|
|
||||||
|
# --- Parameter Name ---
|
||||||
name_frame = ttk.Frame(main_frame)
|
name_frame = ttk.Frame(main_frame)
|
||||||
name_frame.pack(fill=tk.X, pady=(0, 5))
|
name_frame.pack(fill=tk.X, pady=(0, 5))
|
||||||
ttk.Label(name_frame, text="Name:").grid(row=0, column=0, sticky=tk.W, padx=(0,5))
|
ttk.Label(name_frame, text="Name:").grid(row=0, column=0, sticky=tk.W, padx=(0,5))
|
||||||
@ -44,9 +60,11 @@ class BaseParameterDialog(tk.Toplevel):
|
|||||||
self.name_entry.grid(row=0, column=1, sticky=tk.EW)
|
self.name_entry.grid(row=0, column=1, sticky=tk.EW)
|
||||||
name_frame.columnconfigure(1, weight=1)
|
name_frame.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
# --- Description ---
|
||||||
desc_frame = ttk.Frame(main_frame)
|
desc_frame = ttk.Frame(main_frame)
|
||||||
desc_frame.pack(fill=tk.X, pady=(0, 5))
|
desc_frame.pack(fill=tk.X, pady=(0, 5))
|
||||||
ttk.Label(desc_frame, text="Description:").grid(row=0, column=0, sticky=tk.NW, padx=(0,5))
|
ttk.Label(desc_frame, text="Description:").grid(row=0, column=0, sticky=tk.NW, padx=(0,5))
|
||||||
|
# Assicurati che self.description_text sia creato correttamente
|
||||||
self.description_text = tk.Text(desc_frame, width=40, height=4, wrap=tk.WORD)
|
self.description_text = tk.Text(desc_frame, width=40, height=4, wrap=tk.WORD)
|
||||||
self.description_text.grid(row=0, column=1, sticky=tk.EW)
|
self.description_text.grid(row=0, column=1, sticky=tk.EW)
|
||||||
desc_scrollbar = ttk.Scrollbar(desc_frame, orient=tk.VERTICAL, command=self.description_text.yview)
|
desc_scrollbar = ttk.Scrollbar(desc_frame, orient=tk.VERTICAL, command=self.description_text.yview)
|
||||||
@ -54,6 +72,7 @@ class BaseParameterDialog(tk.Toplevel):
|
|||||||
self.description_text.config(yscrollcommand=desc_scrollbar.set)
|
self.description_text.config(yscrollcommand=desc_scrollbar.set)
|
||||||
desc_frame.columnconfigure(1, weight=1)
|
desc_frame.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
# --- Default Value ---
|
||||||
def_val_frame = ttk.Frame(main_frame)
|
def_val_frame = ttk.Frame(main_frame)
|
||||||
def_val_frame.pack(fill=tk.X, pady=(0, 5))
|
def_val_frame.pack(fill=tk.X, pady=(0, 5))
|
||||||
ttk.Label(def_val_frame, text="Default Value:").grid(row=0, column=0, sticky=tk.W, padx=(0,5))
|
ttk.Label(def_val_frame, text="Default Value:").grid(row=0, column=0, sticky=tk.W, padx=(0,5))
|
||||||
@ -62,6 +81,7 @@ class BaseParameterDialog(tk.Toplevel):
|
|||||||
self.default_value_entry.grid(row=0, column=1, sticky=tk.EW)
|
self.default_value_entry.grid(row=0, column=1, sticky=tk.EW)
|
||||||
def_val_frame.columnconfigure(1, weight=1)
|
def_val_frame.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
# --- Parameter Type ---
|
||||||
type_frame = ttk.Frame(main_frame)
|
type_frame = ttk.Frame(main_frame)
|
||||||
type_frame.pack(fill=tk.X, pady=(0, 10))
|
type_frame.pack(fill=tk.X, pady=(0, 10))
|
||||||
ttk.Label(type_frame, text="Type:").grid(row=0, column=0, sticky=tk.W, padx=(0,5))
|
ttk.Label(type_frame, text="Type:").grid(row=0, column=0, sticky=tk.W, padx=(0,5))
|
||||||
@ -77,6 +97,8 @@ class BaseParameterDialog(tk.Toplevel):
|
|||||||
self.type_combo.set("string")
|
self.type_combo.set("string")
|
||||||
type_frame.columnconfigure(1, weight=1)
|
type_frame.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Dialog Buttons (Save, Cancel) ---
|
||||||
dialog_buttons_frame = ttk.Frame(main_frame)
|
dialog_buttons_frame = ttk.Frame(main_frame)
|
||||||
dialog_buttons_frame.pack(fill=tk.X, pady=(10, 0))
|
dialog_buttons_frame.pack(fill=tk.X, pady=(10, 0))
|
||||||
ttk.Frame(dialog_buttons_frame).pack(side=tk.LEFT, expand=True)
|
ttk.Frame(dialog_buttons_frame).pack(side=tk.LEFT, expand=True)
|
||||||
@ -99,7 +121,7 @@ class BaseParameterDialog(tk.Toplevel):
|
|||||||
raise NotImplementedError("Subclasses must implement _on_save")
|
raise NotImplementedError("Subclasses must implement _on_save")
|
||||||
|
|
||||||
def _on_cancel(self):
|
def _on_cancel(self):
|
||||||
logger.debug(f"{self.title()} cancelled or closed.")
|
logger.debug(f"BaseParameterDialog: '{self.title()}' cancelled or closed by user.")
|
||||||
self.result = None
|
self.result = None
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
@ -107,7 +129,10 @@ class BaseParameterDialog(tk.Toplevel):
|
|||||||
class AddParameterDialog(BaseParameterDialog):
|
class AddParameterDialog(BaseParameterDialog):
|
||||||
"""Dialog for adding a new application parameter."""
|
"""Dialog for adding a new application parameter."""
|
||||||
def __init__(self, parent: tk.Widget):
|
def __init__(self, parent: tk.Widget):
|
||||||
super().__init__(parent, title="Add New Parameter")
|
# Non serve caricare dati
|
||||||
|
super().__init__(parent, title="Add New Parameter", load_data_method=None)
|
||||||
|
logger.debug("AddParameterDialog __init__ completed.")
|
||||||
|
|
||||||
|
|
||||||
def _on_save(self):
|
def _on_save(self):
|
||||||
if not self._validate_inputs():
|
if not self._validate_inputs():
|
||||||
@ -124,36 +149,78 @@ class AddParameterDialog(BaseParameterDialog):
|
|||||||
"default_value": param_default_value,
|
"default_value": param_default_value,
|
||||||
"type": param_type
|
"type": param_type
|
||||||
}
|
}
|
||||||
logger.info(f"Parameter '{param_name}' data prepared for adding.")
|
logger.info(f"AddParameterDialog: Parameter '{param_name}' data prepared.")
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
|
|
||||||
class EditParameterDialog(BaseParameterDialog):
|
class EditParameterDialog(BaseParameterDialog):
|
||||||
"""Dialog for editing an existing application parameter."""
|
"""Dialog for editing an existing application parameter."""
|
||||||
def __init__(self, parent: tk.Widget, parameter_data_to_edit: Dict[str, Any]):
|
def __init__(self, parent: tk.Widget, parameter_data_to_edit: Dict[str, Any]):
|
||||||
self.parameter_data_to_edit = parameter_data_to_edit
|
logger.debug(f"EditParameterDialog __init__ - START - Parameter to edit: {parameter_data_to_edit.get('name', 'N/A')}")
|
||||||
super().__init__(parent, title=f"Edit Parameter: {parameter_data_to_edit.get('name', '')}")
|
self.parameter_data_to_edit = parameter_data_to_edit # Imposta PRIMA di super()
|
||||||
self._load_initial_data()
|
|
||||||
|
# Passa il metodo _load_initial_data a super()
|
||||||
|
param_name_for_title = parameter_data_to_edit.get('name', 'Unknown')
|
||||||
|
super().__init__(parent, title=f"Edit Parameter: {param_name_for_title}", load_data_method=self._load_initial_data)
|
||||||
|
|
||||||
|
# Il codice qui viene eseguito solo dopo la chiusura del dialogo (wait_window)
|
||||||
|
logger.debug(f"EditParameterDialog __init__ - END - for '{param_name_for_title}' (after super call completed)")
|
||||||
|
|
||||||
def _load_initial_data(self):
|
def _load_initial_data(self):
|
||||||
|
# Chiamato da BaseParameterDialog.__init__
|
||||||
if not self.parameter_data_to_edit:
|
if not self.parameter_data_to_edit:
|
||||||
logger.warning("EditParameterDialog opened without parameter data.")
|
logger.error("EditParameterDialog _load_initial_data: parameter_data_to_edit is missing.")
|
||||||
self.destroy()
|
if self.parent_widget:
|
||||||
|
messagebox.showerror("Initialization Error",
|
||||||
|
"Cannot edit parameter: Initial data is missing.",
|
||||||
|
parent=self.parent_widget) # Mostra su ApplicationDialog
|
||||||
|
self.after_idle(self.destroy)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.name_entry_var.set(self.parameter_data_to_edit.get("name", ""))
|
param_name = self.parameter_data_to_edit.get("name", "")
|
||||||
self.description_text.delete("1.0", tk.END)
|
logger.debug(f"EditParameterDialog _load_initial_data: Loading data for parameter '{param_name}'")
|
||||||
self.description_text.insert("1.0", self.parameter_data_to_edit.get("description", ""))
|
try:
|
||||||
|
self.name_entry_var.set(param_name)
|
||||||
|
|
||||||
|
# Pulisci e inserisci nella Textbox
|
||||||
|
self.description_text.delete("1.0", tk.END) # Tenta di pulire
|
||||||
|
self.description_text.insert("1.0", self.parameter_data_to_edit.get("description", "")) # Tenta di inserire
|
||||||
|
logger.debug(f"EditParameterDialog _load_initial_data: Description set in Text widget.")
|
||||||
|
|
||||||
self.default_value_entry_var.set(self.parameter_data_to_edit.get("default_value", ""))
|
self.default_value_entry_var.set(self.parameter_data_to_edit.get("default_value", ""))
|
||||||
|
|
||||||
param_type = self.parameter_data_to_edit.get("type", "string")
|
param_type = self.parameter_data_to_edit.get("type", "string")
|
||||||
if param_type in self._parameter_types:
|
if param_type in self._parameter_types:
|
||||||
self.type_combo_var.set(param_type)
|
self.type_combo_var.set(param_type)
|
||||||
else:
|
else:
|
||||||
logger.warning(f"Unknown parameter type '{param_type}' for '{self.name_entry_var.get()}'. Defaulting to 'string'.")
|
logger.warning(f"EditParameterDialog: Unknown type '{param_type}' for param '{param_name}'. Defaulting to 'string'.")
|
||||||
self.type_combo_var.set("string")
|
self.type_combo_var.set("string")
|
||||||
|
|
||||||
|
logger.info(f"EditParameterDialog _load_initial_data: Data loaded successfully for '{param_name}'.")
|
||||||
|
|
||||||
|
except tk.TclError as e: # Cattura specificamente l'errore TclError
|
||||||
|
logger.error(f"EditParameterDialog _load_initial_data: TclError operating on widget (likely description_text): {e}", exc_info=True)
|
||||||
|
if self.parent_widget:
|
||||||
|
messagebox.showerror("Widget Error",
|
||||||
|
f"Error loading data into the description field for '{param_name}'.\n"
|
||||||
|
"The dialog might not have initialized correctly.",
|
||||||
|
parent=self.parent_widget)
|
||||||
|
self.after_idle(self.destroy) # Chiudi se il widget non è utilizzabile
|
||||||
|
except Exception as e: # Cattura altri errori
|
||||||
|
logger.error(f"EditParameterDialog _load_initial_data: Unexpected error loading data for '{param_name}': {e}", exc_info=True)
|
||||||
|
if self.parent_widget:
|
||||||
|
messagebox.showerror("Load Error",
|
||||||
|
f"Could not load data for parameter '{param_name}':\n{e}",
|
||||||
|
parent=self.parent_widget)
|
||||||
|
self.after_idle(self.destroy)
|
||||||
|
|
||||||
|
|
||||||
def _on_save(self):
|
def _on_save(self):
|
||||||
|
if not self.parameter_data_to_edit: # Sicurezza se _load fallisce
|
||||||
|
messagebox.showerror("Save Error", "Cannot save: Initial parameter data was not loaded correctly.", parent=self)
|
||||||
|
logger.error("EditParameterDialog _on_save: Attempted to save without valid initial data.")
|
||||||
|
return
|
||||||
|
|
||||||
if not self._validate_inputs():
|
if not self._validate_inputs():
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -168,5 +235,6 @@ class EditParameterDialog(BaseParameterDialog):
|
|||||||
"default_value": new_param_default_value,
|
"default_value": new_param_default_value,
|
||||||
"type": new_param_type
|
"type": new_param_type
|
||||||
}
|
}
|
||||||
logger.info(f"Parameter '{new_param_name}' (original: '{self.parameter_data_to_edit.get('name')}') data prepared.")
|
original_param_name = self.parameter_data_to_edit.get('name', 'N/A')
|
||||||
|
logger.info(f"EditParameterDialog: Parameter data for '{new_param_name}' (original: '{original_param_name}') prepared.")
|
||||||
self.destroy()
|
self.destroy()
|
||||||
@ -5,13 +5,13 @@ Dialogs for adding and editing sequences of applications.
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk, messagebox
|
from tkinter import ttk, messagebox
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional, List, Dict, Any
|
from typing import Optional, List, Dict, Any, Callable # Aggiunto Callable
|
||||||
import copy # For deep copying steps list if necessary
|
import copy
|
||||||
|
|
||||||
from ...core.config_manager import ConfigManager
|
from ...core.config_manager import ConfigManager
|
||||||
from ...core.exceptions import DuplicateNameError, NameNotFoundError, ConfigError
|
from ...core.exceptions import DuplicateNameError, NameNotFoundError, ConfigError
|
||||||
from ..utils_gui import GuiUtils
|
from ..utils_gui import GuiUtils
|
||||||
from .step_dialogs import AddStepDialog, EditStepDialog
|
from .step_dialogs import AddStepDialog, EditStepDialog # Assicurati che sia importato
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -19,24 +19,41 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
"""
|
"""
|
||||||
Base class for Add and Edit Sequence dialogs.
|
Base class for Add and Edit Sequence dialogs.
|
||||||
"""
|
"""
|
||||||
def __init__(self, parent: tk.Widget, config_manager: ConfigManager, title: str):
|
def __init__(self, parent: tk.Widget, config_manager: ConfigManager, title: str, load_data_method: Optional[Callable[[], None]] = None): # Aggiunto load_data_method
|
||||||
|
logger.debug(f"BaseSequenceDialog __init__ - START - Title: '{title}'")
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.transient(parent)
|
self.transient(parent)
|
||||||
self.title(title)
|
self.title(title)
|
||||||
self.parent_widget = parent
|
self.parent_widget = parent
|
||||||
self.config_manager = config_manager
|
self.config_manager = config_manager
|
||||||
self.result: Optional[Dict[str, Any]] = None
|
self.result: Optional[Dict[str, Any]] = None
|
||||||
self.original_sequence_name: Optional[str] = None
|
# self.original_sequence_name è gestito dalle sottoclassi
|
||||||
|
|
||||||
self.current_steps_data: List[Dict[str, Any]] = []
|
self.current_steps_data: List[Dict[str, Any]] = [] # Lista dei passi per la sequenza corrente
|
||||||
|
|
||||||
|
logger.debug("BaseSequenceDialog: Calling _setup_widgets()")
|
||||||
|
self._setup_widgets() # Crea i widget
|
||||||
|
logger.debug("BaseSequenceDialog: _setup_widgets() completed.")
|
||||||
|
|
||||||
|
# Carica i dati specifici della sottoclasse (es. per EditDialog) PRIMA di rendere modale e attendere
|
||||||
|
if load_data_method:
|
||||||
|
logger.debug("BaseSequenceDialog: Calling provided load_data_method.")
|
||||||
|
load_data_method()
|
||||||
|
logger.debug("BaseSequenceDialog: load_data_method completed.")
|
||||||
|
else:
|
||||||
|
logger.debug("BaseSequenceDialog: No load_data_method provided or needed (e.g., for Add dialog).")
|
||||||
|
if not hasattr(self, 'original_sequence_name'): # Se è AddDialog, popola albero vuoto
|
||||||
|
self._populate_steps_tree()
|
||||||
|
|
||||||
self._setup_widgets()
|
|
||||||
|
|
||||||
GuiUtils.center_window(self, parent)
|
GuiUtils.center_window(self, parent)
|
||||||
self.protocol("WM_DELETE_WINDOW", self._on_cancel)
|
self.protocol("WM_DELETE_WINDOW", self._on_cancel)
|
||||||
|
|
||||||
self.grab_set()
|
self.grab_set()
|
||||||
self.focus_set()
|
self.focus_set()
|
||||||
|
logger.debug(f"BaseSequenceDialog __init__ for '{title}': Now calling wait_window().")
|
||||||
self.wait_window(self)
|
self.wait_window(self)
|
||||||
|
logger.debug(f"BaseSequenceDialog __init__ - END - Title: '{title}' (after wait_window)")
|
||||||
|
|
||||||
|
|
||||||
def _setup_widgets(self):
|
def _setup_widgets(self):
|
||||||
@ -44,6 +61,7 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
main_frame.pack(expand=True, fill=tk.BOTH)
|
main_frame.pack(expand=True, fill=tk.BOTH)
|
||||||
main_frame.columnconfigure(0, weight=1)
|
main_frame.columnconfigure(0, weight=1)
|
||||||
|
|
||||||
|
# --- Sequence Name ---
|
||||||
name_frame = ttk.Frame(main_frame)
|
name_frame = ttk.Frame(main_frame)
|
||||||
name_frame.grid(row=0, column=0, sticky=tk.EW, pady=(0, 5))
|
name_frame.grid(row=0, column=0, sticky=tk.EW, pady=(0, 5))
|
||||||
ttk.Label(name_frame, text="Sequence Name:").pack(side=tk.LEFT, padx=(0, 5))
|
ttk.Label(name_frame, text="Sequence Name:").pack(side=tk.LEFT, padx=(0, 5))
|
||||||
@ -51,6 +69,7 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
self.name_entry = ttk.Entry(name_frame, textvariable=self.name_entry_var, width=50)
|
self.name_entry = ttk.Entry(name_frame, textvariable=self.name_entry_var, width=50)
|
||||||
self.name_entry.pack(side=tk.LEFT, expand=True, fill=tk.X)
|
self.name_entry.pack(side=tk.LEFT, expand=True, fill=tk.X)
|
||||||
|
|
||||||
|
# --- Steps ---
|
||||||
steps_labelframe = ttk.LabelFrame(main_frame, text="Sequence Steps")
|
steps_labelframe = ttk.LabelFrame(main_frame, text="Sequence Steps")
|
||||||
steps_labelframe.grid(row=1, column=0, sticky=tk.NSEW, pady=(0, 10))
|
steps_labelframe.grid(row=1, column=0, sticky=tk.NSEW, pady=(0, 10))
|
||||||
main_frame.rowconfigure(1, weight=1)
|
main_frame.rowconfigure(1, weight=1)
|
||||||
@ -107,11 +126,12 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
|
|
||||||
|
|
||||||
self.steps_tree.bind("<<TreeviewSelect>>", self._on_step_select)
|
self.steps_tree.bind("<<TreeviewSelect>>", self._on_step_select)
|
||||||
self._on_step_select()
|
self._on_step_select() # Init button states
|
||||||
|
|
||||||
|
# --- Dialog Buttons (Save, Cancel) ---
|
||||||
dialog_buttons_frame = ttk.Frame(main_frame)
|
dialog_buttons_frame = ttk.Frame(main_frame)
|
||||||
dialog_buttons_frame.grid(row=2, column=0, sticky=tk.EW, pady=(10, 0))
|
dialog_buttons_frame.grid(row=2, column=0, sticky=tk.EW, pady=(10, 0))
|
||||||
ttk.Frame(dialog_buttons_frame).pack(side=tk.LEFT, expand=True)
|
ttk.Frame(dialog_buttons_frame).pack(side=tk.LEFT, expand=True) # Spacer
|
||||||
|
|
||||||
self.save_button = ttk.Button(dialog_buttons_frame, text="Save", command=self._on_save)
|
self.save_button = ttk.Button(dialog_buttons_frame, text="Save", command=self._on_save)
|
||||||
self.save_button.pack(side=tk.LEFT, padx=(0,5))
|
self.save_button.pack(side=tk.LEFT, padx=(0,5))
|
||||||
@ -119,6 +139,7 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
self.cancel_button.pack(side=tk.LEFT)
|
self.cancel_button.pack(side=tk.LEFT)
|
||||||
|
|
||||||
def _populate_steps_tree(self):
|
def _populate_steps_tree(self):
|
||||||
|
logger.debug(f"BaseSequenceDialog: Populating steps tree with {len(self.current_steps_data)} steps.")
|
||||||
selected_iid_tuple = self.steps_tree.selection()
|
selected_iid_tuple = self.steps_tree.selection()
|
||||||
selected_iid = selected_iid_tuple[0] if selected_iid_tuple else None
|
selected_iid = selected_iid_tuple[0] if selected_iid_tuple else None
|
||||||
|
|
||||||
@ -128,15 +149,16 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
for i, step_data in enumerate(self.current_steps_data):
|
for i, step_data in enumerate(self.current_steps_data):
|
||||||
app_name = step_data.get("application", "N/A")
|
app_name = step_data.get("application", "N/A")
|
||||||
wait_time = step_data.get("wait_time", 0.0)
|
wait_time = step_data.get("wait_time", 0.0)
|
||||||
|
# "parameters" in step_data è un dizionario. Se non è vuoto, ci sono custom params.
|
||||||
custom_params_set = "Yes" if step_data.get("parameters") else "No"
|
custom_params_set = "Yes" if step_data.get("parameters") else "No"
|
||||||
|
|
||||||
# Use index as iid
|
current_iid = str(i) # Usare l'indice come iid per facilitare riordino e selezione
|
||||||
current_iid = str(i)
|
|
||||||
self.steps_tree.insert(
|
self.steps_tree.insert(
|
||||||
'', tk.END,
|
'', tk.END,
|
||||||
iid=current_iid,
|
iid=current_iid,
|
||||||
values=(i + 1, app_name, f"{float(wait_time):.1f}", custom_params_set)
|
values=(i + 1, app_name, f"{float(wait_time):.1f}", custom_params_set)
|
||||||
)
|
)
|
||||||
|
logger.debug(f"Inserted step: iid='{current_iid}', values=({i+1}, '{app_name}', {wait_time}, '{custom_params_set}')")
|
||||||
|
|
||||||
if selected_iid and self.steps_tree.exists(selected_iid):
|
if selected_iid and self.steps_tree.exists(selected_iid):
|
||||||
self.steps_tree.selection_set(selected_iid)
|
self.steps_tree.selection_set(selected_iid)
|
||||||
@ -155,9 +177,10 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
idx = -1
|
idx = -1
|
||||||
if is_selection:
|
if is_selection:
|
||||||
try:
|
try:
|
||||||
idx = int(selected_ids[0])
|
idx = int(selected_ids[0]) # iid è l'indice del passo
|
||||||
except ValueError:
|
except ValueError:
|
||||||
is_selection = False
|
logger.warning(f"Invalid iid '{selected_ids[0]}' in _on_step_select.")
|
||||||
|
is_selection = False # Tratta come se non ci fosse selezione valida per i pulsanti di spostamento
|
||||||
|
|
||||||
can_move_up = is_selection and idx > 0
|
can_move_up = is_selection and idx > 0
|
||||||
self.move_step_up_button.config(state=tk.NORMAL if can_move_up else tk.DISABLED)
|
self.move_step_up_button.config(state=tk.NORMAL if can_move_up else tk.DISABLED)
|
||||||
@ -166,12 +189,12 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
self.move_step_down_button.config(state=tk.NORMAL if can_move_down else tk.DISABLED)
|
self.move_step_down_button.config(state=tk.NORMAL if can_move_down else tk.DISABLED)
|
||||||
|
|
||||||
def _add_step(self):
|
def _add_step(self):
|
||||||
logger.debug("Add Step button clicked.")
|
logger.debug("BaseSequenceDialog: Add Step button clicked.")
|
||||||
dialog = AddStepDialog(self, self.config_manager)
|
dialog = AddStepDialog(self, self.config_manager) # 'self' (BaseSequenceDialog) è il parent
|
||||||
if dialog.result:
|
if dialog.result: # dialog.result è il dizionario step_data
|
||||||
self.current_steps_data.append(dialog.result)
|
self.current_steps_data.append(dialog.result)
|
||||||
self._populate_steps_tree()
|
self._populate_steps_tree() # Aggiorna la treeview con il nuovo passo
|
||||||
logger.info(f"Step for app '{dialog.result['application']}' added to sequence dialog.")
|
logger.info(f"BaseSequenceDialog: Step for app '{dialog.result['application']}' added to current sequence steps.")
|
||||||
|
|
||||||
def _edit_step(self):
|
def _edit_step(self):
|
||||||
selected_ids = self.steps_tree.selection()
|
selected_ids = self.steps_tree.selection()
|
||||||
@ -180,20 +203,21 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
step_index = int(selected_ids[0])
|
step_index = int(selected_ids[0]) # L'iid è l'indice
|
||||||
step_data_to_edit = self.current_steps_data[step_index]
|
step_data_to_edit = self.current_steps_data[step_index]
|
||||||
except (ValueError, IndexError):
|
except (ValueError, IndexError) as e:
|
||||||
logger.error(f"Could not get step data for editing, selection: {selected_ids[0]}")
|
logger.error(f"BaseSequenceDialog: Could not get step data for editing. Selection iid: '{selected_ids[0]}', Error: {e}")
|
||||||
messagebox.showerror("Error", "Could not retrieve step data for editing.", parent=self)
|
messagebox.showerror("Error", "Could not retrieve step data for editing.", parent=self)
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.debug(f"Edit Step button clicked for step index: {step_index}")
|
logger.debug(f"BaseSequenceDialog: Edit Step button clicked for step at index {step_index}.")
|
||||||
# Create a deep copy to pass to the dialog so modifications don't affect current_steps_data until save
|
# Passa una COPIA PROFONDA dei dati del passo al dialogo di modifica, così le modifiche
|
||||||
|
# non si riflettono su self.current_steps_data finché EditStepDialog non ritorna con successo.
|
||||||
dialog = EditStepDialog(self, self.config_manager, copy.deepcopy(step_data_to_edit))
|
dialog = EditStepDialog(self, self.config_manager, copy.deepcopy(step_data_to_edit))
|
||||||
if dialog.result:
|
if dialog.result: # dialog.result è il dizionario step_data aggiornato
|
||||||
self.current_steps_data[step_index] = dialog.result
|
self.current_steps_data[step_index] = dialog.result # Sostituisci con i dati aggiornati
|
||||||
self._populate_steps_tree()
|
self._populate_steps_tree() # Aggiorna la treeview
|
||||||
logger.info(f"Step for app '{dialog.result['application']}' updated in sequence dialog.")
|
logger.info(f"BaseSequenceDialog: Step for app '{dialog.result['application']}' (index {step_index}) updated.")
|
||||||
|
|
||||||
|
|
||||||
def _delete_step(self):
|
def _delete_step(self):
|
||||||
@ -205,8 +229,8 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
try:
|
try:
|
||||||
step_index = int(selected_ids[0])
|
step_index = int(selected_ids[0])
|
||||||
step_app_name = self.current_steps_data[step_index].get("application", "Unknown")
|
step_app_name = self.current_steps_data[step_index].get("application", "Unknown")
|
||||||
except (ValueError, IndexError):
|
except (ValueError, IndexError) as e:
|
||||||
logger.error(f"Could not get step data for deletion, selection: {selected_ids[0]}")
|
logger.error(f"BaseSequenceDialog: Could not get step data for deletion. Selection iid: '{selected_ids[0]}', Error: {e}")
|
||||||
messagebox.showerror("Error", "Could not retrieve step data for deletion.", parent=self)
|
messagebox.showerror("Error", "Could not retrieve step data for deletion.", parent=self)
|
||||||
return
|
return
|
||||||
|
|
||||||
@ -214,8 +238,8 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
f"Are you sure you want to delete step #{step_index + 1} ('{step_app_name}')?",
|
f"Are you sure you want to delete step #{step_index + 1} ('{step_app_name}')?",
|
||||||
parent=self):
|
parent=self):
|
||||||
del self.current_steps_data[step_index]
|
del self.current_steps_data[step_index]
|
||||||
self._populate_steps_tree()
|
self._populate_steps_tree() # Ridisegna la treeview
|
||||||
logger.info(f"Step for app '{step_app_name}' deleted from sequence dialog.")
|
logger.info(f"BaseSequenceDialog: Step for app '{step_app_name}' (original index {step_index}) deleted.")
|
||||||
|
|
||||||
def _move_step_up(self):
|
def _move_step_up(self):
|
||||||
selected_ids = self.steps_tree.selection()
|
selected_ids = self.steps_tree.selection()
|
||||||
@ -225,13 +249,14 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
if idx > 0:
|
if idx > 0:
|
||||||
self.current_steps_data[idx], self.current_steps_data[idx-1] = \
|
self.current_steps_data[idx], self.current_steps_data[idx-1] = \
|
||||||
self.current_steps_data[idx-1], self.current_steps_data[idx]
|
self.current_steps_data[idx-1], self.current_steps_data[idx]
|
||||||
new_selected_iid = str(idx-1)
|
new_selected_iid = str(idx-1) # Il nuovo iid (indice) dell'elemento spostato
|
||||||
self._populate_steps_tree()
|
self._populate_steps_tree()
|
||||||
if self.steps_tree.exists(new_selected_iid):
|
if self.steps_tree.exists(new_selected_iid): # Riprova a selezionare
|
||||||
self.steps_tree.selection_set(new_selected_iid)
|
self.steps_tree.selection_set(new_selected_iid)
|
||||||
self.steps_tree.focus(new_selected_iid)
|
self.steps_tree.focus(new_selected_iid)
|
||||||
|
logger.debug(f"Moved step from index {idx} to {idx-1}.")
|
||||||
except (ValueError, IndexError) as e:
|
except (ValueError, IndexError) as e:
|
||||||
logger.error(f"Error moving step up: {e}, selection: {selected_ids[0]}")
|
logger.error(f"Error moving step up: {e}, selection iid: {selected_ids[0]}")
|
||||||
|
|
||||||
def _move_step_down(self):
|
def _move_step_down(self):
|
||||||
selected_ids = self.steps_tree.selection()
|
selected_ids = self.steps_tree.selection()
|
||||||
@ -246,8 +271,9 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
if self.steps_tree.exists(new_selected_iid):
|
if self.steps_tree.exists(new_selected_iid):
|
||||||
self.steps_tree.selection_set(new_selected_iid)
|
self.steps_tree.selection_set(new_selected_iid)
|
||||||
self.steps_tree.focus(new_selected_iid)
|
self.steps_tree.focus(new_selected_iid)
|
||||||
|
logger.debug(f"Moved step from index {idx} to {idx+1}.")
|
||||||
except (ValueError, IndexError) as e:
|
except (ValueError, IndexError) as e:
|
||||||
logger.error(f"Error moving step down: {e}, selection: {selected_ids[0]}")
|
logger.error(f"Error moving step down: {e}, selection iid: {selected_ids[0]}")
|
||||||
|
|
||||||
|
|
||||||
def _validate_inputs(self) -> bool:
|
def _validate_inputs(self) -> bool:
|
||||||
@ -262,7 +288,7 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
raise NotImplementedError("Subclasses must implement _on_save")
|
raise NotImplementedError("Subclasses must implement _on_save")
|
||||||
|
|
||||||
def _on_cancel(self):
|
def _on_cancel(self):
|
||||||
logger.debug(f"{self.title()} cancelled or closed.")
|
logger.debug(f"BaseSequenceDialog: '{self.title()}' cancelled or closed by user.")
|
||||||
self.result = None
|
self.result = None
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
@ -270,87 +296,133 @@ class BaseSequenceDialog(tk.Toplevel):
|
|||||||
class AddSequenceDialog(BaseSequenceDialog):
|
class AddSequenceDialog(BaseSequenceDialog):
|
||||||
"""Dialog for adding a new sequence."""
|
"""Dialog for adding a new sequence."""
|
||||||
def __init__(self, parent: tk.Widget, config_manager: ConfigManager):
|
def __init__(self, parent: tk.Widget, config_manager: ConfigManager):
|
||||||
super().__init__(parent, config_manager, title="Add New Sequence")
|
# AddSequenceDialog non carica dati esistenti, passa None per load_data_method
|
||||||
self._populate_steps_tree()
|
# La _populate_steps_tree (vuota) viene chiamata dalla base se load_data_method è None
|
||||||
|
super().__init__(parent, config_manager, title="Add New Sequence", load_data_method=None)
|
||||||
|
logger.debug("AddSequenceDialog __init__ completed.")
|
||||||
|
|
||||||
|
|
||||||
def _on_save(self):
|
def _on_save(self):
|
||||||
if not self._validate_inputs():
|
if not self._validate_inputs():
|
||||||
return
|
return
|
||||||
|
|
||||||
seq_name = self.name_entry_var.get().strip()
|
seq_name = self.name_entry_var.get().strip()
|
||||||
|
# self.current_steps_data è già aggiornata dai dialoghi dei passi e dal riordino
|
||||||
|
|
||||||
|
# Salva una copia profonda per evitare che modifiche future a current_steps_data
|
||||||
|
# (se il dialogo venisse riutilizzato o manipolato dopo) influenzino i dati salvati.
|
||||||
sequence_data = {
|
sequence_data = {
|
||||||
"name": seq_name,
|
"name": seq_name,
|
||||||
"steps": copy.deepcopy(self.current_steps_data) # Save a deep copy
|
"steps": copy.deepcopy(self.current_steps_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.config_manager.add_sequence(sequence_data)
|
self.config_manager.add_sequence(sequence_data)
|
||||||
self.result = sequence_data
|
self.result = sequence_data # Imposta result per MainWindow
|
||||||
logger.info(f"Sequence '{seq_name}' added successfully through dialog.")
|
logger.info(f"AddSequenceDialog: Sequence '{seq_name}' added successfully to config.")
|
||||||
# messagebox.showinfo("Success", f"Sequence '{seq_name}' added successfully.", parent=self.parent_widget)
|
self.destroy() # Chiudi dialogo
|
||||||
self.destroy()
|
|
||||||
except DuplicateNameError as e:
|
except DuplicateNameError as e:
|
||||||
logger.warning(f"Failed to add sequence: {e}")
|
logger.warning(f"AddSequenceDialog: Failed to add sequence due to duplicate name: {e}")
|
||||||
messagebox.showerror("Error Adding Sequence", str(e), parent=self)
|
messagebox.showerror("Error Adding Sequence", str(e), parent=self)
|
||||||
self.name_entry.focus_set()
|
self.name_entry.focus_set()
|
||||||
except ConfigError as e:
|
except ConfigError as e:
|
||||||
logger.error(f"Configuration error adding sequence '{seq_name}': {e}", exc_info=True)
|
logger.error(f"AddSequenceDialog: Configuration error adding sequence '{seq_name}': {e}", exc_info=True)
|
||||||
messagebox.showerror("Configuration Error", f"Could not save sequence:\n{e}", parent=self)
|
messagebox.showerror("Configuration Error", f"Could not save sequence:\n{e}", parent=self)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error adding sequence '{seq_name}': {e}", exc_info=True)
|
logger.error(f"AddSequenceDialog: Unexpected error adding sequence '{seq_name}': {e}", exc_info=True)
|
||||||
messagebox.showerror("Unexpected Error", f"An unexpected error occurred:\n{e}", parent=self)
|
messagebox.showerror("Unexpected Error", f"An unexpected error occurred:\n{e}", parent=self)
|
||||||
|
|
||||||
|
|
||||||
class EditSequenceDialog(BaseSequenceDialog):
|
class EditSequenceDialog(BaseSequenceDialog):
|
||||||
"""Dialog for editing an existing sequence."""
|
"""Dialog for editing an existing sequence."""
|
||||||
def __init__(self, parent: tk.Widget, config_manager: ConfigManager, sequence_name_to_edit: str):
|
def __init__(self, parent: tk.Widget, config_manager: ConfigManager, sequence_name_to_edit: str):
|
||||||
self.original_sequence_name = sequence_name_to_edit
|
logger.debug(f"EditSequenceDialog __init__ - START - Sequence to edit: '{sequence_name_to_edit}'")
|
||||||
super().__init__(parent, config_manager, title=f"Edit Sequence: {sequence_name_to_edit}")
|
self.original_sequence_name = sequence_name_to_edit # Deve essere impostato PRIMA di super()
|
||||||
self._load_initial_data()
|
|
||||||
|
# Passa self._load_initial_data al costruttore della classe base
|
||||||
|
super().__init__(parent, config_manager, title=f"Edit Sequence: {self.original_sequence_name}", load_data_method=self._load_initial_data)
|
||||||
|
|
||||||
|
logger.debug(f"EditSequenceDialog __init__ - END - for '{self.original_sequence_name}' (after super call completed)")
|
||||||
|
|
||||||
|
|
||||||
def _load_initial_data(self):
|
def _load_initial_data(self):
|
||||||
|
# Questo metodo è chiamato da BaseSequenceDialog.__init__
|
||||||
|
if not self.original_sequence_name:
|
||||||
|
logger.error("EditSequenceDialog _load_initial_data: original_sequence_name is None or empty. Dialog should close.")
|
||||||
|
if self.parent_widget: # Dovrebbe sempre esistere
|
||||||
|
messagebox.showerror("Initialization Error",
|
||||||
|
"Cannot edit: No sequence name specified for editing.",
|
||||||
|
parent=self.parent_widget)
|
||||||
|
self.after_idle(self.destroy)
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.debug(f"EditSequenceDialog _load_initial_data: Attempting to load for: '{self.original_sequence_name}'")
|
||||||
try:
|
try:
|
||||||
seq_data = self.config_manager.get_sequence_by_name(self.original_sequence_name)
|
seq_data = self.config_manager.get_sequence_by_name(self.original_sequence_name)
|
||||||
self.name_entry_var.set(seq_data.get("name", ""))
|
logger.debug(f"EditSequenceDialog _load_initial_data: Data from ConfigManager for '{self.original_sequence_name}': {seq_data}")
|
||||||
# Make a deep copy of steps for editing
|
|
||||||
self.current_steps_data = copy.deepcopy(seq_data.get("steps", []))
|
|
||||||
self._populate_steps_tree()
|
|
||||||
except NameNotFoundError:
|
|
||||||
logger.error(f"Cannot edit sequence. Name '{self.original_sequence_name}' not found.")
|
|
||||||
messagebox.showerror("Error", f"Sequence '{self.original_sequence_name}' not found.", parent=self.parent_widget)
|
|
||||||
self.destroy()
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Unexpected error loading sequence data for edit: {e}", exc_info=True)
|
|
||||||
messagebox.showerror("Error", f"Could not load sequence data for editing:\n{e}", parent=self.parent_widget)
|
|
||||||
self.destroy()
|
|
||||||
|
|
||||||
|
if not seq_data: # Salvaguardia
|
||||||
|
logger.error(f"EditSequenceDialog _load_initial_data: ConfigManager returned None for '{self.original_sequence_name}'.")
|
||||||
|
messagebox.showerror("Load Error",
|
||||||
|
f"Could not retrieve data for sequence '{self.original_sequence_name}'.",
|
||||||
|
parent=self.parent_widget)
|
||||||
|
self.after_idle(self.destroy)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.name_entry_var.set(seq_data.get("name", ""))
|
||||||
|
# Usa copy.deepcopy per i passi, poiché contengono dizionari (mutabili)
|
||||||
|
self.current_steps_data = copy.deepcopy(seq_data.get("steps", []))
|
||||||
|
|
||||||
|
logger.debug(f"EditSequenceDialog _load_initial_data: Name set to '{self.name_entry_var.get()}'. Number of steps: {len(self.current_steps_data)}")
|
||||||
|
self._populate_steps_tree() # Popola la treeview con i passi caricati
|
||||||
|
|
||||||
|
logger.info(f"EditSequenceDialog _load_initial_data: Successfully loaded data for '{self.original_sequence_name}'.")
|
||||||
|
|
||||||
|
except NameNotFoundError:
|
||||||
|
logger.error(f"EditSequenceDialog _load_initial_data: Sequence '{self.original_sequence_name}' not found (NameNotFoundError).")
|
||||||
|
messagebox.showerror("Load Error",
|
||||||
|
f"Sequence '{self.original_sequence_name}' not found. Cannot edit.",
|
||||||
|
parent=self.parent_widget)
|
||||||
|
self.after_idle(self.destroy)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"EditSequenceDialog _load_initial_data: Unexpected error for '{self.original_sequence_name}': {e}", exc_info=True)
|
||||||
|
messagebox.showerror("Load Error",
|
||||||
|
f"An unexpected error occurred while loading data for '{self.original_sequence_name}':\n{e}",
|
||||||
|
parent=self.parent_widget)
|
||||||
|
self.after_idle(self.destroy)
|
||||||
|
|
||||||
def _on_save(self):
|
def _on_save(self):
|
||||||
|
if not self.original_sequence_name:
|
||||||
|
messagebox.showerror("Save Error", "Cannot save: Critical information about the original sequence is missing.", parent=self)
|
||||||
|
logger.error("EditSequenceDialog _on_save: Attempted to save without a valid original_sequence_name.")
|
||||||
|
return
|
||||||
|
|
||||||
if not self._validate_inputs():
|
if not self._validate_inputs():
|
||||||
return
|
return
|
||||||
|
|
||||||
new_seq_name = self.name_entry_var.get().strip()
|
new_seq_name = self.name_entry_var.get().strip()
|
||||||
|
# self.current_steps_data è già aggiornata
|
||||||
|
|
||||||
updated_sequence_data = {
|
updated_sequence_data = {
|
||||||
"name": new_seq_name,
|
"name": new_seq_name,
|
||||||
"steps": copy.deepcopy(self.current_steps_data) # Save a deep copy
|
"steps": copy.deepcopy(self.current_steps_data) # Salva una copia profonda
|
||||||
}
|
}
|
||||||
|
|
||||||
try:
|
try:
|
||||||
self.config_manager.update_sequence(self.original_sequence_name, updated_sequence_data)
|
self.config_manager.update_sequence(self.original_sequence_name, updated_sequence_data)
|
||||||
self.result = updated_sequence_data
|
self.result = updated_sequence_data
|
||||||
logger.info(f"Sequence '{self.original_sequence_name}' updated to '{new_seq_name}'.")
|
logger.info(f"EditSequenceDialog: Sequence '{self.original_sequence_name}' updated to '{new_seq_name}' in config.")
|
||||||
# messagebox.showinfo("Success", f"Sequence '{new_seq_name}' updated successfully.", parent=self.parent_widget)
|
|
||||||
self.destroy()
|
self.destroy()
|
||||||
except NameNotFoundError as e:
|
except NameNotFoundError as e:
|
||||||
logger.error(f"Update failed: Original sequence '{self.original_sequence_name}' not found: {e}", exc_info=True)
|
logger.error(f"EditSequenceDialog _on_save: Original sequence '{self.original_sequence_name}' no longer found: {e}", exc_info=True)
|
||||||
messagebox.showerror("Error Updating Sequence", str(e), parent=self)
|
messagebox.showerror("Error Updating Sequence", str(e), parent=self)
|
||||||
except DuplicateNameError as e:
|
except DuplicateNameError as e:
|
||||||
logger.warning(f"Failed to update sequence: {e}")
|
logger.warning(f"EditSequenceDialog _on_save: Failed to update due to duplicate name: {e}")
|
||||||
messagebox.showerror("Error Updating Sequence", str(e), parent=self)
|
messagebox.showerror("Error Updating Sequence", str(e), parent=self)
|
||||||
self.name_entry.focus_set()
|
self.name_entry.focus_set()
|
||||||
except ConfigError as e:
|
except ConfigError as e:
|
||||||
logger.error(f"Configuration error updating sequence '{new_seq_name}': {e}", exc_info=True)
|
logger.error(f"EditSequenceDialog _on_save: Config error updating '{new_seq_name}': {e}", exc_info=True)
|
||||||
messagebox.showerror("Configuration Error", f"Could not save sequence updates:\n{e}", parent=self)
|
messagebox.showerror("Configuration Error", f"Could not save sequence updates:\n{e}", parent=self)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Unexpected error updating sequence '{new_seq_name}': {e}", exc_info=True)
|
logger.error(f"EditSequenceDialog _on_save: Unexpected error updating '{new_seq_name}': {e}", exc_info=True)
|
||||||
messagebox.showerror("Unexpected Error", f"An unexpected error occurred:\n{e}", parent=self)
|
messagebox.showerror("Unexpected Error", f"An unexpected error occurred:\n{e}", parent=self)
|
||||||
@ -5,8 +5,8 @@ Dialogs for adding and editing steps within a sequence.
|
|||||||
import tkinter as tk
|
import tkinter as tk
|
||||||
from tkinter import ttk, messagebox
|
from tkinter import ttk, messagebox
|
||||||
import logging
|
import logging
|
||||||
from typing import Optional, List, Dict, Any
|
from typing import Optional, List, Dict, Any, Callable # Aggiunto Callable
|
||||||
import copy # For deep copy of step_specific_parameters
|
import copy
|
||||||
|
|
||||||
from ...core.config_manager import ConfigManager
|
from ...core.config_manager import ConfigManager
|
||||||
from ...core.exceptions import ApplicationNotFoundError
|
from ...core.exceptions import ApplicationNotFoundError
|
||||||
@ -18,7 +18,11 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
"""
|
"""
|
||||||
Base class for Add and Edit Step dialogs.
|
Base class for Add and Edit Step dialogs.
|
||||||
"""
|
"""
|
||||||
def __init__(self, parent: tk.Widget, config_manager: ConfigManager, title: str):
|
# Aggiunto attributo per memorizzare i nomi delle app
|
||||||
|
_application_names: List[str] = []
|
||||||
|
|
||||||
|
def __init__(self, parent: tk.Widget, config_manager: ConfigManager, title: str, load_data_method: Optional[Callable[[], None]] = None): # Aggiunto load_data_method
|
||||||
|
logger.debug(f"BaseStepDialog __init__ - START - Title: '{title}'")
|
||||||
super().__init__(parent)
|
super().__init__(parent)
|
||||||
self.transient(parent)
|
self.transient(parent)
|
||||||
self.title(title)
|
self.title(title)
|
||||||
@ -31,19 +35,35 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
self.step_specific_parameters: Dict[str, str] = {}
|
self.step_specific_parameters: Dict[str, str] = {}
|
||||||
self.application_defined_parameters: List[Dict[str, str]] = []
|
self.application_defined_parameters: List[Dict[str, str]] = []
|
||||||
|
|
||||||
|
logger.debug("BaseStepDialog: Calling _setup_widgets()")
|
||||||
self._setup_widgets()
|
self._setup_widgets()
|
||||||
|
logger.debug("BaseStepDialog: _setup_widgets() completed.")
|
||||||
|
|
||||||
|
if load_data_method:
|
||||||
|
logger.debug("BaseStepDialog: Calling provided load_data_method.")
|
||||||
|
load_data_method()
|
||||||
|
logger.debug("BaseStepDialog: load_data_method completed.")
|
||||||
|
else:
|
||||||
|
logger.debug("BaseStepDialog: No load_data_method provided or needed (e.g., for Add dialog).")
|
||||||
|
# Per AddDialog, carica i parametri per l'app eventualmente selezionata (nessuna)
|
||||||
|
self._load_parameters_for_selected_app(self.app_combo_var.get() or None)
|
||||||
|
|
||||||
|
|
||||||
GuiUtils.center_window(self, parent)
|
GuiUtils.center_window(self, parent)
|
||||||
self.protocol("WM_DELETE_WINDOW", self._on_cancel)
|
self.protocol("WM_DELETE_WINDOW", self._on_cancel)
|
||||||
|
|
||||||
self.grab_set()
|
self.grab_set()
|
||||||
self.focus_set()
|
self.focus_set()
|
||||||
|
logger.debug(f"BaseStepDialog __init__ for '{title}': Now calling wait_window().")
|
||||||
self.wait_window(self)
|
self.wait_window(self)
|
||||||
|
logger.debug(f"BaseStepDialog __init__ - END - Title: '{title}' (after wait_window)")
|
||||||
|
|
||||||
def _setup_widgets(self):
|
def _setup_widgets(self):
|
||||||
main_frame = ttk.Frame(self, padding="10")
|
main_frame = ttk.Frame(self, padding="10")
|
||||||
main_frame.pack(expand=True, fill=tk.BOTH)
|
main_frame.pack(expand=True, fill=tk.BOTH)
|
||||||
main_frame.columnconfigure(0, weight=1)
|
main_frame.columnconfigure(0, weight=1)
|
||||||
|
|
||||||
|
# --- Application Selection ---
|
||||||
app_frame = ttk.Frame(main_frame)
|
app_frame = ttk.Frame(main_frame)
|
||||||
app_frame.grid(row=0, column=0, sticky=tk.EW, pady=(0, 5))
|
app_frame.grid(row=0, column=0, sticky=tk.EW, pady=(0, 5))
|
||||||
ttk.Label(app_frame, text="Application:").pack(side=tk.LEFT, padx=(0,5))
|
ttk.Label(app_frame, text="Application:").pack(side=tk.LEFT, padx=(0,5))
|
||||||
@ -55,13 +75,14 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
width=48
|
width=48
|
||||||
)
|
)
|
||||||
self.app_combo.pack(side=tk.LEFT, expand=True, fill=tk.X)
|
self.app_combo.pack(side=tk.LEFT, expand=True, fill=tk.X)
|
||||||
self._populate_applications_combobox()
|
self._populate_applications_combobox() # Chiama per riempire i valori
|
||||||
self.app_combo.bind("<<ComboboxSelected>>", self._on_application_selected_event)
|
self.app_combo.bind("<<ComboboxSelected>>", self._on_application_selected_event)
|
||||||
|
|
||||||
|
# --- Wait Time ---
|
||||||
wait_frame = ttk.Frame(main_frame)
|
wait_frame = ttk.Frame(main_frame)
|
||||||
wait_frame.grid(row=1, column=0, sticky=tk.EW, pady=(0, 10))
|
wait_frame.grid(row=1, column=0, sticky=tk.EW, pady=(0, 10))
|
||||||
ttk.Label(wait_frame, text="Wait Time (seconds):").pack(side=tk.LEFT, padx=(0,5))
|
ttk.Label(wait_frame, text="Wait Time (seconds):").pack(side=tk.LEFT, padx=(0,5))
|
||||||
self.wait_time_var = tk.StringVar(value="0.0") # Initialize here
|
self.wait_time_var = tk.StringVar(value="0.0")
|
||||||
self.wait_time_spinbox = ttk.Spinbox(
|
self.wait_time_spinbox = ttk.Spinbox(
|
||||||
wait_frame,
|
wait_frame,
|
||||||
from_=0.0,
|
from_=0.0,
|
||||||
@ -73,6 +94,7 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
)
|
)
|
||||||
self.wait_time_spinbox.pack(side=tk.LEFT)
|
self.wait_time_spinbox.pack(side=tk.LEFT)
|
||||||
|
|
||||||
|
# --- Step-Specific Parameters ---
|
||||||
self.params_labelframe = ttk.LabelFrame(main_frame, text="Configure Parameters for this Step")
|
self.params_labelframe = ttk.LabelFrame(main_frame, text="Configure Parameters for this Step")
|
||||||
self.params_labelframe.grid(row=2, column=0, sticky=tk.NSEW, pady=(0,10))
|
self.params_labelframe.grid(row=2, column=0, sticky=tk.NSEW, pady=(0,10))
|
||||||
main_frame.rowconfigure(2, weight=1)
|
main_frame.rowconfigure(2, weight=1)
|
||||||
@ -112,9 +134,10 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
self.params_tree.grid(row=0, column=0, sticky=tk.NSEW)
|
self.params_tree.grid(row=0, column=0, sticky=tk.NSEW)
|
||||||
|
|
||||||
self.params_tree.bind('<Double-1>', self._on_param_tree_double_click)
|
self.params_tree.bind('<Double-1>', self._on_param_tree_double_click)
|
||||||
self.current_edit_item_iid = None # Store iid of item being edited
|
self.current_edit_item_iid = None
|
||||||
self.current_edit_widget = None
|
self.current_edit_widget = None
|
||||||
|
|
||||||
|
# --- Dialog Buttons (Save, Cancel) ---
|
||||||
dialog_buttons_frame = ttk.Frame(main_frame)
|
dialog_buttons_frame = ttk.Frame(main_frame)
|
||||||
dialog_buttons_frame.grid(row=3, column=0, sticky=tk.EW, pady=(10, 0))
|
dialog_buttons_frame.grid(row=3, column=0, sticky=tk.EW, pady=(10, 0))
|
||||||
ttk.Frame(dialog_buttons_frame).pack(side=tk.LEFT, expand=True)
|
ttk.Frame(dialog_buttons_frame).pack(side=tk.LEFT, expand=True)
|
||||||
@ -127,31 +150,32 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
|
|
||||||
def _populate_applications_combobox(self):
|
def _populate_applications_combobox(self):
|
||||||
try:
|
try:
|
||||||
app_names = [app["name"] for app in self.config_manager.get_applications()]
|
# Salva i nomi nell'attributo di classe/istanza
|
||||||
self.app_combo['values'] = sorted(app_names)
|
self._application_names = sorted([app["name"] for app in self.config_manager.get_applications()])
|
||||||
|
self.app_combo['values'] = self._application_names # Imposta i valori nel widget
|
||||||
|
logger.debug(f"Populated application combobox with names: {self._application_names}")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Failed to populate applications combobox: {e}", exc_info=True)
|
logger.error(f"Failed to populate applications combobox: {e}", exc_info=True)
|
||||||
|
self._application_names = [] # Assicura che sia una lista vuota in caso di errore
|
||||||
|
self.app_combo['values'] = []
|
||||||
messagebox.showerror("Error", "Could not load application list.", parent=self)
|
messagebox.showerror("Error", "Could not load application list.", parent=self)
|
||||||
|
|
||||||
def _on_application_selected_event(self, event=None):
|
def _on_application_selected_event(self, event=None):
|
||||||
"""Handles the ComboboxSelected event, clears overrides if app changed by user."""
|
|
||||||
selected_app_name = self.app_combo_var.get()
|
selected_app_name = self.app_combo_var.get()
|
||||||
# Only clear overrides if the app was changed by user interaction,
|
# Solo se l'utente cambia selezione rispetto a quella corrente
|
||||||
# not during initial load where step_application_name might be the same.
|
|
||||||
if self.step_application_name is not None and self.step_application_name != selected_app_name:
|
if self.step_application_name is not None and self.step_application_name != selected_app_name:
|
||||||
logger.debug(f"Application changed by user from '{self.step_application_name}' to '{selected_app_name}'. Clearing step-specific parameter overrides.")
|
logger.debug(f"Application changed by user from '{self.step_application_name}' to '{selected_app_name}'. Clearing step overrides.")
|
||||||
self.step_specific_parameters.clear()
|
self.step_specific_parameters.clear()
|
||||||
|
|
||||||
self._load_parameters_for_selected_app(selected_app_name)
|
self._load_parameters_for_selected_app(selected_app_name)
|
||||||
|
|
||||||
|
|
||||||
def _load_parameters_for_selected_app(self, app_name: Optional[str]):
|
def _load_parameters_for_selected_app(self, app_name: Optional[str]):
|
||||||
"""Loads application defined parameters and populates the tree."""
|
self.step_application_name = app_name
|
||||||
self.step_application_name = app_name # Update current app for the step
|
|
||||||
|
|
||||||
if not app_name:
|
if not app_name:
|
||||||
self.application_defined_parameters = []
|
self.application_defined_parameters = []
|
||||||
self._populate_step_parameters_tree()
|
self._populate_step_parameters_tree() # Aggiorna (svuota) albero parametri
|
||||||
return
|
return
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -159,12 +183,13 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
self.application_defined_parameters = app_data.get("parameters", [])
|
self.application_defined_parameters = app_data.get("parameters", [])
|
||||||
self._populate_step_parameters_tree()
|
self._populate_step_parameters_tree()
|
||||||
except ApplicationNotFoundError:
|
except ApplicationNotFoundError:
|
||||||
logger.warning(f"Selected application '{app_name}' not found in config.")
|
logger.warning(f"Selected application '{app_name}' not found in config (in _load_parameters).")
|
||||||
self.application_defined_parameters = []
|
self.application_defined_parameters = []
|
||||||
# self.step_specific_parameters.clear() # Already handled if app changed via event
|
|
||||||
self._populate_step_parameters_tree()
|
self._populate_step_parameters_tree()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Error loading parameters for app '{app_name}': {e}", exc_info=True)
|
logger.error(f"Error loading parameters for app '{app_name}': {e}", exc_info=True)
|
||||||
|
self.application_defined_parameters = []
|
||||||
|
self._populate_step_parameters_tree()
|
||||||
messagebox.showerror("Error", f"Could not load parameters for {app_name}.", parent=self)
|
messagebox.showerror("Error", f"Could not load parameters for {app_name}.", parent=self)
|
||||||
|
|
||||||
|
|
||||||
@ -173,28 +198,22 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
for item in self.params_tree.get_children():
|
for item in self.params_tree.get_children():
|
||||||
self.params_tree.delete(item)
|
self.params_tree.delete(item)
|
||||||
|
|
||||||
if not self.step_application_name : # No app selected yet
|
if not self.step_application_name :
|
||||||
self.params_labelframe.config(text="Select an Application to Configure Parameters")
|
self.params_labelframe.config(text="Select an Application to Configure Parameters")
|
||||||
return
|
return
|
||||||
if not self.application_defined_parameters:
|
if not self.application_defined_parameters:
|
||||||
self.params_labelframe.config(text=f"No Parameters Defined for '{self.step_application_name}'")
|
self.params_labelframe.config(text=f"No Parameters Defined for '{self.step_application_name}'")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.params_labelframe.config(text=f"Configure Parameters for '{self.step_application_name}' in this Step")
|
self.params_labelframe.config(text=f"Configure Parameters for '{self.step_application_name}' (Double-click 'Override Value' to edit)")
|
||||||
|
|
||||||
for app_param in self.application_defined_parameters:
|
for app_param in self.application_defined_parameters:
|
||||||
param_name = app_param.get("name", "UnnamedParam")
|
param_name = app_param.get("name", "UnnamedParam")
|
||||||
app_default = app_param.get("default_value", "")
|
app_default = app_param.get("default_value", "")
|
||||||
description = app_param.get("description", "")
|
description = app_param.get("description", "")
|
||||||
|
step_value = self.step_specific_parameters.get(param_name, "") # Ottiene override o stringa vuota
|
||||||
|
|
||||||
step_value = self.step_specific_parameters.get(param_name, "")
|
self.params_tree.insert('', tk.END, iid=param_name, values=(param_name, app_default, step_value, description))
|
||||||
|
|
||||||
# Use param_name as iid, assuming it's unique within an app's params
|
|
||||||
self.params_tree.insert(
|
|
||||||
'', tk.END,
|
|
||||||
iid=param_name,
|
|
||||||
values=(param_name, app_default, step_value, description)
|
|
||||||
)
|
|
||||||
|
|
||||||
def _on_param_tree_double_click(self, event):
|
def _on_param_tree_double_click(self, event):
|
||||||
self._finish_editing_param_value(save=True)
|
self._finish_editing_param_value(save=True)
|
||||||
@ -202,7 +221,7 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
region = self.params_tree.identify_region(event.x, event.y)
|
region = self.params_tree.identify_region(event.x, event.y)
|
||||||
column_id_str = self.params_tree.identify_column(event.x)
|
column_id_str = self.params_tree.identify_column(event.x)
|
||||||
|
|
||||||
if region != 'cell' or column_id_str != '#3':
|
if region != 'cell' or column_id_str != '#3': # Colonna 'Step Value'
|
||||||
return
|
return
|
||||||
|
|
||||||
self.current_edit_item_iid = self.params_tree.identify_row(event.y)
|
self.current_edit_item_iid = self.params_tree.identify_row(event.y)
|
||||||
@ -211,7 +230,7 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
|
|
||||||
x, y, width, height = self.params_tree.bbox(self.current_edit_item_iid, column=column_id_str)
|
x, y, width, height = self.params_tree.bbox(self.current_edit_item_iid, column=column_id_str)
|
||||||
current_values = self.params_tree.item(self.current_edit_item_iid, 'values')
|
current_values = self.params_tree.item(self.current_edit_item_iid, 'values')
|
||||||
current_step_value = current_values[2] # 'Step Value'
|
current_step_value = current_values[2]
|
||||||
|
|
||||||
entry_var = tk.StringVar(value=current_step_value)
|
entry_var = tk.StringVar(value=current_step_value)
|
||||||
self.current_edit_widget = ttk.Entry(self.params_tree, textvariable=entry_var)
|
self.current_edit_widget = ttk.Entry(self.params_tree, textvariable=entry_var)
|
||||||
@ -219,7 +238,6 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
self.current_edit_widget.focus_set()
|
self.current_edit_widget.focus_set()
|
||||||
self.current_edit_widget.selection_range(0, tk.END)
|
self.current_edit_widget.selection_range(0, tk.END)
|
||||||
|
|
||||||
|
|
||||||
self.current_edit_widget.bind("<Return>", lambda e: self._finish_editing_param_value(save=True))
|
self.current_edit_widget.bind("<Return>", lambda e: self._finish_editing_param_value(save=True))
|
||||||
self.current_edit_widget.bind("<KP_Enter>", lambda e: self._finish_editing_param_value(save=True))
|
self.current_edit_widget.bind("<KP_Enter>", lambda e: self._finish_editing_param_value(save=True))
|
||||||
self.current_edit_widget.bind("<Escape>", lambda e: self._finish_editing_param_value(save=False))
|
self.current_edit_widget.bind("<Escape>", lambda e: self._finish_editing_param_value(save=False))
|
||||||
@ -227,21 +245,21 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
|
|
||||||
def _finish_editing_param_value(self, save: bool):
|
def _finish_editing_param_value(self, save: bool):
|
||||||
if self.current_edit_widget and self.current_edit_item_iid:
|
if self.current_edit_widget and self.current_edit_item_iid:
|
||||||
|
param_name = self.current_edit_item_iid # iid è il nome del parametro
|
||||||
if save:
|
if save:
|
||||||
new_value = self.current_edit_widget.get()
|
new_value = self.current_edit_widget.get()
|
||||||
# iid is the param_name
|
|
||||||
param_name = self.current_edit_item_iid
|
|
||||||
|
|
||||||
current_values = list(self.params_tree.item(self.current_edit_item_iid, 'values'))
|
current_values = list(self.params_tree.item(self.current_edit_item_iid, 'values'))
|
||||||
current_values[2] = new_value
|
current_values[2] = new_value # Aggiorna la colonna 'Step Value'
|
||||||
self.params_tree.item(self.current_edit_item_iid, values=tuple(current_values))
|
self.params_tree.item(self.current_edit_item_iid, values=tuple(current_values))
|
||||||
|
|
||||||
if new_value.strip() == "" and param_name in self.step_specific_parameters :
|
# Aggiorna il dizionario degli override specifici del passo
|
||||||
|
if new_value.strip() == "": # Se l'override viene cancellato
|
||||||
|
if param_name in self.step_specific_parameters:
|
||||||
del self.step_specific_parameters[param_name]
|
del self.step_specific_parameters[param_name]
|
||||||
logger.debug(f"Override for '{param_name}' cleared.")
|
logger.debug(f"Override for step parameter '{param_name}' cleared.")
|
||||||
elif new_value.strip() != "":
|
else: # Se viene impostato un nuovo override
|
||||||
self.step_specific_parameters[param_name] = new_value
|
self.step_specific_parameters[param_name] = new_value
|
||||||
logger.debug(f"Override for '{param_name}' set to '{new_value}'.")
|
logger.debug(f"Override for step parameter '{param_name}' set to '{new_value}'.")
|
||||||
|
|
||||||
self.current_edit_widget.destroy()
|
self.current_edit_widget.destroy()
|
||||||
self.current_edit_widget = None
|
self.current_edit_widget = None
|
||||||
@ -263,13 +281,17 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
messagebox.showerror("Validation Error", "Wait Time cannot be negative.", parent=self)
|
messagebox.showerror("Validation Error", "Wait Time cannot be negative.", parent=self)
|
||||||
self.wait_time_spinbox.focus_set()
|
self.wait_time_spinbox.focus_set()
|
||||||
return False
|
return False
|
||||||
self.step_wait_time = current_step_wait_time # Store validated float
|
self.step_wait_time = current_step_wait_time # Salva il float validato
|
||||||
except ValueError:
|
except ValueError:
|
||||||
messagebox.showerror("Validation Error", "Invalid Wait Time. Please enter a number.", parent=self)
|
messagebox.showerror("Validation Error", "Invalid Wait Time. Please enter a number.", parent=self)
|
||||||
self.wait_time_spinbox.focus_set()
|
self.wait_time_spinbox.focus_set()
|
||||||
return False
|
return False
|
||||||
|
|
||||||
# self.step_application_name is already set by _load_parameters_for_selected_app
|
# self.step_application_name dovrebbe essere già impostato da _load_parameters_for_selected_app
|
||||||
|
if not self.step_application_name or self.step_application_name != selected_app:
|
||||||
|
logger.warning("Validation mismatch: step_application_name != selected_app in combobox.")
|
||||||
|
self.step_application_name = selected_app # Sincronizza per sicurezza
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
@ -278,7 +300,7 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
|
|
||||||
def _on_cancel(self):
|
def _on_cancel(self):
|
||||||
self._finish_editing_param_value(save=False)
|
self._finish_editing_param_value(save=False)
|
||||||
logger.debug(f"{self.title()} cancelled or closed.")
|
logger.debug(f"BaseStepDialog: '{self.title()}' cancelled or closed by user.")
|
||||||
self.result = None
|
self.result = None
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
@ -286,61 +308,80 @@ class BaseStepDialog(tk.Toplevel):
|
|||||||
class AddStepDialog(BaseStepDialog):
|
class AddStepDialog(BaseStepDialog):
|
||||||
"""Dialog for adding a new step to a sequence."""
|
"""Dialog for adding a new step to a sequence."""
|
||||||
def __init__(self, parent: tk.Widget, config_manager: ConfigManager):
|
def __init__(self, parent: tk.Widget, config_manager: ConfigManager):
|
||||||
super().__init__(parent, config_manager, title="Add New Step")
|
# Non passa load_data_method, verrà gestito dalla base
|
||||||
# Initial call to set the label frame text correctly if no app is selected initially
|
super().__init__(parent, config_manager, title="Add New Step", load_data_method=None)
|
||||||
self._load_parameters_for_selected_app(None)
|
logger.debug("AddStepDialog __init__ completed.")
|
||||||
|
|
||||||
|
|
||||||
def _on_save(self):
|
def _on_save(self):
|
||||||
self._finish_editing_param_value(save=True)
|
self._finish_editing_param_value(save=True) # Finalizza edit inline
|
||||||
if not self._validate_inputs():
|
if not self._validate_inputs():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# I valori validati sono in self.step_application_name, self.step_wait_time
|
||||||
|
# e self.step_specific_parameters
|
||||||
self.result = {
|
self.result = {
|
||||||
"application": self.step_application_name, # Set by _load_parameters_for_selected_app
|
"application": self.step_application_name,
|
||||||
"wait_time": self.step_wait_time, # Set by _validate_inputs
|
"wait_time": self.step_wait_time,
|
||||||
"parameters": copy.deepcopy(self.step_specific_parameters)
|
"parameters": copy.deepcopy(self.step_specific_parameters) # Usa una copia
|
||||||
}
|
}
|
||||||
logger.info(f"Step data for '{self.step_application_name}' prepared for adding.")
|
logger.info(f"AddStepDialog: Step data for '{self.step_application_name}' prepared.")
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
|
|
||||||
class EditStepDialog(BaseStepDialog):
|
class EditStepDialog(BaseStepDialog):
|
||||||
"""Dialog for editing an existing step in a sequence."""
|
"""Dialog for editing an existing step in a sequence."""
|
||||||
def __init__(self, parent: tk.Widget, config_manager: ConfigManager, step_data_to_edit: Dict[str, Any]):
|
def __init__(self, parent: tk.Widget, config_manager: ConfigManager, step_data_to_edit: Dict[str, Any]):
|
||||||
|
logger.debug(f"EditStepDialog __init__ - START - Step data to edit: {step_data_to_edit}")
|
||||||
|
# Salva i dati PRIMA di chiamare super, perché _load_initial_data ne avrà bisogno
|
||||||
self.step_data_to_edit = step_data_to_edit
|
self.step_data_to_edit = step_data_to_edit
|
||||||
super().__init__(parent, config_manager, title=f"Edit Step: {step_data_to_edit.get('application', '')}")
|
# Passa self._load_initial_data a super()
|
||||||
self._load_initial_data()
|
super().__init__(parent, config_manager, title=f"Edit Step: {step_data_to_edit.get('application', '')}", load_data_method=self._load_initial_data)
|
||||||
|
logger.debug(f"EditStepDialog __init__ - END (after super call completed)")
|
||||||
|
|
||||||
def _load_initial_data(self):
|
def _load_initial_data(self):
|
||||||
|
# Chiamato da BaseStepDialog.__init__
|
||||||
|
if not self.step_data_to_edit: # Sicurezza
|
||||||
|
logger.error("EditStepDialog _load_initial_data: step_data_to_edit is missing.")
|
||||||
|
self.after_idle(self.destroy)
|
||||||
|
return
|
||||||
|
|
||||||
app_name = self.step_data_to_edit.get("application")
|
app_name = self.step_data_to_edit.get("application")
|
||||||
wait_time = self.step_data_to_edit.get("wait_time", 0.0)
|
wait_time = self.step_data_to_edit.get("wait_time", 0.0)
|
||||||
# Deep copy to avoid modifying the original dict from sequence_dialog's current_steps_data
|
# Importante: Fa una copia profonda subito per step_specific_parameters
|
||||||
self.step_specific_parameters = copy.deepcopy(self.step_data_to_edit.get("parameters", {}))
|
self.step_specific_parameters = copy.deepcopy(self.step_data_to_edit.get("parameters", {}))
|
||||||
|
logger.debug(f"EditStepDialog _load_initial_data: Initial step overrides: {self.step_specific_parameters}")
|
||||||
|
|
||||||
if app_name and app_name in self.app_combo['values']:
|
# Usa la lista _application_names caricata da _populate_applications_combobox
|
||||||
|
if app_name and app_name in self._application_names:
|
||||||
self.app_combo_var.set(app_name)
|
self.app_combo_var.set(app_name)
|
||||||
# This will trigger _on_application_selected_event, then _load_parameters_for_selected_app
|
# Chiamata esplicita per caricare i parametri dell'app e popolare l'albero
|
||||||
# which loads app_defined_parameters and populates tree using existing step_specific_parameters
|
# Non si affida all'evento ComboboxSelected qui perché potremmo essere ancora nel costruttore
|
||||||
self._load_parameters_for_selected_app(app_name)
|
self._load_parameters_for_selected_app(app_name)
|
||||||
else:
|
else:
|
||||||
if app_name:
|
if app_name:
|
||||||
logger.warning(f"App '{app_name}' from step data not in config. Clearing selection.")
|
logger.warning(f"EditStepDialog _load_initial_data: App '{app_name}' from step data not found in available apps: {self._application_names}. Clearing selection.")
|
||||||
|
messagebox.showwarning("Application Not Found",
|
||||||
|
f"The application '{app_name}' originally selected for this step was not found.\n"
|
||||||
|
"Please select a valid application.", parent=self)
|
||||||
self.app_combo_var.set("")
|
self.app_combo_var.set("")
|
||||||
self._load_parameters_for_selected_app(None)
|
self._load_parameters_for_selected_app(None) # Svuota l'albero dei parametri
|
||||||
|
|
||||||
self.wait_time_var.set(f"{float(wait_time):.1f}")
|
self.wait_time_var.set(f"{float(wait_time):.1f}")
|
||||||
# _populate_step_parameters_tree is implicitly called via _load_parameters_for_selected_app
|
logger.info(f"EditStepDialog _load_initial_data: Data loaded for app '{app_name}'.")
|
||||||
|
|
||||||
|
|
||||||
def _on_save(self):
|
def _on_save(self):
|
||||||
self._finish_editing_param_value(save=True)
|
self._finish_editing_param_value(save=True) # Finalizza edit inline
|
||||||
if not self._validate_inputs():
|
if not self._validate_inputs():
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# I valori validati sono in self.step_application_name, self.step_wait_time
|
||||||
|
# e self.step_specific_parameters
|
||||||
self.result = {
|
self.result = {
|
||||||
"application": self.step_application_name,
|
"application": self.step_application_name,
|
||||||
"wait_time": self.step_wait_time,
|
"wait_time": self.step_wait_time,
|
||||||
"parameters": copy.deepcopy(self.step_specific_parameters)
|
"parameters": copy.deepcopy(self.step_specific_parameters) # Usa una copia
|
||||||
}
|
}
|
||||||
logger.info(f"Step data for '{self.step_application_name}' (original: '{self.step_data_to_edit.get('application')}') prepared.")
|
original_app = self.step_data_to_edit.get('application', 'N/A')
|
||||||
|
logger.info(f"EditStepDialog: Step data for '{self.step_application_name}' (original: '{original_app}') prepared.")
|
||||||
self.destroy()
|
self.destroy()
|
||||||
@ -25,6 +25,26 @@ from .dialogs.sequence_dialogs import AddSequenceDialog, EditSequenceDialog
|
|||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# --- Import Version Info FOR THE WRAPPER ITSELF ---
|
||||||
|
try:
|
||||||
|
# Use absolute import based on package name
|
||||||
|
from launchertool 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 MainWindow(tk.Tk):
|
class MainWindow(tk.Tk):
|
||||||
"""
|
"""
|
||||||
Main application window for the Launcher Tool.
|
Main application window for the Launcher Tool.
|
||||||
@ -39,7 +59,7 @@ class MainWindow(tk.Tk):
|
|||||||
config_file_path (str): Path to the configuration file.
|
config_file_path (str): Path to the configuration file.
|
||||||
"""
|
"""
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self.title("Application Launcher")
|
self.title(f"Application Launcher - {WRAPPER_APP_VERSION_STRING}")
|
||||||
logger.info("Initializing MainWindow.")
|
logger.info("Initializing MainWindow.")
|
||||||
|
|
||||||
self.config_manager = ConfigManager(config_file_path=config_file_path)
|
self.config_manager = ConfigManager(config_file_path=config_file_path)
|
||||||
@ -183,13 +203,19 @@ class MainWindow(tk.Tk):
|
|||||||
if not selected_item_ids:
|
if not selected_item_ids:
|
||||||
messagebox.showinfo("Edit Application", "Please select an application to edit.", parent=self)
|
messagebox.showinfo("Edit Application", "Please select an application to edit.", parent=self)
|
||||||
return
|
return
|
||||||
# Assuming iid is the application name
|
|
||||||
application_name = selected_item_ids[0]
|
application_name = selected_item_ids[0] # Questo è l'iid
|
||||||
logger.info(f"Showing Edit Application dialog for: {application_name}")
|
logger.info(f"Attempting to show Edit Application dialog for: '{application_name}'") # LOG
|
||||||
|
|
||||||
|
if not application_name: # Aggiungi un controllo esplicito
|
||||||
|
messagebox.showerror("Error", "Selected application has an invalid name.", parent=self)
|
||||||
|
logger.error("Selected application for edit has no name (iid was empty).")
|
||||||
|
return
|
||||||
|
|
||||||
dialog = EditApplicationDialog(self, self.config_manager, application_name)
|
dialog = EditApplicationDialog(self, self.config_manager, application_name)
|
||||||
if dialog.result:
|
if dialog.result: # dialog.result è impostato solo se il dialogo salva con successo
|
||||||
self.refresh_applications_tree()
|
self.refresh_applications_tree()
|
||||||
self.refresh_sequences_tree()
|
self.refresh_sequences_tree() # App name change impacts sequences
|
||||||
|
|
||||||
def _delete_selected_application(self):
|
def _delete_selected_application(self):
|
||||||
selected_item_ids = self.applications_tree.selection()
|
selected_item_ids = self.applications_tree.selection()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user