大型 QAbstractTableModel 的 QTableView 动态行高
Posted
技术标签:
【中文标题】大型 QAbstractTableModel 的 QTableView 动态行高【英文标题】:QTableView dynamic row heigh for large QAbstractTableModel 【发布时间】:2021-02-10 13:02:05 【问题描述】:我知道在 *** 上回答了很多次关于如何为 QTableView 设置行高的问题。我再问一次,但我的问题并不完全是关于“如何”,至少不是那么简单。我在源自QAbstractTableModel
的自定义模型的数据方法中借助Qt.SizeHintRole
成功设置了行高 - 请参见下面的代码。 (也尝试了非常相似的示例,但借助QStyledItemDelegate
的sizeHint()
方法 - 结果完全相同。)
当我有 MODEL_ROW_COUNT
大约 100 时,它工作得很好,如下例所示。但是我的数据集有大约 30-40 千行。因此,这个简单的应用程序以 MODEL_ROW_COUNT=35000
启动大约 30 秒。
延迟这么大的原因是这行代码:self.table_view.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
如果我要评论这一行,MODEL_ROW_COUNT=35000
的一切工作都会非常快。但在这种情况下,data()
方法不会被 Qt.SizeHintRole
调用,我无法操作行高。
所以,我的问题是 - 如何为具有数千行的数据集设置每行的行高?下面的示例有效,但需要 30 秒才能从 35 000 行开始(显示窗口后一切都很流畅)...
同时如果我使用QSqlTableModel
就没有这个问题,我可以使用QStyledItemDelegate
的sizeHint()
没有大问题。但是有太多的代表是一团糟……我可以继承QStyledItemDelegate
而不是QAbstractTableModel
来实现我的自定义模型吗? (我不确定它是否可以作为自定义模型的子类QAbstractTableModel
的每个源推荐...)
还是我做错了什么,有比使用QHeaderView.ResizeToContents
更好的方法吗?
附:我真的需要不同的高度。数据库中的某些行数据较少,我可能会在几个单元格中显示它们。但其他人有更多数据,我需要额外的空间来显示它。所有行的高度相同意味着要么浪费空间(屏幕上有很多空白),要么缺少某些数据行的基本细节。我正在使用 contant CUSTOM_ROW_HEIGHT
也只是让示例尽可能简单并且易于重现 - 您可以将任何数据库与任何大表一起使用(我想即使没有数据库我也可以重新创建它......很快就会尝试)
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()
【问题讨论】:
如果所有行都具有相同的高度,你不能只在垂直标题上使用setDefaultSectionSize()
吗?
@musicamante,不,这正是原因 - 我需要根据该行中的数据不同的行高度。数据库中的某些行数据较少,我可能会在行中显示它。但是其他人有很多数据,我需要额外的空间来展示它。我看到的唯一前进方法 - 以某种方式增加行并制作一些自定义绘图,以允许 2-3 个后续行显示为 1 个实心行。 IE。所有行都将是相同的高度,但一些“行”实际上将由几个后续行组成......不确定我能否让它看起来很好......
在项目视图上自定义绘图如果不小心完成,通常是危险的,这就是项目委托存在的原因。如果行高是基于内容的,恐怕对于这么大的数据模型没有解决方案:如果基于内容,项目的布局需要大量计算:每个项目都被定位在基于它之前的 all 项的大小的不同坐标处,并且视口必须知道占用的整个区域(也是为了正确更新滚动条),所以很自然需要很多数万行的时间。
@musicamante,但QSqlTableModel
句柄成功使用相同的数据集,因为它分批加载数据。但是我看不到如何使用QAbstractTableModel
仅加载部分数据。我只是想要更多的自定义,并且很容易维护一个类而不是 20 个不同的代表......
您关于使用代表反对子类化模型的问题尚不清楚。是的,基于 sql 的模型可以成功处理这个问题,因为它们使用 canFetchMore()
和 fetchMore()
,所以你也可以实现它。 “对自定义模型的子类 QAbstractTableModel 的每个源代码建议”:抽象模型的子类只应在需要时完成。为什么需要 custom 模型?你需要什么样的“定制”?
【参考方案1】:
好的,感谢@musicamante,我意识到我错过了canFetchMore()
和fetchMore()
方法。所以,我在MyModel
类中实现了动态大小属性和这些方法。这一点也不难,现在我的性能比QSqlTableModel
更好,并且具有直接控制可见缓冲区大小的相同视觉行为。下面是MyModel
类的新代码:
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()
【讨论】:
以上是关于大型 QAbstractTableModel 的 QTableView 动态行高的主要内容,如果未能解决你的问题,请参考以下文章
QAbstractTableModel:index() 与 createIndex()
QAbstractTableModel::header 数据和 QML TableView