【问题标题】:Smooth lazy loading and unloading of custom IndexWidget in QListViewQListView中自定义IndexWidget的平滑延迟加载和卸载
【发布时间】:2013-03-06 03:09:21
【问题描述】:

我正在编写一个应用程序,它使用自定义 QWidget 代替 PyQt 中的常规列表项或委托。我已经按照Render QWidget in paint() method of QWidgetDelegate for a 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()

【问题讨论】:

  • 为了解决问题 3,您是否尝试过在创建时为按钮赋予父级?即 QListView。

标签: python qt user-interface pyqt qtableview


【解决方案1】:

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

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

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

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

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

【讨论】:

    【解决方案2】:

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

    为了更顺畅地加载项目,您可以连接到 QTableView.verticalScrollBar()valueChanged 信号,根据它的 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)
    

    【讨论】:

    • 延迟加载无助于我想要卸载不再可见的项目的问题。委托显然是无状态的,但我确实需要足够的状态才能让内容响应输入。
    • @lyschoening 我认为子类化QSqlQueryModel 将是一个不错的选择。使用它的setQuery 方法,您可以选择要从数据库中显示的记录数。您可以将其与 QDataWidgetMapper 结合起来,而不是创建多个按钮,并将其映射到一组静态按钮,并可能为它们设置动画以获得更多视觉效果,请查看 this question 中的代码示例
    猜你喜欢
    • 1970-01-01
    • 2023-02-11
    • 1970-01-01
    • 2013-10-09
    • 2015-03-27
    • 1970-01-01
    • 1970-01-01
    • 2018-07-03
    • 1970-01-01
    相关资源
    最近更新 更多