Skip to main content

Automatic UI generation for PySide6 settings from Pydantic models

Project description

PySide6 Settings

A powerful and elegant settings management library for PySide6 applications that automatically generates UI forms from Pydantic models.

Features

  • 🎨 Automatic UI Generation - Generate beautiful Qt forms directly from Pydantic models
  • 🔄 Two-way Data Binding - Automatic synchronization between UI widgets and data models
  • 💾 Multiple File Formats - Support for JSON, YAML, TOML, and more
  • 🎯 Type-Safe - Full type checking with Pydantic
  • 🏗️ Organized Groups - Group related settings with collapsible group boxes
  • 🔌 Extensible - Easy to add custom widgets and loaders
  • Validation - Automatic validation with Pydantic constraints
  • 📝 Rich Widgets - Support for various input types including file browsers, tag inputs, password fields, and more

Installation

pip install pyside6-settings

Or install from source:

git clone https://github.com/yourusername/pyside6-settings.git
cd pyside6-settings
pip install -e .

Quick Start

Basic Example

from pathlib import Path
from pyside6_settings import BaseSettings, Field
from PySide6.QtWidgets import QApplication, QMainWindow

class AppSettings(BaseSettings):
    # Basic fields with automatic widget generation
    username: str = Field(default="user", description="Your username")
    age: int = Field(default=25, ge=0, le=150, description="Your age")
    enabled: bool = Field(default=True, description="Enable feature")
    
    # Field with choices (creates QComboBox)
    theme: str = Field(
        default="dark",
        title="Theme",
        choices=["light", "dark", "auto"]
    )

# Create Qt application
app = QApplication([])

# Load settings from file (creates if doesn't exist)
settings = AppSettings.load("config.json")

# Create main window with settings form
window = QMainWindow()
window.setCentralWidget(settings.create_form())
window.setWindowTitle("Application Settings")
window.show()

# Changes are automatically saved to config.json
settings.username = "new_user"  # Auto-saved!

app.exec()

Grouped Settings

Organize related settings into collapsible groups:

from pydantic import Field
from pyside6_settings import BaseSettings, WidgetMetadata

class MySettings(BaseSettings):
    # Account Group
    username: str = Field(
        default="",
        title="Username",
        group="Account",
        description="Your account username"
    )
    
    password: str = Field(
        default="",
        title="Password",
        widget="password",
        group="Account"
    )
    
    # Appearance Group
    font_size: int = Field(
        default=12,
        ge=8,
        le=32,
        title="Font Size",
        group="Appearance"
    )
    
    theme: str = Field(
        default="dark",
        title="Color Theme",
        choices=["light", "dark", "high-contrast"],
        group="Appearance"
    )

settings = MySettings.load("config.json")
form = settings.create_form()  # Creates form with "Account" and "Appearance" groups

Advanced Widget Types

from pathlib import Path
from typing import List
from pydantic import Field
from pyside6_settings import BaseSettings, WidgetMetadata

class AdvancedSettings(BaseSettings):
    # File/Directory Browser
    project_path: Path = Field(
        default=Path("."),
        title="Project Directory",
        s_mode="directory"
    )
    
    config_file: Path = Field(
        default=Path("config.ini"),
        title="Config File",
        fs_mode="file"
    )
    
    # Tag/List Input
    tags: List[str] = Field(
        default_factory=list,
        title="Tags",
        widget="tags"
    )
    
    # Multi-line Text
    description: str = Field(
        default="",
        title="Description",
        widget="textarea"
    )
    
    # Password Field
    api_key: str = Field(
        default="",
        title="API Key",
        widget="password"
    )
    
    # Numeric with Constraints
    timeout: float = Field(
        default=30.0,
        ge=1.0,
        le=300.0,
        title="Timeout (seconds)"
    )

settings = AdvancedSettings.load("advanced.json")

Widget Types

The library automatically selects the appropriate widget based on field type and metadata:

Field Type Widget Notes
str QLineEdit Default text input
int QSpinBox With min/max from constraints
float QDoubleSpinBox With min/max from constraints
bool QCheckBox Checkbox
List[str] TagInputWidget Custom tag input
Path PathBrowseWidget File/directory browser
str (with choices) QComboBox Dropdown selection

Custom Widget Override

Use widget parameter in WidgetMetadata to force a specific widget type:

description: str = Field(
    default="",
    widget="textarea"
)

Available widget overrides:

  • "textarea" - Multi-line text input
  • "password" - Password field (masked input)
  • "tags" - Tag input widget
  • "path" - File/directory browser
  • "checkbox" - Checkbox
  • "spinbox" - Numeric spinner
  • "doublespinbox" - Float spinner
  • "hidden" - Field exists but no widget created

Working with Individual Widgets

Get individual field widgets for custom layouts:

settings = MySettings.load("config.json")

# Get widget without label
username_widget = settings.get_widget("username", with_label=False)

# Get widget with label (in QFormLayout)
username_field = settings.get_widget("username", with_label=True)

# Get entire group as QGroupBox
account_group = settings.get_group("Account")

Programmatic Access

Settings behave like normal Python objects:

settings = MySettings.load("config.json")

# Read values
print(settings.username)
print(settings.theme)

# Modify values (automatically saved)
settings.username = "new_user"
settings.theme = "light"

# Access all fields
for field_name in settings.__pydantic_fields__:
    value = getattr(settings, field_name)
    print(f"{field_name}: {value}")

File Format Support

Supported configuration file formats:

  • JSON (.json)
  • YAML (.yaml, .yml)
  • TOML (.toml)
  • INI (.ini)

The format is automatically detected from the file extension:

# Use different formats
json_settings = MySettings.load("config.json")
yaml_settings = MySettings.load("config.yaml")
toml_settings = MySettings.load("config.toml")

Custom Loaders

Implement custom loaders for other formats:

from pyside6_settings.loaders import BaseConfigLoader

class CustomLoader(BaseConfigLoader):
    def load(self) -> dict:
        # Your loading logic
        pass
    
    def save(self, data: dict) -> None:
        # Your saving logic
        pass

# Register the loader
from pyside6_settings import DEFAULT_LOADERS
DEFAULT_LOADERS[".custom"] = CustomLoader

Validation

Leverage Pydantic's powerful validation:

from pydantic import Field, field_validator

class ValidatedSettings(BaseSettings):
    email: str = Field(default="")
    port: int = Field(default=8080, ge=1, le=65535)
    
    @field_validator("email")
    @classmethod
    def validate_email(cls, v):
        if v and "@" not in v:
            raise ValueError("Invalid email address")
        return v

settings = ValidatedSettings.load("config.json")
settings.email = "invalid"  # Raises ValidationError

Excluding Fields

Exclude fields from UI or serialization:

internal_value: str = Field(default="secret", exclude=True)

# Or hide from UI only
hidden_field: str = Field(
    default="value",
    widget="hidden"
)

Complete Example

from pathlib import Path
from typing import List, Optional
from pydantic import Field
from pyside6_settings import BaseSettings, Field
from PySide6.QtWidgets import QApplication, QMainWindow, QVBoxLayout, QWidget, QPushButton

class EditorSettings(BaseSettings):
    # General Settings
    window_title: str = Field(
        default="My Editor",
        title="Window Title",
        group="General"
    )
    
    auto_save: bool = Field(
        default=True,
        title="Auto Save",
        description="Automatically save files",
        group="General"
    )
    
    # Editor Settings
    font_family: str = Field(
        default="Monospace",
        title="Font Family",
        choices=["Monospace", "Arial", "Times New Roman"],
        group="Editor"
    )
    
    font_size: int = Field(
        default=12,
        ge=8,
        le=32,
        title="Font Size",
        group="Editor"
    )
    
    tab_size: int = Field(
        default=4,
        ge=1,
        le=8,
        title="Tab Size",
        group="Editor"
    )
    
    # Paths
    workspace: Path = Field(
        default=Path.home(),
        title="Workspace Directory",
        fs_mode="directory",
        group="Paths"
    )
    
    recent_files: List[str] = Field(
        default_factory=list,
        title="Recent Files",
        widget="tags",
        group="Paths"
    )

