最近使用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(类型,数据);
效果: