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.0.tar.gz (16.9 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.0-py3-none-any.whl (17.0 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: pyside6_settings-1.0.0.tar.gz
  • Upload date:
  • Size: 16.9 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.0.tar.gz
Algorithm Hash digest
SHA256 a5bd6ab8ae6bd5af631c1d7a71e8cf283a0da5c7348ae0fb714a8fb61d442ffd
MD5 3253bc8d7ea9fd243a8b277905a430b1
BLAKE2b-256 604800c665552f412618e855c084e80cfd8668c957518a334618055235926f27

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyside6_settings-1.0.0.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.0-py3-none-any.whl.

File metadata

File hashes

Hashes for pyside6_settings-1.0.0-py3-none-any.whl
Algorithm Hash digest
SHA256 939c67a4bc28bf812098a0c4dc795ae19c52718a0e01f1ae3b58269dba1ccfc4
MD5 b9f2173a33e6770e22eae592e926d7f1
BLAKE2b-256 22c29d39af023b21cbf264850aa67ae7065c966443821ef173db49ddc31324dd

See more details on using hashes here.

Provenance

The following attestation bundles were made for pyside6_settings-1.0.0-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