16 bit processing fixes + code unification
This commit is contained in:
@@ -973,27 +973,27 @@ class ConfigEditorDialog(QDialog):
|
||||
self.merge_rule_details_layout.addRow(group)
|
||||
self.merge_rule_widgets["defaults_table"] = defaults_table
|
||||
|
||||
|
||||
# output_bit_depth: QComboBox (Options: "respect_inputs", "force_8bit", "force_16bit"). Label: "Output Bit Depth".
|
||||
if "output_bit_depth" in rule_data:
|
||||
label = QLabel("Output Bit Depth:")
|
||||
widget = QComboBox()
|
||||
options = ["respect_inputs", "force_8bit", "force_16bit"]
|
||||
widget.addItems(options)
|
||||
if rule_data["output_bit_depth"] in options:
|
||||
widget.setCurrentText(rule_data["output_bit_depth"])
|
||||
self.merge_rule_details_layout.addRow(label, widget)
|
||||
self.merge_rule_widgets["output_bit_depth"] = widget
|
||||
|
||||
# Add stretch to push widgets to the top
|
||||
self.merge_rule_details_layout.addStretch()
|
||||
# bit_depth_policy: QComboBox (Options: "preserve", "force_8bit", "force_16bit"). Label: "Bit Depth Policy".
|
||||
if "bit_depth_policy" in rule_data:
|
||||
label = QLabel("Bit Depth Policy:")
|
||||
widget = QComboBox()
|
||||
options = ["preserve", "force_8bit", "force_16bit"]
|
||||
widget.addItems(options)
|
||||
if rule_data["bit_depth_policy"] in options:
|
||||
widget.setCurrentText(rule_data["bit_depth_policy"])
|
||||
self.merge_rule_details_layout.addRow(label, widget)
|
||||
self.merge_rule_widgets["bit_depth_policy"] = widget
|
||||
|
||||
|
||||
# Connect output_bit_depth QComboBox to update rule data
|
||||
if "output_bit_depth" in self.merge_rule_widgets and isinstance(self.merge_rule_widgets["output_bit_depth"], QComboBox):
|
||||
self.merge_rule_widgets["output_bit_depth"].currentTextChanged.connect(
|
||||
lambda text, key="output_bit_depth": self.update_rule_data_simple_field(text, key)
|
||||
)
|
||||
# Add stretch to push widgets to the top
|
||||
self.merge_rule_details_layout.addStretch()
|
||||
|
||||
|
||||
# Connect bit_depth_policy QComboBox to update rule data
|
||||
if "bit_depth_policy" in self.merge_rule_widgets and isinstance(self.merge_rule_widgets["bit_depth_policy"], QComboBox):
|
||||
self.merge_rule_widgets["bit_depth_policy"].currentTextChanged.connect(
|
||||
lambda text, key="bit_depth_policy": self.update_rule_data_simple_field(text, key)
|
||||
)
|
||||
|
||||
|
||||
def update_rule_output_map_type(self, new_text):
|
||||
@@ -1107,7 +1107,7 @@ class ConfigEditorDialog(QDialog):
|
||||
"output_map_type": "NEW_RULE",
|
||||
"inputs": {"R": "", "G": "", "B": "", "A": ""},
|
||||
"defaults": {"R": 0.0, "G": 0.0, "B": 0.0, "A": 1.0},
|
||||
"output_bit_depth": "respect_inputs"
|
||||
"bit_depth_policy": "preserve"
|
||||
}
|
||||
|
||||
# Add to the internal list that backs the UI
|
||||
@@ -1417,8 +1417,9 @@ class ConfigEditorDialog(QDialog):
|
||||
self.widgets["RESOLUTION_THRESHOLD_FOR_JPG"].setCurrentText(current_text_selection)
|
||||
|
||||
|
||||
elif key == "MAP_BIT_DEPTH_RULES" and "MAP_BIT_DEPTH_RULES_TABLE" in self.widgets:
|
||||
self.populate_map_bit_depth_rules_table(self.widgets["MAP_BIT_DEPTH_RULES_TABLE"], value)
|
||||
# The MAP_BIT_DEPTH_RULES table is removed as per refactoring plan.
|
||||
# elif key == "MAP_BIT_DEPTH_RULES" and "MAP_BIT_DEPTH_RULES_TABLE" in self.widgets:
|
||||
# self.populate_map_bit_depth_rules_table(self.widgets["MAP_BIT_DEPTH_RULES_TABLE"], value)
|
||||
|
||||
|
||||
elif key == "MAP_MERGE_RULES" and hasattr(self, 'merge_rules_list'): # Check if the list widget exists
|
||||
@@ -1492,10 +1493,10 @@ class ConfigEditorDialog(QDialog):
|
||||
item_standard_type = QTableWidgetItem(standard_type_str)
|
||||
table.setItem(row, 4, item_standard_type)
|
||||
|
||||
# Bit Depth Rule column (simple QTableWidgetItem for now)
|
||||
bit_depth_rule_str = details.get("bit_depth_rule", "")
|
||||
item_bit_depth_rule = QTableWidgetItem(bit_depth_rule_str)
|
||||
table.setItem(row, 5, item_bit_depth_rule)
|
||||
# Bit Depth Policy column (simple QTableWidgetItem for now)
|
||||
bit_depth_policy_str = details.get("bit_depth_policy", "")
|
||||
item_bit_depth_policy = QTableWidgetItem(bit_depth_policy_str)
|
||||
table.setItem(row, 5, item_bit_depth_policy)
|
||||
|
||||
# Background color is now handled by the delegate's paint method based on data
|
||||
|
||||
@@ -1525,14 +1526,15 @@ class ConfigEditorDialog(QDialog):
|
||||
row += 1
|
||||
|
||||
|
||||
def populate_map_bit_depth_rules_table(self, table: QTableWidget, rules_data: dict):
|
||||
"""Populates the map bit depth rules table."""
|
||||
table.setRowCount(len(rules_data))
|
||||
row = 0
|
||||
for map_type, rule in rules_data.items():
|
||||
table.setItem(row, 0, QTableWidgetItem(map_type))
|
||||
table.setItem(row, 1, QTableWidgetItem(str(rule))) # Rule (respect/force_8bit)
|
||||
row += 1
|
||||
# The populate_map_bit_depth_rules_table method is removed as per refactoring plan.
|
||||
# def populate_map_bit_depth_rules_table(self, table: QTableWidget, rules_data: dict):
|
||||
# """Populates the map bit depth rules table."""
|
||||
# table.setRowCount(len(rules_data))
|
||||
# row = 0
|
||||
# for map_type, rule in rules_data.items():
|
||||
# table.setItem(row, 0, QTableWidgetItem(map_type))
|
||||
# table.setItem(row, 1, QTableWidgetItem(str(rule))) # Rule (respect/force_8bit)
|
||||
# row += 1
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -567,8 +567,8 @@ class DefinitionsEditorDialog(QDialog):
|
||||
|
||||
# Bit Depth Rule
|
||||
self.ft_bit_depth_combo = QComboBox()
|
||||
self.ft_bit_depth_combo.addItems(["respect", "force_8bit", "force_16bit"])
|
||||
details_layout.addRow("Bit Depth Rule:", self.ft_bit_depth_combo)
|
||||
self.ft_bit_depth_combo.addItems(["preserve", "force_8bit", "force_16bit"])
|
||||
details_layout.addRow("Bit Depth Policy:", self.ft_bit_depth_combo)
|
||||
|
||||
# Is Grayscale
|
||||
self.ft_is_grayscale_check = QCheckBox("Is Grayscale")
|
||||
@@ -606,7 +606,7 @@ class DefinitionsEditorDialog(QDialog):
|
||||
logger.warning(f"File type data for '{key}' is not a dict: {ft_data_item}. Using default.")
|
||||
ft_data_item = {
|
||||
"description": str(ft_data_item), "color": "#ffffff", "examples": [],
|
||||
"standard_type": "", "bit_depth_rule": "respect",
|
||||
"standard_type": "", "bit_depth_policy": "preserve",
|
||||
"is_grayscale": False, "keybind": ""
|
||||
}
|
||||
|
||||
@@ -615,7 +615,7 @@ class DefinitionsEditorDialog(QDialog):
|
||||
ft_data_item.setdefault('color', '#ffffff')
|
||||
ft_data_item.setdefault('examples', [])
|
||||
ft_data_item.setdefault('standard_type', '')
|
||||
ft_data_item.setdefault('bit_depth_rule', 'respect')
|
||||
ft_data_item.setdefault('bit_depth_policy', 'preserve')
|
||||
ft_data_item.setdefault('is_grayscale', False)
|
||||
ft_data_item.setdefault('keybind', '')
|
||||
|
||||
@@ -651,7 +651,7 @@ class DefinitionsEditorDialog(QDialog):
|
||||
logger.error(f"Invalid data for file type item {current_item.text()}. Expected dict, got {type(ft_data)}")
|
||||
ft_data = {
|
||||
"description": "Error: Invalid data", "color": "#ff0000", "examples": [],
|
||||
"standard_type": "error", "bit_depth_rule": "respect",
|
||||
"standard_type": "error", "bit_depth_policy": "preserve",
|
||||
"is_grayscale": False, "keybind": "X"
|
||||
}
|
||||
|
||||
@@ -664,11 +664,11 @@ class DefinitionsEditorDialog(QDialog):
|
||||
|
||||
self.ft_standard_type_edit.setText(ft_data.get('standard_type', ''))
|
||||
|
||||
bdr_index = self.ft_bit_depth_combo.findText(ft_data.get('bit_depth_rule', 'respect'))
|
||||
bdr_index = self.ft_bit_depth_combo.findText(ft_data.get('bit_depth_policy', 'preserve'))
|
||||
if bdr_index != -1:
|
||||
self.ft_bit_depth_combo.setCurrentIndex(bdr_index)
|
||||
else:
|
||||
self.ft_bit_depth_combo.setCurrentIndex(0) # Default to 'respect'
|
||||
self.ft_bit_depth_combo.setCurrentIndex(0) # Default to 'preserve'
|
||||
|
||||
self.ft_is_grayscale_check.setChecked(ft_data.get('is_grayscale', False))
|
||||
self.ft_keybind_edit.setText(ft_data.get('keybind', ''))
|
||||
@@ -725,7 +725,7 @@ class DefinitionsEditorDialog(QDialog):
|
||||
"color": "#ffffff",
|
||||
"examples": [],
|
||||
"standard_type": "",
|
||||
"bit_depth_rule": "respect",
|
||||
"bit_depth_policy": "preserve",
|
||||
"is_grayscale": False,
|
||||
"keybind": ""
|
||||
}
|
||||
@@ -869,7 +869,7 @@ class DefinitionsEditorDialog(QDialog):
|
||||
# Update based on which widget triggered (or update all)
|
||||
ft_data['description'] = self.ft_description_edit.toPlainText()
|
||||
ft_data['standard_type'] = self.ft_standard_type_edit.text()
|
||||
ft_data['bit_depth_rule'] = self.ft_bit_depth_combo.currentText()
|
||||
ft_data['bit_depth_policy'] = self.ft_bit_depth_combo.currentText()
|
||||
ft_data['is_grayscale'] = self.ft_is_grayscale_check.isChecked()
|
||||
|
||||
# Keybind validation (force uppercase)
|
||||
|
||||
@@ -786,7 +786,8 @@ class MainWindow(QMainWindow):
|
||||
|
||||
if RuleBasedPredictionHandler and self.prediction_thread is None:
|
||||
self.prediction_thread = QThread(self)
|
||||
self.prediction_handler = RuleBasedPredictionHandler(input_source_identifier="", original_input_paths=[], preset_name="")
|
||||
# Pass the Configuration object to the prediction handler
|
||||
self.prediction_handler = RuleBasedPredictionHandler(config_obj=self.config, input_source_identifier="", original_input_paths=[], preset_name="")
|
||||
self.prediction_handler.moveToThread(self.prediction_thread)
|
||||
|
||||
self.start_prediction_signal.connect(self.prediction_handler.run_prediction, Qt.ConnectionType.QueuedConnection)
|
||||
|
||||
@@ -6,7 +6,7 @@ import re
|
||||
import tempfile
|
||||
import zipfile
|
||||
from collections import defaultdict, Counter
|
||||
from typing import List, Dict, Any
|
||||
from typing import List, Dict, Any, Set, Tuple # Added Set, Tuple
|
||||
|
||||
# --- PySide6 Imports ---
|
||||
from PySide6.QtCore import QObject, Slot # Keep QObject for parent type hint, Slot for classify_files if kept as method
|
||||
@@ -303,17 +303,19 @@ class RuleBasedPredictionHandler(BasePredictionHandler):
|
||||
Inherits from BasePredictionHandler for common threading and signaling.
|
||||
"""
|
||||
|
||||
def __init__(self, input_source_identifier: str, original_input_paths: list[str], preset_name: str, parent: QObject = None):
|
||||
def __init__(self, config_obj: Configuration, input_source_identifier: str, original_input_paths: list[str], preset_name: str, parent: QObject = None):
|
||||
"""
|
||||
Initializes the rule-based handler.
|
||||
Initializes the rule-based handler with a Configuration object.
|
||||
|
||||
Args:
|
||||
config_obj: The main configuration object.
|
||||
input_source_identifier: The unique identifier for the input source (e.g., file path).
|
||||
original_input_paths: List of absolute file paths extracted from the source.
|
||||
preset_name: The name of the preset configuration to use.
|
||||
parent: The parent QObject.
|
||||
"""
|
||||
super().__init__(input_source_identifier, parent)
|
||||
self.config = config_obj # Store the Configuration object
|
||||
self.original_input_paths = original_input_paths
|
||||
self.preset_name = preset_name
|
||||
self._current_input_path = None
|
||||
@@ -362,16 +364,24 @@ class RuleBasedPredictionHandler(BasePredictionHandler):
|
||||
log.warning(f"Input source path does not exist: '{input_source_identifier}'. Skipping prediction.")
|
||||
raise FileNotFoundError(f"Input source path not found: {input_source_identifier}")
|
||||
|
||||
# --- Load Configuration ---
|
||||
config = Configuration(preset_name)
|
||||
log.info(f"Successfully loaded configuration for preset '{preset_name}'.")
|
||||
# --- Use Provided Configuration ---
|
||||
# The Configuration object is now passed during initialization.
|
||||
# Ensure the correct preset is loaded in the passed config object if necessary,
|
||||
# or rely on the caller (MainWindow) to ensure the config object is in the correct state.
|
||||
# MainWindow's load_preset method re-initializes the config, so it should be correct.
|
||||
# We just need to use the stored self.config.
|
||||
log.info(f"Using provided configuration object for preset '{preset_name}'.")
|
||||
# No need to create a new Configuration instance here.
|
||||
# config = Configuration(preset_name) # REMOVED
|
||||
# log.info(f"Successfully loaded configuration for preset '{preset_name}'.") # REMOVED
|
||||
|
||||
if self._is_cancelled: raise RuntimeError("Prediction cancelled before classification.")
|
||||
|
||||
# --- Perform Classification ---
|
||||
self.status_update.emit(f"Classifying files for '{source_path.name}'...")
|
||||
try:
|
||||
classified_assets = classify_files(original_input_paths, config)
|
||||
# Use the stored config object
|
||||
classified_assets = classify_files(original_input_paths, self.config)
|
||||
except Exception as e:
|
||||
log.exception(f"Error during file classification for source '{input_source_identifier}': {e}")
|
||||
raise RuntimeError(f"Error classifying files: {e}") from e
|
||||
@@ -388,26 +398,29 @@ class RuleBasedPredictionHandler(BasePredictionHandler):
|
||||
# --- Build the Hierarchy ---
|
||||
self.status_update.emit(f"Building rule hierarchy for '{source_path.name}'...")
|
||||
try:
|
||||
supplier_identifier = config.supplier_name
|
||||
# Use the stored config object
|
||||
supplier_identifier = self.config.supplier_name
|
||||
source_rule = SourceRule(
|
||||
input_path=input_source_identifier,
|
||||
supplier_identifier=supplier_identifier,
|
||||
# Use the internal display name from the config object
|
||||
preset_name=config.internal_display_preset_name
|
||||
# Use the internal display name from the stored config object
|
||||
preset_name=self.config.internal_display_preset_name
|
||||
)
|
||||
asset_rules = []
|
||||
file_type_definitions = config._core_settings.get('FILE_TYPE_DEFINITIONS', {})
|
||||
# Access file type definitions via the public getter method from the stored config object
|
||||
file_type_definitions = self.config.get_file_type_definitions_with_examples()
|
||||
|
||||
for asset_name, files_info in classified_assets.items():
|
||||
if self._is_cancelled: raise RuntimeError("Prediction cancelled during hierarchy building (assets).")
|
||||
if not files_info: continue
|
||||
|
||||
asset_category_rules = config.asset_category_rules
|
||||
asset_type_definitions = config.get_asset_type_definitions()
|
||||
# Use the stored config object
|
||||
asset_category_rules = self.config.asset_category_rules
|
||||
asset_type_definitions = self.config.get_asset_type_definitions()
|
||||
asset_type_keys = list(asset_type_definitions.keys())
|
||||
|
||||
# Initialize predicted_asset_type using the validated default
|
||||
predicted_asset_type = config.default_asset_category
|
||||
# Initialize predicted_asset_type using the validated default from stored config
|
||||
predicted_asset_type = self.config.default_asset_category
|
||||
log.debug(f"Asset '{asset_name}': Initial predicted_asset_type set to default: '{predicted_asset_type}'.")
|
||||
|
||||
# 1. Check asset_category_rules from preset
|
||||
@@ -415,7 +428,8 @@ class RuleBasedPredictionHandler(BasePredictionHandler):
|
||||
|
||||
# Check for Model type based on file patterns
|
||||
if "Model" in asset_type_keys:
|
||||
model_patterns_regex = config.compiled_model_regex
|
||||
# Use the stored config object
|
||||
model_patterns_regex = self.config.compiled_model_regex
|
||||
for f_info in files_info:
|
||||
if f_info['item_type'] in ["EXTRA", "FILE_IGNORE"]:
|
||||
continue
|
||||
@@ -447,12 +461,13 @@ class RuleBasedPredictionHandler(BasePredictionHandler):
|
||||
pass
|
||||
|
||||
# 2. If not determined by specific rules, check for Surface (if not Model/Decal by rule)
|
||||
if not determined_by_rule and predicted_asset_type == config.default_asset_category and "Surface" in asset_type_keys:
|
||||
if not determined_by_rule and predicted_asset_type == self.config.default_asset_category and "Surface" in asset_type_keys:
|
||||
item_types_in_asset = {f_info['item_type'] for f_info in files_info}
|
||||
# Ensure we are checking against standard map types from FILE_TYPE_DEFINITIONS
|
||||
# This check is primarily for PBR texture sets.
|
||||
# Use the stored config object
|
||||
material_indicators = {
|
||||
ft_key for ft_key, ft_def in config.get_file_type_definitions_with_examples().items()
|
||||
ft_key for ft_key, ft_def in self.config.get_file_type_definitions_with_examples().items()
|
||||
if ft_def.get('standard_type') and ft_def.get('standard_type') not in ["", "EXTRA", "FILE_IGNORE", "MODEL"]
|
||||
}
|
||||
# Add common direct standard types as well for robustness
|
||||
@@ -466,7 +481,7 @@ class RuleBasedPredictionHandler(BasePredictionHandler):
|
||||
has_material_map = True
|
||||
break
|
||||
# Check standard type if item_type is a key in FILE_TYPE_DEFINITIONS
|
||||
item_def = config.get_file_type_definitions_with_examples().get(item_type)
|
||||
item_def = self.config.get_file_type_definitions_with_examples().get(item_type)
|
||||
if item_def and item_def.get('standard_type') in material_indicators:
|
||||
has_material_map = True
|
||||
break
|
||||
@@ -478,8 +493,8 @@ class RuleBasedPredictionHandler(BasePredictionHandler):
|
||||
# 3. Final validation: Ensure predicted_asset_type is a valid key.
|
||||
if predicted_asset_type not in asset_type_keys:
|
||||
log.warning(f"Derived AssetType '{predicted_asset_type}' for asset '{asset_name}' is not in ASSET_TYPE_DEFINITIONS. "
|
||||
f"Falling back to default: '{config.default_asset_category}'.")
|
||||
predicted_asset_type = config.default_asset_category
|
||||
f"Falling back to default: '{self.config.default_asset_category}'.")
|
||||
predicted_asset_type = self.config.default_asset_category
|
||||
|
||||
asset_rule = AssetRule(asset_name=asset_name, asset_type=predicted_asset_type)
|
||||
file_rules = []
|
||||
@@ -494,7 +509,8 @@ class RuleBasedPredictionHandler(BasePredictionHandler):
|
||||
# No need for the old MAP_ prefixing logic here.
|
||||
|
||||
# Validate the final_item_type against definitions, unless it's EXTRA or FILE_IGNORE
|
||||
if final_item_type not in ["EXTRA", "FILE_IGNORE"] and file_type_definitions and final_item_type not in file_type_definitions:
|
||||
# Use the stored config object
|
||||
if final_item_type not in ["EXTRA", "FILE_IGNORE"] and self.config.get_file_type_definitions_with_examples() and final_item_type not in self.config.get_file_type_definitions_with_examples():
|
||||
log.warning(f"Predicted ItemType '{final_item_type}' for file '{file_info['file_path']}' is not in FILE_TYPE_DEFINITIONS. Setting to FILE_IGNORE.")
|
||||
final_item_type = "FILE_IGNORE"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user