大型 QAbstractTableModel 的 QTableView 动态行高

QTableView dynamic row heigh for large QAbstractTableModel

我知道在 Whosebug 上已经回答了很多次关于如何为 QTableView 设置行高的问题。我再问一次,但我的问题不完全是关于“如何”,至少不是那么简单。我在派生自 QAbstractTableModel 的自定义模型的数据方法中借助 Qt.SizeHintRole 成功设置了行高 - 请参阅下面的代码。 (也尝试了非常相似的示例,但在 QStyledItemDelegatesizeHint() 方法的帮助下 - 结果完全相同。)

当我有 MODEL_ROW_COUNT 大约 100 个时效果很好,如下例所示。但我的数据集有 ~30-40 千行。结果,这个简单的应用程序启动大约 30 秒,例如 MODEL_ROW_COUNT=35000
延迟这么大的原因是这行代码:
self.table_view.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
MODEL_ROW_COUNT=35000 如果我要评论这一行,一切都非常快。但在这种情况下,data() 方法不会用 Qt.SizeHintRole 调用,我无法操作行高。

所以,我的问题是 - 如何为具有数千行的数据集设置每行的行高?下面的示例有效,但需要 30 秒才能从 35 000 行开始(在 window 显示后一切都流畅)...

同时如果我使用 QSqlTableModel 就没有这个问题,我可能会使用 sizeHint()QStyledItemDelegate 而没有大问题。但是有太多代表是一团糟……我可以子类化 QStyledItemDelegate 而不是 QAbstractTableModel 来实现我的自定义模型吗? (我不确定它是否会像每个来源推荐的那样为自定义模型子类 QAbstractTableModel 工作...) 或者我做错了什么,有比使用 QHeaderView.ResizeToContents?

更好的方法

P.S。我真的需要不同的高度。数据库中的某些行数据较少,我可能会在几个单元格中显示它们。但是其他人有更多数据,我需要额外的 space 来显示它。所有行的相同高度将意味着 space 的浪费(屏幕上有很多白色 space)或某些数据行缺少必要的细节。我只使用 contant CUSTOM_ROW_HEIGHT 来保持示例尽可能简单并且易于重现 - 您可以使用任何具有任何大 table 的数据库(我想即使没有数据库我也可以重新创建它。 ..很快就会尝试)

from PySide2.QtWidgets import QApplication, QWidget, QVBoxLayout, QTableView, QHeaderView
from PySide2.QtSql import QSqlDatabase, QSqlQuery
from PySide2.QtCore import Qt, QAbstractTableModel, QSize


class MyWindow(QWidget):
    def __init__(self):
        QWidget.__init__(self)

        self.db = QSqlDatabase.addDatabase("QSQLITE")
        self.db.setDatabaseName("/home/db.sqlite")
        self.db.open()

        self.table_model = MyModel(parent=self, db=self.db)
        self.table_view = QTableView()
        self.table_view.setModel(self.table_model)

        # SizeHint is not triggered without this line but it causes delay
        self.table_view.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)

        layout = QVBoxLayout(self)
        layout.addWidget(self.table_view)
        self.setLayout(layout)

class MyModel(QAbstractTableModel):
    CUSTOM_ROW_HEIGHT = 300
    MODEL_ROW_COUNT = 100
    MODEL_COL_COUNT = 5

    def __init__(self, parent, db):
        QAbstractTableModel.__init__(self, parent)
        self.query = QSqlQuery(db)
        self.query.prepare("SELECT * FROM big_table")
        self.query.exec_()

    def rowCount(self, parent=None):
        return self.MODEL_ROW_COUNT

    def columnCount(self, parent=None):
        return self.MODEL_COL_COUNT

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return None
        if role == Qt.DisplayRole:
            if self.query.seek(index.row()):
                return str(self.query.value(index.column()))
        if role == Qt.SizeHintRole:
            return QSize(0, self.CUSTOM_ROW_HEIGHT)
        return None


def main():
    app = QApplication([])
    win = MyWindow()
    win.show()
    app.exec_()

if __name__ == "__main__":
    main()

好的,感谢@musicamante,我意识到我错过了 canFetchMore()fetchMore() 方法。因此,我在 MyModel class 中实现了动态大小 属性 和这些方法。这一点都不难,现在我的性能比 QSqlTableModel 更好,并且具有相同的视觉行为,可以直接控制可见缓冲区大小。下面是 MyModel class 的新代码:

class MyModel(QAbstractTableModel):
    CUSTOM_ROW_HEIGHT = 300
    MODEL_ROW_COUNT = 37000
    MODEL_COL_COUNT = 5
    PAGE_SIZE = 500

    def __init__(self, parent, db):
        QAbstractTableModel.__init__(self, parent)
        self.query = QSqlQuery(db)
        self.query.prepare("SELECT * FROM big_table")
        self.query.exec_()

        self._current_size = self.PAGE_SIZE

    def rowCount(self, parent=None):
        return self._current_size

    def columnCount(self, parent=None):
        return self.MODEL_COL_COUNT

    def data(self, index, role=Qt.DisplayRole):
        if not index.isValid():
            return None
        if role == Qt.DisplayRole:
            if self.query.seek(index.row()):
                return str(self.query.value(index.column()))
        if role == Qt.SizeHintRole:
            return QSize(0, self.CUSTOM_ROW_HEIGHT)
        return None

    def canFetchMore(self, index):
        return self._current_size < self.MODEL_ROW_COUNT

    def fetchMore(self, index):
        self.beginInsertRows(index, self._current_size, self._current_size + self.PAGE_SIZE - 1)
        self._current_size += self.PAGE_SIZE
        self.endInsertRows()