A simple settings manager for PySide6
Project description
pyside-settings-manager
Recursively save and restore states of PySide6 widgets using a handler-based system, providing a 'touched' state indicator and the ability to check for unsaved changes.
Features
- Recursive Traversal: Automatically finds and manages state for supported
widgets within a
QMainWindowor other container widgets likeQTabWidgetandQGroupBox. - Handler-Based: Provides a flexible system where the logic for saving, loading, comparing, and monitoring specific widget types is encapsulated in dedicated handler classes. Default handlers are provided for common PySide6 widgets.
- Extensible: Easily register custom handlers for your own widget types or to override default behavior.
- Touched State: Tracks whether any managed widget or custom data has been changed since the last
saveorloadcall. - Unsaved Changes Check: Provides a method to explicitly check if the current state of the managed widgets differs from a saved state (in the default settings or a specific file).
- Custom Data: Save and load arbitrary pickleable Python objects alongside widget states.
- Skipping: Explicitly exclude specific widgets from management.
- File-Based Settings: Save and load states to/from
.inifiles usingQSettings, including options to save/load to specific files.
Supported Widgets (with default handlers)
QMainWindow(geometry and state)QCheckBoxQLineEditQPushButton(if checkable)QComboBox(index and text if editable)QSpinBoxQDoubleSpinBoxQRadioButtonQTextEditQTabWidget(current index if managed)QSlider
(Note: Container widgets like QWidget, QGroupBox and QTabWidget are traversed to find managed children, but don't have their own state saved by default unless a specific handler is registered for them).
Installation
Via PyPI:
pip install pyside-settings-manager
Via GitHub, e.g. using uv:
uv add "git+https://github.com/danicc097/pyside-settings-manager.git@vX.Y.Z"
Usage
-
Set the
SETTINGS_PROPERTYproperty: Add the property entry to widgets that you want to be managed. The property value should be a unique string identifier for that widget's state within the settings file scope. Widgets without this property will be ignored by default. -
Create a
QSettingsinstance: Decide where you want your settings to be stored (e.g., application-specific user settings, a project file). -
Create the
SettingsManager: Instantiate the manager with yourQSettingsobject. -
Load State: Call
manager.load()usually after your UI is fully set up. This will restore the saved states of your managed widgets from the defaultQSettings. -
Save State: Call
manager.save()when you want to persist the current state of your managed widgets to the defaultQSettings(e.g., on application close, via a save button/shortcut). -
Monitor Touched State: Connect to the
touched_changedsignal or check theis_touchedproperty to update UI elements (like enabling a "Save" button or adding an asterisk to the window title).def on_touched_changed(touched): save_button.setEnabled(touched) window.setWindowTitle(f"My App {'*' if touched else ''}") manager.touched_changed.connect(on_touched_changed)
-
Check for Unsaved Changes: Use
manager.has_unsaved_changes()to determine if the current UI state differs from the last saved state, especially before closing or performing actions that would discard unsaved changes.
Check out the examples folder for self-contained demo apps:
uv run .\examples\basic.py
Custom Handlers
You can register custom handlers for specific widget types or your own custom widgets by creating a class that implements the pyside_settings_manager.settings::SettingsHandler protocol:
from typing import List, Any
from PySide6.QtCore import QSettings, SignalInstance, QWidget
from PySide6.QtWidgets import QLineEdit
from pyside_settings_manager.settings import SettingsHandler, SETTINGS_PROPERTY
class CustomUppercaseLineEditHandler(SettingsHandler):
def save(self, widget: QLineEdit, settings: QSettings) -> None:
settings.setValue(widget.property(SETTINGS_PROPERTY), widget.text().upper())
def load(self, widget: QLineEdit, settings: QSettings) -> None:
value = settings.value(widget.property(SETTINGS_PROPERTY), type=str)
if value is not None:
widget.setText(str(value))
def compare(self, widget: QLineEdit, settings: QSettings) -> bool:
"""Returns True if current state != saved state"""
key = widget.property(SETTINGS_PROPERTY)
current_value: str = widget.text()
# Load the value saved by this handler's save method (uppercase)
saved_value = settings.value(key, type=str)
if saved_value is None:
return bool(current_value)
return current_value.upper() != str(saved_value)
def get_signals_to_monitor(self, widget: QLineEdit) -> List[SignalInstance]:
# Return a list of signals that indicate a change requiring the 'touched' state
return [widget.textChanged]
manager.register_handler(QLineEdit, CustomUppercaseLineEditHandler())
# Now, any QLineEdit with the ``SETTINGS_PROPERTY`` property will use your custom handler
# instead of the default one.
Custom Data
You can save and load arbitrary pickleable data using save_custom_data and load_custom_data:
manager.save_custom_data("user_preferences", {"theme": "dark", "font_size": 12})
prefs = manager.load_custom_data("user_preferences", dict) # {"theme": "dark", "font_size": 12}
Custom data is stored under a specific group based on CUSTOM_DATA_GROUP (e.g., [customData]). When using save_to_file and load_from_file, this custom data is also transferred. Note that load_from_file will overwrite any existing custom data in the manager's default QSettings with the custom data from the loaded file.
Skipping Widgets
To prevent a specific widget instance from being saved, loaded, compared, or monitored for the touched state, use skip_widget():
manager.skip_widget(window.my_checkbox)
# Changes to window.my_checkbox will NOT mark the state as touched,
# and its state won't be saved, loaded, or compared.
# To re-enable management:
manager.unskip_widget(window.my_checkbox)
Checking for Unsaved Changes
The has_unsaved_changes() method allows you to check if the current UI state for all managed (not skipped, has property, has handler) widgets differs from a saved state.
# Check against the default settings file used by the manager
manager.has_unsaved_changes()
# Check against a specific settings file
manager.has_unsaved_changes(source="backup_settings.ini")
# Check against a specific QSettings object
alt_settings = QSettings("temp.ini", QSettings.Format.IniFormat)
# ...
manager.has_unsaved_changes(source=alt_settings)
This method traverses the managed widgets and calls their respective handlers'
compare methods. It returns True as soon as the first difference is
detected. It does not modify the UI or the manager's is_touched state.
Changes in custom data saved via save_custom_data are not considered by
has_unsaved_changes; these are immediately synced.
Development
See the src/tests directory for detailed examples covering various widgets and handler behaviors.
To run tests using uv:
uv run pytest
Contribution
Contributions are welcome! Please open an issue or submit a pull request.
License
This project is licensed under the Apache 2.0 License - see the LICENSE file for details.
However, it relies on PySide6, which is licensed under the LGPL v3.
While the code specific to pyside-settings-manager is provided under the
Apache 2.0 license, any usage of this library inherently involves PySide6.
Users must ensure they comply with the terms of the LGPL v3 license regarding
their use and distribution.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file pyside_settings_manager-0.0.23.tar.gz.
File metadata
- Download URL: pyside_settings_manager-0.0.23.tar.gz
- Upload date:
- Size: 68.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c5a97a8ec6a3acab885b0e77a2b61790c8f1c1df59bbec50f31b4b2229d111a6
|
|
| MD5 |
0ee84e9b2183283f2f0cd0a8846cc765
|
|
| BLAKE2b-256 |
b1e04b1212e6dc57ba90a33a3b363deb34f5a5dd5e9144fc7bd1bd6c8405cfa4
|
Provenance
The following attestation bundles were made for pyside_settings_manager-0.0.23.tar.gz:
Publisher:
publish-pypi.yaml on danicc097/pyside-settings-manager
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyside_settings_manager-0.0.23.tar.gz -
Subject digest:
c5a97a8ec6a3acab885b0e77a2b61790c8f1c1df59bbec50f31b4b2229d111a6 - Sigstore transparency entry: 248910981
- Sigstore integration time:
-
Permalink:
danicc097/pyside-settings-manager@9644e87f41fa34f08dc6fe48a1f5a0d90e4ecb0c -
Branch / Tag:
refs/tags/v0.0.23 - Owner: https://github.com/danicc097
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yaml@9644e87f41fa34f08dc6fe48a1f5a0d90e4ecb0c -
Trigger Event:
push
-
Statement type:
File details
Details for the file pyside_settings_manager-0.0.23-py3-none-any.whl.
File metadata
- Download URL: pyside_settings_manager-0.0.23-py3-none-any.whl
- Upload date:
- Size: 18.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.12.9
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
d202d07965cb21867f446c51d8aabd2a10e78e1ecd9a654a68c6ed450672c541
|
|
| MD5 |
c2e310a1ac9e253498d03c2f9b78d462
|
|
| BLAKE2b-256 |
73472641ef49c8261e59cab7d9d1684c49282e3c3712f636bfe8a1addbf34f64
|
Provenance
The following attestation bundles were made for pyside_settings_manager-0.0.23-py3-none-any.whl:
Publisher:
publish-pypi.yaml on danicc097/pyside-settings-manager
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
pyside_settings_manager-0.0.23-py3-none-any.whl -
Subject digest:
d202d07965cb21867f446c51d8aabd2a10e78e1ecd9a654a68c6ed450672c541 - Sigstore transparency entry: 248910987
- Sigstore integration time:
-
Permalink:
danicc097/pyside-settings-manager@9644e87f41fa34f08dc6fe48a1f5a0d90e4ecb0c -
Branch / Tag:
refs/tags/v0.0.23 - Owner: https://github.com/danicc097
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish-pypi.yaml@9644e87f41fa34f08dc6fe48a1f5a0d90e4ecb0c -
Trigger Event:
push
-
Statement type: