Skip to content

Commit a4581f3

Browse files
committed
ModelItemCache: Fix invalidation and add doc strings
1 parent ce2a16f commit a4581f3

File tree

1 file changed

+39
-34
lines changed

1 file changed

+39
-34
lines changed

Orange/widgets/utils/itemdelegates.py

Lines changed: 39 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -26,39 +26,41 @@
2626
class ModelItemCache(QObject):
2727
"""
2828
An item data cache for accessing QAbstractItemModel.data
29+
30+
>>> cache = ModelItemCache()
31+
>>> cache.itemData(index, (Qt.DisplayRole, Qt.DecorationRole))
32+
{0: ...
2933
"""
3034
__slots__ = ("__model", "__cache_data")
3135

32-
def __init__(self, *args, model=None, **kwargs):
36+
def __init__(self, *args, maxsize=100 * 200, **kwargs):
3337
super().__init__(*args, **kwargs)
34-
self.__model = None
35-
self.__cache_data = LRUCache(100 * 200)
36-
if model is not None:
37-
self.setModel(model)
38-
39-
def __connect_helper(self, model):
40-
model.dataChanged.connect(self.__invalidate_cache)
41-
model.layoutAboutToBeChanged.connect(self.__invalidate_cache)
42-
model.modelReset.connect(self.__invalidate_cache)
43-
model.rowsInserted.connect(self.__invalidate_cache)
44-
model.rowsRemoved.connect(self.__invalidate_cache)
45-
model.rowsMoved.connect(self.__invalidate_cache)
46-
model.columnsInserted.connect(self.__invalidate_cache)
47-
model.columnsRemoved.connect(self.__invalidate_cache)
48-
model.columnsMoved.connect(self.__invalidate_cache)
49-
50-
def __disconnect_helper(self, model):
51-
model.dataChanged.disconnect(self.__invalidate_cache)
52-
model.layoutAboutToBeChanged.disconnect(self.__invalidate_cache)
53-
model.modelReset.disconnect(self.__invalidate_cache)
54-
model.rowsInserted.disconnect(self.__invalidate_cache)
55-
model.rowsRemoved.disconnect(self.__invalidate_cache)
56-
model.rowsMoved.disconnect(self.__invalidate_cache)
57-
model.columnsInserted.disconnect(self.__invalidate_cache)
58-
model.columnsRemoved.disconnect(self.__invalidate_cache)
59-
model.columnsMoved.disconnect(self.__invalidate_cache)
60-
61-
def setModel(self, model: QAbstractItemModel):
38+
self.__model: Optional[QAbstractItemModel] = None
39+
self.__cache_data: 'LRUCache[QPersistentModelIndex, Any]' = LRUCache(maxsize)
40+
41+
def __connect_helper(self, model: QAbstractItemModel) -> None:
42+
model.dataChanged.connect(self.invalidate)
43+
model.layoutAboutToBeChanged.connect(self.invalidate)
44+
model.modelAboutToBeReset.connect(self.invalidate)
45+
model.rowsAboutToBeInserted.connect(self.invalidate)
46+
model.rowsAboutToBeRemoved.connect(self.invalidate)
47+
model.rowsAboutToBeMoved.connect(self.invalidate)
48+
model.columnsAboutToBeInserted.connect(self.invalidate)
49+
model.columnsAboutToBeRemoved.connect(self.invalidate)
50+
model.columnsAboutToBeMoved.connect(self.invalidate)
51+
52+
def __disconnect_helper(self, model: QAbstractItemModel) -> None:
53+
model.dataChanged.disconnect(self.invalidate)
54+
model.layoutAboutToBeChanged.disconnect(self.invalidate)
55+
model.modelAboutToBeReset.disconnect(self.invalidate)
56+
model.rowsAboutToBeInserted.disconnect(self.invalidate)
57+
model.rowsAboutToBeRemoved.disconnect(self.invalidate)
58+
model.rowsAboutToBeMoved.disconnect(self.invalidate)
59+
model.columnsAboutToBeInserted.disconnect(self.invalidate)
60+
model.columnsAboutToBeRemoved.disconnect(self.invalidate)
61+
model.columnsAboutToBeMoved.disconnect(self.invalidate)
62+
63+
def setModel(self, model: QAbstractItemModel) -> None:
6264
if model is self.__model:
6365
return
6466
if self.__model is not None:
@@ -69,11 +71,12 @@ def setModel(self, model: QAbstractItemModel):
6971
if model is not None:
7072
self.__connect_helper(model)
7173

72-
def model(self):
74+
def model(self) -> Optional[QAbstractItemModel]:
7375
return self.__model
7476

7577
@Slot()
76-
def __invalidate_cache(self):
78+
def invalidate(self) -> None:
79+
"""Invalidate all cached data."""
7780
self.__cache_data.clear()
7881

7982
def itemData(
@@ -83,12 +86,14 @@ def itemData(
8386
Return item data from `index` for `roles`.
8487
8588
The returned mapping is a read only view of *all* data roles accessed
86-
for the index through this interface. It will contain at least data for
87-
`roles`, but can also contain other ones.
89+
for the index through this caching interface. It will contain at least
90+
data for `roles`, but can also contain other ones.
8891
"""
8992
model = index.model()
9093
if model is not self.__model:
9194
self.setModel(model)
95+
# NOTE: QPersistentModelIndex's hash changes when it is invalidated;
96+
# it must be purged from __cache_data before that (`__connect_helper`)
9297
key = QPersistentModelIndex(index)
9398
try:
9499
item = self.__cache_data[key]
@@ -104,6 +109,7 @@ def itemData(
104109
return view
105110

106111
def data(self, index: QModelIndex, role: int) -> Any:
112+
"""Return item data for `index` and `role`"""
107113
model = index.model()
108114
if model is not self.__model:
109115
self.setModel(model)
@@ -165,7 +171,6 @@ def cast_(type_: Type[A], value: Any) -> Optional[A]:
165171
except Exception: # pylint: disable=broad-except
166172
return None
167173

168-
169174
def item_data(
170175
index: QModelIndex, roles: Sequence[int]
171176
) -> Dict[int, Any]:

0 commit comments

Comments
 (0)