The implementation is just
bool QAbstractItemModel::hasIndex(int row, int column, const QModelIndex &parent) const
{
if (row < 0 || column < 0)
return false;
return row < rowCount(parent) && column < columnCount(parent);
}
and yet it features as the most prominent part of the profile,
encompassing up to 25% of refresh()
Open-coding it makes refresh() 40% faster. Measurements from between
after wallet.get_full_history() and return from refresh()
Before:
1.8637257907539606
1.8563996930606663
1.7696567592211068
1.8933695629239082
After:
1.3133591176010668
1.3686819169670343
1.2470976510085166
1.2455544411204755
101 lines
2.9 KiB
Python
101 lines
2.9 KiB
Python
# loosely based on
|
|
# http://trevorius.com/scrapbook/uncategorized/pyqt-custom-abstractitemmodel/
|
|
|
|
from PyQt6 import QtCore
|
|
|
|
|
|
class CustomNode:
|
|
|
|
def __init__(self, model: 'CustomModel', data):
|
|
self.model = model
|
|
self._data = data
|
|
self._children = []
|
|
self._parent = None
|
|
self._row = 0
|
|
|
|
def get_data(self):
|
|
return self._data
|
|
|
|
def get_data_for_role(self, index, role):
|
|
# define in child class
|
|
raise NotImplementedError()
|
|
|
|
def childCount(self):
|
|
return len(self._children)
|
|
|
|
def child(self, row):
|
|
if row >= 0 and row < self.childCount():
|
|
return self._children[row]
|
|
|
|
def parent(self):
|
|
return self._parent
|
|
|
|
def row(self):
|
|
return self._row
|
|
|
|
def addChild(self, child):
|
|
child._parent = self
|
|
child._row = len(self._children)
|
|
self._children.append(child)
|
|
|
|
|
|
class CustomModel(QtCore.QAbstractItemModel):
|
|
|
|
def __init__(self, parent, columncount):
|
|
QtCore.QAbstractItemModel.__init__(self, parent)
|
|
self._root = CustomNode(self, None)
|
|
self._columncount = columncount
|
|
|
|
def rowCount(self, index):
|
|
if index.isValid():
|
|
return index.internalPointer().childCount()
|
|
return self._root.childCount()
|
|
|
|
def columnCount(self, index):
|
|
return self._columncount
|
|
|
|
def addChild(self, node, _parent):
|
|
if not _parent or not _parent.isValid():
|
|
parent = self._root
|
|
else:
|
|
parent = _parent.internalPointer()
|
|
parent.addChild(self, node)
|
|
|
|
def index(self, row, column, _parent=None):
|
|
# Performance-critical function
|
|
|
|
if not _parent or not _parent.isValid():
|
|
parent = self._root
|
|
else:
|
|
parent = _parent.internalPointer()
|
|
|
|
# Open-coded
|
|
# if not QtCore.QAbstractItemModel.hasIndex(self, row, column, _parent):
|
|
# the implementation is equivalent but it's in C++,
|
|
# so VM entries take up inordinate amounts of time (up to 25% of refresh()):
|
|
if row < 0 or column < 0 or row >= self.rowCount(_parent) or column >= self._columncount:
|
|
return QtCore.QModelIndex()
|
|
|
|
child = parent.child(row)
|
|
if child:
|
|
return QtCore.QAbstractItemModel.createIndex(self, row, column, child)
|
|
else:
|
|
return QtCore.QModelIndex()
|
|
|
|
def parent(self, index):
|
|
if index.isValid():
|
|
node = index.internalPointer()
|
|
if node:
|
|
p = node.parent()
|
|
if p:
|
|
return QtCore.QAbstractItemModel.createIndex(self, p.row(), 0, p)
|
|
else:
|
|
return QtCore.QModelIndex()
|
|
return QtCore.QModelIndex()
|
|
|
|
def data(self, index, role):
|
|
if not index.isValid():
|
|
return None
|
|
node = index.internalPointer()
|
|
return node.get_data_for_role(index, role)
|