Widgets - Capsize-Games/airunner GitHub Wiki

Overview

Widgets in AI Runner are built using PySide6 and extend from the BaseWidget class, which provides common functionality such as state management, signal handling, and UI initialization. This document provides an overview of how widgets work, including the BaseWidget class, MediatorMixin, and specific widget implementations.


BaseWidget

The BaseWidget class is the foundation for all widgets in AI Runner. It provides: - State Persistence: Methods for saving and restoring widget state (save_state and restore_state). - Signal Handling: Uses MediatorMixin to register and emit signals. - UI Initialization: Automatically sets up the widget’s UI and icons. - Worker Initialization: Supports background tasks through worker class mapping.

Key Properties and Methods

Properties

  • splitters: List of splitter names in the UI for saving and restoring positions.

  • current_tool: Retrieves the currently active tool in the canvas.

  • is_dark: Returns whether the application is in dark mode.

Methods

  • initialize(): Calls initialize_workers and initialize_form to set up the widget.

  • initialize_workers(): Instantiates background worker tasks.

  • save_state(): Saves the state of UI elements (such as splitters).

  • restore_state(): Restores the widget’s previous state.

  • handle_close(): Handles application quit events.

  • set_icons(): Updates icons based on the theme.


MediatorMixin

MediatorMixin enables widgets to communicate using a centralized SignalMediator. This is useful for decoupling components and handling global events.

How It Works

  • Widgets define a dictionary signal_handlers that maps signal codes to functions.

  • MediatorMixin automatically registers these handlers with the SignalMediator.

  • Signals can be emitted using emit_signal() and received using the registered handlers.

Example Usage

class MyWidget(BaseWidget):
    def __init__(self, *args, **kwargs):
        self.signal_handlers = {
            SignalCode.MY_CUSTOM_SIGNAL: self.handle_custom_signal
        }
        super().__init__(*args, **kwargs)
    
    def handle_custom_signal(self, message):
        print("Received signal:", message)

Creating a Widget

When creating a new widget, it must extend BaseWidget and define signal_handlers before calling super().init(*args, **kwargs). This ensures that signals are properly registered before the widget is initialized.

Example Widget Implementation

from airunner.widgets.base_widget import BaseWidget
from airunner.enums import SignalCode

class ExampleWidget(BaseWidget):
    def __init__(self, *args, **kwargs):
        self.signal_handlers = {
            SignalCode.EXAMPLE_SIGNAL: self.handle_example_signal
        }
        super().__init__(*args, **kwargs)
    
    def handle_example_signal(self, message):
        print("Handling example signal:", message)

Using PySide6 Templates in AI Runner

AI Runner uses UI templates created and modified with the PySide6 Designer. To launch the designer:

pyside6-designer

Template Structure

Templates (.ui files) are stored in the templates directories within widgets and windows.

Example:

airunner/src/airunner/gui
├── widgets
│   ├── llm
│   │   ├── templates
│   │   │   ├── chat_prompt.ui  # UI Template
│   │   │   └── chat_prompt_ui.py  # Compiled UI
│   │   └── chat_prompt_widget.py  # Widget Implementation

Connecting Slots to Signals

Widgets use PySide6’s signal-slot mechanism for event handling.

Example:

@Slot(str)
def handle_token_signal(self, val: str):
    if val != "[END]":
        text = self.ui.conversation.toPlainText()
        text += val
        self.ui.conversation.setPlainText(text)
    else:
        self.stop_progress_bar()
        self.generating = False
        self.enable_send_button()

Compiling UI Templates

To compile .ui files into Python files, run:

python bin/build_ui.py
⚠️ **GitHub.com Fallback** ⚠️