88 lines
3.2 KiB
Python
88 lines
3.2 KiB
Python
from dataclasses import asdict
|
|
from typing import Any
|
|
|
|
from krita import QObject, QReadWriteLock, QSettings
|
|
|
|
from .defaults import CFG_FOLDER, CFG_NAME, DEFAULTS, ERR_MISSING_CONFIG
|
|
|
|
|
|
class Config(QObject):
|
|
def __init__(self, folder=CFG_FOLDER, name=CFG_NAME, model=DEFAULTS):
|
|
"""Sorta like a controller for QSettings.
|
|
|
|
I'm going to treat this as a singleton global app state, but implemented
|
|
correctly such that it should be theoretically possible to have multiple
|
|
instances (maybe multiple dockers controlling multiple remotes?)
|
|
|
|
If model is None, Config will not check if keys exist.
|
|
|
|
Args:
|
|
folder (str, optional): Which folder to store settings in. Defaults to CFG_FOLDER.
|
|
name (str, optional): Name of settings file. Defaults to CFG_NAME.
|
|
model (Any, optional): Data model representing config & defaults. Defaults to DEFAULTS.
|
|
"""
|
|
# See: https://doc.qt.io/qt-6/qsettings.html#accessing-settings-from-multiple-threads-or-processes-simultaneously
|
|
# but im too lazy to figure out creating separate QSettings per worker, so we will just lock
|
|
super(Config, self).__init__()
|
|
self.model = model # is immutable
|
|
self.lock = QReadWriteLock()
|
|
self.config = QSettings(QSettings.IniFormat, QSettings.UserScope, folder, name)
|
|
|
|
# add in new config settings
|
|
self.restore_defaults(overwrite=False)
|
|
|
|
def __call__(self, key: str, type: type = str):
|
|
"""Shorthand for Config.get()"""
|
|
return self.get(key, type)
|
|
|
|
def get(self, key: str, type: type = str):
|
|
"""Get config value by key & cast to type.
|
|
|
|
Args:
|
|
key (str): Name of config option.
|
|
type (type, optional): Type to cast config value to. Defaults to str.
|
|
|
|
Returns:
|
|
Any: Config value.
|
|
"""
|
|
self.lock.lockForRead()
|
|
try:
|
|
# notably QSettings assume strings too unless specified
|
|
if self.model is not None:
|
|
assert self.config.contains(key) and hasattr(
|
|
self.model, key
|
|
), ERR_MISSING_CONFIG
|
|
val = self.config.value(key, type=type)
|
|
return val
|
|
finally:
|
|
self.lock.unlock()
|
|
|
|
def set(self, key: str, val: Any, overwrite: bool = True):
|
|
"""Set config value by key.
|
|
|
|
Args:
|
|
key (str): Name of config option.
|
|
val (Any): Config value.
|
|
overwrite (bool, optional): Whether to overwrite an existing value. Defaults to False.
|
|
"""
|
|
self.lock.lockForWrite()
|
|
try:
|
|
if self.model is not None:
|
|
assert hasattr(self.model, key), ERR_MISSING_CONFIG
|
|
if overwrite or not self.config.contains(key):
|
|
self.config.setValue(key, val)
|
|
finally:
|
|
self.lock.unlock()
|
|
|
|
def restore_defaults(self, overwrite: bool = True):
|
|
"""Reset settings to default.
|
|
|
|
Args:
|
|
overwrite (bool, optional): Whether to overwrite existing settings, else add only new ones. Defaults to True.
|
|
"""
|
|
if self.model is None:
|
|
return
|
|
defaults = asdict(self.model)
|
|
for k, v in defaults.items():
|
|
self.set(k, v, overwrite)
|