def main():
    app = QApplication([])
    
    # Load settings
    settings = EditorSettings.load("editor_config.json")
    
    # Create main window
    window = QMainWindow()
    window.setWindowTitle("Editor Settings")
    
    # Create central widget with settings form
    central = QWidget()
    layout = QVBoxLayout(central)
    
    # Add settings form
    settings_form = settings.create_form()
    layout.addWidget(settings_form)
    
    # Add close button
    close_btn = QPushButton("Close")
    close_btn.clicked.connect(window.close)
    layout.addWidget(close_btn)
    
    window.setCentralWidget(central)
    window.resize(600, 500)
    window.show()
    
    app.exec()

if __name__ == "__main__":
    main()

gui

API Reference

BaseSettings

Class Methods

  • load(config_file: str | Path) -> Self - Load settings from file

Instance Methods

  • create_form(parent: QWidget | None = None) -> QWidget - Create complete settings form
  • get_widget(field_name: str, with_label: bool = True) -> QWidget - Get widget for specific field
  • get_group(group_name: str) -> QGroupBox - Get group box for specific group

Properties

  • _config_file: Path - Path to configuration file
  • _config_loader: BaseConfigLoader - Loader instance
  • _bridge: _SettingsBridge - Signal bridge for widget synchronization

WidgetMetadata

Configuration for widget behavior and appearance:

  • title: str - Display label for the field
  • description: str - Tooltip text
  • group: str - Group name (default: "General")
  • widget: str - Force specific widget type
  • choices: List[str] - Options for dropdown (creates QComboBox)
  • fs_mode: str - File system mode: "file" or "directory"

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

  1. Fork the repository
  2. Create your feature branch (git checkout -b feature/amazing-feature)
  3. Commit your changes (git commit -m 'Add amazing feature')
  4. Push to the branch (git push origin feature/amazing-feature)
  5. Open a Pull Request

Testing

Run the test suite:

pytest tests/ -v

With coverage:

pytest tests/ --cov=pyside6_settings --cov-report=html

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Built with Pydantic for data validation
  • Uses PySide6 for Qt bindings
  • Inspired by various settings management libraries

Support

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyside6_settings-1.0.1.tar.gz (17.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

pyside6_settings-1.0.1-py3-none-any.whl (17.1 kB view details)

Uploaded Python 3

File details

Details for the file pyside6_settings-1.0.1.tar.gz.

File metadata

  • Download URL: pyside6_settings-1.0.1.tar.gz
  • Upload date:
  • Size: 17.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/6.1.0 CPython/3.13.7

File hashes

Hashes for pyside6_settings-1.0.1.tar.gz
Algorithm Hash digest
SHA256 15f8a52a8471ed91953a1ffbf1eba0553b0b582dfe549f480d3f879203e46199
MD5 d91a6e1ebb3f0ee260e17b45af5b2902
BLAKE2b-256 c4ab59a33a5b4015a069b8d1cba14bedb86508d81e80d8bdf8b0f6cde9c17be6

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyside6_settings-1.0.1.tar.gz:

Publisher: release.yaml on AstralMortem/pyside6-settings

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file pyside6_settings-1.0.1-py3-none-any.whl.

File metadata

File hashes

Hashes for pyside6_settings-1.0.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8ff5fc3695dcb4bb438cc965b972ed82416c0528f440d24af23f570f6ad3b961
MD5 fc89b80fa53bc73a73beb6749197a376
BLAKE2b-256 b92380f7da0a4d3e90aff48a718cc7c3a21433180d7b4d3deea56f6005be5c05

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyside6_settings-1.0.1-py3-none-any.whl:

Publisher: release.yaml on AstralMortem/pyside6-settings

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page