Динамическая высота строки QTableView для большой QAbstractTableModel
Я знаю, что в stackoverflow много раз отвечали на вопрос о том, как установить высоту строки для QTableView. Я спрашиваю еще раз, но мой вопрос не совсем о том, "как", по крайней мере, не так просто. Я успешно устанавливаю высоту строки с помощью метода данных моей пользовательской модели, полученной из - см. код ниже. (Также попробовал очень похожий пример, но с помощью метода - результат точно такой же.)
Это работает очень хорошо, когда у меня есть
MODEL_ROW_COUNT
около 100, как в примере ниже. Но в моем наборе данных ~30-40 тысяч строк. В результате это простое приложение запускается, например, примерно за 30 секунд.
Причиной этой большой задержки является эта строка кода:
self.table_view.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
Все работает очень быстро с
MODEL_ROW_COUNT=35000
если бы я прокомментировал эту строку. Но в этом случае
data()
метод не вызывается с
Qt.SizeHintRole
и я не могу управлять высотой строки.
Итак, мой вопрос: как установить высоту строки для каждой строки для набора данных с тысячами строк? Пример ниже работает, но требуется 30 секунд, чтобы начать с 35000 строк (после того, как окно показано, все свободно)...
В то же время, если я использую
QSqlTableModel
у него нет этой проблемы, и я могу использовать
sizeHint()
из без больших проблем. Но беспорядок иметь слишком много делегатов... Могу ли я создать подкласс
QStyledItemDelegate
вместо того, чтобы реализовать мою пользовательскую модель? (Я не уверен, что это сработает, так как каждый источник рекомендует подкласс
QAbstractTableModel
для пользовательских моделей...) Или я сделал что-то не так, и есть способ лучше, чем использование
QHeaderView.ResizeToContents
?
PS Мне очень нужны разные высоты. В некоторых строках базы данных меньше данных, и я могу показать их в нескольких ячейках. Но у других больше данных, и мне нужно дополнительное место для их отображения. Одинаковая высота для всех строк будет означать либо пустую трату места (много пустого места на экране), либо отсутствие важных деталей для некоторых строк данных. я использую константу
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()
1 ответ
Хорошо, благодаря @musicamante я понял, что пропустил
canFetchMore()
а также
fetchMore()
методы. Итак, я реализовал свойство динамического размера и эти методы в классе. Это было совсем несложно, и теперь у меня результаты лучше, чем у
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()