Fix room sorting for good
Do it the right way with a QSortFilterProxyModel.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from PyQt5.QtCore import QDateTime
|
||||
from PyQt5.QtCore import QDateTime, QSortFilterProxyModel
|
||||
|
||||
from .list_item import ListItem
|
||||
from .list_model import ListModel
|
||||
@@ -20,24 +20,26 @@ class Room(ListItem):
|
||||
_required_init_values = {"roomId", "displayName"}
|
||||
_constant = {"roomId"}
|
||||
|
||||
roomId: str = ""
|
||||
displayName: str = ""
|
||||
topic: Optional[str] = None
|
||||
typingUsers: List[str] = []
|
||||
roomId: str = ""
|
||||
displayName: str = ""
|
||||
topic: Optional[str] = None
|
||||
typingUsers: List[str] = []
|
||||
lastEventDateTime: Optional[QDateTime] = None
|
||||
|
||||
inviter: Optional[Dict[str, str]] = None
|
||||
leftEvent: Optional[Dict[str, str]] = None
|
||||
|
||||
|
||||
class RoomCategory(ListItem):
|
||||
_required_init_values = {"name", "rooms"}
|
||||
_constant = {"rooms"}
|
||||
_required_init_values = {"name", "rooms", "sortedRooms"}
|
||||
_constant = {"rooms", "sortedRooms"}
|
||||
|
||||
name: str = ""
|
||||
|
||||
# Must be provided at init, else it will be the same object
|
||||
# for every RoomCategory
|
||||
rooms: ListModel = ListModel()
|
||||
rooms: ListModel = ListModel()
|
||||
sortedRooms: QSortFilterProxyModel = QSortFilterProxyModel()
|
||||
|
||||
|
||||
class Account(ListItem):
|
||||
|
@@ -118,7 +118,7 @@ class ListItem(QObject, metaclass=_ListItemMeta):
|
||||
|
||||
# Set properties from provided positional arguments
|
||||
for prop, value in zip(self._props, args):
|
||||
setattr(self, f"_{prop}", value)
|
||||
setattr(self, f"_{prop}", self._set_parent(value))
|
||||
already_set.add(prop)
|
||||
|
||||
# Set properties from provided keyword arguments
|
||||
@@ -129,7 +129,7 @@ class ListItem(QObject, metaclass=_ListItemMeta):
|
||||
if prop not in self._props:
|
||||
raise TypeError(f"{method} got an unexpected keyword "
|
||||
f"argument {prop!r}")
|
||||
setattr(self, f"_{prop}", value)
|
||||
setattr(self, f"_{prop}", self._set_parent(value))
|
||||
already_set.add(prop)
|
||||
|
||||
# Check for required init arguments not provided
|
||||
@@ -140,7 +140,13 @@ class ListItem(QObject, metaclass=_ListItemMeta):
|
||||
|
||||
# Set default values for properties not provided in arguments
|
||||
for prop in set(self._props) - already_set:
|
||||
setattr(self, f"_{prop}", self._props[prop][1])
|
||||
setattr(self, f"_{prop}", self._set_parent(self._props[prop][1]))
|
||||
|
||||
|
||||
def _set_parent(self, value: Any) -> Any:
|
||||
if isinstance(value, QObject):
|
||||
value.setParent(self)
|
||||
return value
|
||||
|
||||
|
||||
def __repr__(self) -> str:
|
||||
|
@@ -89,10 +89,15 @@ class ListModel(QAbstractListModel):
|
||||
return self._data[0].mainKey if self._data else None
|
||||
|
||||
|
||||
def roleNumbers(self) -> Dict[str, int]:
|
||||
return {name: Qt.UserRole + i
|
||||
for i, name in enumerate(self.roles, 1)} \
|
||||
if self._data else {}
|
||||
|
||||
|
||||
def roleNames(self) -> Dict[int, bytes]:
|
||||
return {Qt.UserRole + i: bytes(f, "utf-8")
|
||||
for i, f in enumerate(self.roles, 1)} \
|
||||
return {Qt.UserRole + i: bytes(name, "utf-8")
|
||||
for i, name in enumerate(self.roles, 1)} \
|
||||
if self._data else {}
|
||||
|
||||
|
||||
@@ -219,8 +224,10 @@ class ListModel(QAbstractListModel):
|
||||
except AttributeError: # constant/not settable
|
||||
pass
|
||||
|
||||
qidx = QAbstractListModel.index(self, i_index, 0)
|
||||
self.dataChanged.emit(qidx, qidx, self.roleNames())
|
||||
qidx = QAbstractListModel.index(self, i_index, 0)
|
||||
updated = [number for name, number in self.roleNumbers().items()
|
||||
if name not in ignore_roles]
|
||||
self.dataChanged.emit(qidx, qidx, updated)
|
||||
self.changed.emit()
|
||||
return i_index
|
||||
|
||||
@@ -265,7 +272,7 @@ class ListModel(QAbstractListModel):
|
||||
|
||||
setattr(self[i_index], prop, value)
|
||||
qidx = QAbstractListModel.index(self, i_index, 0)
|
||||
self.dataChanged.emit(qidx, qidx, self.roleNames())
|
||||
self.dataChanged.emit(qidx, qidx, (self.roleNumbers()[prop],))
|
||||
self.changed.emit()
|
||||
|
||||
|
||||
|
69
harmonyqml/backend/model/sort_filter_proxy.py
Normal file
69
harmonyqml/backend/model/sort_filter_proxy.py
Normal file
@@ -0,0 +1,69 @@
|
||||
from typing import Dict
|
||||
|
||||
from PyQt5.QtCore import (
|
||||
QObject, QSortFilterProxyModel, Qt, pyqtProperty, pyqtSignal, pyqtSlot
|
||||
)
|
||||
|
||||
from .list_model import ListModel
|
||||
|
||||
|
||||
class SortFilterProxy(QSortFilterProxyModel):
|
||||
sortByRoleChanged = pyqtSignal()
|
||||
|
||||
def __init__(self,
|
||||
source_model: ListModel,
|
||||
sort_by_role: str,
|
||||
ascending: bool = True,
|
||||
parent: QObject = None) -> None:
|
||||
super().__init__(parent)
|
||||
self.setDynamicSortFilter(False)
|
||||
|
||||
self.ascending = ascending
|
||||
|
||||
self.sortByRoleChanged.connect(self._set_sort_role)
|
||||
|
||||
self.setSourceModel(source_model)
|
||||
source_model.rolesSet.connect(self._set_sort_role)
|
||||
source_model.changed.connect(self._sort)
|
||||
|
||||
self._sort_by_role = sort_by_role
|
||||
self._set_sort_role()
|
||||
|
||||
|
||||
@pyqtProperty(str, notify=sortByRoleChanged)
|
||||
def sortByRole(self) -> str:
|
||||
return self._sort_by_role
|
||||
|
||||
|
||||
@sortByRole.setter # type: ignore
|
||||
def sortByRole(self, role: str) -> None:
|
||||
self._sort_by_role = role
|
||||
self.sortByRoleChanged.emit()
|
||||
|
||||
|
||||
def _set_sort_role(self) -> None:
|
||||
numbers = self.sourceModel().roleNumbers()
|
||||
try:
|
||||
self.setSortRole(numbers[self._sort_by_role])
|
||||
except KeyError:
|
||||
pass # Model doesn't have its roles set yet (empty model)
|
||||
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return "%s(sortByRole=%r, sourceModel=%r)" % (
|
||||
type(self).__name__, self.sortByRole, self.sourceModel(),
|
||||
)
|
||||
|
||||
|
||||
@pyqtSlot(result=str)
|
||||
def repr(self) -> str:
|
||||
return self.__repr__()
|
||||
|
||||
|
||||
def roleNames(self) -> Dict[int, bytes]:
|
||||
return self.sourceModel().roleNames()
|
||||
|
||||
|
||||
def _sort(self) -> None:
|
||||
order = Qt.AscendingOrder if self.ascending else Qt.DescendingOrder
|
||||
self.sort(0, order)
|
Reference in New Issue
Block a user