首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何防止QListView调用每个项目的sizeHint?

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

Stack Overflow用户
提问于 2020-11-20 06:02:21
回答 1查看 317关注 0票数 1

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

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

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

我还尝试在模型中使用canFetchMore/fetchMore,但这会导致用户体验不良--滚动条的位置不再准确,而且当加载更多的项目时,列表会跳来跳去,它一点也不流畅。

因此,问题是:

  1. 是否有办法防止QListView调用sizeHint来获取无形物品?
  2. 如果唯一的方法是使用canFetchMore/取多,如何获得平滑的滚动和稳定而准确的滚动条?

非常感谢!

UPD:是再现此行为的最小示例:https://github.com/ajenter/qt_hugelistview

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

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2020-11-23 13:05:40

好吧,看来我找到了一个解决方案,所以为了任何有同样问题的人,我会在这里分享它,并搜索这个线程。

首先,我发现这实际上是2011年注册的Qt中的一个bug,至今仍处于打开状态:https://bugreports.qt.io/browse/QTBUG-16592

我已经投了我的一票(你也应该这么做!)然后决定尝试使用QTableView而不是QListView --而且,很显然,我设法让它工作了,或者看起来是这样的。

与QListView不同,QTableView只在显式请求时通过调用resizeRowToContents(rowNum)来调整行的大小。所以诀窍是,对于那些在视图中可见的行,以一种及时的方式调用它。

我所做的是:

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

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

代码语言:javascript
复制
   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
    }

  1. In MyTableView,添加一个QItemSelection私有字段和一个公共函数,用于计算行的实际高度,但只计算当前可见的行:

代码语言:javascript
复制
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";
        }
    }
}

  1. 备注:如果项高度取决于控件的宽度,则需要重写resizeEvent()、清除_itemsWithKnownHeights并再次调用updateVisibleRowsHeight() .

  1. 在将模型分配给MyTableView实例后调用updateVisibleRowHeights(),以便初始视图是正确的:

代码语言:javascript
复制
   table.setModel(&myModel);
   table.updateVisibleRowHeights();

实际上,它应该在MyTableView的一些方法中完成,该方法可以对更改进行建模,但我将把它作为一个练习。

  1. 现在剩下的就是在表的垂直滚动位置发生变化时调用updateRowHeights。因此,我们需要在MyTableView的constructor:

中添加以下内容

代码语言:javascript
复制
connect(verticalScrollBar(), &QScrollBar::valueChanged, [this](int) {
        updateRowHeights();
    });

完成-它的工作非常快,即使与模型的10万件!而且启动是即时的!

这个技术的基本概念证明(使用纯QTableView而不是子类)可以在这里找到:https://github.com/ajenter/qt_hugelistview/blob/tableview-experiment/src/main.cpp

警告:这种技术还没有被证明,而且可能包含一些未知的问题。自食其果!

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/64924570

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档