Remove obsolete optimization and resource monitoring documentation; update scenario management to ensure user directory saving and template protection.

This commit is contained in:
2025-08-06 17:33:03 +02:00
parent a31b01238a
commit 1e8123efdb
11 changed files with 546 additions and 521 deletions
+187 -30
View File
@@ -678,29 +678,46 @@ class Scenario:
def save(self):
"""
Save the scenario to disk as a JSON file.
Save the scenario to disk as a JSON file in the user's scenarios directory.
Always saves to the user directory, never to pre-made scenarios.
"""
try:
scenario_dir = self.get_scenario_dir()
# Always save to user directory, even if originally loaded from pre-made
scenario_dir = os.path.join(CONFIG_DIR, self.name)
if not os.path.exists(scenario_dir):
os.makedirs(scenario_dir)
os.makedirs(scenario_dir, exist_ok=True)
# If this scenario was originally from pre-made folder and doesn't exist in user folder,
# copy all assets first
if not os.path.exists(os.path.join(scenario_dir, 'scenario.json')):
self.copy_to_user_scenarios()
with open(os.path.join(scenario_dir, 'scenario.json'), 'w') as f:
json.dump(self.to_dict(), f, indent=2)
logger.info(f"Scenario '{self.name}' saved.")
logger.info(f"Scenario '{self.name}' saved to user directory.")
except Exception as e:
logger.error(f"Failed to save scenario '{self.name}': {e}")
@staticmethod
def load(name):
def load(name, from_premade=False):
"""
Load a scenario from disk by name.
If from_premade is True, loads from the pre-made scenarios folder.
"""
try:
scenario_dir = os.path.join(CONFIG_DIR, name)
with open(os.path.join(scenario_dir, 'scenario.json'), 'r') as f:
if from_premade:
scenario_dir = os.path.join(os.path.dirname(__file__), 'templates', name)
else:
scenario_dir = os.path.join(CONFIG_DIR, name)
scenario_file = os.path.join(scenario_dir, 'scenario.json')
if not os.path.exists(scenario_file):
logger.error(f"Scenario file not found: {scenario_file}")
return None
with open(scenario_file, 'r') as f:
scenario = Scenario.from_dict(json.load(f))
logger.info(f"Scenario '{name}' loaded.")
logger.info(f"Scenario '{name}' loaded from {'pre-made' if from_premade else 'user'} directory.")
return scenario
except Exception as e:
logger.error(f"Failed to load scenario '{name}': {e}")
@@ -709,9 +726,57 @@ class Scenario:
@staticmethod
def list_all():
"""
List all scenario directories.
List all scenario directories from the user's config directory.
"""
if not os.path.exists(CONFIG_DIR):
os.makedirs(CONFIG_DIR, exist_ok=True)
return [d for d in os.listdir(CONFIG_DIR) if os.path.isdir(os.path.join(CONFIG_DIR, d))]
@staticmethod
def list_premade():
"""
List all pre-made scenario directories.
"""
premade_dir = os.path.join(os.path.dirname(__file__), 'templates')
if not os.path.exists(premade_dir):
return []
return [d for d in os.listdir(premade_dir) if os.path.isdir(os.path.join(premade_dir, d))]
def copy_to_user_scenarios(self):
"""
Copy this scenario (and its assets) to the user's scenarios directory.
This is used when a pre-made scenario is selected and needs to be editable.
"""
try:
import shutil
# Source directory (could be pre-made or user directory)
source_dir = self.get_scenario_dir()
# Destination directory (always user directory)
dest_dir = os.path.join(CONFIG_DIR, self.name)
# If the destination already exists, we don't need to copy
if os.path.exists(dest_dir):
logger.debug(f"User scenario '{self.name}' already exists, skipping copy.")
return True
# Check if source is a pre-made scenario
premade_dir = os.path.join(os.path.dirname(__file__), 'templates', self.name)
if os.path.exists(premade_dir):
source_dir = premade_dir
if os.path.exists(source_dir):
shutil.copytree(source_dir, dest_dir)
logger.info(f"Copied scenario '{self.name}' to user scenarios directory.")
return True
else:
logger.error(f"Source scenario directory not found: {source_dir}")
return False
except Exception as e:
logger.error(f"Failed to copy scenario '{self.name}' to user directory: {e}")
return False
def take_screenshot_with_tkinter():
"""
@@ -2067,27 +2132,41 @@ class MainWindow(QtWidgets.QMainWindow):
def delete_scenario(self):
"""
Delete the current scenario after confirmation.
Only deletes user scenarios - templates are protected.
"""
if not self.current_scenario:
return
name = self.current_scenario.name
scenario_dir = self.current_scenario.get_scenario_dir()
# Check if this is trying to delete a template (should never happen but extra protection)
templates_dir = os.path.join(os.path.dirname(__file__), 'templates', name)
if os.path.samefile(scenario_dir, templates_dir) if os.path.exists(templates_dir) else False:
QtWidgets.QMessageBox.warning(
self,
"Cannot Delete Template",
f"Cannot delete template scenario '{name}'. Templates are protected from deletion."
)
return
reply = QtWidgets.QMessageBox.question(
self,
"Delete Scenario",
f"Are you sure you want to delete the scenario '{name}'? This cannot be undone.",
f"Are you sure you want to delete the scenario '{name}'? This will delete the entire scenario folder and cannot be undone.",
QtWidgets.QMessageBox.StandardButton.Yes | QtWidgets.QMessageBox.StandardButton.No
)
if reply == QtWidgets.QMessageBox.StandardButton.Yes:
try:
scenario_dir = self.current_scenario.get_scenario_dir()
if os.path.exists(scenario_dir):
import shutil
shutil.rmtree(scenario_dir)
logger.info(f"Deleted scenario: {name}")
logger.info(f"Deleted scenario folder: {scenario_dir}")
self.current_scenario = None
self.load_scenarios()
except Exception as e:
logger.error(f"Failed to delete scenario '{name}': {e}")
QtWidgets.QMessageBox.critical(self, "Delete Error", f"Failed to delete scenario.\n{e}")
QtWidgets.QMessageBox.critical(self, "Delete Error", f"Failed to delete scenario folder.\n{e}")
def _log_steps_list(self, idx):
"""
@@ -2267,16 +2346,32 @@ class MainWindow(QtWidgets.QMainWindow):
def load_scenarios(self):
"""
Load all scenarios and populate the scenario combo box.
Includes both user scenarios and pre-made scenarios.
"""
logger.debug('Loading scenarios...')
self.combo.clear()
scenarios = Scenario.list_all()
for name in scenarios:
self.combo.addItem(name)
# Load user scenarios
user_scenarios = Scenario.list_all()
premade_scenarios = Scenario.list_premade()
# Add user scenarios first
for name in user_scenarios:
self.combo.addItem(f"📁 {name}", {'name': name, 'is_premade': False})
# Add separator if both types exist
if user_scenarios and premade_scenarios:
self.combo.insertSeparator(self.combo.count())
# Add pre-made scenarios with a different icon/prefix
for name in premade_scenarios:
# Only add if not already in user scenarios
if name not in user_scenarios:
self.combo.addItem(f"{name} (Template)", {'name': name, 'is_premade': True})
self.refresh_window_list()
if not scenarios:
if not user_scenarios and not premade_scenarios:
choice_dialog = QtWidgets.QMessageBox(self)
choice_dialog.setWindowTitle('No Scenarios Found')
choice_dialog.setText('No scenarios found. Would you like to create a new scenario or import one?')
@@ -2291,8 +2386,8 @@ class MainWindow(QtWidgets.QMainWindow):
logger.info(f'Creating new scenario: {name}')
s = Scenario(name)
s.save()
self.combo.addItem(name)
self.combo.setCurrentText(name)
self.combo.addItem(f"📁 {name}", {'name': name, 'is_premade': False})
self.combo.setCurrentIndex(0)
else:
self.refresh_lists()
elif clicked == import_btn:
@@ -2300,23 +2395,79 @@ class MainWindow(QtWidgets.QMainWindow):
else:
self.refresh_lists()
else:
# Try to restore last scenario
last_scenario_name = self.read_last_scenario()
if last_scenario_name and last_scenario_name in scenarios:
self.combo.setCurrentText(last_scenario_name)
if last_scenario_name:
# Look for the scenario in the combo box
for i in range(self.combo.count()):
item_data = self.combo.itemData(i)
if item_data and item_data.get('name') == last_scenario_name:
self.combo.setCurrentIndex(i)
break
else:
# If last scenario not found, select first available
if self.combo.count() > 0:
self.combo.setCurrentIndex(0)
else:
self.combo.setCurrentIndex(0)
# Select first scenario if available
if self.combo.count() > 0:
self.combo.setCurrentIndex(0)
def select_scenario(self):
"""
Load the selected scenario and update the UI.
Handles both user scenarios and pre-made scenario templates.
"""
name = self.combo.currentText()
logger.info(f'Scenario selected: {name}')
if name:
self.current_scenario = Scenario.load(name)
self.save_last_scenario(name)
self.refresh_lists()
self.load_selected_window_from_config()
current_index = self.combo.currentIndex()
item_data = self.combo.itemData(current_index)
if not item_data:
# Might be a separator or invalid selection
logger.warning("No valid scenario data found for selection")
return
scenario_name = item_data.get('name')
is_premade = item_data.get('is_premade', False)
logger.info(f'Scenario selected: {scenario_name} ({"pre-made" if is_premade else "user"})')
if scenario_name:
# Load the scenario from appropriate location
self.current_scenario = Scenario.load(scenario_name, from_premade=is_premade)
if self.current_scenario:
# If it's a pre-made scenario, immediately copy it to user folder to protect the template
if is_premade:
logger.info(f"Loaded template scenario '{scenario_name}'. Copying to user scenarios folder for editing.")
success = self.current_scenario.copy_to_user_scenarios()
if success:
# Reload the scenario from the user directory to ensure we're working with the copy
self.current_scenario = Scenario.load(scenario_name, from_premade=False)
# Show a brief tooltip or status message
QtWidgets.QToolTip.showText(
self.combo.mapToGlobal(self.combo.rect().bottomLeft()),
"Template copied to your scenarios folder for editing!",
self.combo,
QtCore.QRect(),
3000 # Show for 3 seconds
)
else:
QtWidgets.QMessageBox.warning(
self,
"Copy Error",
f"Failed to copy template '{scenario_name}' to user scenarios folder. You may not be able to save changes."
)
self.save_last_scenario(scenario_name)
self.refresh_lists()
self.load_selected_window_from_config()
else:
logger.error(f"Failed to load scenario: {scenario_name}")
QtWidgets.QMessageBox.warning(
self,
"Load Error",
f"Failed to load scenario '{scenario_name}'. The scenario file may be corrupted."
)
def create_scenario(self):
"""
@@ -2328,7 +2479,13 @@ class MainWindow(QtWidgets.QMainWindow):
s = Scenario(name)
s.save()
self.load_scenarios()
self.combo.setCurrentText(name)
# Find and select the newly created scenario
for i in range(self.combo.count()):
item_data = self.combo.itemData(i)
if item_data and item_data.get('name') == name and not item_data.get('is_premade', False):
self.combo.setCurrentIndex(i)
break
def rename_scenario(self):
"""