# Developer Guide: GUI Internals This document provides technical details about the implementation of the Graphical User Interface (GUI) for developers. ## Framework The GUI is built using `PySide6`, which provides Python bindings for the Qt framework. ## Main Window (`gui/main_window.py`) The `MainWindow` class acts as the central **coordinator** for the GUI application. It is responsible for: * Setting up the main application window structure and menu bar. * **Layout:** Arranging the main GUI components using a `QSplitter`. * **Left Pane:** Contains the preset selection controls (from `PresetEditorWidget`) permanently displayed at the top. Below this, a `QStackedWidget` switches between the preset JSON editor (also from `PresetEditorWidget`) and the `LLMEditorWidget`. * **Right Pane:** Contains the `MainPanelWidget`. * Instantiating and managing the major GUI widgets: * `PresetEditorWidget` (`gui/preset_editor_widget.py`): Provides the preset selector and the JSON editor parts. * `LLMEditorWidget` (`gui/llm_editor_widget.py`): Provides the editor for LLM settings. * `MainPanelWidget` (`gui/main_panel_widget.py`): Contains the rule hierarchy view and processing controls. * `LogConsoleWidget` (`gui/log_console_widget.py`): Displays application logs. * Instantiating key models and handlers: * `UnifiedViewModel` (`gui/unified_view_model.py`): The model for the rule hierarchy view. * `LLMInteractionHandler` (`gui/llm_interaction_handler.py`): Manages communication with the LLM service. * `AssetRestructureHandler` (`gui/asset_restructure_handler.py`): Handles rule restructuring. * Connecting signals and slots between these components to orchestrate the application flow. * **Editor Switching:** Handling the `preset_selection_changed_signal` from `PresetEditorWidget` in its `_on_preset_selection_changed` slot. This slot: * Switches the `QStackedWidget` (`editor_stack`) to display either the `PresetEditorWidget`'s JSON editor or the `LLMEditorWidget` based on the selected mode ("preset", "llm", "placeholder"). * Calls `llm_editor_widget.load_settings()` when switching to LLM mode. * Updates the window title. * Triggers `update_preview()`. * Handling top-level user interactions like drag-and-drop for loading sources (`add_input_paths`). This method now handles the "placeholder" state (no preset selected) by scanning directories or inspecting archives (ZIP) and creating placeholder `SourceRule`/`AssetRule`/`FileRule` objects to immediately populate the `UnifiedViewModel` with the file structure. * Initiating predictions based on the selected preset mode (Rule-Based or LLM) when presets change or sources are added (`update_preview`). * Starting the processing task (`_on_process_requested`): This slot now filters the `SourceRule` list obtained from the `UnifiedViewModel`, excluding sources where no asset has a `Target Asset` name assigned, before emitting the `start_backend_processing` signal. It also manages enabling/disabling controls. * Managing the background prediction threads (`RuleBasedPredictionHandler` via `QThread`, `LLMPredictionHandler` via `LLMInteractionHandler`). * Implementing slots to handle results from background tasks: * `_on_rule_hierarchy_ready`: Handles results from `RuleBasedPredictionHandler`. * `_on_llm_prediction_ready_from_handler`: Handles results from `LLMInteractionHandler`. * `_on_prediction_error`: Handles errors from both prediction paths. * `_handle_prediction_completion`: Centralized logic to track completion and update UI state after each prediction result or error. * Slots to handle status and state changes from `LLMInteractionHandler`. ## Threading and Background Tasks To keep the UI responsive, prediction tasks run in background threads managed by a `QThreadPool`. * **`BasePredictionHandler` (`gui/base_prediction_handler.py`):** An abstract `QRunnable` base class defining the common interface and signals (`prediction_signal`, `status_signal`) for prediction tasks. * **`RuleBasedPredictionHandler` (`gui/prediction_handler.py`):** Inherits from `BasePredictionHandler`. Runs as a `QRunnable` in the thread pool when a rule-based preset is selected. Generates the `SourceRule` hierarchy based on preset rules and emits `prediction_signal`. * **`LLMPredictionHandler` (`gui/llm_prediction_handler.py`):** Inherits from `BasePredictionHandler`. Runs as a `QRunnable` in the thread pool when "- LLM Interpretation -" is selected. Interacts with `LLMInteractionHandler`, parses the response, generates the `SourceRule` hierarchy for a *single* input item, and emits `prediction_signal` and `status_signal`. * **`LLMInteractionHandler` (`gui/llm_interaction_handler.py`):** Manages the communication with the LLM service. This handler itself may perform network operations but typically runs synchronously within the `LLMPredictionHandler`'s thread. *(Note: The actual processing via `ProcessingEngine` is now handled by `main.ProcessingTask`, which runs in a separate process managed outside the GUI's direct threading model, though the GUI initiates it).* ## Communication (Signals and Slots) Communication between the `MainWindow` (main UI thread) and the background prediction tasks relies on Qt's signals and slots. * Prediction handlers (`RuleBasedPredictionHandler`, `LLMPredictionHandler`) emit signals from the `BasePredictionHandler`: * `prediction_signal(source_id, source_rule_list)`: Indicates prediction for a source is complete. * `status_signal(message)`: Provides status updates (primarily from LLM handler). * The `MainWindow` connects slots to these signals: * `prediction_signal` -> `MainWindow._handle_prediction_completion(source_id, source_rule_list)` * `status_signal` -> `MainWindow._on_status_update(message)` (updates status bar) * Signals from the `UnifiedViewModel` (`dataChanged`, `layoutChanged`) trigger updates in the `QTreeView`. * Signals from the `UnifiedViewModel` (`targetAssetOverrideChanged`) trigger the `AssetRestructureHandler`. ## Preset Editor (`gui/preset_editor_widget.py`) The `PresetEditorWidget` provides a dedicated interface for managing presets. It handles loading, displaying, editing, and saving preset `.json` files. * **Refactoring:** This widget has been refactored to expose its main components: * `selector_container`: A `QWidget` containing the preset list (`QListWidget`) and New/Delete buttons. Used statically by `MainWindow`. * `json_editor_container`: A `QWidget` containing the tabbed editor (`QTabWidget`) for preset JSON details and the Save/Save As buttons. Placed in `MainWindow`'s `QStackedWidget`. * **Functionality:** Still manages the logic for populating the preset list, loading/saving presets, handling unsaved changes, and providing the editor UI for preset details. * **Communication:** Emits `preset_selection_changed_signal(mode, preset_name)` when the user selects a preset, the LLM option, or the placeholder. This signal is crucial for `MainWindow` to switch the editor stack and trigger preview updates. ## LLM Settings Editor (`gui/llm_editor_widget.py`) This new widget provides a dedicated interface for editing LLM-specific settings stored in `config/llm_settings.json`. * **Purpose:** Allows users to configure the LLM predictor's behavior without directly editing the JSON file. * **Structure:** Uses a `QTabWidget` with two tabs: * **"Prompt Settings":** Contains a `QPlainTextEdit` for the main prompt and a nested `QTabWidget` for managing examples (add/delete/edit JSON in `QTextEdit` widgets). * **"API Settings":** Contains fields (`QLineEdit`, `QDoubleSpinBox`, `QSpinBox`) for endpoint URL, API key, model name, temperature, and timeout. * **Functionality:** * `load_settings()`: Reads `config/llm_settings.json` and populates the UI fields. Handles file not found or JSON errors. Called by `MainWindow` when switching to LLM mode. * `_save_settings()`: Gathers data from the UI, validates example JSON, constructs the settings dictionary, and calls `configuration.save_llm_config()` to write back to the file. Emits `settings_saved` signal on success. * Manages unsaved changes state and enables/disables the "Save LLM Settings" button accordingly. ## Unified Hierarchical View The core rule editing interface is built around a `QTreeView` managed within the `MainPanelWidget`, using a custom model and delegates. * **`UnifiedViewModel` (`gui/unified_view_model.py`):** Implements `QAbstractItemModel`. * Wraps the `RuleHierarchyModel` to expose the `SourceRule` list (Source -> Asset -> File) to the `QTreeView`. * Provides data for display and flags for editing. * **Handles `setData` requests:** Validates input and updates the underlying `RuleHierarchyModel`. Crucially, it **delegates** complex restructuring (when `target_asset_name_override` changes) to the `AssetRestructureHandler` by emitting the `targetAssetOverrideChanged` signal. * **Row Coloring:** Provides data for `Qt.ForegroundRole` (text color) based on the `item_type` and the colors defined in `config/app_settings.json`. Provides data for `Qt.BackgroundRole` based on calculating a 30% darker shade of the parent asset's background color. * **Caching:** Caches configuration data (`ASSET_TYPE_DEFINITIONS`, `FILE_TYPE_DEFINITIONS`, color maps) in `__init__` for performance. * **`update_rules_for_sources` Method:** Intelligently merges new prediction results or placeholder rules into the existing model data, preserving user overrides where applicable. * *(Note: The previous concept of switching between "simple" and "detailed" display modes has been removed. The model always represents the full detailed structure.)* * **`RuleHierarchyModel` (`gui/rule_hierarchy_model.py`):** A non-Qt model holding the actual list of `SourceRule` objects. Provides methods for accessing and modifying the hierarchy (used by `UnifiedViewModel` and `AssetRestructureHandler`). * **`AssetRestructureHandler` (`gui/asset_restructure_handler.py`):** Contains the logic to modify the `RuleHierarchyModel` when a file's target asset is changed. It listens for the `targetAssetOverrideChanged` signal from the `UnifiedViewModel` and uses methods on the `RuleHierarchyModel` (`moveFileRule`, `createAssetRule`, `removeAssetRule`) to perform the restructuring safely. * **`Delegates` (`gui/delegates.py`):** Custom `QStyledItemDelegate` implementations provide inline editors: * **`ComboBoxDelegate`:** For selecting predefined types (from `Configuration`). * **`LineEditDelegate`:** For free-form text editing. * **`SupplierSearchDelegate`:** For supplier names with auto-completion (using `config/suppliers.json`). **Data Flow Diagram (GUI Rule Management - Refactored):** ```mermaid graph TD subgraph MainWindow [MainWindow Coordinator] direction LR MW_Input[User Input (Drag/Drop)] --> MW(MainWindow); MW -- Owns/Manages --> Splitter(QSplitter); MW -- Owns/Manages --> LLMIH(LLMInteractionHandler); MW -- Owns/Manages --> ARH(AssetRestructureHandler); MW -- Owns/Manages --> VM(UnifiedViewModel); MW -- Owns/Manages --> LCW(LogConsoleWidget); MW -- Initiates --> PredPool{Prediction Threads}; MW -- Connects Signals --> VM; MW -- Connects Signals --> ARH; MW -- Connects Signals --> LLMIH; MW -- Connects Signals --> PEW(PresetEditorWidget); MW -- Connects Signals --> LLMEDW(LLMEditorWidget); end subgraph LeftPane [Left Pane Widgets] direction TB Splitter -- Adds Widget --> LPW(Left Pane Container); LPW -- Contains --> PEW_Sel(PresetEditorWidget - Selector); LPW -- Contains --> Stack(QStackedWidget); Stack -- Contains --> PEW_Edit(PresetEditorWidget - JSON Editor); Stack -- Contains --> LLMEDW; end subgraph RightPane [Right Pane Widgets] direction TB Splitter -- Adds Widget --> MPW(MainPanelWidget); MPW -- Contains --> TV(QTreeView - Rule View); MPW_UI[UI Controls (Process Btn, etc)]; MPW_UI --> MPW; end subgraph Prediction [Background Prediction] direction TB PredPool -- Runs --> RBP(RuleBasedPredictionHandler); PredPool -- Runs --> LLMP(LLMPredictionHandler); LLMIH -- Manages/Starts --> LLMP; RBP -- prediction_ready/error/status --> MW; LLMIH -- llm_prediction_ready/error/status --> MW; end subgraph ModelView [Model/View Components] direction TB TV -- Sets Model --> VM; TV -- Displays Data From --> VM; TV -- Uses Delegates --> Del(Delegates); UserEdit[User Edits Rules] --> TV; TV -- setData --> VM; VM -- Wraps --> RHM(RuleHierarchyModel); VM -- dataChanged/layoutChanged --> TV; VM -- targetAssetOverrideChanged --> ARH; ARH -- Modifies --> RHM; Del -- Get/Set Data --> VM; end %% MainWindow Interactions MW_Input -- Triggers --> MW; PEW -- preset_selection_changed_signal --> MW; LLMEDW -- settings_saved --> MW; MPW -- process_requested/etc --> MW; MW -- _on_preset_selection_changed --> Stack; MW -- _on_preset_selection_changed --> LLMEDW; MW -- _handle_prediction_completion --> VM; MW -- Triggers Processing --> ProcTask(main.ProcessingTask); %% Connections between subgraphs PEW --> LPW; %% PresetEditorWidget parts are in Left Pane LLMEDW --> Stack; %% LLMEditorWidget is in Stack MPW --> Splitter; %% MainPanelWidget is in Right Pane VM --> MW; ARH --> MW; LLMIH --> MW; LCW --> MW; ``` ## Application Styling The application style is explicitly set to 'Fusion' in `gui/main_window.py`. A custom `QPalette` adjusts default colors. ## Logging (`gui/log_console_widget.py`) The `LogConsoleWidget` displays logs captured by a custom `QtLogHandler` from Python's `logging` module. ## Cancellation The GUI provides a "Cancel" button. Cancellation logic for the actual processing is now likely handled within the `main.ProcessingTask` or the code that manages it, as the `ProcessingHandler` has been removed. The GUI button would signal this external task manager. ## GUI Configuration Editor (`gui/config_editor_dialog.py`) A dedicated dialog for editing `config/app_settings.json`. * **Functionality:** Loads `config/app_settings.json`, presents in tabs, allows editing basic fields, definitions tables (with color editing), and merge rules list/detail. * **Limitations:** Editing complex fields like `IMAGE_RESOLUTIONS` or full `MAP_MERGE_RULES` details might still be limited. * **Integration:** Launched by `MainWindow` ("Edit" -> "Preferences..."). * **Persistence:** Saves changes to `config/app_settings.json`. Requires application restart for changes to affect processing logic loaded by the `Configuration` class. The refactored GUI separates concerns into distinct widgets and handlers, coordinated by the `MainWindow`. Background tasks use `QThreadPool` and `QRunnable`. The `UnifiedViewModel` focuses on data presentation and simple edits, delegating complex restructuring to the `AssetRestructureHandler`.