13 KiB
Implementation Plan: GUI User-Friendliness Enhancements
This document outlines the plan for implementing three key GUI improvements for the Asset Processor Tool, focusing on user-friendliness and workflow efficiency.
Target Audience: Developers implementing these features. Status: Planning Phase
Feature 1: Editable Asset Name
Goal: Allow users to edit the name of an asset directly in the main view, and automatically update the 'Target Asset' field of all associated child files to reflect the new name.
Affected Components:
gui/unified_view_model.py(UnifiedViewModel)gui/delegates.py(LineEditDelegate)gui/main_window.py(or view setup location)rule_structure.py(AssetRule,FileRule)- Potentially a new handler or modifications to
gui/asset_restructure_handler.py
Implementation Steps:
-
Enable Editing in Model (
UnifiedViewModel):- Modify
flags(): For an index pointing to anAssetRule, returnQt.ItemIsEditablein addition to default flags whenindex.column()isCOL_NAME. - Modify
setData():- Add logic to handle
isinstance(item, AssetRule)andcolumn == self.COL_NAME. - Get the
new_asset_namefrom thevalue. - Validation: Before proceeding, check if an
AssetRulewithnew_asset_namealready exists within the same parentSourceRule. If so, log a warning and returnFalseto prevent duplicate names. - Store the
old_asset_name = item.asset_name. - If
new_asset_nameis valid and different fromold_asset_name:- Update
item.asset_name = new_asset_name. - Set
changed = True. - Crucial - Child Update: Iterate through all
SourceRules,AssetRules, andFileRules in the model (self._source_rules). For eachFileRulefound wherefile_rule.target_asset_name_override == old_asset_name, updatefile_rule.target_asset_name_override = new_asset_name. EmitdataChangedfor theCOL_TARGET_ASSETindex of each modifiedFileRule. (See Potential Challenges regarding performance). - Emit
dataChangedfor the editedAssetRule'sCOL_NAMEindex.
- Update
- Return
changed.
- Add logic to handle
- (Alternative Signal Approach): Instead of performing the child update directly in
setData, emit a new signal likeassetNameChanged = Signal(QModelIndex, str, str)carrying theAssetRuleindex, old name, and new name. A dedicated handler would connect to this signal to perform the child updates. This improves separation of concerns.
- Modify
-
Assign Delegate (
main_window.py/ View Setup):- Ensure the
LineEditDelegateis assigned to the view for theCOL_NAMEusingview.setItemDelegateForColumn(UnifiedViewModel.COL_NAME, line_edit_delegate_instance).
- Ensure the
-
Handling Child Updates (if using Signal Approach):
- Create a new handler class (e.g.,
AssetNameChangeHandler) or add a slot toAssetRestructureHandler. - Connect the
UnifiedViewModel.assetNameChangedsignal to this slot. - The slot receives the
AssetRuleindex, old name, and new name. It iterates through the model'sFileRules, updates theirtarget_asset_name_overridewhere it matches the old name, and emitsdataChangedfor those files.
- Create a new handler class (e.g.,
Data Model Impact:
AssetRule.asset_namebecomes directly mutable via the GUI.- The relationship between files and their intended parent asset (represented by
FileRule.target_asset_name_override) is maintained automatically when the parent asset's name changes.
Potential Challenges/Considerations:
- Performance: The child update logic requires iterating through potentially all files in the model. For very large datasets, this could be slow. Consider optimizing by maintaining an index/lookup map (
Dict[str, List[FileRule]]) mapping target asset override names to the list ofFileRules using them. This map would need careful updating whenever overrides change or files are moved. - Duplicate Asset Names: The plan includes basic validation in
setData. Robust handling (e.g., user feedback, preventing the edit) is needed. - Undo/Redo: Reversing an asset name change requires reverting the name and reverting all the child
target_asset_name_overridechanges, adding complexity. - Scope of Child Update: The current plan updates any
FileRulewhose override matches the old name. Confirm if this update should be restricted only to files originally under the renamed asset or within the sameSourceRule. The current approach seems most logical based on howtarget_asset_name_overrideworks.
sequenceDiagram
participant User
participant View
participant LineEditDelegate
participant UnifiedViewModel
participant AssetNameChangeHandler
User->>View: Edits AssetRule Name in COL_NAME
View->>LineEditDelegate: setModelData(editor, model, index)
LineEditDelegate->>UnifiedViewModel: setData(index, new_name, EditRole)
UnifiedViewModel->>UnifiedViewModel: Validate new_name (no duplicates)
UnifiedViewModel->>UnifiedViewModel: Update AssetRule.asset_name
alt Signal Approach
UnifiedViewModel->>AssetNameChangeHandler: emit assetNameChanged(index, old_name, new_name)
AssetNameChangeHandler->>UnifiedViewModel: Iterate through FileRules
loop For each FileRule where target_override == old_name
AssetNameChangeHandler->>UnifiedViewModel: Update FileRule.target_asset_name_override = new_name
UnifiedViewModel->>View: emit dataChanged(file_rule_target_index)
end
else Direct Approach in setData
UnifiedViewModel->>UnifiedViewModel: Iterate through FileRules
loop For each FileRule where target_override == old_name
UnifiedViewModel->>UnifiedViewModel: Update FileRule.target_asset_name_override = new_name
UnifiedViewModel->>View: emit dataChanged(file_rule_target_index)
end
end
UnifiedViewModel->>View: emit dataChanged(asset_rule_name_index)
Feature 2: Item Type Field Conversion
Goal: Replace the QComboBox delegate for the "Item Type" column (for FileRules) with a QLineEdit that provides auto-suggestions based on defined file types, similar to the existing "Supplier" field.
Affected Components:
gui/main_window.py(or view setup location)gui/delegates.py(Requires a new delegate)gui/unified_view_model.py(UnifiedViewModel)config/app_settings.json(Source of file type definitions)
Implementation Steps:
-
Create New Delegate (
delegates.py):- Create a new class
ItemTypeSearchDelegate(QStyledItemDelegate). createEditor(self, parent, option, index):- Create a
QLineEditinstance. - Get the list of valid item type keys:
item_keys = index.model()._file_type_keys(add error handling). - Create a
QCompleterusingitem_keysand set it on theQLineEdit(configure case sensitivity, filter mode, completion mode as inSupplierSearchDelegate). - Return the editor.
- Create a
setEditorData(self, editor, index):- Get the current value using
index.model().data(index, Qt.EditRole). - Set the editor's text (
editor.setText(str(value) if value is not None else "")).
- Get the current value using
setModelData(self, editor, model, index):- Get the
final_text = editor.text().strip(). - Determine the
value_to_set = final_text if final_text else None. - Call
model.setData(index, value_to_set, Qt.EditRole). - Important: Unlike
SupplierSearchDelegate, do not addfinal_textto the list of known types or save anything back to config. Suggestions are strictly based onconfig/app_settings.json.
- Get the
updateEditorGeometry(self, editor, option, index):- Standard implementation:
editor.setGeometry(option.rect).
- Standard implementation:
- Create a new class
-
Assign Delegate (
main_window.py/ View Setup):- Instantiate the new
ItemTypeSearchDelegate. - Find where delegates are set for the view.
- Replace the
ComboBoxDelegateassignment forUnifiedViewModel.COL_ITEM_TYPEwith the newItemTypeSearchDelegateinstance:view.setItemDelegateForColumn(UnifiedViewModel.COL_ITEM_TYPE, item_type_search_delegate_instance).
- Instantiate the new
Data Model Impact:
- None. The underlying data (
FileRule.item_type_override) and its handling remain the same. Only the GUI editor changes.
Potential Challenges/Considerations:
- None significant. This is a relatively straightforward replacement of one delegate type with another, leveraging existing patterns from
SupplierSearchDelegateand data loading fromUnifiedViewModel.
Feature 3: Drag-and-Drop File Re-parenting
Goal: Enable users to drag one or more FileRule rows and drop them onto an AssetRule row to change the parent asset of the dragged files.
Affected Components:
gui/main_panel_widget.pyorgui/main_window.py(View management)gui/unified_view_model.py(UnifiedViewModel)
Implementation Steps:
-
Enable Drag/Drop in View (
main_panel_widget.py/main_window.py):- Get the
QTreeViewinstance (view). view.setSelectionMode(QAbstractItemView.ExtendedSelection)(Allow selecting multiple files)view.setDragEnabled(True)view.setAcceptDrops(True)view.setDropIndicatorShown(True)view.setDefaultDropAction(Qt.MoveAction)view.setDragDropMode(QAbstractItemView.InternalMove)
- Get the
-
Implement Drag/Drop Support in Model (
UnifiedViewModel):flags(self, index):- Modify to include
Qt.ItemIsDragEnabledifindex.internalPointer()is aFileRule. - Modify to include
Qt.ItemIsDropEnabledifindex.internalPointer()is anAssetRule. - Return the combined flags.
- Modify to include
supportedDropActions(self):- Return
Qt.MoveAction.
- Return
mimeData(self, indexes):- Create
QMimeData. - Encode information about the dragged rows (which must be
FileRules). Store a list of tuples, each containing(source_parent_row, source_parent_col, source_row)for each validFileRuleindex inindexes. Use a custom MIME type (e.g.,"application/x-filerule-index-list"). - Return the
QMimeData.
- Create
canDropMimeData(self, data, action, row, column, parent):- Check if
action == Qt.MoveAction. - Check if
data.hasFormat("application/x-filerule-index-list"). - Check if
parent.isValid()andparent.internalPointer()is anAssetRule. - Return
Trueif all conditions met,Falseotherwise.
- Check if
dropMimeData(self, data, action, row, column, parent):- Check
actionand MIME type again for safety. - Get the target
AssetRuleitem:target_asset = parent.internalPointer(). If not anAssetRule, returnFalse. - Decode the
QMimeDatato get the list of source index information. - Create a list
files_to_move = []containing the actualQModelIndexobjects for the sourceFileRules (reconstruct them using the decoded info andself.index()). - Iterate through
files_to_move:- Get the
source_file_index. - Get the
file_item = source_file_index.internalPointer(). - Get the
old_parent_asset = getattr(file_item, 'parent_asset', None). - If
target_asset != old_parent_asset:- Call
self.moveFileRule(source_file_index, parent). This handles the actual move within the model structure and emitsbeginMoveRows/endMoveRows. - After successful move: Update the file's override:
file_item.target_asset_name_override = target_asset.asset_name. - Emit
self.dataChanged.emit(moved_file_index, moved_file_index, [Qt.DisplayRole, Qt.EditRole])for theCOL_TARGET_ASSETcolumn of the now moved file (get its new index).
- Call
- Get the
- Cleanup: After the loop, identify any original parent
AssetRules that became empty as a result of the moves. Callself.removeAssetRule(empty_asset_rule)for each. - Return
True.
- Check
Data Model Impact:
- Changes the parentage of
FileRuleitems within the model's internal structure. - Updates
FileRule.target_asset_name_overrideto match theasset_nameof the new parentAssetRule, ensuring consistency between the visual structure and the override field.
Potential Challenges/Considerations:
- MIME Data Encoding/Decoding: Ensure the index information is reliably encoded and decoded, especially handling potential model changes between drag start and drop. Using persistent IDs instead of row/column numbers might be more robust if available.
- Cleanup Logic: Reliably identifying and removing empty parent assets after potentially moving multiple files from different original parents requires careful tracking.
- Transactionality: If moving multiple files and one part fails, should the whole operation roll back? The current plan doesn't explicitly handle this; errors are logged, and subsequent steps might proceed.
- Interaction with
AssetRestructureHandler: The plan suggests handling the move and override update directly withindropMimeData. This means the existingAssetRestructureHandlerwon't be triggered by the override change during the drop. Ensure the cleanup logic (removing empty parents) is correctly handled either indropMimeDataor by ensuringmoveFileRuleemits signals that the handler can use for cleanup.