Rework Backend, models and items organization
This commit is contained in:
@@ -3,6 +3,7 @@ from typing import Any, Dict, List, Optional
|
||||
|
||||
from PyQt5.QtCore import QDateTime, QSortFilterProxyModel
|
||||
|
||||
from ..pyqt_future import PyQtFuture
|
||||
from .list_item import ListItem
|
||||
from .list_model import ListModel
|
||||
|
||||
@@ -11,21 +12,15 @@ class Account(ListItem):
|
||||
_required_init_values = {"userId", "roomCategories"}
|
||||
_constant = {"userId", "roomCategories"}
|
||||
|
||||
userId: str = ""
|
||||
roomCategories: ListModel = ListModel()
|
||||
displayName: Optional[str] = None
|
||||
avatarUrl: Optional[str] = None
|
||||
statusMessage: Optional[str] = None
|
||||
userId: str = ""
|
||||
roomCategories: ListModel = ListModel()
|
||||
|
||||
|
||||
class RoomCategory(ListItem):
|
||||
_required_init_values = {"name", "rooms", "sortedRooms"}
|
||||
_constant = {"rooms", "sortedRooms"}
|
||||
_constant = {"name", "rooms", "sortedRooms"}
|
||||
|
||||
name: str = ""
|
||||
|
||||
# Must be provided at init, else it will be the same object
|
||||
# for every RoomCategory
|
||||
name: str = ""
|
||||
rooms: ListModel = ListModel()
|
||||
sortedRooms: QSortFilterProxyModel = QSortFilterProxyModel()
|
||||
|
||||
@@ -37,13 +32,16 @@ class Room(ListItem):
|
||||
roomId: str = ""
|
||||
displayName: str = ""
|
||||
topic: Optional[str] = None
|
||||
typingUsers: List[str] = []
|
||||
lastEventDateTime: Optional[QDateTime] = None
|
||||
typingMembers: List[str] = []
|
||||
members: List[str] = []
|
||||
|
||||
inviter: Optional[Dict[str, str]] = None
|
||||
leftEvent: Optional[Dict[str, str]] = None
|
||||
|
||||
|
||||
# ----------
|
||||
|
||||
class RoomEvent(ListItem):
|
||||
_required_init_values = {"type", "dict"}
|
||||
_constant = {"type"}
|
||||
@@ -56,6 +54,20 @@ class RoomEvent(ListItem):
|
||||
|
||||
# ----------
|
||||
|
||||
class User(ListItem):
|
||||
_required_init_values = {"userId", "devices"}
|
||||
_constant = {"userId", "devices"}
|
||||
|
||||
# Use PyQtFutures because the info might or might not need a request
|
||||
# to be fetched, and we don't want to block the UI in any case.
|
||||
# QML's property binding ability is used on the PyQtFuture.value
|
||||
userId: str = ""
|
||||
displayName: Optional[PyQtFuture] = None
|
||||
avatarUrl: Optional[PyQtFuture] = None
|
||||
statusMessage: Optional[PyQtFuture] = None
|
||||
devices: ListModel = ListModel()
|
||||
|
||||
|
||||
class Trust(Enum):
|
||||
blacklisted = -1
|
||||
undecided = 0
|
||||
|
@@ -154,27 +154,18 @@ class ListItem(QObject, metaclass=_ListItemMeta):
|
||||
|
||||
|
||||
def __repr__(self) -> str:
|
||||
from .list_model import ListModel
|
||||
multiline = any((
|
||||
isinstance(v, ListModel) for _, v in self._props.values()
|
||||
))
|
||||
|
||||
prop_strings = (
|
||||
"\033[{0}m{1}{2}={2}{3}\033[0m".format(
|
||||
"\033[{0};34m{1}\033[0,{0}m = \033[{0};32m{2}\033[0m".format(
|
||||
1 if p == self.mainKey else 0, # 1 = term bold
|
||||
p,
|
||||
" " if multiline else "",
|
||||
getattr(self, p)
|
||||
repr(getattr(self, p))
|
||||
) for p in list(self._props.keys()) + self._direct_props
|
||||
)
|
||||
|
||||
if any((isinstance(v, ListModel) for _, v in self._props.values())):
|
||||
return "%s(\n%s\n)" % (
|
||||
type(self).__name__,
|
||||
textwrap.indent(",\n".join(prop_strings), prefix=" " * 4)
|
||||
)
|
||||
|
||||
return "%s(%s)" % (type(self).__name__, ", ".join(prop_strings))
|
||||
return "\033[35m%s\033[0m(\n%s\n)" % (
|
||||
type(self).__name__,
|
||||
textwrap.indent(",\n".join(prop_strings), prefix=" " * 4)
|
||||
)
|
||||
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
|
@@ -30,21 +30,24 @@ class ListModel(QAbstractListModel):
|
||||
countChanged = pyqtSignal(int)
|
||||
|
||||
def __init__(self,
|
||||
initial_data: Optional[List[NewItem]] = None,
|
||||
container: Callable[..., MutableSequence] = list,
|
||||
parent: QObject = None) -> None:
|
||||
initial_data: Optional[List[NewItem]] = None,
|
||||
container: Callable[..., MutableSequence] = list,
|
||||
default_factory: Optional[Callable[[str], ListItem]] = None,
|
||||
parent: QObject = None) -> None:
|
||||
super().__init__(parent)
|
||||
self._data: MutableSequence[ListItem] = container()
|
||||
|
||||
self.default_factory = default_factory
|
||||
|
||||
if initial_data:
|
||||
self.extend(initial_data)
|
||||
|
||||
|
||||
def __repr__(self) -> str:
|
||||
if not self._data:
|
||||
return "%s()" % type(self).__name__
|
||||
return "\033[35m%s\033[0m()" % type(self).__name__
|
||||
|
||||
return "%s(\n%s\n)" % (
|
||||
return "\033[35m%s\033[0m(\n%s\n)" % (
|
||||
type(self).__name__,
|
||||
textwrap.indent(
|
||||
",\n".join((repr(item) for item in self._data)),
|
||||
@@ -56,7 +59,7 @@ class ListModel(QAbstractListModel):
|
||||
def __contains__(self, index: Index) -> bool:
|
||||
if isinstance(index, str):
|
||||
try:
|
||||
self.indexWhere(self.mainKey, index)
|
||||
self.indexWhere(index)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
@@ -84,6 +87,10 @@ class ListModel(QAbstractListModel):
|
||||
return iter(self._data)
|
||||
|
||||
|
||||
def __bool__(self) -> bool:
|
||||
return bool(self._data)
|
||||
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def repr(self) -> str:
|
||||
return self.__repr__()
|
||||
@@ -157,14 +164,22 @@ class ListModel(QAbstractListModel):
|
||||
return len(self)
|
||||
|
||||
|
||||
@pyqtSlot(str, "QVariant", result=int)
|
||||
def indexWhere(self, prop: str, is_value: Any) -> int:
|
||||
@pyqtSlot("QVariant", result=int)
|
||||
def indexWhere(self,
|
||||
main_key_is_value: Any,
|
||||
_can_use_default_factory: bool = True) -> int:
|
||||
|
||||
for i, item in enumerate(self._data):
|
||||
if getattr(item, prop) == is_value:
|
||||
if getattr(item, self.mainKey) == main_key_is_value:
|
||||
return i
|
||||
|
||||
raise ValueError(f"No item in model data with "
|
||||
f"property {prop!r} set to {is_value!r}.")
|
||||
if _can_use_default_factory and self.default_factory:
|
||||
return self.append(self.default_factory(main_key_is_value))
|
||||
|
||||
raise ValueError(
|
||||
f"No item in model data with "
|
||||
f"property {self.mainKey} is set to {main_key_is_value!r}."
|
||||
)
|
||||
|
||||
|
||||
@pyqtSlot(int, result="QVariant")
|
||||
@@ -173,19 +188,25 @@ class ListModel(QAbstractListModel):
|
||||
@pyqtSlot(str, "QVariant", result="QVariant")
|
||||
def get(self, index: Index, default: Any = _GetFail()) -> ListItem:
|
||||
try:
|
||||
i_index: int = self.indexWhere(self.mainKey, index) \
|
||||
if isinstance(index, str) else index
|
||||
i_index: int = \
|
||||
self.indexWhere(index, _can_use_default_factory=False) \
|
||||
if isinstance(index, str) else index
|
||||
|
||||
return self._data[i_index]
|
||||
|
||||
except (ValueError, IndexError):
|
||||
if isinstance(default, _GetFail):
|
||||
if self.default_factory and isinstance(index, str):
|
||||
item = self.default_factory(index)
|
||||
self.append(item)
|
||||
return item
|
||||
raise
|
||||
|
||||
return default
|
||||
|
||||
|
||||
@pyqtSlot(int, "QVariantMap")
|
||||
def insert(self, index: int, value: NewItem) -> None:
|
||||
@pyqtSlot(int, "QVariantMap", result=int)
|
||||
def insert(self, index: int, value: NewItem) -> int:
|
||||
value = self._convert_new_value(value)
|
||||
|
||||
self.beginInsertRows(QModelIndex(), index, index)
|
||||
@@ -199,11 +220,12 @@ class ListModel(QAbstractListModel):
|
||||
|
||||
self.countChanged.emit(len(self))
|
||||
self.changed.emit()
|
||||
return index
|
||||
|
||||
|
||||
@pyqtSlot("QVariantMap")
|
||||
def append(self, value: NewItem) -> None:
|
||||
self.insert(len(self), value)
|
||||
@pyqtSlot("QVariantMap", result=int)
|
||||
def append(self, value: NewItem) -> int:
|
||||
return self.insert(len(self), value)
|
||||
|
||||
|
||||
@pyqtSlot(list)
|
||||
@@ -222,7 +244,7 @@ class ListModel(QAbstractListModel):
|
||||
ignore_roles: Sequence[str] = ()) -> int:
|
||||
value = self._convert_new_value(value)
|
||||
|
||||
i_index: int = self.indexWhere(self.mainKey, index) \
|
||||
i_index: int = self.indexWhere(index, _can_use_default_factory=False) \
|
||||
if isinstance(index, str) else index
|
||||
|
||||
to_update = self[i_index]
|
||||
@@ -272,7 +294,7 @@ class ListModel(QAbstractListModel):
|
||||
@pyqtSlot(int, list)
|
||||
@pyqtSlot(str, list)
|
||||
def set(self, index: Index, value: NewItem) -> None:
|
||||
i_index: int = self.indexWhere(self.mainKey, index) \
|
||||
i_index: int = self.indexWhere(index) \
|
||||
if isinstance(index, str) else index
|
||||
|
||||
qidx = QAbstractListModel.index(self, i_index, 0)
|
||||
@@ -285,7 +307,7 @@ class ListModel(QAbstractListModel):
|
||||
@pyqtSlot(int, str, "QVariant")
|
||||
@pyqtSlot(str, str, "QVariant")
|
||||
def setProperty(self, index: Index, prop: str, value: Any) -> None:
|
||||
i_index: int = self.indexWhere(self.mainKey, index) \
|
||||
i_index: int = self.indexWhere(index) \
|
||||
if isinstance(index, str) else index
|
||||
|
||||
if getattr(self[i_index], prop) != value:
|
||||
@@ -301,7 +323,7 @@ class ListModel(QAbstractListModel):
|
||||
@pyqtSlot(str, int, int)
|
||||
def move(self, from_: Index, to: int, n: int = 1) -> None:
|
||||
# pylint: disable=invalid-name
|
||||
i_from: int = self.indexWhere(self.mainKey, from_) \
|
||||
i_from: int = self.indexWhere(from_) \
|
||||
if isinstance(from_, str) else from_
|
||||
|
||||
qlast = i_from + n - 1
|
||||
@@ -332,7 +354,7 @@ class ListModel(QAbstractListModel):
|
||||
@pyqtSlot(int)
|
||||
@pyqtSlot(str)
|
||||
def remove(self, index: Index) -> None:
|
||||
i_index: int = self.indexWhere(self.mainKey, index) \
|
||||
i_index: int = self.indexWhere(index) \
|
||||
if isinstance(index, str) else index
|
||||
|
||||
self.beginRemoveRows(QModelIndex(), i_index, i_index)
|
||||
@@ -347,7 +369,7 @@ class ListModel(QAbstractListModel):
|
||||
@pyqtSlot(str, result="QVariant")
|
||||
def pop(self, index: Index, default: Any = _PopFail()) -> ListItem:
|
||||
try:
|
||||
i_index: int = self.indexWhere(self.mainKey, index) \
|
||||
i_index: int = self.indexWhere(index) \
|
||||
if isinstance(index, str) else index
|
||||
item = self[i_index]
|
||||
|
||||
|
@@ -1,4 +1,4 @@
|
||||
from typing import Any, Callable, DefaultDict, MutableSequence
|
||||
from typing import Any, DefaultDict
|
||||
|
||||
from PyQt5.QtCore import QObject, pyqtSlot
|
||||
|
||||
@@ -6,15 +6,15 @@ from .list_model import ListModel
|
||||
|
||||
|
||||
class ListModelMap(QObject):
|
||||
def __init__(self,
|
||||
models_container: Callable[..., MutableSequence] = list,
|
||||
parent: QObject = None) -> None:
|
||||
def __init__(self, *models_args, parent: QObject = None, **models_kwargs
|
||||
) -> None:
|
||||
super().__init__(parent)
|
||||
models_kwargs["parent"] = self
|
||||
|
||||
# Set the parent to prevent item garbage-collection on the C++ side
|
||||
self.dict: DefaultDict[Any, ListModel] = \
|
||||
DefaultDict(
|
||||
lambda: ListModel(container=models_container, parent=self)
|
||||
lambda: ListModel(*models_args, **models_kwargs)
|
||||
)
|
||||
|
||||
|
||||
|
Reference in New Issue
Block a user