Skip to main content

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

Release files for pyside6-settings 1.0.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for pyside6-settings 1.0.3
File Size Uploaded
pyside6_settings-1.0.3.tar.gz 17.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for pyside6-settings 1.0.3
File Interpreter ABI Platform
pyside6_settings-1.0.3-py3-none-any.whl Python 3 none any Details

Total release size: 35.7 kB

Release files / pyside6_settings-1.0.3.tar.gz

Download URL pyside6_settings-1.0.3.tar.gz
Size 17.7 kB
Tags Source
SHA-256 checksum
How to use checksums
5808e4535988e417268395d08cc0256710bffecd1622cea46afa584d5d79583d
BLAKE2b-256 checksum
How to use checksums
087ab527ab9924672ab3d55bc0d015894bfbd95557e8711b8c1b10225a03899a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 13, 2025.

Transparency log

Release files / pyside6_settings-1.0.3-py3-none-any.whl

Download URL pyside6_settings-1.0.3-py3-none-any.whl
Size 17.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
e0a733a06e068b16ff98d13c1de4c17b649d071e675c8fe78aade1fd20c97049
BLAKE2b-256 checksum
How to use checksums
aee8fbeedf66450c3706b149ccbb8c608c2192cfc71e059f7a0c26b204e387c4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.7

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 13, 2025.

Transparency log

Release history Release notifications | RSS feed

This release

1.0.3 This release

2 release files

1.0.2

2 release files

1.0.1

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page