4

我正在编写一个应用程序,它使用自定义 QWidget 代替 PyQt 中的常规列表项或委托。我已经按照QWidgetDelegate 的 paint() 方法中的 Render QWidget 中的答案为 QListView实现了带有自定义小部件的 QTableModel。生成的示例代码位于此问题的底部。实现中有一些我不知道如何解决的问题:

  1. 不显示时卸载项目。我计划为一个包含数千个条目的列表构建我的应用程序,但我无法在内存中保留那么多小部件。
  2. 加载尚未在视图中的项目或至少异步加载它们。小部件需要一些时间来呈现,下面的示例代码在滚动列表时有一些明显的滞后。
  3. 在下面的实现中滚动列表时,加载时每个新加载的按钮都会在 QListView 的左上角显示一瞬间,然后弹回原位。怎么能避免呢?

--

import sys
from PyQt4 import QtGui, QtCore
from PyQt4.QtCore import Qt


class TestListModel(QtCore.QAbstractListModel):
    def __init__(self, parent=None):
        QtCore.QAbstractListModel.__init__(self, parent)
        self.list = parent

    def rowCount(self, index):
        return 1000

    def data(self, index, role):
        if role == Qt.DisplayRole:
            if not self.list.indexWidget(index):
                button = QtGui.QPushButton("This is item #%s" % index.row())
                self.list.setIndexWidget(index, button)
            return QtCore.QVariant()

        if role == Qt.SizeHintRole:
            return QtCore.QSize(100, 50)

    def columnCount(self, index):
        pass


def main():
    app = QtGui.QApplication(sys.argv)

    window = QtGui.QWidget()

    list = QtGui.QListView()
    model = TestListModel(list)

    list.setModel(model)
    list.setVerticalScrollMode(QtGui.QAbstractItemView.ScrollPerPixel)

    layout = QtGui.QVBoxLayout(window)
    layout.addWidget(list)

    window.setLayout(layout)
    window.show()

    sys.exit(app.exec_())


if __name__ == '__main__':
    main()
4

2 回答 2

2

您可以使用代理模型来避免加载所有小部件。代理模型可以使用视口和小部件的高度来计算行数。他可以计算带有滚动条值的项目的索引。

这是一个不稳定的解决方案,但它应该可以工作。

如果你修改你的 data() 方法:

button = QtGui.QPushButton("This is item #%s" % index.row())
self.list.setIndexWidget(index, button)
button.setVisible(False)

在将它们移动到它们的位置之前,这些项目不会显示(这对我有用)。

于 2013-03-21T13:20:52.037 回答
1

QTableView仅向模型请求其视口中项目的数据,因此数据的大小并不会真正影响速度。由于您已经进行了子类QAbstractListModel化,因此您可以重新实现它以在初始化时仅返回一小组行,并修改其canFetchMore方法以True在未显示总记录量时返回。尽管根据数据的大小,您可能需要考虑创建一个数据库并使用QSqlQueryModelorQSqlTableModel代替,它们都以 256 个为一组进行延迟加载。

为了获得更流畅的项目负载,您可以连接到valueChanged您的信号,QTableView.verticalScrollBar()并根据它之间的差异valuemaximum有类似的东西:

while xCondition:
   if self.model.canFetchMore():
      self.model.fetchMore()

使用setIndexWidget会大大减慢您的应用程序。您可以使用 aQItemDelegate并自定义它的paint方法来显示具有以下内容的按钮:

class MyItemDelegate(QtGui.QItemDelegate):
    def __init__(self, parent=None):
        super(MyItemDelegate, self).__init__(parent)

    def paint(self, painter, option, index):
        text = index.model().data(index, QtCore.Qt.DisplayRole).toString()

        pushButton = QtGui.QPushButton()
        pushButton.setText(text)
        pushButton.setGeometry(option.rect)

        painter.save()
        painter.translate(option.rect.x(), option.rect.y())

        pushButton.render(painter)

        painter.restore()

并设置它:

myView.setItemDelegateForColumn(columnNumber, myItemDelegate)
于 2013-03-22T18:11:44.067 回答