自定义Widget插入QListWidget中,大量数据会卡问题

最近使用QListWidget的setItemWidget方法做自定义的列表。可是绘制大量数据时,会给出卡顿。为解决这个问题提供两种方案。

一、利用time计时器的方法分批次插入。

// 连接定时器的timeout信号到控件添加槽函数

connect(timer, &QTimer::timeout, this, &LocalListWidget::slot_Timer);

cpp 复制代码
#include "LocalListWidget.h"
#include "LocalItemWidget.h"
#include <QTimer>
LocalListWidget::LocalListWidget(QWidget* parent)
    :QListWidget(parent),
    timer(new QTimer(this))
{
	setMouseTracking(true);
	setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    m_DrawData.clear();
    // 连接定时器的timeout信号到控件添加槽函数  
    connect(timer, &QTimer::timeout, this, &LocalListWidget::slot_Timer);
}

LocalListWidget::~LocalListWidget()
{

}

//为了避免卡顿先加载10条
void LocalListWidget::Refresh(int type, QList<WidgetItem> info)
{
    m_type = type;
    this->clear();
    m_DrawData.clear();
    m_DrawData.append(info);
    来3百个数据看看
    //for (int i = 0; i < 30; i++)
    //{
    //    m_DrawData.append(info);
    //}
    if (type == 0)
    {
        int i = 0;
        foreach(WidgetItem item, m_DrawData)
        {
            addVedioWidget(item, true);
            i++;
            if (i == 10) break;
        }
        if (i == 10)
        {
            m_DrawData.erase(m_DrawData.begin(), m_DrawData.begin() + 10);
            if (m_DrawData.size() > 0) timer->start(100);
        }
    }
    else if (type == 1)
    {
        int i = 0;
        foreach(WidgetItem item, m_DrawData)
        {
            addVedioWidget(item, false);
            i++;
            if (i == 10) break;
        }
        if (i == 10)
        {
            m_DrawData.erase(m_DrawData.begin(), m_DrawData.begin() + 10);
            if (m_DrawData.size() > 0) timer->start(100);
        }
    }
    else if (type == 2)
    {
        int i = 0;
        foreach(WidgetItem item, m_DrawData)
        {
            addImageWidget(item, true);
            i++;
            if (i == 10) break;
        }
        if (i == 10)
        {
            m_DrawData.erase(m_DrawData.begin(), m_DrawData.begin() + 10);
            if (m_DrawData.size() > 0) timer->start(100);
        }
    }
    else if (type == 3)
    {
        int i = 0;
        foreach(WidgetItem item, m_DrawData)
        {
            addImageWidget(item, false);
            i++;
            if (i == 10) break;
        }
        if (i == 10)
        {
            m_DrawData.erase(m_DrawData.begin(), m_DrawData.begin() + 10);
            if (m_DrawData.size() > 0) timer->start(100);
        }
    }
}
//添加视频列表项 true:大图模式 false:小图模式
void LocalListWidget::addVedioWidget(WidgetItem info, bool style)
{
    VedioItemWidget* pWdt = new VedioItemWidget(this, style);
    QStringList labelStr;
    labelStr << info.one << info.two << info.three << info.four << info.PicUrl;
    pWdt->setText(labelStr, info.id);
    QListWidgetItem* pItem = new QListWidgetItem(this);
    if (style)
        pItem->setSizeHint(QSize(260, 219 + 14));//14为间距
    else
        pItem->setSizeHint(QSize(260, 86 + 14));
    addItem(pItem);
    setItemWidget(pItem, pWdt);
}

//添加图片列表项 true:大图模式 false:小图模式
void LocalListWidget::addImageWidget(WidgetItem info, bool style)
{
    ImageItemWidget* pWdt = new ImageItemWidget(this, style);
    QStringList labelStr;
    labelStr << info.one << info.two << info.three << info.four << info.PicUrl;
    pWdt->setText(labelStr, info.id);
    QListWidgetItem* pItem = new QListWidgetItem(this);
    if (style)
        pItem->setSizeHint(QSize(260, 219 + 14));
    else
        pItem->setSizeHint(QSize(260, 86 + 14));
    addItem(pItem);
    setItemWidget(pItem, pWdt);
}

