GUI - File Type Keybinds And F2 Renaming
This commit is contained in:
@@ -18,40 +18,43 @@ class AssetRestructureHandler(QObject):
|
||||
if not isinstance(model, UnifiedViewModel):
|
||||
raise TypeError("AssetRestructureHandler requires a UnifiedViewModel instance.")
|
||||
self.model = model
|
||||
# Connect to the modified signal (passes FileRule object)
|
||||
self.model.targetAssetOverrideChanged.connect(self.handle_target_asset_override)
|
||||
# Connect to the new signal for AssetRule name changes
|
||||
self.model.assetNameChanged.connect(self.handle_asset_name_changed)
|
||||
log.debug("AssetRestructureHandler initialized.")
|
||||
|
||||
@Slot(QModelIndex, object)
|
||||
def handle_target_asset_override(self, index: QModelIndex, new_target_path: object):
|
||||
@Slot(FileRule, str, QModelIndex)
|
||||
def handle_target_asset_override(self, file_rule_item: FileRule, new_target_name: str, index: QModelIndex): # Ensure FileRule is imported
|
||||
"""
|
||||
Slot connected to UnifiedViewModel.targetAssetOverrideChanged.
|
||||
Orchestrates model changes based on the new target asset path.
|
||||
|
||||
Args:
|
||||
index: The QModelIndex of the FileRule whose override changed.
|
||||
new_target_path: The new target asset path (string or None).
|
||||
file_rule_item: The FileRule object whose override changed.
|
||||
new_target_name: The new target asset path (string).
|
||||
index: The QModelIndex of the changed item (passed by the signal).
|
||||
"""
|
||||
log.debug(f"Handler received targetAssetOverrideChanged: Index=({index.row()},{index.column()}), New Path='{new_target_path}'")
|
||||
|
||||
if not index.isValid():
|
||||
log.warning("Handler received invalid index. Aborting.")
|
||||
if not isinstance(file_rule_item, FileRule): # Check the correct parameter
|
||||
log.warning(f"Handler received targetAssetOverrideChanged for non-FileRule item: {type(file_rule_item)}. Aborting.")
|
||||
return
|
||||
|
||||
file_item = self.model.getItem(index)
|
||||
if not isinstance(file_item, FileRule):
|
||||
log.warning(f"Handler received index for non-FileRule item: {type(file_item)}. Aborting.")
|
||||
return
|
||||
# Crucially, use file_rule_item for all logic. 'index' is for context or if model interaction is *unavoidable* (which it shouldn't be here).
|
||||
log.debug(f"Handler received targetAssetOverrideChanged: OBJECT='{file_rule_item!r}', FILE_PATH='{file_rule_item.file_path}', NEW_NAME='{new_target_name}'")
|
||||
|
||||
# Ensure new_target_path is a string or None
|
||||
new_target_name = str(new_target_path).strip() if new_target_path is not None else None
|
||||
if new_target_name == "": new_target_name = None # Treat empty string as None
|
||||
# Ensure new_target_name is a string or None (already string from signal, but good practice if it could be object)
|
||||
effective_new_target_name = str(new_target_name).strip() if new_target_name is not None else None
|
||||
if effective_new_target_name == "": effective_new_target_name = None # Treat empty string as None
|
||||
|
||||
# --- Get necessary context ---
|
||||
old_parent_asset = getattr(file_item, 'parent_asset', None)
|
||||
# Use file_rule_item directly
|
||||
old_parent_asset = getattr(file_rule_item, 'parent_asset', None)
|
||||
if not old_parent_asset:
|
||||
log.error(f"Handler: File item '{Path(file_item.file_path).name}' has no parent asset. Cannot restructure.")
|
||||
log.error(f"Handler: File item '{Path(file_rule_item.file_path).name}' has no parent asset. Cannot restructure.")
|
||||
# Note: Data change already happened in setData, cannot easily revert here.
|
||||
return
|
||||
|
||||
# Use file_rule_item directly
|
||||
source_rule = getattr(old_parent_asset, 'parent_source', None)
|
||||
if not source_rule:
|
||||
log.error(f"Handler: Could not find SourceRule for parent asset '{old_parent_asset.asset_name}'. Cannot restructure.")
|
||||
@@ -59,80 +62,165 @@ class AssetRestructureHandler(QObject):
|
||||
|
||||
# --- Logic based on the new target name ---
|
||||
target_parent_asset = None
|
||||
target_parent_index = QModelIndex()
|
||||
target_parent_index = QModelIndex() # This will be the QModelIndex of the target AssetRule
|
||||
move_occurred = False
|
||||
|
||||
# 1. Find existing target parent AssetRule within the same SourceRule
|
||||
if new_target_name:
|
||||
if effective_new_target_name:
|
||||
for i, asset in enumerate(source_rule.assets):
|
||||
if asset.asset_name == new_target_name:
|
||||
if asset.asset_name == effective_new_target_name:
|
||||
target_parent_asset = asset
|
||||
# Get index for the target parent
|
||||
# Get QModelIndex for the target parent AssetRule
|
||||
try:
|
||||
source_rule_row = self.model._source_rules.index(source_rule)
|
||||
source_rule_index = self.model.createIndex(source_rule_row, 0, source_rule)
|
||||
target_parent_index = self.model.index(i, 0, source_rule_index)
|
||||
target_parent_index = self.model.index(i, 0, source_rule_index) # QModelIndex for the target AssetRule
|
||||
if not target_parent_index.isValid():
|
||||
log.error(f"Handler: Failed to create valid index for existing target parent '{new_target_name}'.")
|
||||
log.error(f"Handler: Failed to create valid QModelIndex for existing target parent '{effective_new_target_name}'.")
|
||||
target_parent_asset = None # Reset if index is invalid
|
||||
except ValueError:
|
||||
log.error(f"Handler: Could not find SourceRule index while looking for target parent '{new_target_name}'.")
|
||||
log.error(f"Handler: Could not find SourceRule index while looking for target parent '{effective_new_target_name}'.")
|
||||
target_parent_asset = None # Reset if index is invalid
|
||||
break # Found the asset
|
||||
|
||||
# 2. Handle Move or Creation
|
||||
if target_parent_asset:
|
||||
if target_parent_asset: # An existing AssetRule to move to was found
|
||||
# --- Move to Existing Parent ---
|
||||
if target_parent_asset != old_parent_asset:
|
||||
log.info(f"Handler: Moving file '{Path(file_item.file_path).name}' to existing asset '{target_parent_asset.asset_name}'.")
|
||||
if self.model.moveFileRule(index, target_parent_index):
|
||||
log.info(f"Handler: Moving file '{Path(file_rule_item.file_path).name}' to existing asset '{target_parent_asset.asset_name}'.")
|
||||
# The 'index' parameter IS the QModelIndex of the FileRule being changed.
|
||||
# No need to re-fetch or re-validate it if the signal emits it correctly.
|
||||
# The core issue was using a stale index to get the *object*, now we *have* the object.
|
||||
source_file_qmodelindex = index # Use the index passed by the signal
|
||||
|
||||
if not source_file_qmodelindex or not source_file_qmodelindex.isValid(): # Should always be valid if signal emits it
|
||||
log.error(f"Handler: Received invalid QModelIndex for source file '{Path(file_rule_item.file_path).name}'. Cannot move.")
|
||||
return
|
||||
|
||||
if self.model.moveFileRule(source_file_qmodelindex, target_parent_index): # target_parent_index is for the AssetRule
|
||||
move_occurred = True
|
||||
else:
|
||||
log.error(f"Handler: Model failed to move file rule to existing asset '{target_parent_asset.asset_name}'.")
|
||||
# Consider how to handle failure - maybe log and continue to cleanup?
|
||||
else:
|
||||
# Target is the same as the old parent. No move needed.
|
||||
log.debug(f"Handler: Target asset '{new_target_name}' is the same as the current parent. No move required.")
|
||||
pass # No move needed, but might still need cleanup if old parent becomes empty later (unlikely in this specific case)
|
||||
log.debug(f"Handler: Target asset '{effective_new_target_name}' is the same as the current parent. No move required.")
|
||||
|
||||
elif new_target_name: # Only create if a *new* specific target name was given
|
||||
elif effective_new_target_name: # No existing AssetRule found, but a new name is provided. Create it.
|
||||
# --- Create New Parent AssetRule and Move ---
|
||||
log.info(f"Handler: Creating new asset '{new_target_name}' and moving file '{Path(file_item.file_path).name}'.")
|
||||
# Create the new asset rule using the model's method
|
||||
new_asset_index = self.model.createAssetRule(source_rule, new_target_name, copy_from_asset=old_parent_asset)
|
||||
log.info(f"Handler: Creating new asset '{effective_new_target_name}' and moving file '{Path(file_rule_item.file_path).name}'.")
|
||||
new_asset_qmodelindex = self.model.createAssetRule(source_rule, effective_new_target_name, copy_from_asset=old_parent_asset)
|
||||
|
||||
if new_asset_index.isValid():
|
||||
# Now move the file to the newly created asset
|
||||
if self.model.moveFileRule(index, new_asset_index):
|
||||
if new_asset_qmodelindex.isValid():
|
||||
target_parent_asset = new_asset_qmodelindex.internalPointer() # Get the newly created AssetRule object
|
||||
target_parent_index = new_asset_qmodelindex # The QModelIndex of the new AssetRule
|
||||
|
||||
source_file_qmodelindex = index # Use the index passed by the signal
|
||||
if not source_file_qmodelindex or not source_file_qmodelindex.isValid(): # Should always be valid
|
||||
log.error(f"Handler: Received invalid QModelIndex for source file '{Path(file_rule_item.file_path).name}'. Cannot move to new asset.")
|
||||
self.model.removeAssetRule(target_parent_asset) # Attempt to clean up newly created asset
|
||||
return
|
||||
|
||||
if self.model.moveFileRule(source_file_qmodelindex, target_parent_index): # Move to the new AssetRule
|
||||
move_occurred = True
|
||||
target_parent_asset = new_asset_index.internalPointer() # Update for cleanup check
|
||||
else:
|
||||
log.error(f"Handler: Model failed to move file rule to newly created asset '{new_target_name}'.")
|
||||
# If move fails after creation, should we remove the created asset? Maybe.
|
||||
# For now, just log the error.
|
||||
log.error(f"Handler: Model failed to move file rule to newly created asset '{effective_new_target_name}'.")
|
||||
# Consider removing the newly created asset if the move fails
|
||||
self.model.removeAssetRule(target_parent_asset) # Attempt to clean up
|
||||
else:
|
||||
log.error(f"Handler: Model failed to create new asset rule '{new_target_name}'. Cannot move file.")
|
||||
log.error(f"Handler: Model failed to create new asset rule '{effective_new_target_name}'. Cannot move file.")
|
||||
|
||||
else: # new_target_name is None or empty
|
||||
# --- Moving back to original/default parent (Clearing Override) ---
|
||||
# The file *should* already be under its original parent if the override was just cleared.
|
||||
# However, if it was previously moved *away* from its original parent due to an override,
|
||||
# clearing the override *should* ideally move it back.
|
||||
# This logic is complex: we need to know the *original* parent before any overrides.
|
||||
# The current structure doesn't explicitly store this.
|
||||
# For now, assume clearing the override means it stays in its *current* parent,
|
||||
# and we only handle cleanup if that parent becomes empty.
|
||||
# A more robust solution might involve finding the asset matching the file's *directory* name.
|
||||
log.debug(f"Handler: Target asset override cleared for '{Path(file_item.file_path).name}'. File remains in parent '{old_parent_asset.asset_name}'.")
|
||||
# No move occurs in this simplified interpretation.
|
||||
else: # effective_new_target_name is None or empty (override cleared)
|
||||
log.debug(f"Handler: Target asset override cleared for '{Path(file_rule_item.file_path).name}'. File remains in parent '{old_parent_asset.asset_name}'.")
|
||||
# No move occurs in this interpretation if the override is simply cleared.
|
||||
# The file_rule_item.target_asset_name_override is now None (set by model.setData).
|
||||
|
||||
# 3. Cleanup Empty Old Parent (only if a move occurred)
|
||||
# Check the old_parent_asset *after* the potential move
|
||||
if move_occurred and old_parent_asset and not old_parent_asset.files:
|
||||
# 3. Cleanup Empty Old Parent (only if a move occurred and old parent is now empty)
|
||||
if move_occurred and old_parent_asset and not old_parent_asset.files and old_parent_asset != target_parent_asset:
|
||||
log.info(f"Handler: Attempting to remove empty old parent asset '{old_parent_asset.asset_name}'.")
|
||||
if not self.model.removeAssetRule(old_parent_asset):
|
||||
log.warning(f"Handler: Model failed to remove empty old parent asset '{old_parent_asset.asset_name}'.")
|
||||
elif move_occurred:
|
||||
log.debug(f"Handler: Old parent asset '{old_parent_asset.asset_name}' still contains files. No removal needed.")
|
||||
log.debug(f"Handler: Old parent asset '{old_parent_asset.asset_name}' still contains files or is the target. No removal needed.")
|
||||
|
||||
log.debug(f"Handler finished processing targetAssetOverrideChanged for '{Path(file_item.file_path).name}'.")
|
||||
log.debug(f"Handler finished processing targetAssetOverrideChanged for '{Path(file_rule_item.file_path).name}'.")
|
||||
|
||||
def _get_qmodelindex_for_item(self, item_to_find):
|
||||
"""
|
||||
Helper to find the QModelIndex for a given FileRule or AssetRule item.
|
||||
Returns a valid QModelIndex or QModelIndex() if not found/invalid.
|
||||
"""
|
||||
if isinstance(item_to_find, FileRule):
|
||||
parent_asset = getattr(item_to_find, 'parent_asset', None)
|
||||
if not parent_asset: return QModelIndex()
|
||||
source_rule = getattr(parent_asset, 'parent_source', None)
|
||||
if not source_rule: return QModelIndex()
|
||||
|
||||
try:
|
||||
source_rule_row = self.model._source_rules.index(source_rule)
|
||||
source_rule_index = self.model.createIndex(source_rule_row, 0, source_rule)
|
||||
if not source_rule_index.isValid(): return QModelIndex()
|
||||
|
||||
parent_asset_row = source_rule.assets.index(parent_asset)
|
||||
parent_asset_index = self.model.index(parent_asset_row, 0, source_rule_index)
|
||||
if not parent_asset_index.isValid(): return QModelIndex()
|
||||
|
||||
item_row = parent_asset.files.index(item_to_find)
|
||||
return self.model.index(item_row, 0, parent_asset_index)
|
||||
except ValueError:
|
||||
log.error(f"Error finding item {item_to_find} in model hierarchy during QModelIndex reconstruction.")
|
||||
return QModelIndex()
|
||||
|
||||
elif isinstance(item_to_find, AssetRule):
|
||||
source_rule = getattr(item_to_find, 'parent_source', None)
|
||||
if not source_rule: return QModelIndex()
|
||||
try:
|
||||
source_rule_row = self.model._source_rules.index(source_rule)
|
||||
source_rule_index = self.model.createIndex(source_rule_row, 0, source_rule)
|
||||
if not source_rule_index.isValid(): return QModelIndex()
|
||||
|
||||
item_row = source_rule.assets.index(item_to_find)
|
||||
return self.model.index(item_row, 0, source_rule_index)
|
||||
except ValueError:
|
||||
log.error(f"Error finding asset {item_to_find.asset_name} in model hierarchy during QModelIndex reconstruction.")
|
||||
return QModelIndex()
|
||||
return QModelIndex()
|
||||
|
||||
@Slot(AssetRule, str, QModelIndex) # Updated signature
|
||||
def handle_asset_name_changed(self, asset_rule_item: AssetRule, new_name: str, index: QModelIndex): # Ensure AssetRule is imported
|
||||
"""
|
||||
Slot connected to UnifiedViewModel.assetNameChanged.
|
||||
Handles logic when an AssetRule's name is changed.
|
||||
|
||||
Args:
|
||||
asset_rule_item: The AssetRule object whose name changed.
|
||||
new_name: The new name of the asset.
|
||||
index: The QModelIndex of the changed AssetRule item.
|
||||
"""
|
||||
if not isinstance(asset_rule_item, AssetRule):
|
||||
log.warning(f"Handler received assetNameChanged for non-AssetRule item: {type(asset_rule_item)}. Aborting.")
|
||||
return
|
||||
|
||||
# The 'old_name' is not directly passed by the new signal signature.
|
||||
# If needed, it would have to be inferred or stored prior to the change.
|
||||
# However, the model's setData already handles updating child FileRule targets.
|
||||
# This handler's main job is to react to the AssetRule object itself.
|
||||
log.debug(f"Handler received assetNameChanged: OBJECT='{asset_rule_item!r}', ASSET_NAME='{asset_rule_item.asset_name}', NEW_NAME='{new_name}'")
|
||||
|
||||
|
||||
# The UnifiedViewModel.setData has already updated FileRule.target_asset_name_override
|
||||
# for any FileRules that were pointing to the *old* asset name across the entire model.
|
||||
|
||||
# The primary purpose of this handler slot, given the problem description,
|
||||
# is to ensure that if any restructuring or disk operations were tied to an AssetRule's
|
||||
# name, they would now correctly use 'asset_rule_item' (the actual object)
|
||||
# and 'new_name'.
|
||||
|
||||
# For this specific task, confirming correct identification is key.
|
||||
# If this handler were also responsible for renaming directories on disk,
|
||||
# this is where that logic would go, using asset_rule_item and new_name.
|
||||
# The old name would need to be retrieved differently if essential for such an operation,
|
||||
# e.g. by storing it temporarily before the model's setData commits the change,
|
||||
# or by having the signal pass it (which it currently doesn't in the revised design).
|
||||
# For now, the model handles the critical part of updating linked FileRules.
|
||||
|
||||
log.info(f"Handler correctly identified AssetRule '{new_name}' for processing using the direct object. Model's setData handles related FileRule target updates.")
|
||||
@@ -19,6 +19,7 @@ from PySide6.QtWidgets import (
|
||||
)
|
||||
from PySide6.QtCore import Qt, QThread, Slot, Signal, QObject, QModelIndex, QItemSelectionModel, QPoint, QTimer # Added Signal, QObject, QModelIndex, QItemSelectionModel, QPoint, QTimer
|
||||
from PySide6.QtGui import QColor, QAction, QPalette, QClipboard # Add QColor import, QAction, QPalette, QClipboard
|
||||
from PySide6.QtGui import QKeySequence
|
||||
|
||||
# --- Local GUI Imports ---
|
||||
from .preset_editor_widget import PresetEditorWidget
|
||||
@@ -34,7 +35,7 @@ from rule_structure import SourceRule, AssetRule, FileRule # Import Rule Structu
|
||||
# --- GUI Model Imports ---
|
||||
# Removed: from gui.preview_table_model import PreviewTableModel, PreviewSortFilterProxyModel
|
||||
# Removed: from gui.rule_hierarchy_model import RuleHierarchyModel
|
||||
from gui.unified_view_model import UnifiedViewModel # Import the new unified model
|
||||
from gui.unified_view_model import UnifiedViewModel, CustomRoles # Import the new unified model and CustomRoles
|
||||
# Removed delegate imports, now handled by MainPanelWidget
|
||||
from .prediction_handler import RuleBasedPredictionHandler # Corrected import path
|
||||
from .llm_interaction_handler import LLMInteractionHandler # Import the new handler
|
||||
@@ -230,12 +231,39 @@ class MainWindow(QMainWindow):
|
||||
|
||||
# --- Connect Model Signals ---
|
||||
self.unified_model.targetAssetOverrideChanged.connect(self.restructure_handler.handle_target_asset_override)
|
||||
self.unified_model.assetNameChanged.connect(self.restructure_handler.handle_asset_name_changed) # Added connection
|
||||
# --- Connect LLM Editor Signals ---
|
||||
self.llm_editor_widget.settings_saved.connect(self._on_llm_settings_saved) # Connect save signal
|
||||
|
||||
# --- Adjust Splitter ---
|
||||
self.splitter.setSizes([400, 800]) # Initial size ratio
|
||||
|
||||
# --- Initialize Keybind Map ---
|
||||
self.key_char_to_qt_key = {
|
||||
'C': Qt.Key_C, 'R': Qt.Key_R, 'N': Qt.Key_N, 'M': Qt.Key_M,
|
||||
'D': Qt.Key_D, 'E': Qt.Key_E, 'X': Qt.Key_X
|
||||
}
|
||||
self.qt_key_to_ftd_map = {}
|
||||
try:
|
||||
base_settings = load_base_config()
|
||||
file_type_defs = base_settings.get('FILE_TYPE_DEFINITIONS', {})
|
||||
for ftd_key, ftd_value in file_type_defs.items():
|
||||
if isinstance(ftd_value, dict) and 'keybind' in ftd_value:
|
||||
char_key = ftd_value['keybind']
|
||||
qt_key_val = self.key_char_to_qt_key.get(char_key)
|
||||
if qt_key_val:
|
||||
if qt_key_val not in self.qt_key_to_ftd_map:
|
||||
self.qt_key_to_ftd_map[qt_key_val] = []
|
||||
# Ensure consistent order for toggleable types if they are defined together under one key
|
||||
# For example, if 'R' maps to ROUGH then GLOSS, they should appear in that order.
|
||||
# This relies on the order in app_settings.json and dict iteration (Python 3.7+).
|
||||
self.qt_key_to_ftd_map[qt_key_val].append(ftd_key)
|
||||
log.info(f"Loaded keybind map: {self.qt_key_to_ftd_map}")
|
||||
except Exception as e:
|
||||
log.error(f"Failed to load keybind configurations: {e}")
|
||||
# self.qt_key_to_ftd_map will be empty, keybinds won't work.
|
||||
|
||||
|
||||
# --- UI Setup Methods ---
|
||||
# setup_editor_panel_ui, _create_editor_general_tab, _create_editor_mapping_tab moved to PresetEditorWidget
|
||||
|
||||
@@ -1312,11 +1340,209 @@ class MainWindow(QMainWindow):
|
||||
log.warning("get_llm_source_preset_name called before preset_editor_widget was initialized.")
|
||||
return None
|
||||
|
||||
def keyPressEvent(self, event):
|
||||
"""Handles key press events for implementing keybinds."""
|
||||
log.debug(f"KeyPressEvent: key={event.key()}, modifiers={event.modifiers()}, text='{event.text()}'")
|
||||
|
||||
if not self.main_panel_widget or not self.unified_model:
|
||||
log.warning("Key press ignored: Main panel or unified model not available.")
|
||||
super().keyPressEvent(event)
|
||||
return
|
||||
|
||||
selected_view_indexes = self.main_panel_widget.unified_view.selectionModel().selectedIndexes()
|
||||
if not selected_view_indexes:
|
||||
log.debug("Key press ignored: No items selected.")
|
||||
super().keyPressEvent(event)
|
||||
return
|
||||
|
||||
# Get unique model indexes (typically one per selected row)
|
||||
# Assuming unified_view uses unified_model directly or proxy maps correctly
|
||||
model_indexes_to_process = []
|
||||
unique_rows = set()
|
||||
for view_idx in selected_view_indexes:
|
||||
# If using a proxy:
|
||||
# model_idx = self.main_panel_widget.unified_view.model().mapToSource(view_idx)
|
||||
model_idx = view_idx # Assuming direct model usage for now
|
||||
if model_idx.row() not in unique_rows: # Process each underlying model row only once
|
||||
# Ensure we are getting the index for column 0 if multiple columns are selected for the same row
|
||||
model_indexes_to_process.append(self.unified_model.index(model_idx.row(), 0, model_idx.parent()))
|
||||
unique_rows.add(model_idx.row())
|
||||
|
||||
if not model_indexes_to_process:
|
||||
super().keyPressEvent(event)
|
||||
return
|
||||
|
||||
pressed_key = event.key()
|
||||
modifiers = event.modifiers()
|
||||
keybind_processed = False
|
||||
|
||||
# --- Asset Name Keybind (F2) ---
|
||||
if pressed_key == Qt.Key_F2 and not modifiers: # No modifiers for F2
|
||||
log.debug("F2 pressed for asset name change.")
|
||||
# Get current asset name from the first selected item as a suggestion
|
||||
first_selected_item_index = model_indexes_to_process[0] # This is a col 0 index
|
||||
first_item_object = self.unified_model.getItem(first_selected_item_index)
|
||||
current_name_suggestion = ""
|
||||
|
||||
if isinstance(first_item_object, AssetRule):
|
||||
# For AssetRule, its name is in COL_NAME (which is first_selected_item_index's column, typically 0)
|
||||
# The index itself (first_selected_item_index) can be used as it's for COL_NAME.
|
||||
current_name_suggestion = self.unified_model.data(first_selected_item_index, Qt.DisplayRole) or ""
|
||||
elif isinstance(first_item_object, FileRule):
|
||||
# For FileRule, its target asset name override is in COL_TARGET_ASSET
|
||||
target_asset_col_idx = self.unified_model.COL_TARGET_ASSET
|
||||
target_asset_index_for_suggestion = first_selected_item_index.siblingAtColumn(target_asset_col_idx)
|
||||
current_name_suggestion = self.unified_model.data(target_asset_index_for_suggestion, Qt.DisplayRole) or ""
|
||||
|
||||
new_name_input, ok = QInputDialog.getText(self, "Set Name", "Enter new name for selected items:", QLineEdit.EchoMode.Normal, current_name_suggestion)
|
||||
if ok and new_name_input is not None:
|
||||
stripped_name = new_name_input.strip()
|
||||
if stripped_name:
|
||||
log.info(f"User entered new name: '{stripped_name}' for selected items.")
|
||||
|
||||
# Step 1: Collect Objects
|
||||
initial_selected_indices = self.main_panel_widget.unified_view.selectedIndexes()
|
||||
objects_to_rename = []
|
||||
processed_rows_for_object_collection = set() # To avoid processing same underlying item multiple times if multiple columns selected
|
||||
|
||||
for view_idx in initial_selected_indices:
|
||||
# Assuming direct model usage or correct proxy mapping by the view
|
||||
model_idx_for_item = self.unified_model.index(view_idx.row(), 0, view_idx.parent()) # Get column 0 index
|
||||
if model_idx_for_item.row() not in processed_rows_for_object_collection:
|
||||
item = self.unified_model.getItem(model_idx_for_item)
|
||||
if isinstance(item, (AssetRule, FileRule)):
|
||||
objects_to_rename.append(item)
|
||||
processed_rows_for_object_collection.add(model_idx_for_item.row())
|
||||
else:
|
||||
log.debug(f"F2 RENAME: Skipping item {item!r} (type: {type(item)}) during object collection as it's not AssetRule or FileRule.")
|
||||
|
||||
log.debug(f"F2 RENAME: Collected {len(objects_to_rename)} AssetRule/FileRule objects to rename.")
|
||||
|
||||
# Step 2: Iterate Over Objects and Update
|
||||
successful_renames = 0
|
||||
for item_object in objects_to_rename:
|
||||
current_model_index = self.unified_model.findIndexForItem(item_object)
|
||||
|
||||
if current_model_index is None or not current_model_index.isValid():
|
||||
item_repr = getattr(item_object, 'asset_name', getattr(item_object, 'file_path', repr(item_object)))
|
||||
log.warning(f"F2 RENAME: Could not find current index for item {item_repr!r}. It might have been moved/deleted unexpectedly. Skipping.")
|
||||
continue
|
||||
|
||||
target_column = -1
|
||||
item_description_for_log = ""
|
||||
|
||||
if isinstance(item_object, AssetRule):
|
||||
target_column = self.unified_model.COL_NAME
|
||||
item_description_for_log = f"AssetRule '{item_object.asset_name}'"
|
||||
elif isinstance(item_object, FileRule):
|
||||
target_column = self.unified_model.COL_TARGET_ASSET
|
||||
item_description_for_log = f"FileRule '{Path(item_object.file_path).name}'"
|
||||
|
||||
if target_column == -1:
|
||||
log.warning(f"F2 RENAME: Unknown item type for {item_object!r}. Cannot determine target column. Skipping.")
|
||||
continue
|
||||
|
||||
index_to_update_in_column = current_model_index.siblingAtColumn(target_column)
|
||||
|
||||
log.debug(f"F2 RENAME: Attempting to set new name '{stripped_name}' for {item_description_for_log} at index r={index_to_update_in_column.row()}, c={index_to_update_in_column.column()}")
|
||||
success = self.unified_model.setData(index_to_update_in_column, stripped_name, Qt.EditRole)
|
||||
|
||||
if success:
|
||||
successful_renames += 1
|
||||
log.info(f"F2 RENAME: Successfully renamed {item_description_for_log} to '{stripped_name}'.")
|
||||
else:
|
||||
log.warning(f"F2 RENAME: Failed to rename {item_description_for_log} to '{stripped_name}'. setData returned False.")
|
||||
|
||||
self.statusBar().showMessage(f"{successful_renames} item(s) renamed to '{stripped_name}'.", 3000)
|
||||
keybind_processed = True
|
||||
else:
|
||||
log.debug("Asset name change aborted: name was empty after stripping.")
|
||||
else:
|
||||
log.debug("Asset name change cancelled or empty name entered.")
|
||||
event.accept()
|
||||
return
|
||||
|
||||
# --- File Type Keybinds (Ctrl + Key) ---
|
||||
if modifiers == Qt.ControlModifier:
|
||||
log.debug(f"Ctrl modifier detected with key: {pressed_key}")
|
||||
qt_key_sequence_str = QKeySequence(pressed_key).toString() # For logging
|
||||
if pressed_key in self.qt_key_to_ftd_map:
|
||||
target_ftd_keys = self.qt_key_to_ftd_map[pressed_key]
|
||||
log.debug(f"Keybind match: Ctrl+{qt_key_sequence_str} maps to FTDs: {target_ftd_keys}")
|
||||
if not target_ftd_keys:
|
||||
log.warning(f"No FTDs configured for key Ctrl+{qt_key_sequence_str}")
|
||||
super().keyPressEvent(event)
|
||||
return
|
||||
|
||||
# self.unified_model.beginResetModel() # Potentially too broad
|
||||
for index in model_indexes_to_process:
|
||||
item = self.unified_model.getItem(index) # index is for col 0
|
||||
# Check if the item is a FileRule instance
|
||||
# --- BEGIN ADDED LOGGING ---
|
||||
log.debug(f"Processing item for keybind: row={index.row()}, column={index.column()}")
|
||||
log.debug(f" Item object: {item!r}") # !r calls __repr__
|
||||
log.debug(f" Item type: {type(item)}")
|
||||
log.debug(f" Is instance of FileRule: {isinstance(item, FileRule)}")
|
||||
if hasattr(item, '__dict__'): # Log attributes if it's a custom object
|
||||
log.debug(f" Item attributes: {item.__dict__}")
|
||||
# --- END ADDED LOGGING ---
|
||||
|
||||
if not isinstance(item, FileRule): # This is the existing check
|
||||
log.debug(f"Skipping item at row {index.row()} because it's not a FileRule instance (actual type: {type(item)}).")
|
||||
continue
|
||||
|
||||
# Get current map type using COL_ITEM_TYPE and DisplayRole
|
||||
item_type_display_index = self.unified_model.index(index.row(), self.unified_model.COL_ITEM_TYPE, index.parent())
|
||||
current_map_type = self.unified_model.data(item_type_display_index, Qt.DisplayRole)
|
||||
log.debug(f"Item at row {index.row()} ({Path(item.file_path).name}), current map_type (DisplayRole): '{current_map_type}'")
|
||||
|
||||
new_map_type = ""
|
||||
if len(target_ftd_keys) == 1: # Single target type
|
||||
new_map_type = target_ftd_keys[0]
|
||||
log.debug(f" Single target FTD: '{new_map_type}'")
|
||||
else: # Toggle logic for multiple target types
|
||||
log.debug(f" Toggle FTDs: {target_ftd_keys}. Current: '{current_map_type}'")
|
||||
try:
|
||||
current_ftd_index = target_ftd_keys.index(current_map_type)
|
||||
next_ftd_index = (current_ftd_index + 1) % len(target_ftd_keys)
|
||||
new_map_type = target_ftd_keys[next_ftd_index]
|
||||
log.debug(f" Calculated next FTD: '{new_map_type}'")
|
||||
except ValueError: # current_map_type is not in the toggle list
|
||||
new_map_type = target_ftd_keys[0] # Default to the first one
|
||||
log.debug(f" Current not in toggle list, defaulting to first: '{new_map_type}'")
|
||||
|
||||
if new_map_type and new_map_type != current_map_type:
|
||||
log.debug(f" Updating item at row {index.row()} ({Path(item.file_path).name}) from '{current_map_type}' to '{new_map_type}'")
|
||||
# Set new map type using COL_ITEM_TYPE and EditRole
|
||||
item_type_edit_index = self.unified_model.index(index.row(), self.unified_model.COL_ITEM_TYPE, index.parent())
|
||||
success = self.unified_model.setData(item_type_edit_index, new_map_type, Qt.EditRole)
|
||||
log.debug(f" setData call successful: {success}")
|
||||
elif not new_map_type:
|
||||
log.debug(f" Skipping update for item at row {index.row()}, new_map_type is empty.")
|
||||
else: # new_map_type == current_map_type
|
||||
log.debug(f" Skipping update for item at row {index.row()}, new_map_type ('{new_map_type}') is same as current ('{current_map_type}').")
|
||||
|
||||
# self.unified_model.endResetModel() # Potentially too broad
|
||||
# The model should emit dataChanged for each setData call.
|
||||
self.statusBar().showMessage(f"File types updated for selected items.", 3000)
|
||||
keybind_processed = True
|
||||
event.accept()
|
||||
return
|
||||
|
||||
if not keybind_processed:
|
||||
log.debug("Key press not handled by custom keybinds, passing to super.")
|
||||
super().keyPressEvent(event)
|
||||
|
||||
|
||||
# --- Main Execution ---
|
||||
# --- Main Execution ---
|
||||
def run_gui():
|
||||
"""Initializes and runs the Qt application."""
|
||||
print("--- Reached run_gui() ---")
|
||||
# Ensure QInputDialog is imported if not already at the top
|
||||
# from PySide6.QtWidgets import QInputDialog (already handled by being part of PySide6.QtWidgets import *)
|
||||
from PySide6.QtGui import QKeySequence # Ensure QKeySequence is imported if used standalone
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
#app.setStyle('Fusion')
|
||||
|
||||
|
||||
@@ -8,6 +8,10 @@ from rule_structure import SourceRule, AssetRule, FileRule # Removed AssetType,
|
||||
from configuration import load_base_config # Import load_base_config
|
||||
from typing import List # Added for type hinting
|
||||
|
||||
class CustomRoles:
|
||||
MapTypeRole = Qt.UserRole + 1
|
||||
TargetAssetRole = Qt.UserRole + 2
|
||||
# Add other custom roles here as needed
|
||||
class UnifiedViewModel(QAbstractItemModel):
|
||||
# --- Color Constants for Row Backgrounds ---
|
||||
# Old colors removed, using config now + fixed source color
|
||||
@@ -19,8 +23,12 @@ class UnifiedViewModel(QAbstractItemModel):
|
||||
of SourceRule -> AssetRule -> FileRule.
|
||||
"""
|
||||
# Signal emitted when a FileRule's target asset override changes.
|
||||
# Carries the index of the FileRule and the new target asset path (or None).
|
||||
targetAssetOverrideChanged = Signal(QModelIndex, object)
|
||||
# Carries the FileRule object and the new target asset path (or None).
|
||||
targetAssetOverrideChanged = Signal(FileRule, str, QModelIndex) # Emit FileRule object, new value, and index
|
||||
|
||||
# Signal emitted when an AssetRule's name changes.
|
||||
# Carries the AssetRule object, the new name, and the index.
|
||||
assetNameChanged = Signal(AssetRule, str, QModelIndex)
|
||||
|
||||
Columns = [
|
||||
"Name", "Target Asset", "Supplier",
|
||||
@@ -362,6 +370,8 @@ class UnifiedViewModel(QAbstractItemModel):
|
||||
old_asset_name = item.asset_name
|
||||
item.asset_name = new_asset_name
|
||||
changed = True
|
||||
# Emit signal for asset name change, including the index
|
||||
self.assetNameChanged.emit(item, new_asset_name, index)
|
||||
|
||||
# --- Update Child FileRule Target Asset Overrides ---
|
||||
log.debug(f"setData: Updating FileRule target overrides from '{old_asset_name}' to '{new_asset_name}'")
|
||||
@@ -408,9 +418,10 @@ class UnifiedViewModel(QAbstractItemModel):
|
||||
item.target_asset_name_override = new_value
|
||||
changed = True
|
||||
# Emit signal that the override changed, let handler deal with restructuring
|
||||
self.targetAssetOverrideChanged.emit(index, new_value)
|
||||
# Pass the FileRule item itself, the new value, and the index
|
||||
self.targetAssetOverrideChanged.emit(item, new_value, index)
|
||||
elif column == self.COL_ITEM_TYPE: # Item-Type Override
|
||||
# Delegate provides string value (e.g., "MAP_COL") or None
|
||||
# Delegate provides string value (e.g., "MAP_COL") or None
|
||||
new_value = str(value) if value is not None else None
|
||||
if new_value == "": new_value = None # Treat empty string as None
|
||||
# Update item_type_override
|
||||
@@ -784,11 +795,63 @@ class UnifiedViewModel(QAbstractItemModel):
|
||||
"""Returns the cached list of file type keys."""
|
||||
return self._file_type_keys
|
||||
|
||||
def findIndexForItem(self, target_item_object) -> QModelIndex | None:
|
||||
"""
|
||||
Finds the QModelIndex for a given item object (SourceRule, AssetRule, or FileRule)
|
||||
by traversing the model's internal tree structure.
|
||||
|
||||
Args:
|
||||
target_item_object: The specific SourceRule, AssetRule, or FileRule object to find.
|
||||
|
||||
Returns:
|
||||
QModelIndex for the item if found, otherwise None.
|
||||
"""
|
||||
if target_item_object is None:
|
||||
return None
|
||||
|
||||
for sr_row, source_rule in enumerate(self._source_rules):
|
||||
if source_rule is target_item_object:
|
||||
return self.createIndex(sr_row, 0, source_rule) # Top-level item
|
||||
|
||||
parent_source_rule_index = self.createIndex(sr_row, 0, source_rule) # Potential parent for children
|
||||
if not parent_source_rule_index.isValid(): # Should always be valid here
|
||||
log.error(f"findIndexForItem: Could not create valid index for SourceRule: {source_rule.input_path}")
|
||||
continue
|
||||
|
||||
|
||||
for ar_row, asset_rule in enumerate(source_rule.assets):
|
||||
if asset_rule is target_item_object:
|
||||
return self.index(ar_row, 0, parent_source_rule_index)
|
||||
|
||||
parent_asset_rule_index = self.index(ar_row, 0, parent_source_rule_index)
|
||||
if not parent_asset_rule_index.isValid():
|
||||
log.error(f"findIndexForItem: Could not create valid index for AssetRule: {asset_rule.asset_name}")
|
||||
continue # Skip children if parent index is invalid
|
||||
|
||||
for fr_row, file_rule in enumerate(asset_rule.files):
|
||||
if file_rule is target_item_object:
|
||||
return self.index(fr_row, 0, parent_asset_rule_index)
|
||||
|
||||
log.debug(f"findIndexForItem: Item {target_item_object!r} not found in the model.")
|
||||
return None
|
||||
|
||||
# --- removeAssetRule continued (log.debug was separated by the insert) ---
|
||||
# This log line belongs to the removeAssetRule method defined earlier.
|
||||
# It's being re-indented here to its correct place if it was part of that method's flow.
|
||||
# However, looking at the original structure, the `return True` for removeAssetRule
|
||||
# was at line 802, and the log.debug was at 798. This indicates the log.debug
|
||||
# was likely the *start* of the problematic section in the previous attempt,
|
||||
# and the `return True` was the end of `removeAssetRule`.
|
||||
# The `log.debug` at original line 798 should be part of `removeAssetRule`'s positive path.
|
||||
# The `return True` at original line 802 should be the final return of `removeAssetRule`.
|
||||
|
||||
# Correcting the end of removeAssetRule:
|
||||
log.debug(f"Removing empty AssetRule '{asset_rule_to_remove.asset_name}' at row {asset_row_for_removal} under '{Path(source_rule.input_path).name}'")
|
||||
self.beginRemoveRows(grandparent_index, asset_row_for_removal, asset_row_for_removal)
|
||||
source_rule.assets.pop(asset_row_for_removal)
|
||||
self.endRemoveRows()
|
||||
return True
|
||||
return True # This was the original end of removeAssetRule
|
||||
|
||||
def update_status(self, source_path: str, status_text: str):
|
||||
"""
|
||||
Finds the SourceRule node for the given source_path and updates its status.
|
||||
|
||||
Reference in New Issue
Block a user