Asset-Frameworker/gui/config_editor_dialog.py
2025-05-01 19:16:37 +02:00

623 lines
31 KiB
Python

# gui/config_editor_dialog.py
import json
from PySide6.QtWidgets import ( # Changed from PyQt5
QDialog, QVBoxLayout, QHBoxLayout, QTabWidget, QWidget,
QLineEdit, QSpinBox, QDoubleSpinBox, QComboBox, QCheckBox,
QPushButton, QFileDialog, QLabel, QTableWidget, # Removed QColorDialog
QTableWidgetItem, QDialogButtonBox, QMessageBox, QListWidget,
QListWidgetItem, QFormLayout, QGroupBox
)
from PySide6.QtGui import QColor # Changed from PyQt5
from PySide6.QtCore import Qt # Changed from PyQt5
from PySide6.QtWidgets import QColorDialog # Import QColorDialog separately for PySide6
# Assuming configuration.py is in the parent directory or accessible
# Adjust import path if necessary
try:
from configuration import load_base_config, save_base_config
except ImportError:
# Fallback import for testing or different project structure
from ..configuration import load_base_config, save_base_config
class ConfigEditorDialog(QDialog):
def __init__(self, parent=None):
super().__init__(parent)
self.setWindowTitle("Configuration Editor")
self.setGeometry(100, 100, 800, 600)
self.settings = {}
self.widgets = {} # Dictionary to hold references to created widgets
self.main_layout = QVBoxLayout(self)
self.tab_widget = QTabWidget()
self.main_layout.addWidget(self.tab_widget)
self.button_box = QDialogButtonBox(QDialogButtonBox.Save | QDialogButtonBox.Cancel)
self.button_box.accepted.connect(self.save_settings)
self.button_box.rejected.connect(self.reject)
self.main_layout.addWidget(self.button_box)
self.load_settings()
self.create_tabs()
self.populate_widgets()
def load_settings(self):
"""Loads settings from the configuration file."""
try:
self.settings = load_base_config()
print("Configuration loaded successfully.") # Debug print
except Exception as e:
QMessageBox.critical(self, "Loading Error", f"Failed to load configuration: {e}")
self.settings = {} # Use empty settings on failure
def create_tabs(self):
"""Creates tabs based on logical groupings of settings."""
if not self.settings:
return
# --- Create Tabs ---
self.tabs = {
"definitions": QWidget(),
"paths_output": QWidget(),
"image_settings": QWidget(),
"blender": QWidget(),
"misc": QWidget() # For settings that don't fit elsewhere
}
self.tab_widget.addTab(self.tabs["definitions"], "Definitions")
self.tab_widget.addTab(self.tabs["paths_output"], "Paths & Output")
self.tab_widget.addTab(self.tabs["image_settings"], "Image Settings")
self.tab_widget.addTab(self.tabs["blender"], "Blender")
self.tab_widget.addTab(self.tabs["misc"], "Miscellaneous")
# --- Setup Layouts for Tabs ---
self.tab_layouts = {name: QVBoxLayout(tab) for name, tab in self.tabs.items()}
# --- Populate Tabs ---
self.populate_definitions_tab(self.tab_layouts["definitions"])
self.populate_paths_output_tab(self.tab_layouts["paths_output"])
self.populate_image_settings_tab(self.tab_layouts["image_settings"])
self.populate_blender_tab(self.tab_layouts["blender"])
self.populate_misc_tab(self.tab_layouts["misc"])
def create_widget_for_setting(self, parent_layout, key, value, setting_key_prefix=""):
"""Creates an appropriate widget for a single setting key-value pair."""
full_key = f"{setting_key_prefix}{key}" if setting_key_prefix else key
label_text = key.replace('_', ' ').title()
label = QLabel(label_text + ":")
widget = None
layout_to_add = None # Use this for widgets needing extra controls (like browse button)
if isinstance(value, str):
if 'PATH' in key.upper() or 'DIR' in key.upper() or key == "BLENDER_EXECUTABLE_PATH":
widget = QLineEdit(value)
button = QPushButton("Browse...")
# Determine if it's a file or directory browse
is_dir = 'DIR' in key.upper()
button.clicked.connect(lambda checked, w=widget, k=full_key, is_dir=is_dir: self.browse_path(w, k, is_dir))
h_layout = QHBoxLayout()
h_layout.addWidget(widget)
h_layout.addWidget(button)
layout_to_add = h_layout
elif 'COLOR' in key.upper() or 'COLOUR' in key.upper():
widget = QLineEdit(value)
button = QPushButton("Pick Color...")
button.clicked.connect(lambda checked, w=widget: self.pick_color(w))
h_layout = QHBoxLayout()
h_layout.addWidget(widget)
h_layout.addWidget(button)
layout_to_add = h_layout
else:
widget = QLineEdit(value)
elif isinstance(value, int):
widget = QSpinBox()
widget.setRange(-2147483648, 2147483647)
widget.setValue(value)
elif isinstance(value, float):
widget = QDoubleSpinBox()
widget.setRange(-1.7976931348623157e+308, 1.7976931348623157e+308)
widget.setValue(value)
elif isinstance(value, bool):
widget = QCheckBox()
widget.setChecked(value)
elif isinstance(value, list) and key != "MAP_MERGE_RULES": # Handle simple lists (excluding complex ones)
# Assuming list of strings or simple types
widget = QLineEdit(", ".join(map(str, value)))
# Complex dicts/lists like ASSET_TYPE_DEFINITIONS, MAP_MERGE_RULES etc. are handled in dedicated methods
if widget or layout_to_add:
if layout_to_add:
parent_layout.addRow(label, layout_to_add)
else:
parent_layout.addRow(label, widget)
# Store reference using the full key only if a widget was created
if widget:
self.widgets[full_key] = widget
else:
# Optionally handle unsupported types or log a warning
# print(f"Skipping widget creation for key '{full_key}' with unsupported type: {type(value)}")
pass
def populate_definitions_tab(self, layout):
"""Populates the Definitions tab."""
if "ASSET_TYPE_DEFINITIONS" in self.settings:
group = QGroupBox("Asset Type Definitions")
group_layout = QVBoxLayout(group)
self.create_asset_definitions_widget(group_layout, self.settings["ASSET_TYPE_DEFINITIONS"])
layout.addWidget(group)
if "FILE_TYPE_DEFINITIONS" in self.settings:
group = QGroupBox("File Type Definitions")
group_layout = QVBoxLayout(group)
self.create_file_type_definitions_widget(group_layout, self.settings["FILE_TYPE_DEFINITIONS"])
layout.addWidget(group)
# Add STANDARD_MAP_TYPES and RESPECT_VARIANT_MAP_TYPES here
form_layout = QFormLayout()
if "STANDARD_MAP_TYPES" in self.settings:
self.create_widget_for_setting(form_layout, "STANDARD_MAP_TYPES", self.settings["STANDARD_MAP_TYPES"])
if "RESPECT_VARIANT_MAP_TYPES" in self.settings:
self.create_widget_for_setting(form_layout, "RESPECT_VARIANT_MAP_TYPES", self.settings["RESPECT_VARIANT_MAP_TYPES"])
if "DEFAULT_ASSET_CATEGORY" in self.settings:
self.create_widget_for_setting(form_layout, "DEFAULT_ASSET_CATEGORY", self.settings["DEFAULT_ASSET_CATEGORY"])
layout.addLayout(form_layout)
layout.addStretch()
def populate_paths_output_tab(self, layout):
"""Populates the Paths & Output tab."""
form_layout = QFormLayout()
keys_to_include = [
"OUTPUT_BASE_DIR", "EXTRA_FILES_SUBDIR", "METADATA_FILENAME",
"TARGET_FILENAME_PATTERN", "TEMP_DIR_PREFIX"
]
for key in keys_to_include:
if key in self.settings:
self.create_widget_for_setting(form_layout, key, self.settings[key])
layout.addLayout(form_layout)
layout.addStretch()
def populate_image_settings_tab(self, layout):
"""Populates the Image Settings tab."""
form_layout = QFormLayout()
simple_keys = [
"PNG_COMPRESSION_LEVEL", "JPG_QUALITY", "RESOLUTION_THRESHOLD_FOR_JPG",
"ASPECT_RATIO_DECIMALS", "CALCULATE_STATS_RESOLUTION",
"OUTPUT_FORMAT_16BIT_PRIMARY", "OUTPUT_FORMAT_16BIT_FALLBACK",
"OUTPUT_FORMAT_8BIT"
]
for key in simple_keys:
if key in self.settings:
self.create_widget_for_setting(form_layout, key, self.settings[key])
layout.addLayout(form_layout)
# Add complex widgets
if "IMAGE_RESOLUTIONS" in self.settings:
group = QGroupBox("Image Resolutions")
group_layout = QVBoxLayout(group)
self.create_image_resolutions_widget(group_layout, self.settings["IMAGE_RESOLUTIONS"])
layout.addWidget(group)
if "MAP_BIT_DEPTH_RULES" in self.settings:
group = QGroupBox("Map Bit Depth Rules")
group_layout = QVBoxLayout(group)
self.create_map_bit_depth_rules_widget(group_layout, self.settings["MAP_BIT_DEPTH_RULES"])
layout.addWidget(group)
if "MAP_MERGE_RULES" in self.settings:
group = QGroupBox("Map Merge Rules")
group_layout = QVBoxLayout(group)
self.create_map_merge_rules_widget(group_layout, self.settings["MAP_MERGE_RULES"])
layout.addWidget(group)
layout.addStretch()
def populate_blender_tab(self, layout):
"""Populates the Blender tab."""
form_layout = QFormLayout()
keys_to_include = [
"DEFAULT_NODEGROUP_BLEND_PATH", "DEFAULT_MATERIALS_BLEND_PATH",
"BLENDER_EXECUTABLE_PATH"
]
for key in keys_to_include:
if key in self.settings:
self.create_widget_for_setting(form_layout, key, self.settings[key])
layout.addLayout(form_layout)
layout.addStretch()
def populate_misc_tab(self, layout):
"""Populates the Miscellaneous tab with any remaining settings."""
form_layout = QFormLayout()
handled_keys = set()
# Collect keys handled by other tabs
handled_keys.update([
"ASSET_TYPE_DEFINITIONS", "FILE_TYPE_DEFINITIONS", "STANDARD_MAP_TYPES",
"RESPECT_VARIANT_MAP_TYPES", "DEFAULT_ASSET_CATEGORY", "OUTPUT_BASE_DIR",
"EXTRA_FILES_SUBDIR", "METADATA_FILENAME", "TARGET_FILENAME_PATTERN",
"TEMP_DIR_PREFIX", "PNG_COMPRESSION_LEVEL", "JPG_QUALITY",
"RESOLUTION_THRESHOLD_FOR_JPG", "ASPECT_RATIO_DECIMALS",
"CALCULATE_STATS_RESOLUTION", "OUTPUT_FORMAT_16BIT_PRIMARY",
"OUTPUT_FORMAT_16BIT_FALLBACK", "OUTPUT_FORMAT_8BIT",
"IMAGE_RESOLUTIONS", "MAP_BIT_DEPTH_RULES", "MAP_MERGE_RULES",
"DEFAULT_NODEGROUP_BLEND_PATH", "DEFAULT_MATERIALS_BLEND_PATH",
"BLENDER_EXECUTABLE_PATH"
])
for key, value in self.settings.items():
if key not in handled_keys:
# Only create widgets for simple types here
if isinstance(value, (str, int, float, bool, list)):
# Check if list is simple
is_simple_list = isinstance(value, list) and (not value or not isinstance(value[0], (dict, list)))
if not isinstance(value, list) or is_simple_list:
self.create_widget_for_setting(form_layout, key, value)
handled_keys.add(key) # Mark as handled
if form_layout.rowCount() == 0:
layout.addWidget(QLabel("No miscellaneous settings found."))
layout.addLayout(form_layout)
layout.addStretch()
# Remove the old create_widgets_for_section method as it's replaced
# def create_widgets_for_section(self, layout, section_data, section_key):
# ... (old implementation removed) ...
def create_asset_definitions_widget(self, layout, definitions_data):
"""Creates a widget for editing asset type definitions."""
table = QTableWidget()
table.setColumnCount(3) # Asset Type, Description, Color
table.setHorizontalHeaderLabels(["Asset Type", "Description", "Color"])
table.setRowCount(len(definitions_data))
row = 0
for asset_type, details in definitions_data.items():
table.setItem(row, 0, QTableWidgetItem(asset_type))
table.setItem(row, 1, QTableWidgetItem(details.get("description", "")))
color_widget = QLineEdit(details.get("color", ""))
color_button = QPushButton("Pick Color...")
color_button.clicked.connect(lambda checked, w=color_widget: self.pick_color(w))
h_layout = QHBoxLayout()
h_layout.addWidget(color_widget)
h_layout.addWidget(color_button)
cell_widget = QWidget()
cell_widget.setLayout(h_layout)
table.setCellWidget(row, 2, cell_widget)
row += 1
table.horizontalHeader().setStretchLastSection(True)
layout.addWidget(table)
self.widgets["DEFINITION_SETTINGS.ASSET_TYPE_DEFINITIONS"] = table # Store table reference
def create_file_type_definitions_widget(self, layout, definitions_data):
"""Creates a widget for editing file type definitions."""
table = QTableWidget()
table.setColumnCount(3) # File Type, Description, Color
table.setHorizontalHeaderLabels(["File Type", "Description", "Color"])
table.setRowCount(len(definitions_data))
row = 0
for file_type, details in definitions_data.items():
table.setItem(row, 0, QTableWidgetItem(file_type))
table.setItem(row, 1, QTableWidgetItem(details.get("description", "")))
color_widget = QLineEdit(details.get("color", ""))
color_button = QPushButton("Pick Color...")
color_button.clicked.connect(lambda checked, w=color_widget: self.pick_color(w))
h_layout = QHBoxLayout()
h_layout.addWidget(color_widget)
h_layout.addWidget(color_button)
cell_widget = QWidget()
cell_widget.setLayout(h_layout)
table.setCellWidget(row, 2, cell_widget)
row += 1
table.horizontalHeader().setStretchLastSection(True)
layout.addWidget(table)
self.widgets["DEFINITION_SETTINGS.FILE_TYPE_DEFINITIONS"] = table # Store table reference
def create_image_resolutions_widget(self, layout, resolutions_data):
"""Creates a widget for editing image resolutions."""
table = QTableWidget()
table.setColumnCount(2) # Width, Height
table.setHorizontalHeaderLabels(["Width", "Height"])
table.setRowCount(len(resolutions_data))
for row, resolution in enumerate(resolutions_data):
table.setItem(row, 0, QTableWidgetItem(str(resolution[0])))
table.setItem(row, 1, QTableWidgetItem(str(resolution[1])))
table.horizontalHeader().setStretchLastSection(True)
layout.addWidget(table)
self.widgets["IMAGE_PROCESSING_SETTINGS.IMAGE_RESOLUTIONS"] = table # Store table reference
def create_map_bit_depth_rules_widget(self, layout, rules_data: dict):
"""Creates a widget for editing map bit depth rules (Map Type -> Rule)."""
table = QTableWidget()
table.setColumnCount(2) # Map Type, Rule
table.setHorizontalHeaderLabels(["Map Type", "Rule (respect/force_8bit)"])
table.setRowCount(len(rules_data))
# Iterate through dictionary items (key-value pairs)
for row, (map_type, rule_string) in enumerate(rules_data.items()):
table.setItem(row, 0, QTableWidgetItem(map_type))
# Optionally use a ComboBox for the rule selection later
table.setItem(row, 1, QTableWidgetItem(str(rule_string)))
table.horizontalHeader().setStretchLastSection(True)
layout.addWidget(table)
# Store reference using a more specific key if needed, or handle in save_settings
self.widgets["MAP_BIT_DEPTH_RULES_TABLE"] = table # Use a distinct key for the table widget
def create_map_merge_rules_widget(self, layout, rules_data):
"""Creates a widget for editing map merge rules."""
# This is a more complex structure (list of dicts)
# Using a ListWidget to select rules and a separate form to edit details
h_layout = QHBoxLayout()
layout.addLayout(h_layout)
self.merge_rules_list = QListWidget()
self.merge_rules_list.currentItemChanged.connect(self.display_merge_rule_details)
h_layout.addWidget(self.merge_rules_list, 1) # Give list more space
self.merge_rule_details_group = QGroupBox("Rule Details")
self.merge_rule_details_layout = QFormLayout(self.merge_rule_details_group)
h_layout.addWidget(self.merge_rule_details_group, 2) # Give details form more space
self.merge_rule_widgets = {} # Widgets for the currently displayed rule
self.populate_merge_rules_list(rules_data)
self.widgets["IMAGE_PROCESSING_SETTINGS.MAP_MERGE_RULES"] = rules_data # Store original data reference
def populate_merge_rules_list(self, rules_data):
"""Populates the list widget with map merge rules."""
self.merge_rules_list.clear()
for rule in rules_data:
item = QListWidgetItem(rule.get("output_name", "Unnamed Rule"))
item.setData(Qt.UserRole, rule) # Store the rule dictionary in the item
self.merge_rules_list.addItem(item)
def display_merge_rule_details(self, current, previous):
"""Displays details of the selected merge rule."""
# Clear previous widgets
for i in reversed(range(self.merge_rule_details_layout.count())):
widget_item = self.merge_rule_details_layout.itemAt(i)
if widget_item:
widget = widget_item.widget()
if widget:
widget.deleteLater()
layout = widget_item.layout()
if layout:
# Recursively delete widgets in layout
while layout.count():
item = layout.takeAt(0)
widget = item.widget()
if widget:
widget.deleteLater()
elif item.layout():
# Handle nested layouts if necessary
pass # For simplicity, assuming no deeply nested layouts here
self.merge_rule_widgets.clear()
if current:
rule_data = current.data(Qt.UserRole)
if rule_data:
for key, value in rule_data.items():
label = QLabel(key.replace('_', ' ').title() + ":")
if isinstance(value, str):
widget = QLineEdit(value)
elif isinstance(value, (int, float)):
if isinstance(value, int):
widget = QSpinBox()
widget.setRange(-2147483648, 2147483647)
widget.setValue(value)
else:
widget = QDoubleSpinBox()
widget.setRange(-1.7976931348623157e+308, 1.7976931348623157e+308)
widget.setValue(value)
elif isinstance(value, bool):
widget = QCheckBox()
widget.setChecked(value)
elif isinstance(value, list):
# Assuming list of strings or simple types for now
widget = QLineEdit(", ".join(map(str, value)))
elif isinstance(value, dict):
# Assuming simple key-value dicts for now
widget = QLineEdit(json.dumps(value)) # Display as JSON string
else:
widget = QLabel(f"Unsupported type: {type(value)}")
self.merge_rule_details_layout.addRow(label, widget)
self.merge_rule_widgets[key] = widget # Store widget reference
def populate_widgets(self):
"""Populates the created widgets with loaded settings (for simple types)."""
# This method is less critical with the recursive create_widgets_for_section
# but could be used for specific post-creation population if needed.
pass
def browse_path(self, widget, key):
"""Opens a file or directory dialog based on the setting key."""
if 'DIR' in key.upper():
path = QFileDialog.getExistingDirectory(self, "Select Directory", widget.text())
else:
path, _ = QFileDialog.getOpenFileName(self, "Select File", widget.text())
if path:
widget.setText(path)
def pick_color(self, widget):
"""Opens a color dialog and sets the selected color in the widget."""
color = QColorDialog.getColor(QColor(widget.text()))
if color.isValid():
widget.setText(color.name()) # Get color as hex string
def save_settings(self):
"""Reads values from widgets and saves them to the configuration file."""
new_settings = {}
# Reconstruct the settings dictionary from widgets
# This requires iterating through the widgets and mapping them back
# to the original structure. This is a simplified approach and might
# need refinement for complex nested structures or dynamic lists/tables.
# Start with a deep copy of the original settings structure to preserve
# sections/keys that might not have dedicated widgets (though ideally all should)
import copy
new_settings = copy.deepcopy(self.settings)
# Iterate through the stored widgets and update the new_settings dictionary
for key, widget in self.widgets.items():
# Handle simple widgets
if isinstance(widget, (QLineEdit, QSpinBox, QDoubleSpinBox, QCheckBox)):
# Split the key to navigate the dictionary structure
keys = key.split('.')
current_dict = new_settings
for i, k in enumerate(keys):
if i == len(keys) - 1:
# This is the final key, update the value
if isinstance(widget, QLineEdit):
current_dict[k] = widget.text()
elif isinstance(widget, QSpinBox):
current_dict[k] = widget.value()
elif isinstance(widget, QDoubleSpinBox):
current_dict[k] = widget.value()
elif isinstance(widget, QCheckBox):
current_dict[k] = widget.isChecked()
else:
# Navigate to the next level
if k not in current_dict or not isinstance(current_dict[k], dict):
# This should not happen if create_tabs is correct, but handle defensively
print(f"Warning: Structure mismatch for key part '{k}' in '{key}'")
break # Stop processing this key
current_dict = current_dict[k]
# Handle TableWidgets (for definitions, resolutions, bit depth rules)
elif isinstance(widget, QTableWidget):
keys = key.split('.')
if len(keys) >= 2:
section_key = keys[0]
list_key = keys[1]
if section_key in new_settings and list_key in new_settings[section_key]:
if list_key == "ASSET_TYPE_DEFINITIONS":
new_definitions = {}
for row in range(widget.rowCount()):
asset_type_item = widget.item(row, 0)
description_item = widget.item(row, 1)
color_widget_container = widget.cellWidget(row, 2)
if asset_type_item and color_widget_container:
asset_type = asset_type_item.text()
description = description_item.text() if description_item else ""
color_widget = color_widget_container.findChild(QLineEdit)
if color_widget:
color = color_widget.text()
new_definitions[asset_type] = {"description": description, "color": color}
new_settings[section_key][list_key] = new_definitions
elif list_key == "FILE_TYPE_DEFINITIONS":
new_definitions = {}
for row in range(widget.rowCount()):
file_type_item = widget.item(row, 0)
description_item = widget.item(row, 1)
color_widget_container = widget.cellWidget(row, 2)
if file_type_item and color_widget_container:
file_type = file_type_item.text()
description = description_item.text() if description_item else ""
color_widget = color_widget_container.findChild(QLineEdit)
if color_widget:
color = color_widget.text()
new_definitions[file_type] = {"description": description, "color": color}
new_settings[section_key][list_key] = new_definitions
elif list_key == "IMAGE_RESOLUTIONS":
new_resolutions = []
for row in range(widget.rowCount()):
width_item = widget.item(row, 0)
height_item = widget.item(row, 1)
if width_item and height_item:
try:
width = int(width_item.text())
height = int(height_item.text())
new_resolutions.append([width, height])
except ValueError:
print(f"Warning: Invalid resolution value at row {row}")
new_settings[section_key][list_key] = new_resolutions
elif list_key == "MAP_BIT_DEPTH_RULES":
new_rules = []
for row in range(widget.rowCount()):
pattern_item = widget.item(row, 0)
bit_depth_item = widget.item(row, 1)
if pattern_item and bit_depth_item:
try:
bit_depth = int(bit_depth_item.text())
new_rules.append({"pattern": pattern_item.text(), "bit_depth": bit_depth})
except ValueError:
print(f"Warning: Invalid bit depth value at row {row}")
new_settings[section_key][list_key] = new_rules
# Handle Map Merge Rules (more complex)
# This requires reading from the details form for the currently selected item
# and updating the corresponding dictionary in the original list stored in self.widgets
elif key == "IMAGE_PROCESSING_SETTINGS.MAP_MERGE_RULES":
# The original list is stored in self.widgets["IMAGE_PROCESSING_SETTINGS.MAP_MERGE_RULES"]
# We need to iterate through the list widget items and update the corresponding
# dictionary in the list based on the details form if that item was selected and edited.
# A simpler approach for now is to just read the currently displayed rule details
# and update the corresponding item in the list widget's data, then reconstruct the list.
# Reconstruct the list from the list widget items' data
new_merge_rules = []
for i in range(self.merge_rules_list.count()):
item = self.merge_rules_list.item(i)
rule_data = item.data(Qt.UserRole)
if rule_data:
# If this item is the currently selected one, update its data from the details widgets
if item == self.merge_rules_list.currentItem():
updated_rule_data = {}
for detail_key, detail_widget in self.merge_rule_widgets.items():
if isinstance(detail_widget, QLineEdit):
updated_rule_data[detail_key] = detail_widget.text()
elif isinstance(detail_widget, QSpinBox):
updated_rule_data[detail_key] = detail_widget.value()
elif isinstance(detail_widget, QDoubleSpinBox):
updated_rule_data[detail_key] = detail_widget.value()
elif isinstance(detail_widget, QCheckBox):
updated_rule_data[detail_key] = detail_widget.isChecked()
# Add handling for other widget types in details form if needed
# Merge updated data with original data (in case some fields weren't in details form)
rule_data.update(updated_rule_data)
new_merge_rules.append(rule_data)
# Update the new_settings dictionary with the reconstructed list
keys = key.split('.')
if len(keys) == 2:
section_key = keys[0]
list_key = keys[1]
if section_key in new_settings and list_key in new_settings[section_key]:
new_settings[section_key][list_key] = new_merge_rules
# Save the new settings
try:
save_base_config(new_settings)
QMessageBox.information(self, "Settings Saved", "Configuration saved successfully.\nRestart the application to apply changes.")
self.accept() # Close the dialog
except Exception as e:
QMessageBox.critical(self, "Saving Error", f"Failed to save configuration: {e}")
# Example usage (for testing the dialog independently)
if __name__ == '__main__':
from PyQt5.QtWidgets import QApplication
import sys
app = QApplication(sys.argv)
dialog = ConfigEditorDialog()
dialog.exec_()
sys.exit(app.exec_())