GUI - File Type Keybinds And F2 Renaming
This commit is contained in:
parent
ff548e902e
commit
9a27d23a4c
@ -28,6 +28,15 @@ python -m gui.main_window
|
||||
* **Drag-and-Drop Re-parenting:** File rows can be dragged and dropped onto different Asset rows to change their parent asset association.
|
||||
* **Right-Click Context Menu:** Right-clicking on Source, Asset, or File rows brings up a context menu:
|
||||
* **Re-interpret selected source:** This sub-menu allows re-running the prediction process for the selected source item(s) using either a specific preset or the LLM predictor. The available presets and the "LLM" option are listed dynamically. This replaces the previous standalone "Re-interpret Selected with LLM" button.
|
||||
* **Keybinds for Item Management:** When items are selected in the Preview Table, the following keybinds can be used:
|
||||
* `Ctrl + C`: Sets the file type of selected items to Color/Albedo (`MAP_COL`).
|
||||
* `Ctrl + R`: Toggles the file type of selected items between Roughness (`MAP_ROUGH`) and Glossiness (`MAP_GLOSS`).
|
||||
* `Ctrl + N`: Sets the file type of selected items to Normal (`MAP_NRM`).
|
||||
* `Ctrl + M`: Toggles the file type of selected items between Metalness (`MAP_METAL`) and Reflection/Specular (`MAP_REFL`).
|
||||
* `Ctrl + D`: Sets the file type of selected items to Displacement/Height (`MAP_DISP`).
|
||||
* `Ctrl + E`: Sets the file type of selected items to Extra (`EXTRA`).
|
||||
* `Ctrl + X`: Sets the file type of selected items to Ignore (`FILE_IGNORE`).
|
||||
* `F2`: Prompts to set the asset name for all selected items. This name propagates to the `AssetRule` name or the `FileRule` `target_asset_name_override` for the files under the selected assets. If individual files are selected, it will affect their `target_asset_name_override`.
|
||||
* **Prediction Population:** If a valid preset is selected in the Preset Selector (or if re-interpretation is triggered), the table populates with prediction results as they become available. If no preset is selected, added items show empty prediction fields.
|
||||
* **Columns:** The table displays columns: Name, Target Asset, Supplier, Asset Type, Item Type. The "Target Asset" column stretches to fill available space.
|
||||
* **Coloring:** The *text color* of file items is determined by their Item Type (colors defined in `config/app_settings.json`). The *background color* of file items is a 30% darker shade of their parent asset's background, helping to visually group files within an asset. Asset rows themselves may use alternating background colors based on the application theme.
|
||||
|
||||
@ -7,6 +7,34 @@ This document provides technical details about the configuration system and the
|
||||
The tool utilizes a two-tiered configuration system managed by the `configuration.py` module:
|
||||
|
||||
1. **Application Settings (`config/app_settings.json`):** This JSON file defines the core global default settings, constants, and rules that apply generally across different asset sources (e.g., the global `OUTPUT_DIRECTORY_PATTERN` and `OUTPUT_FILENAME_PATTERN`, standard image resolutions, map merge rules, output format rules, Blender paths, `FILE_TYPE_DEFINITIONS`, `ASSET_TYPE_DEFINITIONS`). See the [User Guide: Output Structure](../01_User_Guide/09_Output_Structure.md#available-tokens) for a list of available tokens for these patterns.
|
||||
* **`FILE_TYPE_DEFINITIONS` Enhancements:**
|
||||
* **`keybind` Property:** Each file type object within `FILE_TYPE_DEFINITIONS` can now optionally include a `keybind` property. This property accepts a single character string (e.g., `"C"`, `"R"`) representing the keyboard key. In the GUI, this key (typically combined with `Ctrl`, or standalone like `F2` for asset naming) is used as a shortcut to set or toggle the corresponding file type for selected items in the Preview Table.
|
||||
*Example:*
|
||||
```json
|
||||
"MAP_COL": {
|
||||
"description": "Color/Albedo Map",
|
||||
"color": [200, 200, 200],
|
||||
"examples": ["albedo", "col", "basecolor"],
|
||||
"standard_type": "COL",
|
||||
"bit_depth_rule": "respect",
|
||||
"is_grayscale": false,
|
||||
"keybind": "C"
|
||||
},
|
||||
```
|
||||
* **New File Type `MAP_GLOSS`:** A new standard file type, `MAP_GLOSS`, has been added. It is typically configured as follows:
|
||||
*Example:*
|
||||
```json
|
||||
"MAP_GLOSS": {
|
||||
"description": "Glossiness Map",
|
||||
"color": [180, 180, 220],
|
||||
"examples": ["gloss", "gls"],
|
||||
"standard_type": "GLOSS",
|
||||
"bit_depth_rule": "respect",
|
||||
"is_grayscale": true,
|
||||
"keybind": "R"
|
||||
}
|
||||
```
|
||||
Note: The `keybind` "R" for `MAP_GLOSS` is often shared with `MAP_ROUGH` to allow toggling between them.
|
||||
2. **LLM Settings (`config/llm_settings.json`):** This JSON file contains settings specifically related to the LLM predictor, such as the API endpoint, model name, prompt template, and examples. These settings can be edited through the GUI using the `LLMEditorWidget`.
|
||||
3. **Preset Files (`Presets/*.json`):** These JSON files define supplier-specific rules and overrides. They contain patterns to interpret filenames, classify map types, handle variants, define naming conventions, and specify other source-specific behaviors.
|
||||
|
||||
|
||||
@ -57,7 +57,8 @@
|
||||
],
|
||||
"standard_type": "COL",
|
||||
"bit_depth_rule": "force_8bit",
|
||||
"is_grayscale": false
|
||||
"is_grayscale": false,
|
||||
"keybind": "C"
|
||||
},
|
||||
"MAP_NRM": {
|
||||
"description": "Normal Map",
|
||||
@ -68,7 +69,8 @@
|
||||
],
|
||||
"standard_type": "NRM",
|
||||
"bit_depth_rule": "respect",
|
||||
"is_grayscale": false
|
||||
"is_grayscale": false,
|
||||
"keybind": "N"
|
||||
},
|
||||
"MAP_METAL": {
|
||||
"description": "Metalness Map",
|
||||
@ -79,7 +81,8 @@
|
||||
],
|
||||
"standard_type": "METAL",
|
||||
"bit_depth_rule": "force_8bit",
|
||||
"is_grayscale": true
|
||||
"is_grayscale": true,
|
||||
"keybind": "M"
|
||||
},
|
||||
"MAP_ROUGH": {
|
||||
"description": "Roughness Map",
|
||||
@ -91,7 +94,20 @@
|
||||
],
|
||||
"standard_type": "ROUGH",
|
||||
"bit_depth_rule": "force_8bit",
|
||||
"is_grayscale": true
|
||||
"is_grayscale": true,
|
||||
"keybind": "R"
|
||||
},
|
||||
"MAP_GLOSS": {
|
||||
"description": "Glossiness Map",
|
||||
"color": "#d6bfd6",
|
||||
"examples": [
|
||||
"_gloss.",
|
||||
"_gls."
|
||||
],
|
||||
"standard_type": "GLOSS",
|
||||
"bit_depth_rule": "force_8bit",
|
||||
"is_grayscale": true,
|
||||
"keybind": "R"
|
||||
},
|
||||
"MAP_AO": {
|
||||
"description": "Ambient Occlusion Map",
|
||||
@ -113,7 +129,8 @@
|
||||
],
|
||||
"standard_type": "DISP",
|
||||
"bit_depth_rule": "respect",
|
||||
"is_grayscale": true
|
||||
"is_grayscale": true,
|
||||
"keybind": "D"
|
||||
},
|
||||
"MAP_REFL": {
|
||||
"description": "Reflection/Specular Map",
|
||||
@ -124,7 +141,8 @@
|
||||
],
|
||||
"standard_type": "REFL",
|
||||
"bit_depth_rule": "force_8bit",
|
||||
"is_grayscale": true
|
||||
"is_grayscale": true,
|
||||
"keybind": "M"
|
||||
},
|
||||
"MAP_SSS": {
|
||||
"description": "Subsurface Scattering Map",
|
||||
@ -210,7 +228,8 @@
|
||||
],
|
||||
"standard_type": "",
|
||||
"bit_depth_rule": "",
|
||||
"is_grayscale": false
|
||||
"is_grayscale": false,
|
||||
"keybind": "E"
|
||||
},
|
||||
"FILE_IGNORE": {
|
||||
"description": "File to be ignored",
|
||||
@ -221,7 +240,8 @@
|
||||
],
|
||||
"standard_type": "",
|
||||
"bit_depth_rule": "",
|
||||
"is_grayscale": false
|
||||
"is_grayscale": false,
|
||||
"keybind": "X"
|
||||
}
|
||||
},
|
||||
"TARGET_FILENAME_PATTERN": "{base_name}_{map_type}_{resolution}.{ext}",
|
||||
|
||||
@ -559,6 +559,29 @@ class Configuration:
|
||||
"""Returns the LLM request timeout in seconds from LLM settings."""
|
||||
return self._llm_settings.get('llm_request_timeout', 120) # Default timeout
|
||||
|
||||
@property
|
||||
def keybind_config(self) -> dict[str, list[str]]:
|
||||
"""
|
||||
Processes FILE_TYPE_DEFINITIONS to create a mapping of keybinds
|
||||
to their associated file type keys.
|
||||
Example: {'C': ['MAP_COL'], 'R': ['MAP_ROUGH', 'MAP_GLOSS']}
|
||||
"""
|
||||
keybinds = {}
|
||||
file_type_defs = self._core_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:
|
||||
key = ftd_value['keybind']
|
||||
if key not in keybinds:
|
||||
keybinds[key] = []
|
||||
keybinds[key].append(ftd_key)
|
||||
|
||||
# Ensure toggleable keybinds have their file types in a consistent order if necessary
|
||||
# For example, for 'R': ['MAP_ROUGH', 'MAP_GLOSS']
|
||||
# The order from app_settings.json is generally preserved by dict iteration in Python 3.7+
|
||||
# but explicit sorting could be added if a specific cycle order is critical beyond config file order.
|
||||
# For now, we rely on the order they appear in the config.
|
||||
return keybinds
|
||||
|
||||
# --- Standalone Base Config Functions ---
|
||||
|
||||
def load_base_config() -> dict:
|
||||
|
||||
@ -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,7 +418,8 @@ 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
|
||||
new_value = str(value) if value is not None else None
|
||||
@ -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.
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user