Skip to content

Commit

Permalink
chore: wip
Browse files Browse the repository at this point in the history
  • Loading branch information
leocov-dev committed Sep 6, 2024
1 parent 9a42559 commit 54b7d51
Show file tree
Hide file tree
Showing 12 changed files with 312 additions and 357 deletions.
121 changes: 67 additions & 54 deletions examples/toolbar_app/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@
from PySide6.QtGui import QAction
from PySide6.QtWidgets import QLabel, QVBoxLayout

from pyside_app_core.services.preferences_service import PreferencesService
from pyside_app_core.types.preferences import Pref, PrefsConfig, PrefsGroup
from pyside_app_core.services.preferences_service import PreferencesService, PrefGroup, PrefValue
from pyside_app_core.ui.standard import MainWindow
from pyside_app_core.ui.widgets.connection_manager import ConnectionManager
from pyside_app_core.ui.widgets.core_icon import CoreIcon
from pyside_app_core.ui.widgets.multi_combo_box import MultiComboBox
from pyside_app_core.ui.widgets.preferences_manager import PreferencesManager
from pyside_app_core.ui.widgets.tool_bar_ctx import ToolBarContext


class SimpleMainWindow(MainWindow):
Expand All @@ -21,23 +21,23 @@ def __init__(self) -> None:
self.setMinimumSize(QSize(480, 240))