void LocalListWidget::slot_Timer()
{
    int i = 0;
    foreach(WidgetItem item, m_DrawData)
    {
        if (m_type == 0)
        {
            addVedioWidget(item, true);
        }
        else if (m_type == 1)
        {
            addVedioWidget(item, false);
        }
        else if (m_type == 2)
        {
            addImageWidget(item, true);
        }
        else if (m_type == 3)
        {
            addImageWidget(item, false);
        }
        i++;
        if (i == 10) break;
    }
    if (i == 10)
    {
        m_DrawData.erase(m_DrawData.begin(), m_DrawData.begin() + 10);
        if (m_DrawData.size() == 0) timer->stop();
    }
    else
    {
        timer->stop();
    }
}

头文件:

cpp 复制代码
#pragma once
#include <QListWidget>

struct WidgetItem
{
	QString one;//第1行
	QString two;//第2行
	QString three;//第3行
	QString four;//第4行

	QString PicUrl;//图
	int id;//ID
};

//本地列表
class LocalListWidget:public QListWidget
{
	Q_OBJECT
public:
	LocalListWidget(QWidget* parent = nullptr);
	~LocalListWidget();
	/***** type:0,录像大图   1,录像小图  2,图片大图  3,图片小图*****/
	void Refresh(int type, QList<WidgetItem> info);
private:
	void addVedioWidget(WidgetItem info, bool style);//添加视频列表项
	void addImageWidget(WidgetItem info, bool style);//添加图片列表项
private:
	int m_type = -1;

	QTimer* timer;
	QList<WidgetItem> m_DrawData;
private slots:
	void slot_Timer();
};

其中VedioItemWidget和ImageItemWidget就是我自定义的widget,具体代码为:

cpp 复制代码
#pragma once
/******自定义的listwidget的itemWidget******/
#include <QListWidget>
#include <QLabel>

//本地录像
class VedioItemWidget :public QFrame
{
    Q_OBJECT
public:
	VedioItemWidget(QWidget* parent = nullptr,bool style = true);
	~VedioItemWidget();

    void setText(QList<QString> info, int id);
private:
    void InitConnct();
    virtual void mouseMoveEvent(QMouseEvent* event);
    virtual void leaveEvent(QEvent* event);
private:
    QLabel* m_Image;//图
    QLabel* m_Icon;//图标
    QLabel* m_One;
    QLabel* m_Two;
    QLabel* m_Three;
    QLabel* m_Four;
};

//本地图片
class ImageItemWidget :public QFrame
{
public:
	ImageItemWidget(QWidget* parent = nullptr,bool style = true);
	~ImageItemWidget();

