I have problems implementing Qt tree model with lazy loading using canFetchMore()
and fetchMore()
. I have this code:
from PySide.QtCore import Qt, QAbstractItemModel, QModelIndex
from PySide.QtWidgets import QTreeView, QApplication
BATCH_SIZE = 100
class ObjectItem(object):
def __init__(self, parent, name, data, row):
self.parent = parent
self.name = name
self.data = data
self.row = row
self.hasChildren = False
self.canFetchMore = False
# ints have no children
if isinstance(data, int):
return
self.childCount = 0
self.childItems = []
if len(data) > 0:
self.canFetchMore = True
self.hasChildren = True
self.childCount = len(self.childItems)
if self.childCount > 0:
self.hasChildren = True
def fetchMore(self):
loadedCount = len(self.childItems)
totalCount = len(self.data)
remainder = totalCount - loadedCount
fetchCount = min(BATCH_SIZE, remainder)
for _ in range(fetchCount):
name = "[{}]".format(loadedCount)
value = self.data[loadedCount]
self.childItems.append(ObjectItem(self, name, value, loadedCount))
loadedCount += 1
self.canFetchMore = loadedCount < totalCount
return fetchCount
class MyTreeModel(QAbstractItemModel):
def __init__(self, data, parent=None):
super(MyTreeModel, self).__init__(parent)
self.rootItem = ObjectItem(None, "root", data, 0)
self.headerLabels = ["Name", "Type", "Value"]
def hasChildren(self, parent=QModelIndex()):
return parent.internalPointer().hasChildren if parent.isValid() else True
def rowCount(self, parent=QModelIndex()):
return parent.internalPointer().childCount if parent.isValid() else 1
def columnCount(self, parent=QModelIndex()):
return 3
def index(self, row, column, parent=QModelIndex()):
if not parent.isValid():
return self.createIndex(row, column, self.rootItem)
parentItem = parent.internalPointer()
return self.createIndex(row, column, parentItem.childItems[row])
def parent(self, index):
item = index.internalPointer()
if item == self.rootItem:
return QModelIndex()
parentItem = item.parent
return self.createIndex(parentItem.row, 0, parentItem)
def data(self, index, role):
item = index.internalPointer()
if role == Qt.DisplayRole:
if index.column() == 0:
return item.name
elif index.column() == 1:
return item.data.__class__.__name__
elif index.column() == 2:
return repr(item.data)
return None
def headerData(self, index, orientation, role):
if orientation == Qt.Horizontal and role == Qt.DisplayRole:
return self.headerLabels[index]
return None
def canFetchMore(self, parent=QModelIndex()):
if parent.isValid():
return parent.internalPointer().canFetchMore
else:
return False
def fetchMore(self, parent=QModelIndex()):
parentItem = parent.internalPointer()
firstIndex = parentItem.childCount
fetchedCount = parentItem.fetchMore()
lastIndex = firstIndex + fetchedCount - 1
self.beginInsertRows(parent, firstIndex, lastIndex)
parentItem.childCount = len(parentItem.childItems)
self.endInsertRows()
# test data
data = [list(range(1000)), list(range(1000))]
app = QApplication([])
view = QTreeView()
model = MyTreeModel(data)
view.setModel(model)
view.show()
app.exec_()
It is probably the shortest version (simplified, unoptimized, not general enough etc.) of the code which can replicate the problem. I want to display a tree of list
s of int
s. Note that in the example above I have two list
s of int
s, each containing 1000 int
s. But for some unknown reason, only he first 300 int
s are displayed when the node is expanded and after I scroll to the very bottom. I would expect that the items should be added as I am scrolling down. However they are only added when I collapse and expand again the node.
In other words, the nodes are being added only as a reaction to collapsing and expanding of the parent nodes, not as a reaction to scrolling to the bottom.
What is the problem? Have I overlooked anything?
UPDATE: I simplified the code even more, compared to the first version.
UPDATE2: To compare it with a model without hierarchy, which works as expected (fetching more data as user scrolls down), look at this code:
from PySide.QtCore import Qt, QAbstractItemModel, QModelIndex
from PySide.QtGui import QApplication, QTreeView
BATCH_SIZE = 100
class LazyModel(QAbstractItemModel):
def __init__(self, totalCount, parent=None):
super(LazyModel, self).__init__(parent)
self.items = []
self.totalCount = totalCount
self.loadedCount = 0
def hasChildren(self, parent=QModelIndex()):
if not parent.isValid():
return True
else:
return False
def rowCount(self, parent=QModelIndex()):
return self.loadedCount
def columnCount(self, parent=QModelIndex()):
return 2
def index(self, row, column, parent=QModelIndex()):
return self.createIndex(row, column, None)
def parent(self, index):
return QModelIndex()
def data(self, index, role):
if role == Qt.DisplayRole:
if index.column() == 0:
return str(index.row())
else:
return str(self.items[index.row()])
return None
def canFetchMore(self, parent=QModelIndex()):
return self.loadedCount < self.totalCount
def fetchMore(self, parent=QModelIndex()):
remainder = self.totalCount - self.loadedCount
fetchCount = min(BATCH_SIZE, remainder)
for i in range(fetchCount):
self.items.append(len(self.items))
self.beginInsertRows(parent, self.loadedCount,
self.loadedCount + fetchCount - 1)
self.loadedCount += fetchCount
self.endInsertRows()
app = QApplication([])
view = QTreeView()
model = LazyModel(10000)
view.setModel(model)
view.show()
app.exec_()
Does it mean I got something wrong with hierarchy?
来源:https://stackoverflow.com/questions/41684064/canfetchmore-and-fetchmore-are-not-working-as-expected