在QListView中平滑延迟加载和卸载自定义IndexWidget

时间:2013-03-17 20:15:15

标签: python qt user-interface pyqt qtableview

我正在编写一个应用程序,它使用自定义QWidget代替PyQt中的常规listitems或委托。我已经按照Render QWidget in paint() method of QWidgetDelegate for a QListView中的答案 - 以及其他 - 来实现带有自定义小部件的QTableModel。生成的示例代码位于此问题的底部。实施中存在一些我不知道如何解决的问题:

  1. 在未显示项目时卸载项目。我打算为一个包含数千个条目的列表构建我的应用程序,而且我不能在内存中保留那么多小部件。
  2. 加载尚未查看的项目或至少异步加载它们。窗口小部件需要花费一些时间进行渲染,下面的示例代码在滚动列表时会有一些明显的延迟。
  3. 在下面的实现中滚动列表时,加载时每个新加载的按钮会在弹回到位之前显示在QListView的左上角一瞬间。如何避免这种情况?
  4. -

    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()
    

2 个答案:

答案 0 :(得分:2)

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

这是一个不稳定的解决方案,但它应该有效。

如果使用以下命令修改data()方法:

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

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

答案 1 :(得分:1)

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

要获得更顺畅的项目加载,您可以连接到valueChangedQTableView.verticalScrollBar()信号,并根据valuemaximum之间的差异,具有以下内容:

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

使用setIndexWidget会大大降低您的应用程序速度。您可以使用QItemDelegate并自定义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)