203 lines
8.7 KiB
Python
203 lines
8.7 KiB
Python
import sys
|
|
from PySide6.QtWidgets import (QApplication, QWidget, QVBoxLayout, QLabel, QLineEdit,
|
|
QFormLayout, QComboBox, QCheckBox, QSpinBox, QDoubleSpinBox)
|
|
from PySide6.QtCore import Signal, Slot, QObject
|
|
|
|
# Assuming rule_structure.py is in the parent directory or accessible via PYTHONPATH
|
|
# from ..rule_structure import SourceRule, AssetRule, FileRule # Adjust import based on actual structure
|
|
# For now, we'll use placeholder classes or assume rule_structure is directly importable
|
|
# from rule_structure import SourceRule, AssetRule, FileRule # Assuming direct import is possible
|
|
|
|
class RuleEditorWidget(QWidget):
|
|
"""
|
|
A widget to display and edit hierarchical processing rules (Source, Asset, File).
|
|
"""
|
|
rule_updated = Signal(object) # Signal emitted when a rule is updated
|
|
|
|
def __init__(self, asset_types: list[str] | None = None, parent=None):
|
|
"""
|
|
Initializes the RuleEditorWidget.
|
|
|
|
Args:
|
|
asset_types (list[str] | None): A list of available asset type names. Defaults to None.
|
|
parent: The parent widget.
|
|
"""
|
|
super().__init__(parent)
|
|
self.asset_types = asset_types if asset_types else [] # Store asset types
|
|
self.current_rule_type = None
|
|
self.current_rule_object = None
|
|
|
|
self.layout = QVBoxLayout(self)
|
|
self.rule_type_label = QLabel("Select an item in the hierarchy to view/edit rules.")
|
|
self.layout.addWidget(self.rule_type_label)
|
|
|
|
self.form_layout = QFormLayout()
|
|
self.layout.addLayout(self.form_layout)
|
|
|
|
self.layout.addStretch() # Add stretch to push content to the top
|
|
|
|
self.setLayout(self.layout)
|
|
self.clear_editor()
|
|
|
|
@Slot(object, str)
|
|
def load_rule(self, rule_object, rule_type_name):
|
|
"""
|
|
Loads a rule object into the editor.
|
|
|
|
Args:
|
|
rule_object: The SourceRule, AssetRule, or FileRule object.
|
|
rule_type_name: The name of the rule type ('SourceRule', 'AssetRule', 'FileRule').
|
|
"""
|
|
self.clear_editor()
|
|
self.current_rule_object = rule_object
|
|
self.current_rule_type = rule_type_name
|
|
self.rule_type_label.setText(f"Editing: {rule_type_name}")
|
|
|
|
if rule_object:
|
|
# Dynamically create form fields based on rule object attributes
|
|
for attr_name, attr_value in vars(rule_object).items():
|
|
if attr_name.startswith('_'): # Skip private attributes
|
|
continue
|
|
|
|
label = QLabel(attr_name.replace('_', ' ').title() + ":")
|
|
editor_widget = self._create_editor_widget(attr_name, attr_value)
|
|
if editor_widget:
|
|
self.form_layout.addRow(label, editor_widget)
|
|
# Connect signal to update rule object
|
|
self._connect_editor_signal(editor_widget, attr_name)
|
|
|
|
def _create_editor_widget(self, attr_name, attr_value):
|
|
"""
|
|
Creates an appropriate editor widget based on the attribute type.
|
|
"""
|
|
# --- Special Handling for Asset Type Dropdown ---
|
|
if self.current_rule_type == 'AssetRule' and attr_name == 'asset_type' and self.asset_types:
|
|
widget = QComboBox()
|
|
widget.addItems(self.asset_types)
|
|
if attr_value in self.asset_types:
|
|
widget.setCurrentText(attr_value)
|
|
elif self.asset_types: # Select first item if current value is invalid
|
|
widget.setCurrentIndex(0)
|
|
return widget
|
|
# --- Standard Type Handling ---
|
|
elif isinstance(attr_value, bool):
|
|
widget = QCheckBox()
|
|
widget.setChecked(attr_value)
|
|
return widget
|
|
elif isinstance(attr_value, int):
|
|
widget = QSpinBox()
|
|
widget.setRange(-2147483648, 2147483647) # Default integer range
|
|
widget.setValue(attr_value)
|
|
return widget
|
|
elif isinstance(attr_value, float):
|
|
widget = QDoubleSpinBox()
|
|
widget.setRange(-sys.float_info.max, sys.float_info.max) # Default float range
|
|
widget.setValue(attr_value)
|
|
return widget
|
|
elif isinstance(attr_value, (str, type(None))): # Handle None for strings
|
|
widget = QLineEdit()
|
|
widget.setText(str(attr_value) if attr_value is not None else "")
|
|
return widget
|
|
# Add more types as needed
|
|
# elif isinstance(attr_value, list):
|
|
# # Example for a simple list of strings
|
|
# widget = QLineEdit()
|
|
# widget.setText(", ".join(map(str, attr_value)))
|
|
# return widget
|
|
else:
|
|
# For unsupported types, just display the value
|
|
label = QLabel(str(attr_value))
|
|
return label
|
|
|
|
def _connect_editor_signal(self, editor_widget, attr_name):
|
|
"""
|
|
Connects the appropriate signal of the editor widget to the update logic.
|
|
"""
|
|
if isinstance(editor_widget, QLineEdit):
|
|
editor_widget.textChanged.connect(lambda text: self._update_rule_attribute(attr_name, text))
|
|
elif isinstance(editor_widget, QCheckBox):
|
|
editor_widget.toggled.connect(lambda checked: self._update_rule_attribute(attr_name, checked))
|
|
elif isinstance(editor_widget, QSpinBox):
|
|
editor_widget.valueChanged.connect(lambda value: self._update_rule_attribute(attr_name, value))
|
|
elif isinstance(editor_widget, QDoubleSpinBox):
|
|
editor_widget.valueChanged.connect(lambda value: self._update_rule_attribute(attr_name, value))
|
|
elif isinstance(editor_widget, QComboBox):
|
|
# Use currentTextChanged to get the string value directly
|
|
editor_widget.currentTextChanged.connect(lambda text: self._update_rule_attribute(attr_name, text))
|
|
# Add connections for other widget types
|
|
|
|
def _update_rule_attribute(self, attr_name, value):
|
|
"""
|
|
Updates the attribute of the current rule object and emits the signal.
|
|
"""
|
|
if self.current_rule_object:
|
|
# Basic type conversion based on the original attribute type
|
|
original_value = getattr(self.current_rule_object, attr_name)
|
|
try:
|
|
if isinstance(original_value, bool):
|
|
converted_value = bool(value)
|
|
elif isinstance(original_value, int):
|
|
converted_value = int(value)
|
|
elif isinstance(original_value, float):
|
|
converted_value = float(value)
|
|
elif isinstance(original_value, (str, type(None))):
|
|
converted_value = str(value) if value != "" else None # Convert empty string to None for original None types
|
|
else:
|
|
converted_value = value # Fallback for other types
|
|
setattr(self.current_rule_object, attr_name, converted_value)
|
|
self.rule_updated.emit(self.current_rule_object)
|
|
# print(f"Updated {attr_name} to {converted_value} in {self.current_rule_type}") # Debugging
|
|
except ValueError:
|
|
# Handle potential conversion errors (e.g., non-numeric input for int/float)
|
|
print(f"Error converting value '{value}' for attribute '{attr_name}'")
|
|
# Optionally, revert the editor widget to the original value or show an error indicator
|
|
|
|
def clear_editor(self):
|
|
"""
|
|
Clears the form layout.
|
|
"""
|
|
self.current_rule_object = None
|
|
self.current_rule_type = None
|
|
self.rule_type_label.setText("Select an item in the hierarchy to view/edit rules.")
|
|
while self.form_layout.rowCount() > 0:
|
|
self.form_layout.removeRow(0)
|
|
|
|
if __name__ == '__main__':
|
|
app = QApplication(sys.argv)
|
|
|
|
# Placeholder Rule Classes for testing
|
|
from dataclasses import dataclass, field
|
|
|
|
@dataclass
|
|
class SourceRule:
|
|
source_setting_1: str = "default_source_string"
|
|
source_setting_2: int = 123
|
|
source_setting_3: bool = True
|
|
|
|
@dataclass
|
|
class AssetRule:
|
|
asset_setting_a: float = 4.56
|
|
asset_setting_b: str = None
|
|
asset_setting_c: bool = False
|
|
|
|
@dataclass
|
|
class FileRule:
|
|
file_setting_x: int = 789
|
|
file_setting_y: str = "default_file_string"
|
|
|
|
# Example usage: Provide asset types during instantiation
|
|
asset_types_from_config = ["Surface", "Model", "Decal", "Atlas", "UtilityMap"] # Example list
|
|
editor = RuleEditorWidget(asset_types=asset_types_from_config)
|
|
|
|
# Test loading different rule types
|
|
source_rule = SourceRule()
|
|
asset_rule = AssetRule()
|
|
file_rule = FileRule()
|
|
|
|
editor.load_rule(source_rule, "SourceRule")
|
|
# editor.load_rule(asset_rule, "AssetRule")
|
|
# editor.load_rule(file_rule, "FileRule")
|
|
|
|
|
|
editor.show()
|
|
sys.exit(app.exec()) |