    void setText(QList<QString> info, int id);
private:
    void InitConnct();
private:
    QLabel* m_Image;
    QLabel* m_Icon;
    QLabel* m_One;
    QLabel* m_Two;
    QLabel* m_Three;
    QLabel* m_Four;
};
cpp 复制代码
#include "LocalItemWidget.h"
#include <QVBoxLayout>
#include <QCheckBox>
VedioItemWidget::VedioItemWidget(QWidget* parent, bool style)
{
    setMouseTracking(true);
    QVBoxLayout* layout = new QVBoxLayout(this);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->setSpacing(0);

    QHBoxLayout* Imagelayout = new QHBoxLayout(this);
    Imagelayout->setContentsMargins(6, 6, 6, 0);
    m_Image = new QLabel(this);
    m_Image->setObjectName("qss_Video_Image");
    if (!style)//小图模式隐藏图片
    { 
        Imagelayout->setContentsMargins(0, 0, 0, 0);
        layout->addSpacing(-4);
        m_Image->hide();
    }
    Imagelayout->addWidget(m_Image);

    m_Icon = new QLabel(this);
    m_Icon->setObjectName("qss_Video_Icon");
    m_One = new QLabel(this);
    m_One->setObjectName("qss_Video_One");
    QCheckBox* check = new QCheckBox(this);
    QHBoxLayout* Hlayout = new QHBoxLayout(this);
    Hlayout->setContentsMargins(6, 6, 6, 0);
    Hlayout->setSpacing(0);
    Hlayout->addWidget(m_Icon);
    Hlayout->addSpacing(8);
    Hlayout->addWidget(m_One);
    Hlayout->addStretch();
    Hlayout->addWidget(check);

    m_Two = new QLabel(this);
    m_Two->setObjectName("qss_Video_Two");
    m_Two->setContentsMargins(32, 0, 0, 0);
    m_Three = new QLabel(this);
    m_Three->setObjectName("qss_Video_Three");
    m_Three->setContentsMargins(32, 0, 0, 0);
    m_Four = new QLabel(this);
    m_Four->setObjectName("qss_Video_Four");
    m_Four->setContentsMargins(32, 0, 0, 0);

    layout->addLayout(Imagelayout);
    layout->addLayout(Hlayout);
    layout->addSpacing(-4);
    layout->addWidget(m_Two);
    layout->addWidget(m_Three);
    layout->addWidget(m_Four);
    this->setLayout(layout);
}

VedioItemWidget::~VedioItemWidget()
{

}

