Preferences, Config.py Migration, Bug Fixes, and Documention updates
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -425,13 +425,19 @@ class MainWindow(QMainWindow):
|
||||
self.unified_view.setItemDelegateForColumn(UnifiedViewModel.COL_ITEM_TYPE, comboBoxDelegate)
|
||||
|
||||
# Configure View Appearance (optional, customize as needed)
|
||||
self.unified_view.setSizePolicy(QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Expanding) # Expand horizontally and vertically
|
||||
self.unified_view.setAlternatingRowColors(True)
|
||||
self.unified_view.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
|
||||
self.unified_view.setEditTriggers(QAbstractItemView.EditTrigger.DoubleClicked | QAbstractItemView.EditTrigger.SelectedClicked | QAbstractItemView.EditTrigger.EditKeyPressed)
|
||||
self.unified_view.header().setStretchLastSection(False) # Adjust as needed
|
||||
# Set the "Name" column (index 0) to resize to contents
|
||||
self.unified_view.header().setSectionResizeMode(UnifiedViewModel.COL_NAME, QHeaderView.ResizeMode.ResizeToContents)
|
||||
# self.unified_view.header().setSectionResizeMode(1, QHeaderView.ResizeMode.ResizeToContents) # Example: Resize others to contents
|
||||
|
||||
# Configure Header Resize Modes based on new column order
|
||||
header = self.unified_view.header()
|
||||
header.setStretchLastSection(False) # Don't stretch the last section by default
|
||||
header.setSectionResizeMode(UnifiedViewModel.COL_NAME, QHeaderView.ResizeMode.ResizeToContents)
|
||||
header.setSectionResizeMode(UnifiedViewModel.COL_TARGET_ASSET, QHeaderView.ResizeMode.Stretch) # Stretch Target Asset
|
||||
header.setSectionResizeMode(UnifiedViewModel.COL_SUPPLIER, QHeaderView.ResizeMode.ResizeToContents)
|
||||
header.setSectionResizeMode(UnifiedViewModel.COL_ASSET_TYPE, QHeaderView.ResizeMode.ResizeToContents)
|
||||
header.setSectionResizeMode(UnifiedViewModel.COL_ITEM_TYPE, QHeaderView.ResizeMode.ResizeToContents)
|
||||
|
||||
# Add the Unified View to the main layout
|
||||
main_layout.addWidget(self.unified_view, 1) # Give it stretch factor 1
|
||||
@@ -1613,80 +1619,129 @@ class MainWindow(QMainWindow):
|
||||
@Slot(list)
|
||||
def _on_rule_hierarchy_ready(self, source_rules_list: list):
|
||||
"""Receives prediction results (a list containing one SourceRule) for a single input path,
|
||||
accumulates them, and updates the model when all are ready."""
|
||||
finds the corresponding existing rule in the model, updates it while preserving overrides,
|
||||
and emits dataChanged/layoutChanged signals."""
|
||||
|
||||
# --- Extract input_path from the received rule ---
|
||||
# --- Extract input_path and the new rule from the received list ---
|
||||
input_path = None
|
||||
source_rule = None
|
||||
new_source_rule = None
|
||||
if source_rules_list and isinstance(source_rules_list[0], SourceRule):
|
||||
source_rule = source_rules_list[0]
|
||||
input_path = source_rule.input_path
|
||||
log.debug(f"--> Entered _on_rule_hierarchy_ready for '{input_path}' with {len(source_rules_list)} SourceRule(s)")
|
||||
new_source_rule = source_rules_list[0]
|
||||
input_path = new_source_rule.input_path
|
||||
log.debug(f"--> Entered _on_rule_hierarchy_ready for '{input_path}' with 1 SourceRule")
|
||||
elif source_rules_list:
|
||||
log.error(f"Received non-SourceRule object in list: {type(source_rules_list[0])}. Cannot process.")
|
||||
# Attempt to find which pending prediction this might correspond to? Difficult.
|
||||
# For now, we can't reliably remove from pending without the path.
|
||||
return
|
||||
else:
|
||||
# This case might happen if prediction failed critically before creating a rule.
|
||||
# The prediction_finished signal (which now includes input_path) should handle removing from pending.
|
||||
log.warning("Received empty source_rules_list in _on_rule_hierarchy_ready. Prediction likely failed.")
|
||||
return # Nothing to accumulate
|
||||
# Try to deduce input_path if possible (e.g., if only one is pending)
|
||||
if len(self._pending_predictions) == 1:
|
||||
input_path = list(self._pending_predictions)[0]
|
||||
log.warning(f"Assuming failed prediction corresponds to pending path: {input_path}")
|
||||
else:
|
||||
log.error("Cannot determine input_path for empty/failed prediction result when multiple predictions are pending.")
|
||||
return
|
||||
|
||||
if input_path is None:
|
||||
log.error("Could not determine input_path from received source_rules_list. Aborting accumulation.")
|
||||
log.error("Could not determine input_path from received source_rules_list or pending state.")
|
||||
return
|
||||
|
||||
# Log received rule details (even if it's None due to failure)
|
||||
log.debug(f"_on_rule_hierarchy_ready: Processing result for '{input_path}'. Received Supplier ID='{getattr(new_source_rule, 'supplier_identifier', 'N/A')}', Received Override='{getattr(new_source_rule, 'supplier_override', 'N/A')}'")
|
||||
|
||||
if input_path not in self._pending_predictions:
|
||||
log.warning(f"Received rule hierarchy for '{input_path}', but it was not in the pending set. Ignoring stale result? Pending: {self._pending_predictions}")
|
||||
return # Ignore if not expected
|
||||
|
||||
# --- Accumulate Result ---
|
||||
if source_rule: # Check if we successfully got the rule object
|
||||
self._accumulated_rules[input_path] = source_rule
|
||||
log.debug(f"Accumulated rule for '{input_path}'. Total accumulated: {len(self._accumulated_rules)}")
|
||||
else:
|
||||
# This path is already handled by the initial checks, but log just in case.
|
||||
log.warning(f"No valid SourceRule found for '{input_path}' to accumulate.")
|
||||
# --- Find existing rule in the model's internal list ---
|
||||
# Access the model directly
|
||||
existing_rule = None
|
||||
existing_rule_index = -1
|
||||
model_rules = self.unified_model.get_all_source_rules() # Get current rules from model
|
||||
for i, rule in enumerate(model_rules):
|
||||
if rule.input_path == input_path:
|
||||
existing_rule = rule
|
||||
existing_rule_index = i
|
||||
break
|
||||
|
||||
# --- Mark as Completed ---
|
||||
if existing_rule:
|
||||
log.debug(f"Found existing rule for '{input_path}' in model at index {existing_rule_index}. Updating it.")
|
||||
if new_source_rule: # Only update if prediction was successful
|
||||
# Preserve existing user overrides from the rule currently in the model
|
||||
preserved_supplier_override = existing_rule.supplier_override
|
||||
# Preserve other potential user overrides if they exist
|
||||
preserved_asset_overrides = {asset.asset_name: asset.asset_type_override for asset in existing_rule.assets}
|
||||
preserved_file_overrides = {(file.file_path, 'target'): file.target_asset_name_override for asset in existing_rule.assets for file in asset.files}
|
||||
preserved_file_overrides.update({(file.file_path, 'item'): file.item_type_override for asset in existing_rule.assets for file in asset.files})
|
||||
|
||||
# --- Update existing rule with new prediction data ---
|
||||
existing_rule.supplier_identifier = new_source_rule.supplier_identifier
|
||||
existing_rule.preset_name = new_source_rule.preset_name
|
||||
existing_rule.assets = new_source_rule.assets # Replace assets list
|
||||
|
||||
# Re-apply preserved overrides
|
||||
existing_rule.supplier_override = preserved_supplier_override
|
||||
for asset in existing_rule.assets:
|
||||
asset.asset_type_override = preserved_asset_overrides.get(asset.asset_name)
|
||||
asset.parent_source = existing_rule # Set parent reference
|
||||
for file in asset.files:
|
||||
file.target_asset_name_override = preserved_file_overrides.get((file.file_path, 'target'))
|
||||
file.item_type_override = preserved_file_overrides.get((file.file_path, 'item'))
|
||||
file.parent_asset = asset # Set parent reference
|
||||
# --- End Update ---
|
||||
|
||||
# Emit dataChanged and layoutChanged for the updated existing rule via the model
|
||||
start_index = self.unified_model.createIndex(existing_rule_index, 0, existing_rule)
|
||||
end_index = self.unified_model.createIndex(existing_rule_index, self.unified_model.columnCount() - 1, existing_rule)
|
||||
log.debug(f"Emitting dataChanged and layoutChanged for updated existing rule at index {existing_rule_index}")
|
||||
self.unified_model.dataChanged.emit(start_index, end_index)
|
||||
self.unified_model.layoutChanged.emit() # Signal layout change
|
||||
else:
|
||||
log.warning(f"Prediction failed for '{input_path}'. Not updating existing rule data in model.")
|
||||
|
||||
else:
|
||||
# If no existing rule was found (e.g., first time result arrives)
|
||||
if new_source_rule: # Only add if prediction was successful
|
||||
log.debug(f"No existing rule found for '{input_path}'. Adding new rule to model.")
|
||||
# Ensure parent references are set
|
||||
for asset_rule in new_source_rule.assets:
|
||||
asset_rule.parent_source = new_source_rule
|
||||
for file_rule in asset_rule.files:
|
||||
file_rule.parent_asset = asset_rule
|
||||
# Add to model's internal list and emit signal via model methods
|
||||
self.unified_model.beginInsertRows(QModelIndex(), len(model_rules), len(model_rules))
|
||||
model_rules.append(new_source_rule) # Append to the list obtained from the model
|
||||
self.unified_model.endInsertRows()
|
||||
else:
|
||||
log.warning(f"Prediction failed for '{input_path}' and no existing rule found. Nothing to add to model.")
|
||||
|
||||
|
||||
# --- Remove from pending ---
|
||||
self._pending_predictions.discard(input_path)
|
||||
log.debug(f"Removed '{input_path}' from pending predictions. Remaining: {self._pending_predictions}")
|
||||
log.debug(f"Removed '{input_path}' from pending predictions. Remaining: {len(self._pending_predictions)} -> {self._pending_predictions}")
|
||||
|
||||
# --- Check for Completion ---
|
||||
if not self._pending_predictions:
|
||||
log.info("All pending predictions received. Finalizing model update.")
|
||||
self._finalize_model_update()
|
||||
log.info("All pending predictions processed. Model should be up-to-date.")
|
||||
self.statusBar().showMessage(f"Preview complete.", 5000) # Update status
|
||||
# Optional: Resize columns after all updates are done
|
||||
for col in range(self.unified_model.columnCount()):
|
||||
self.unified_view.resizeColumnToContents(col)
|
||||
self.unified_view.expandToDepth(1) # Expand Source -> Asset level
|
||||
else:
|
||||
# Update status bar with progress
|
||||
completed_count = len(self._accumulated_rules)
|
||||
completed_count = len(self.unified_model.get_all_source_rules()) # Count rules in model
|
||||
pending_count = len(self._pending_predictions)
|
||||
# total_count = completed_count + pending_count # This might be slightly off if some failed without rules
|
||||
# We don't have the total count of *requested* predictions here easily,
|
||||
# but we can use the initial number of items added.
|
||||
total_requested = len(self.current_asset_paths) # Use the total number of items added
|
||||
status_msg = f"Preview finished for {Path(input_path).name}. Waiting for {pending_count} more ({completed_count}/{total_requested} requested)..."
|
||||
total_requested = completed_count + pending_count # Estimate total
|
||||
status_msg = f"Preview updated for {Path(input_path).name}. Waiting for {pending_count} more ({completed_count}/{total_requested} processed)..."
|
||||
self.statusBar().showMessage(status_msg, 5000)
|
||||
log.debug(status_msg)
|
||||
|
||||
|
||||
def _finalize_model_update(self):
|
||||
"""Combines accumulated rules and updates the UI model and view."""
|
||||
log.debug("Entering _finalize_model_update")
|
||||
final_rules = list(self._accumulated_rules.values())
|
||||
log.info(f"Finalizing model with {len(final_rules)} accumulated SourceRule(s).")
|
||||
|
||||
# Load the FINAL LIST of data into the UnifiedViewModel
|
||||
self.unified_model.load_data(final_rules)
|
||||
log.debug("Unified view model updated with final list of SourceRules.")
|
||||
|
||||
# Resize columns to fit content after loading data
|
||||
for col in range(self.unified_model.columnCount()):
|
||||
self.unified_view.resizeColumnToContents(col)
|
||||
log.debug("Unified view columns resized to contents.")
|
||||
self.unified_view.expandToDepth(1) # Expand Source -> Asset level
|
||||
|
||||
self.statusBar().showMessage(f"Preview complete for {len(final_rules)} asset(s).", 5000)
|
||||
# REMOVED _finalize_model_update method as it's no longer needed
|
||||
# def _finalize_model_update(self):
|
||||
# """Combines accumulated rules and updates the UI model and view."""
|
||||
# ... (old code removed) ...
|
||||
|
||||
|
||||
# --- Main Execution ---
|
||||
|
||||
@@ -18,14 +18,14 @@ class UnifiedViewModel(QAbstractItemModel):
|
||||
of SourceRule -> AssetRule -> FileRule.
|
||||
"""
|
||||
Columns = [
|
||||
"Name", "Supplier", "Asset Type",
|
||||
"Target Asset", "Item Type"
|
||||
"Name", "Target Asset", "Supplier",
|
||||
"Asset Type", "Item Type"
|
||||
]
|
||||
|
||||
COL_NAME = 0
|
||||
COL_SUPPLIER = 1
|
||||
COL_ASSET_TYPE = 2
|
||||
COL_TARGET_ASSET = 3
|
||||
COL_TARGET_ASSET = 1
|
||||
COL_SUPPLIER = 2
|
||||
COL_ASSET_TYPE = 3
|
||||
COL_ITEM_TYPE = 4
|
||||
# COL_STATUS = 5 # Removed
|
||||
# COL_OUTPUT_PATH = 6 # Removed
|
||||
@@ -242,7 +242,7 @@ class UnifiedViewModel(QAbstractItemModel):
|
||||
if isinstance(item, SourceRule):
|
||||
if role == Qt.DisplayRole or role == Qt.EditRole: # Combine Display and Edit logic
|
||||
if column == self.COL_NAME:
|
||||
return Path(item.input_path).name # Display only basename for SourceRule
|
||||
return Path(item.input_path).name
|
||||
elif column == self.COL_SUPPLIER:
|
||||
# Return override if set, otherwise the original identifier, else empty string
|
||||
display_value = item.supplier_override if item.supplier_override is not None else item.supplier_identifier
|
||||
@@ -253,21 +253,21 @@ class UnifiedViewModel(QAbstractItemModel):
|
||||
elif isinstance(item, AssetRule):
|
||||
if role == Qt.DisplayRole:
|
||||
if column == self.COL_NAME: return item.asset_name
|
||||
if column == self.COL_ASSET_TYPE:
|
||||
elif column == self.COL_ASSET_TYPE:
|
||||
display_value = item.asset_type_override if item.asset_type_override is not None else item.asset_type
|
||||
return display_value if display_value else ""
|
||||
# Removed Status and Output Path columns
|
||||
elif role == Qt.EditRole:
|
||||
if column == self.COL_ASSET_TYPE:
|
||||
return item.asset_type_override # Return string or None
|
||||
return item.asset_type_override
|
||||
return None # Default for AssetRule
|
||||
|
||||
elif isinstance(item, FileRule):
|
||||
if role == Qt.DisplayRole:
|
||||
if column == self.COL_NAME: return Path(item.file_path).name # Display only filename
|
||||
if column == self.COL_TARGET_ASSET:
|
||||
elif column == self.COL_TARGET_ASSET:
|
||||
return item.target_asset_name_override if item.target_asset_name_override is not None else ""
|
||||
if column == self.COL_ITEM_TYPE:
|
||||
elif column == self.COL_ITEM_TYPE:
|
||||
# Reverted Logic: Display override if set, otherwise base type. Shows prefixed keys.
|
||||
override = item.item_type_override
|
||||
initial_type = item.item_type
|
||||
@@ -278,8 +278,8 @@ class UnifiedViewModel(QAbstractItemModel):
|
||||
return initial_type if initial_type else ""
|
||||
# Removed Status and Output Path columns
|
||||
elif role == Qt.EditRole:
|
||||
if column == self.COL_TARGET_ASSET: return item.target_asset_name_override if item.target_asset_name_override is not None else ""
|
||||
if column == self.COL_ITEM_TYPE: return item.item_type_override # Return string or None
|
||||
if column == self.COL_TARGET_ASSET: return item.target_asset_name_override if item.target_asset_name_override is not None else "" # Return string or ""
|
||||
elif column == self.COL_ITEM_TYPE: return item.item_type_override # Return string or None
|
||||
return None # Default for FileRule
|
||||
|
||||
return None # Default return if role/item combination not handled
|
||||
@@ -299,6 +299,7 @@ class UnifiedViewModel(QAbstractItemModel):
|
||||
if isinstance(item, SourceRule): # If SourceRule is editable
|
||||
if column == self.COL_SUPPLIER:
|
||||
# Get the new value, strip whitespace, treat empty as None
|
||||
log.debug(f"setData COL_SUPPLIER: Index=({index.row()},{column}), Value='{value}', Type={type(value)}") # <-- ADDED LOGGING (Corrected Indentation)
|
||||
new_value = str(value).strip() if value is not None and str(value).strip() else None
|
||||
|
||||
# Get the original identifier (assuming it exists on SourceRule)
|
||||
@@ -516,12 +517,12 @@ class UnifiedViewModel(QAbstractItemModel):
|
||||
can_edit = False
|
||||
# Determine editability based on item type and column
|
||||
if isinstance(item, SourceRule): # If SourceRule is displayed/editable
|
||||
if column == 1: can_edit = True
|
||||
if column == self.COL_SUPPLIER: can_edit = True # Supplier is editable
|
||||
elif isinstance(item, AssetRule):
|
||||
if column == 2: can_edit = True
|
||||
if column == self.COL_ASSET_TYPE: can_edit = True # Asset Type is editable
|
||||
elif isinstance(item, FileRule):
|
||||
if column == 3: can_edit = True
|
||||
if column == 4: can_edit = True
|
||||
if column == self.COL_TARGET_ASSET: can_edit = True # Target Asset is editable
|
||||
if column == self.COL_ITEM_TYPE: can_edit = True # Item Type is editable
|
||||
|
||||
if can_edit:
|
||||
return default_flags | Qt.ItemIsEditable
|
||||
@@ -545,4 +546,5 @@ class UnifiedViewModel(QAbstractItemModel):
|
||||
item = index.internalPointer()
|
||||
if item: # Ensure internal pointer is not None
|
||||
return item
|
||||
return None # Return None for invalid index or None pointer
|
||||
return None # Return None for invalid index or None pointer
|
||||
|
||||
Reference in New Issue
Block a user