C++ 如何防止QListView调用每个项目的sizeHint?

C++ 如何防止QListView调用每个项目的sizeHint?,c++,qt,qlistview,qtwidgets,C++,Qt,Qlistview,Qtwidgets,我有一个QListView,里面有很多不同高度的项目。我实现了一个用于绘制项目的自定义委托,并将布局模式设置为批处理 但是,当分配模型时,列表视图会预先为模型中的每个项目请求sizeHint,忽略批处理设置,从而破坏性能,因为要计算大小,代理必须布局大量文本(这不是很快) 也许它这样做是为了计算滚动条的位置,但我认为当项目数量很大时,滚动条的位置只能基于项目索引,而不考虑项目高度。然而,QListView似乎不是这样工作的 我还尝试在模型中使用canFetchMore/fetchMore,但这会

我有一个QListView,里面有很多不同高度的项目。我实现了一个用于绘制项目的自定义委托,并将布局模式设置为批处理

但是,当分配模型时,列表视图会预先为模型中的每个项目请求sizeHint,忽略批处理设置,从而破坏性能,因为要计算大小,代理必须布局大量文本(这不是很快)

也许它这样做是为了计算滚动条的位置,但我认为当项目数量很大时,滚动条的位置只能基于项目索引,而不考虑项目高度。然而,QListView似乎不是这样工作的

我还尝试在模型中使用canFetchMore/fetchMore,但这会导致糟糕的用户体验-滚动条位置不再准确,当加载更多项目时列表会跳转,这一点都不平滑

因此,问题是:

  • 有没有办法防止QListView为不可见项调用sizeHint
  • 如果唯一的方法是使用canFetchMore/fetchMore,如何获得平滑的滚动和稳定准确的滚动条
  • 非常感谢

    UPD:以下是再现此行为的最简单示例:


    请注意巨大的启动延迟和调试消息,显示所有5000项的sizeHint都是预先请求的。

    看来我已经找到了解决方案,所以我将在这里分享,以方便有相同问题的人使用谷歌搜索此线程

    首先,我发现这实际上是Qt中的一个bug,该bug在2011年注册并仍然存在:

    我已经投了我的一票(你也应该投!)。 然后决定尝试使用QTableView而不是QListView——令人惊讶的是,我成功地使它工作了,或者看起来是这样

    与QListView不同,QTableView仅在显式请求时通过调用resizeRowToContents(rowNum)调整行的大小。因此,诀窍是对视口中可见的行以实时方式调用它

    以下是我所做的:

  • 从QTableView继承(我们称之为MyTableView)

  • 用MyTableView替换QListView,并在构造函数中像这样初始化它。这将指定自定义项委托,隐藏表格标题并应用“按行”选择模式:

  • 在MyTableView中,添加一个QItemSelection私有字段和一个公共函数,用于计算行的实际高度,但仅限于当前可见的行:
  • 事实上,它应该在MyTableView的一些方法中完成,该方法可以对模型更改做出反应,但我将把它作为一个练习

  • 现在剩下的就是在表的垂直滚动位置改变时调用updateRowHeights。因此,我们需要将以下内容添加到MyTableView的构造函数中:
  • 完成-它的工作速度真的很快,甚至与100000件的模型!启动是瞬间的

    此技术的一个基本概念证明示例(使用纯QTableView而不是子类)可以在以下位置找到:


    警告:这项技术尚未经过战斗验证,可能包含一些未知问题。使用风险自负

    如果您使用
    qabstractemview::ScrollPerItem
    并预先提供项目数(即使使用
    canFetchMore()
    )?关于跳转滚动条。。。我曾经做过一个演示,让当前项目保持在稳定的视图位置,不管项目是在它之前还是之后插入的:(不过,这取决于使用
    ScrollPerPixel
    ):-(@Scheff:无论垂直滚动模式如何,行为都是相似的-所有项目的大小提示都是预先请求的,这对我来说根本没有意义,因为我已经明确地将批量大小设置为只有10个项目。我使用的是Qt 6中的错误吗?关于canFetchMore,感谢链接!据我所知,它需要QTableView-您是否建议切换到QTableView而不是QListView?我在
    QTreeView
    QTableView
    中一直在努力解决性能问题。(使用
    QListView
    ,我实际上没有多少经验。)布局(即确定大量项目的大小)这也是我认为最重要的问题之一。我认为这是再好不过的了,但事实上,我曾经在gtkmm中看到过更好的情况,当Windows对gtkmm 3的支持变得不可靠时,我们从gtkmm切换到Qt。很抱歉,我无法提供任何类似的答案,但我想与您分享我的感受。;-)我明白了,谢谢你分享你的经验!
       MyTableView::MyTableView(QWidget* parent) : QTableView(parent)
        {
           setSelectionBehavior(QAbstractItemView::SelectRows);
           horizontalHeader()->setStretchLastSection(true); 
           horizontalHeader()->hide();
           verticalHeader()->hide();
           setItemDelegateForColumn(0, new CustomDelegate(&table)); // for custom-drawn items
        }
    
    QItemSelection _itemsWithKnownHeight; // private member of MyTableView
    
    void MyTableView::updateVisibleRowHeights()
    {
        const QRect viewportRect = table.viewport()->rect();
    
        QModelIndex topRowIndex = table.indexAt(QPoint(viewportRect.x() + 5, viewportRect.y() + 5));
        QModelIndex bottomRowIndex = table.indexAt(QPoint(viewportRect.x() + 5, viewportRect.y() + viewportRect.height() - 5));
        qDebug() << "top row: " << topRowIndex.row() << ", bottom row: " << bottomRowIndex.row();
    
        for (auto i = topRowIndex.row() ; i < bottomRowIndex.row() + 1; ++i)
        {
            auto index = model()->index(i, 0);
            if (!_itemsWithKnownHeights.contains(index))
            {
                resizeRowToContents(i);
                _itemsWithKnownHeights.select(index, index);
                qDebug() << "Marked row #" << i << " as resized";
            }
        }
    }
    
       table.setModel(&myModel);
       table.updateVisibleRowHeights();
    
    connect(verticalScrollBar(), &QScrollBar::valueChanged, [this](int) {
            updateRowHeights();
        });