2626class 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-
169174def item_data (
170175 index : QModelIndex , roles : Sequence [int ]
171176) -> Dict [int , Any ]:
0 commit comments