void VedioItemWidget::setText(QList<QString> info, int id)
{
    if (info.size() == 5)
    {
        QString str = info.at(0);
        QFontMetrics metrics(m_One->font());
        str = QFontMetrics(m_One->font()).elidedText(str, Qt::ElideRight, 190);
        m_One->setText(str);
        m_Two->setText(info.at(1));
        m_Three->setText(info.at(2));
        m_Four->setText(info.at(3));
        //if (style)
        {
            QPixmap* pixmap = new QPixmap(info.at(4));
            pixmap->scaled(m_Image->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
            m_Image->setScaledContents(true);
            m_Image->setPixmap(*pixmap);
        }
    }
}

void VedioItemWidget::InitConnct()
{
}

void VedioItemWidget::mouseMoveEvent(QMouseEvent* event)
{

}

void VedioItemWidget::leaveEvent(QEvent* event)
{

}

ImageItemWidget::ImageItemWidget(QWidget* parent, bool style)
{
    setMouseTracking(true);
    QVBoxLayout* layout = new QVBoxLayout(this);
    layout->setContentsMargins(0, 0, 0, 0);
    layout->setSpacing(0);

    QHBoxLayout* Imagelayout = new QHBoxLayout(this);
    Imagelayout->setContentsMargins(6, 6, 6, 0);
    m_Image = new QLabel(this);
    m_Image->setObjectName("qss_Image_Image");
    if (!style)//小图模式隐藏图片
    {
        Imagelayout->setContentsMargins(0, 0, 0, 0);
        layout->addSpacing(-4);
        m_Image->hide();
    }
    Imagelayout->addWidget(m_Image);

    m_Icon = new QLabel(this);
    m_Icon->setObjectName("qss_Image_Icon");
    m_One = new QLabel(this);
    m_One->setObjectName("qss_Video_One");
    QHBoxLayout* Hlayout = new QHBoxLayout(this);
    Hlayout->setContentsMargins(6, 6, 6, 0);
    Hlayout->setSpacing(0);
    Hlayout->addWidget(m_Icon);
    Hlayout->addSpacing(8);
    Hlayout->addWidget(m_One);
    Hlayout->addStretch();

    m_Two = new QLabel(this);
    m_Two->setObjectName("qss_Video_Two");
    m_Two->setContentsMargins(32, 0, 0, 0);
    m_Three = new QLabel(this);
    m_Three->setObjectName("qss_Video_Three");
    m_Three->setContentsMargins(32, 0, 0, 0);
    m_Four = new QLabel(this);
    m_Four->setObjectName("qss_Video_Four");
    m_Four->setContentsMargins(32, 0, 0, 0);

    layout->addLayout(Imagelayout);
    layout->addLayout(Hlayout);
    layout->addSpacing(-4);
    layout->addWidget(m_Two);
    layout->addWidget(m_Three);
    layout->addWidget(m_Four);
    this->setLayout(layout);
}

ImageItemWidget::~ImageItemWidget()
{
}

void ImageItemWidget::setText(QList<QString> info, int id)
{
    if (info.size() == 5)
    {
        QString str = info.at(0);
        QFontMetrics metrics(m_One->font());
        str = QFontMetrics(m_One->font()).elidedText(str, Qt::ElideRight, 190);
        m_One->setText(str);
        m_Two->setText(info.at(1));
        m_Three->setText(info.at(2));
        m_Four->setText(info.at(3));
        //if (style)
        {
            QPixmap* pixmap = new QPixmap(info.at(4));
            pixmap->scaled(m_Image->size(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
            m_Image->setScaledContents(true);
            m_Image->setPixmap(*pixmap);
        }
    }
}

void ImageItemWidget::InitConnct()
{

}

方案二:利用滚动条来解决。滚动时逐渐加入数据

connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &LocalListWidget::onDragBottom);

代码实现:

cpp 复制代码
#include "LocalListWidget.h"
#include "LocalItemWidget.h"
#include <QScrollBar>
#include <QTimer>
LocalListWidget::LocalListWidget(QWidget* parent)
    :QListWidget(parent)
{
	setMouseTracking(true);
	setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
    connect(verticalScrollBar(), &QScrollBar::valueChanged, this, &LocalListWidget::onDragBottom);
    connect(this, &LocalListWidget::itemDoubleClicked, this, &LocalListWidget::onItemDoubleClicked);
    connect(this, &LocalListWidget::itemClicked, this, &LocalListWidget::onItemClicked);
   // connect(this, &LocalListWidget::itemSelectionChanged, this, &LocalListWidget::onItemClicked);

}

LocalListWidget::~LocalListWidget()
{

}

void LocalListWidget::Refresh(LOCAL_MODEL local, ITEM_MODEL item, const QList<WidgetItem>& info)
{
    Clear();
    m_localModel = local;
    m_itemModel = item;
    int nCount = info.size();
    int nSize = nCount > 15 ? 15 : nCount;
    for (int i = 0; i < nSize; ++i)
    {
        AddItem(info[i]);
    }
    if (nCount > 15)
    {
        m_itemList = info.mid(15, nCount - 15);
    }
}
void LocalListWidget::Clear()
{
    QListWidgetItem* pItem = nullptr;
    QWidget* pWidget = nullptr;
    while (count() > 0)
    {
        pItem = takeItem(0);
        pWidget = itemWidget(pItem);
        if (pWidget)
            delete pWidget;
        delete pItem;
       
    }
    clear();
    m_itemList.clear();
}

void LocalListWidget::ChangeItem(int nNext)
{
    int index = currentRow();
    int nCount = count();
    int row = index;
    if (nNext)
    {
        if (index < nCount -1)
        {
            row += 1;
        }
    }
    else
    {
        if (index > 0 && index < nCount)
        {
            row -= 1;
        }
    }
    if (row != index)
    {
        QListWidgetItem* pItem = item(row);
        setCurrentItem(pItem);
        onItemClicked(pItem);
    }
   
}

void LocalListWidget::onDragBottom(int value)
{
    if (m_itemList.isEmpty()) return;

    if (value >= verticalScrollBar()->maximum())
    {
        int nCount = m_itemList.size();
        int nSize = nCount > 15 ? 15 : nCount;
        for (int i = 0; i < nSize; ++i)
        {
            AddItem(m_itemList[i]);
        }
        if (nCount > 15)
        {
            m_itemList = m_itemList.mid(15, nCount - 15);
        }
        else
        {
            m_itemList.clear();
        }
    }
}
void LocalListWidget::AddItem(const WidgetItem& info)
{
    ItemFrameBase* pWidget = nullptr;
    QStringList strlist;
    if (m_localModel == LOCAL_VIDEO)
    {
        pWidget = new ItemVideoFrame(m_itemModel, this);
        strlist << info.one << info.two << info.three << info.four << info.PicUrl;
    }
    else
    {
        pWidget = new ItemImageFrame(m_itemModel, this);
        strlist << info.one << info.two << info.three << info.four << info.PicUrl;
    }
    QListWidgetItem* pItem = new QListWidgetItem(this);
    if (m_itemModel == ITEM_VIEW)
        pItem->setSizeHint(QSize(260, 219+ 14));//14为间距
    else
        pItem->setSizeHint(QSize(260, 86+14));
    addItem(pItem);
    setItemWidget(pItem, pWidget);
    pWidget->Refresh(strlist);
}

void LocalListWidget::onItemDoubleClicked(QListWidgetItem* pItem)
{
    if (m_localModel == LOCAL_VIDEO)
    {
        ItemFrameBase* pFrm = static_cast<ItemFrameBase*>(itemWidget(pItem));
        if (pFrm)
        {
            emit  sgnItemClicked(m_localModel, 1, pFrm->Value());
        }
    }
}

void LocalListWidget::onItemClicked(QListWidgetItem* pItem)
{
    ItemFrameBase* pFrm = static_cast<ItemFrameBase*>(itemWidget(pItem));
    if (pFrm)
    {
        emit  sgnItemClicked(m_localModel, 0, pFrm->Value());
    }
}
cpp 复制代码
#pragma once
#include <QListWidget>
#include "LocalItemWidget.h"
#include "ReplayTypedef.h"

struct WidgetItem
{
	QString one;	//第1行
	QString two;	//第2行
	QString three;	//第3行
	QString four;	//第4行
	QString PicUrl;	//url 图像/录像路径
	int id;//ID
};



//本地列表
class LocalListWidget:public QListWidget
{
	Q_OBJECT
public:
	LocalListWidget(QWidget* parent = nullptr);
	~LocalListWidget();

	void Refresh(LOCAL_MODEL, ITEM_MODEL, const QList<WidgetItem>& info);

	void Clear();
	void ChangeItem(int);
signals:
	void sgnItemClicked(int localModel, int clickedType, const QVariant& value);
private:
	void onDragBottom(int value);
	void AddItem(const WidgetItem& info);

	void onItemDoubleClicked(QListWidgetItem* pItem);
	void onItemClicked(QListWidgetItem* pItem);
private:
	QList<WidgetItem> m_itemList;
	LOCAL_MODEL m_localModel = LOCAL_VIDEO;
	ITEM_MODEL m_itemModel = ITEM_VIEW;
};

调用:ui->listWidget->Refresh(类型,数据);

效果:

相关推荐
「QT(C++)开发工程师」5 小时前
【qt版本概述】
开发语言·qt
一路冰雨9 小时前
Qt打开文件对话框选择文件之后弹出两次
开发语言·qt
老赵的博客10 小时前
QT 自定义界面布局要诀
开发语言·qt
码码哈哈0.010 小时前
VSCode 2022 离线安装插件QT VSTOOl报错此扩展不能安装在任何当前安装的产品上。
ide·vscode·qt
feiyangqingyun14 小时前
Qt/C++离线地图的加载和交互/可以离线使用/百度和天地图离线/支持手机上运行
c++·qt·qt天地图·qt离线地图·qt地图导航
gz94561 天前
windows下,用CMake编译qt项目,出现错误By not providing “FindQt5.cmake“...
开发语言·qt
「QT(C++)开发工程师」1 天前
Ubuntu 26.04 LTS 大升级:Qt 6 成为未来新引擎
qt
兆。1 天前
python实战案例----使用 PyQt5 构建简单的 HTTP 接口测试工具
爬虫·python·qt
喝哈喝哈1 天前
pycharm中配置pyqt5
python·qt·pycharm
Qt云程序员1 天前
Qt、C++实现五子棋人机对战与本地双人对战(高难度AI,极少代码)
c++·人工智能·qt