如何在 Qt C++ 应用程序中显示具有大量小部件的可滚动列表作为项目?

Posted

技术标签:

【中文标题】如何在 Qt C++ 应用程序中显示具有大量小部件的可滚动列表作为项目?【英文标题】:How to display a scrollable list with a substantial amount of widgets as items in a Qt C++ app? 【发布时间】:2017-07-29 12:55:16 【问题描述】:

目标:在 Windows 7、10 下的 Qt5 C++ 应用程序中拥有一个可滚动的自定义小部件列表,数量达到数百(甚至可能更多)。

问题:程序在将窗口最小化到任务栏并再次恢复后停止响应。虽然它不会崩溃。 CPU 使用率恒定为 25%。即使等待几分钟,GUI 也不会再次响应。此外,通常会消耗大量内存(超过 200M),我认为即使对于 100k QLabels(每个 QLabel 大约 2k)也太多了。

Here 是针对类似问题的一些建议解决方案,我认为这些解决方案不适合我的情况。

示例:以下示例说明了该问题。为了演示,使用了 QLabels 列表,但它可以是从 QWidget 派生的任何类。

MainWindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QScrollArea>
#include <QVBoxLayout>
#include <QLabel>

class MainWindow : public QMainWindow

    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = 0);
;

#endif // MAINWINDOW_H

MainWindow.cpp

#include "MainWindow.h"

MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)

    QScrollArea *scrollArea = new QScrollArea(this);
    QFrame *frame = new QFrame();
    QVBoxLayout *l = new QVBoxLayout(frame);
    int N = 121004;

    scrollArea->setWidget(frame);
    scrollArea->setWidgetResizable(true);

    for (int n = 0; n < N; n++)  l->addWidget(new QLabel(QString::number(n), this)); 

    resize(640, 480);
    setCentralWidget(scrollArea);

【问题讨论】:

【参考方案1】:

我有一些坏消息和一些好消息:

坏消息:你不能直接用 Qt Widgets 做到这一点。

好消息:有一种方法可以做到这一点,它与列表中的项目数量(甚至数十亿)无关,但你需要给自己时间来学习如何这样做。

所以,第一件事:QScrollArea 不是这样做的方法。正确的方法是使用Model/View/Controller programming paradigm。具有要显示的信息的数据模型必须与视图完全分离,这样 Qt 就只能担心显示用户试图查看的项目。想一想:如果你有十亿个元素要放在那个列表中,这是否意味着用户必须一次看到它们?这是否意味着 Qt 必须全部渲染它们?在您的代码中,这就是您要求 Qt 执行的操作。你对它的慢感到惊讶吗?

建议编号1:阅读Qt如何管理Model/View programming,然后选择正确的查看工具。对于您所描述的内容,我建议QListView。 QTableView 如果你能把东西放在桌子上,你会更轻松。

对列表的控制是通过委托来完成的。委托是负责在视图中绘制小部件的类。默认只会做文本与图标。

建议编号2: 忘记为每个元素创建 Qt 小部件。 I just finished answering another guy's question 为什么这不起作用,即使使用委托也是如此。看看Qt Torrent Example,看看那里是如何绘制控件的。

您可以做的是绘制控件,而不是小部件。这是因为您创建的每个小部件都必须转到 Qt 中的主事件循环,这会使您的程序变慢(您已经经历过)。如果您从一到百万循环只是为了添加数字,则将花费大量时间。您真的希望 Qt 的事件循环遍历所有小部件以处理每个小部件吗?

建议编号3:从简单开始!你似乎有很多事情要做。从模型/视图开始,然后添加一个将绘制自定义控件的委托,然后展开它。给自己时间来学习这一切。

祝你好运!

【讨论】:

你检查过我提供的例子吗?创建小部件并将它们呈现在屏幕上并不需要很多时间。它肯定比处理几个元素要慢,但它是在合理的时间内完成的。当窗口最小化到任务栏并再次恢复时,会出现此问题。 THEN 并且只有 THEN 需要永远(字面意思)重新绘制应用程序窗口。 另外,QListView 显示的所有项目中都有一个按钮怎么样? @scopchanov 是的,我看到了你在上面的代码。你必须明白,有正确的做事方式,还有一百万种其他错误的方式。 Qt 作为一个库,作为一个产品,以某种方式解决了所有问题。如果你一起破解一些东西并且它有效,那么对你有好处!但是你不能指望你渲染一个 100k 元素然后抱怨它很慢。再一次,你强迫你的 Qt 做一些它不应该做的事情。即使它适用于此版本或计算机或此操作系统,它也可能不适用于其他人。 [...] @scopchanov 现在关于绘制按钮,按钮被认为是“复杂控件”。而不是使用drawControl,如洪流示例中所示,您使用drawComplexControl,它处理另一个更复杂控件(组合框、按钮等)的列表。你可以看到这些here ***.com/questions/45485492/…【参考方案2】:

我已经实现了一个列表小部件,它可以显示数十亿个项目,每个项目具有任意数量的小部件,而不会出现任何性能问题。很遗憾,我无法分享代码。

它是在QAbstractScrollArea 之上实现的,不使用Qt 的模型/视图框架。它只处理跟踪视图中的项目范围,在这些项目上调用适当的绘制函数,并跟踪所有项目组合的总高度。就是这样。

每个项目都可能有一个关联的小部件。这个小部件可能是任意复杂的。当项目在视图中时,项目小部件是可见的。在我的实现中,项目通常会延迟创建小部件,这也是速度快的原因之一。例如,如果您有 10 亿个项目,那么其中只有很小的一部分会显示出来,因此花费任何精力来为这些项目构建小部件是一种浪费。

由于每个项目都对其外观负责,因此在使用这种非常通用的列表小部件可以显示的内容方面提供了很大的灵活性。

【讨论】:

子类化QAbstractScrollArea 可能是一个有效的解决方案,但我仍然更喜欢模型视图方法。

以上是关于如何在 Qt C++ 应用程序中显示具有大量小部件的可滚动列表作为项目?的主要内容,如果未能解决你的问题,请参考以下文章

如何为 Qt GUI 小部件使用图像

Qt Creator 5.6 如何在一个项目中使用大量(3000+)小部件加速编译

如何在 Qt 中制作可展开/可折叠的部分小部件

如何在颤动中加载主小部件之前显示加载小部件?

Qt 创建者:小部件未添加到布局中

如何检测 qt 标签(或任何小部件)是不是具有所需的所有空间?