# 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() # Load settings FIRST self.create_tabs() # THEN create widgets based on settings # self.populate_widgets() # Removed as population is now in load_settings def load_settings(self): """Loads settings from the configuration file and populates widgets.""" try: self.settings = load_base_config() print("Configuration loaded successfully.") # Debug print # Populate widgets after loading settings and creating tabs self.populate_widgets_from_settings() except Exception as e: QMessageBox.critical(self, "Loading Error", f"Failed to load configuration: {e}") self.settings = {} # Use empty settings on failure # Optionally disable save button or widgets if loading fails self.button_box.button(QDialogButtonBox.Save).setEnabled(False) def create_tabs(self): """Creates tabs based on the redesigned UI plan.""" if not self.settings: return # --- Create Tabs --- self.tabs = { "general": QWidget(), "output_naming": QWidget(), "image_processing": QWidget(), "definitions": QWidget(), "map_merging": QWidget(), "postprocess_scripts": QWidget() } self.tab_widget.addTab(self.tabs["general"], "General") self.tab_widget.addTab(self.tabs["output_naming"], "Output & Naming") self.tab_widget.addTab(self.tabs["image_processing"], "Image Processing") self.tab_widget.addTab(self.tabs["definitions"], "Definitions") self.tab_widget.addTab(self.tabs["map_merging"], "Map Merging") self.tab_widget.addTab(self.tabs["postprocess_scripts"], "Postprocess Scripts") # --- Setup Layouts for Tabs --- self.tab_layouts = {name: QVBoxLayout(tab) for name, tab in self.tabs.items()} # --- Populate Tabs --- self.populate_general_tab(self.tab_layouts["general"]) self.populate_output_naming_tab(self.tab_layouts["output_naming"]) self.populate_image_processing_tab(self.tab_layouts["image_processing"]) self.populate_definitions_tab(self.tab_layouts["definitions"]) self.populate_map_merging_tab(self.tab_layouts["map_merging"]) self.populate_postprocess_scripts_tab(self.tab_layouts["postprocess_scripts"]) 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.""" # Reuse existing methods for Asset and File Type Definitions 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"]) layout.addLayout(form_layout) layout.addStretch() def populate_general_tab(self, layout): """Populates the General tab.""" form_layout = QFormLayout() # Settings from app_settings.json that fit 'General' keys_to_include = [ "DEFAULT_ASSET_CATEGORY" ] 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_output_naming_tab(self, layout): """Populates the Output & Naming tab.""" form_layout = QFormLayout() # Settings from app_settings.json that fit 'Output & Naming' 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_processing_tab(self, layout): """Populates the Image Processing tab.""" form_layout = QFormLayout() # Simple settings from app_settings.json that fit 'Image Processing' 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) # Complex widgets for Image Processing if "IMAGE_RESOLUTIONS" in self.settings: group = QGroupBox("Image Resolutions") group_layout = QVBoxLayout(group) # IMAGE_RESOLUTIONS is a dict in app_settings, need to adapt create_image_resolutions_widget # For now, display as a simple text field or defer # Deferring complex dict/list handling for now, except for Definitions and Map Merging # self.create_image_resolutions_widget(group_layout, self.settings["IMAGE_RESOLUTIONS"]) # Placeholder for IMAGE_RESOLUTIONS layout.addWidget(QLabel("Image Resolutions (complex structure - deferred)")) # Add a simple widget for now to show the data if "IMAGE_RESOLUTIONS" in self.settings: self.create_widget_for_setting(form_layout, "IMAGE_RESOLUTIONS", str(self.settings["IMAGE_RESOLUTIONS"])) if "MAP_BIT_DEPTH_RULES" in self.settings: group = QGroupBox("Map Bit Depth Rules") group_layout = QVBoxLayout(group) # MAP_BIT_DEPTH_RULES is a dict in app_settings, need to adapt create_map_bit_depth_rules_widget # For now, display as a simple text field or defer # Deferring complex dict/list handling for now, except for Definitions and Map Merging # self.create_map_bit_depth_rules_widget(group_layout, self.settings["MAP_BIT_DEPTH_RULES"]) # Placeholder for MAP_BIT_DEPTH_RULES layout.addWidget(QLabel("Map Bit Depth Rules (complex structure - deferred)")) # Add a simple widget for now to show the data if "MAP_BIT_DEPTH_RULES" in self.settings: self.create_widget_for_setting(form_layout, "MAP_BIT_DEPTH_RULES", str(self.settings["MAP_BIT_DEPTH_RULES"])) layout.addStretch() def populate_map_merging_tab(self, layout): """Populates the Map Merging tab.""" # Implement Map Merging UI (ListWidget + Details Form) if "MAP_MERGE_RULES" in self.settings: self.create_map_merge_rules_widget(layout, self.settings["MAP_MERGE_RULES"]) layout.addStretch() def populate_postprocess_scripts_tab(self, layout): """Populates the Postprocess Scripts tab.""" form_layout = QFormLayout() # No explicit settings for postprocess scripts in app_settings.json currently layout.addWidget(QLabel("No postprocess script 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 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): # Handle simple lists displayed as comma-separated strings if key in ["STANDARD_MAP_TYPES", "RESPECT_VARIANT_MAP_TYPES"]: current_dict[k] = [item.strip() for item in widget.text().split(',') if item.strip()] else: 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) 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 # Note: IMAGE_RESOLUTIONS and MAP_BIT_DEPTH_RULES tables are not saved here # as they are currently displayed as simple text fields due to deferred UI complexity. # Handle Map Merge Rules (more complex) # Reconstruct the list from the list widget items' data. # Note: Changes made in the details form are NOT saved with this implementation # due to deferred complexity in updating the list item's data. elif key == "IMAGE_PROCESSING_SETTINGS.MAP_MERGE_RULES": 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: # Append the rule data stored in the list item. # This data reflects the state when the dialog was opened, # not changes made in the details form. 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}") def populate_widgets_from_settings(self): """Populates the created widgets with loaded settings.""" if not self.settings or not self.widgets: return for key, value in self.settings.items(): # Handle simple settings directly if they have a corresponding widget if key in self.widgets and isinstance(self.widgets[key], (QLineEdit, QSpinBox, QDoubleSpinBox, QCheckBox)): widget = self.widgets[key] if isinstance(widget, QLineEdit): # Handle simple lists displayed as comma-separated strings if key in ["STANDARD_MAP_TYPES", "RESPECT_VARIANT_MAP_TYPES"] and isinstance(value, list): widget.setText(", ".join(map(str, value))) elif isinstance(value, (str, int, float, bool)): # Also handle cases where simple types might be in QLineEdit widget.setText(str(value)) elif isinstance(widget, QSpinBox) and isinstance(value, int): widget.setValue(value) elif isinstance(widget, QDoubleSpinBox) and isinstance(value, (int, float)): widget.setValue(float(value)) elif isinstance(widget, QCheckBox) and isinstance(value, bool): widget.setChecked(value) # Add other simple widget types if needed # Handle complex structures with dedicated widgets elif key == "ASSET_TYPE_DEFINITIONS" and "DEFINITION_SETTINGS.ASSET_TYPE_DEFINITIONS" in self.widgets: self.populate_asset_definitions_table(self.widgets["DEFINITION_SETTINGS.ASSET_TYPE_DEFINITIONS"], value) elif key == "FILE_TYPE_DEFINITIONS" and "DEFINITION_SETTINGS.FILE_TYPE_DEFINITIONS" in self.widgets: self.populate_file_type_definitions_table(self.widgets["DEFINITION_SETTINGS.FILE_TYPE_DEFINITIONS"], value) elif key == "MAP_MERGE_RULES" and hasattr(self, 'merge_rules_list'): # Check if the list widget exists self.populate_merge_rules_list(value) # Select the first item to display details if the list is not empty if self.merge_rules_list.count() > 0: self.merge_rules_list.setCurrentRow(0) # Handle complex dicts/lists displayed as strings (if they were created with create_widget_for_setting) # These are already handled by the simple widget logic above if they were created as QLineEdit # with the string representation. def populate_asset_definitions_table(self, table: QTableWidget, definitions_data: dict): """Populates the asset definitions table.""" 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", ""))) # Recreate the color widget for population 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 def populate_file_type_definitions_table(self, table: QTableWidget, definitions_data: dict): """Populates the file type definitions table.""" 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", ""))) # Recreate the color widget for population 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 # 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_())