self._prefs_mgr: PreferencesManager | None = None
self._preferences = PrefsConfig(
PrefsGroup(
"Application",
[
Pref("Remember Position", True),
Pref("Remember Size", True),
Pref("Default Path", Path.home() / "one" / "two" / "three" / "four"),
],
PreferencesService.add_groups(
PrefGroup(
"app", "Application",
PrefGroup(
"remember", "Keep Between Sessions",
PrefValue("pos", "Remember Position", True),
PrefValue("size", "Remember Size", True),
),
PrefValue("path", "Default Path", Path.home() / "one" / "two" / "three" / "four"),
),
PrefsGroup(
"Developer",
[
Pref("Debug Mode", False),
],
PrefGroup(
"dev", "Developer",
PrefValue("debug", "Debug Mode", False),
),
)
PreferencesService.load_config(self._preferences)

print(PreferencesService.instance())

self._menus()
self._content()
Expand All @@ -48,46 +48,59 @@ def _menus(self) -> None:
file_menu.action("Preferences...") as prefs_action,
):
prefs_action.setMenuRole(QAction.MenuRole.PreferencesRole)
prefs_action.triggered.connect(self._open_preferences)
prefs_action.triggered.connect(PreferencesManager.open)

def _content(self) -> None:
_tool_bar = self.addToolBar("main")
_tool_bar = ToolBarContext("top", self)
_tool_bar.setObjectName("main-tool-bar")
plug_action = _tool_bar.addAction(
CoreIcon(
":/core/iconoir/ev-plug-charging.svg",
":/core/iconoir/ev-plug-xmark.svg",
),
"Connect",
)
plug_action.setCheckable(True)
plug_action2 = _tool_bar.addAction(
CoreIcon(
":/core/iconoir/ev-plug-charging.svg",
":/core/iconoir/ev-plug-xmark.svg",
),
"Connect",
)
plug_action2.setCheckable(True)
plug_action2.setChecked(True)
reload_action = _tool_bar.addAction(
CoreIcon(
":/core/iconoir/refresh-circle.svg",
),
"Reload",
)
reload_action.setDisabled(True)
_raise_action = _tool_bar.addAction(
CoreIcon(
":/core/iconoir/floppy-disk.svg",
),
"Save",
)

def _raise() -> None:
raise Exception("This is a test error") # noqa

_raise_action.triggered.connect(_raise)
with _tool_bar.add_action(
"Connect",
CoreIcon(
":/core/iconoir/ev-plug-charging.svg",
":/core/iconoir/ev-plug-xmark.svg",
),
) as plug_action:
plug_action.setCheckable(True)

with _tool_bar.add_action(
"Connect",
CoreIcon(
":/core/iconoir/ev-plug-charging.svg",
":/core/iconoir/ev-plug-xmark.svg",
),
) as plug_action:
plug_action.setCheckable(True)
plug_action.setChecked(True)

with _tool_bar.add_action(
"Reload",
CoreIcon(
":/core/iconoir/refresh-circle.svg",
),
) as reload_action:
reload_action.setDisabled(True)

with _tool_bar.add_action(
"Save",
CoreIcon(
":/core/iconoir/floppy-disk.svg",
),
) as raise_action:
def _raise() -> None:
raise Exception("This is a test error") # noqa

raise_action.triggered.connect(_raise)

_tool_bar.add_stretch()

with _tool_bar.add_action(
"Preferences",
CoreIcon(
":/core/iconoir/settings.svg",
),
) as prefs_action:
prefs_action.triggered.connect(PreferencesManager.open)

# -----
_central_layout = QVBoxLayout()
Expand All @@ -107,9 +120,9 @@ def _raise() -> None:

self.statusBar().showMessage("Hi There")

def _open_preferences(self) -> None:
def open_preferences(self) -> None:
if self._prefs_mgr:
self._prefs_mgr.close()

self._prefs_mgr = PreferencesManager(self._preferences)
self._prefs_mgr = PreferencesManager()
self._prefs_mgr.show()
10 changes: 2 additions & 8 deletions src/pyside_app_core/mixin/settings_mixin.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
from typing import Any, TypeVar, cast
from typing import Any, cast, TypeVar

from PySide6.QtCore import QCoreApplication, QObject, QSettings
from PySide6.QtGui import QShowEvent
from PySide6.QtWidgets import QWidget

from pyside_app_core.app.application_service import AppMetadata

_SV = TypeVar("_SV")


class SettingsMixin:
def __init__(self, parent: QObject | None = None, *args: object, **kwargs: object):
super().__init__(*args, **kwargs)

self._settings = QSettings(
AppMetadata.id,
AppMetadata.name,
parent,
)
self._settings = QSettings(parent)

self._restored = False

Expand Down
1 change: 1 addition & 0 deletions src/pyside_app_core/resources/core/iconoir/settings.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
182 changes: 163 additions & 19 deletions src/pyside_app_core/services/preferences_service.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,141 @@
from typing import Any
from __future__ import annotations

from PySide6.QtCore import QCoreApplication, QObject, Signal
from enum import IntEnum
from typing import Any, Iterator, cast

from PySide6.QtCore import QCoreApplication, QObject, Qt, Signal, QAbstractProxyModel
from PySide6.QtGui import QStandardItem, QStandardItemModel

from pyside_app_core.mixin.settings_mixin import SettingsMixin
from pyside_app_core.types.preferences import Pref, PrefsConfig, PrefsGroup, PrefsValue
from pyside_app_core.utils.property import ro_classproperty


class PrefRole(IntEnum):
DISPLAY_NAME = Qt.ItemDataRole.DisplayRole
NAME = Qt.ItemDataRole.UserRole + 1
DTYPE = Qt.ItemDataRole.UserRole + 2
EDITOR_WIDGET = Qt.ItemDataRole.UserRole + 3
VALUE = Qt.ItemDataRole.UserRole + 4


class Node(SettingsMixin, QStandardItem):

def __init__(
self,
name: str,
display_name: str,
*children: Node
):
super().__init__()
self.setData(name, PrefRole.NAME)
self.setData(display_name, PrefRole.DISPLAY_NAME)

self.appendRows(children)

@property
def name(self) -> str:
return self.data(PrefRole.NAME)

@property
def display_name(self) -> str:
return self.data(PrefRole.DISPLAY_NAME)

@property
def fqdn(self) -> str:
return f"{self.parent().fqdn}.{self.name}" if self.parent() is not None else self.name

@property
def level(self) -> int:
def _lvl(node, lvl):
if node and node.parent():
return _lvl(node.parent(), lvl + 1)
return lvl

return _lvl(self, 0)

def __str__(self):
children = "\n".join(
str(self.child(r, 0)) for r in range(self.rowCount())
)
return f"[{self.level}] {' '*self.level*2}{self.name}:\n{children}"


class PrefGroup(Node):
def __init__(
self,
name: str,
display_name: str,
*children: Node
):
super().__init__(
name,
display_name,
*children,
)


class PrefValue(Node):
def __init__(
self,
name: str,
display_name: str,
default_value: PrefsValue,
widget: type[PrefWidget[PrefsValue]] | None = None,
*children: Node
):
super().__init__(
name,
display_name,
*children,
)
self.setData(type(default_value), PrefRole.DTYPE)
self.setData(widget, PrefRole.EDITOR_WIDGET)

# update from local storage ----
self.setData(self.data_type(self.get_setting(self.fqdn, default_value)), PrefRole.VALUE)

def setData(self, value: Any, role=Qt.ItemDataRole):
if role == PrefRole.VALUE:
self.store_setting(self.fqdn, value)
super().setData(value, role)

@property
def data_type(self) -> type[PrefsValue]:
return self.data(PrefRole.DTYPE)

@property
def widget(self) -> type[PrefWidget[PrefsValue]] | None:
return self.data(PrefRole.EDITOR_WIDGET)

class PreferencesService(SettingsMixin, QObject):
pref_changed = Signal(str, PrefsValue) # type: ignore[arg-type]
@property
def value(self) -> PrefsValue:
return self.data(PrefRole.VALUE)

def __str__(self):
children = "\n".join(
str(self.child(r, 0)) for r in range(self.rowCount())
)
return f"[{self.level}] {' '*self.level*2}{self.name}: \"{self.fqdn}\" {self.data_type.__name__}<{self.value}>{children}"


class PrefGroupsProxy(QAbstractProxyModel):

def __init__(self, parent: QObject | None = None):
super().__init__(parent)
self._root = None

def setRootIndex(self, index: QModelIndex):


def mapToSource(self, proxyIndex: QModelIndex) -> QModelIndex:
pass

def mapFromSource(self, source: QModelIndex) -> QModelIndex:
pass


class PreferencesService(QObject):
_pref_changed = Signal(str, object) # type: ignore[arg-type]

def __new__(cls) -> "PreferencesService":
if hasattr(cls, "_instance"):
Expand All @@ -16,6 +144,10 @@ def __new__(cls) -> "PreferencesService":
cls._instance = super().__new__(cls)
return cls._instance

@ro_classproperty
def pref_changed(cls) -> Signal:
return cls.instance()._pref_changed

@classmethod
def instance(cls) -> "PreferencesService":
if not hasattr(cls, "_instance"):
Expand All @@ -24,22 +156,34 @@ def instance(cls) -> "PreferencesService":
return cls._instance

@classmethod
def save_pref(cls, pref: Pref[Any]) -> None:
cls.instance().store_setting(pref.fqdn, pref.value)

@classmethod
def load_pref(cls, pref: Pref[Any]) -> None:
pref.value = pref.data_type(cls.instance().get_setting(pref.fqdn, pref.value))

@classmethod
def load_group(cls, group: PrefsGroup) -> None:
for item in group.items:
cls.load_pref(item)
def add_groups(cls, *groups: PrefGroup) -> None:
cls.instance()._model.invisibleRootItem().appendRows(list(groups))

@classmethod
def load_config(cls, config: PrefsConfig) -> None:
for group in config:
cls.load_group(group)
def model(cls) -> QStandardItemModel:
return cls.instance()._model

def __init__(self) -> None:
super().__init__(parent=QCoreApplication.instance())

self._model = QStandardItemModel(parent=self)

def __getitem__(
self,
key: str,
) -> PrefGroup:
return self._model[key]

def __len__(self) -> int:
return self._model.rowCount()

def __iter__(self) -> Iterator[PrefGroup]:
return iter(cast(PrefGroup, self._model.item(r, 0)) for r in range(self._model.rowCount()))

def __str__(self) -> str:
return "\n".join(
[
"Preferences:",
*[str(g) for g in self],
]
)
Loading

0 comments on commit 54b7d51

Please sign in to comment.