用到表格加载数据,少量数据看不出什么异常,当数据过于庞大,特别是表格中可能还加入了一些界面实时刷新如进度条等,那最终体现出来的效果很差,加载卡,占内存。
所以,下面总结分享的是一种懒加载的策略。
QStringList headList;
headList<<"序号"<<"影片名"<<"票房(万元)"<<"票房占比";
m_table->setColumnCount(headList.length());
m_table->setHorizontalHeaderLabels(headList);
m_table->verticalHeader()->setVisible(false);//隐藏行号
m_table->verticalHeader()->setDefaultSectionSize(m_itemHeight);//默认行高
// 需加 #include
//m_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);//自动分均每列列宽
//m_table->horizontalHeader()->setMinimumSectionSize(100);//默认列宽
m_table->setColumnWidth(0,200);//设置第0列列宽
m_table->horizontalHeader()->setDefaultAlignment(Qt::AlignCenter);//表头居中对齐
m_table->horizontalHeaderItem(0)->setTextAlignment(Qt::AlignRight);//设置第0列列宽
m_table->setSelectionBehavior(QAbstractItemView::SelectRows);//以行为单位选择
//m_table->setEditTriggers(QAbstractItemView::NoEditTriggers);//禁止编辑
m_table->horizontalHeader()->setStyleSheet
("QHeaderView::section{background-color:rgb(216,226,236);"
"border:0.5px solid rgb(254,254,254);}");
主要有两个点:
1、对于需要实时更新加载的情况,每次加载数据时,不用调clearContents()、removeRow(int row)等方法去清空再new,而是去重复利用上次已经new出的Item,改变其内容为最新即可
2、只加载界面可见范围内Item,数据的具体数量只需通过滚动条的值体现出来,每次改变滚动条时,再去按照上述1去刷新Item的内容
#ifndef MYTABLEWIDGET_H
#define MYTABLEWIDGET_H
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
class TableWidget: public QTableWidget
{
public:
TableWidget(QWidget *parent = nullptr):QTableWidget(parent){}
~TableWidget(){}
protected:
void wheelEvent(QWheelEvent *event) override {}//屏蔽鼠标滑轮事件
};
class MyTableWidget: public QWidget
{
public:
MyTableWidget(QWidget *parent = nullptr);
~MyTableWidget();
virtual void InitTable();
virtual void LoadTableData();
void MakeData();
QString GetStr();
void SetMenu(QMenu* menu);
void SetItemText(int row, int colunm, QString str);
void SetItemProgress(int row, int colunm, double value, double total);
protected:
void wheelEvent(QWheelEvent *event) override;
void resizeEvent(QResizeEvent *event) override;
void contextMenuEvent(QContextMenuEvent *event) override;
void showEvent(QShowEvent *event) override;
void hideEvent(QHideEvent *event) override;
public:
private:
TableWidget* m_table;
QScrollBar *m_scrollBar;
int m_itemHeight;
int m_currentRow;
int m_dataCnt;
bool m_bShow;
public:
//data
std::map<QString, double> m_mapData;//name boxOffice
QMenu* m_menu = nullptr;
};
#endif // MYTABLEWIDGET_H
mytablewidget.cpp
#include "mytablewidget.h"
#include
#include
MyTableWidget::MyTableWidget(QWidget *parent):QWidget(parent)
{
m_bShow = false;
m_scrollBar = nullptr;
m_table = nullptr;
m_itemHeight = 20;
m_dataCnt = 0;
}
MyTableWidget::~MyTableWidget()
{
}
void MyTableWidget::InitTable()
{
m_table = new TableWidget(this);
//点击表头排序
// connect(m_table->horizontalHeader(),&QHeaderView::sectionClicked,this,[=](int colunm){
// m_table->sortItems(colunm,Qt::AscendingOrder);
// });
m_table->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);//关闭滚动条
QHBoxLayout* hbox =new QHBoxLayout(this);
hbox->setMargin(0);
hbox->setSpacing(0);
hbox->addWidget(m_table);
m_scrollBar = new QScrollBar(Qt::Vertical);
connect(m_scrollBar,&QScrollBar::valueChanged,this,[=](int value){
LoadTableData();
});
hbox->addWidget(m_scrollBar);
QStringList headList;
headList<<"序号"<<"影片名"<<"票房(万元)"<<"票房占比";
m_table->setColumnCount(headList.length());
m_table->setHorizontalHeaderLabels(headList);
m_table->verticalHeader()->setVisible(false);//隐藏行号
m_table->verticalHeader()->setDefaultSectionSize(m_itemHeight);//默认行高
// 需加 #include
//m_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);//自动分均每列列宽
//m_table->horizontalHeader()->setMinimumSectionSize(100);//默认列宽
m_table->setColumnWidth(0,200);//设置第0列列宽
m_table->horizontalHeader()->setDefaultAlignment(Qt::AlignCenter);//表头居中对齐
m_table->horizontalHeaderItem(0)->setTextAlignment(Qt::AlignRight);//设置第0列列宽
m_table->setSelectionBehavior(QAbstractItemView::SelectRows);//以行为单位选择
m_table->setEditTriggers(QAbstractItemView::NoEditTriggers);//禁止编辑
m_table->horizontalHeader()->setStyleSheet
("QHeaderView::section{background-color:rgb(216,226,236);"
"border:0.5px solid rgb(254,254,254);}");
//线程加载数据
QThread* thread = new QThread();
connect(thread, &QThread::started, thread, [=]()mutable{
qDebug()<<"started!";
MakeData();
thread->quit();
}, Qt::DirectConnection);
connect(thread, &QThread::finished, this, [=]()mutable{
thread->wait();
thread->deleteLater();
qDebug()<<"finished!";
LoadTableData();
});
thread->start();
}
void MyTableWidget::LoadTableData()
{
if(!m_table)
return;
int oldRow = m_table->rowCount() - 1;
if(m_currentRow < oldRow)//如果item有了就不删了重新创建,在之前item修改内容即可,界面尺寸改变,显示的行就改变,多了就删除,
for (int i=oldRow - 1; i>oldRow - m_currentRow - 1; i--)
{
m_table->removeRow(i);
}
m_table->setRowCount(m_currentRow+1);
int total = 1000;
std::map<QString, double>::iterator itor = m_mapData.begin();
int beginRow = m_scrollBar->value();//滚动条值,表格行
for (int i=0; i<beginRow; i++){ itor++;}//从滚动条的值对应显示
for (int i =0 ; i<m_currentRow; i++,itor++)
{
if(itor == m_mapData.end())
{
m_table->removeRow(i);
break;
}
SetItemText(i, 0, QString::number(beginRow++));
SetItemText(i, 1, itor->first);
SetItemText(i, 2, QString::number(itor->second,'f',2));
SetItemProgress(i, 3, itor->second, total);
}
}
void MyTableWidget::SetMenu(QMenu *menu)
{
m_menu = menu;
}
void MyTableWidget::resizeEvent(QResizeEvent *event)
{
m_currentRow = height()/m_itemHeight;
m_dataCnt = m_mapData.size();
if(m_dataCnt - m_currentRow > 0)
{
if(m_scrollBar)
{
m_scrollBar->setRange(0,m_dataCnt - m_currentRow + 2);
m_scrollBar->show();
}
}
else
{
if(m_scrollBar)
{
m_scrollBar->setRange(0,0);
m_scrollBar->hide();
}
}
LoadTableData();
}
void MyTableWidget::contextMenuEvent(QContextMenuEvent *event)
{
QTableWidgetItem* item = m_table->itemAt(event->pos());
if(item && m_menu)
m_menu->exec(event->globalPos());
}
void MyTableWidget::wheelEvent(QWheelEvent *event)
{
int value = m_scrollBar->value();
if(event->delta() > 0)
{
value -= m_currentRow;
if(value < m_scrollBar->minimum())
value = m_scrollBar->minimum();
m_scrollBar->setValue(value);
}
else
{
value += m_currentRow;
if(value > m_scrollBar->maximum())
value = m_scrollBar->maximum();
m_scrollBar->setValue(value);
}
}
void MyTableWidget::showEvent(QShowEvent *event)
{
if(! m_bShow)
{
InitTable();
m_bShow = true;
}
}
void MyTableWidget::hideEvent(QHideEvent *event)
{
}
void MyTableWidget::MakeData()
{
int dataCnt = 1000000;
for (int i=0; i<dataCnt;i++) {
double value = rand()%1000000+1000;
m_mapData[GetStr()] = value/100;
}
}
QString MyTableWidget::GetStr()
{
int length = rand()%15+5;
const char ch[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
int size = sizeof(ch);
char* str = new char[length + 1];
int num = 0;
int i;
for (i = 0; i < length; ++i)
{
num = rand() % (size - 1);
str[i] = ch[num];
}
str[i] = '\0';
QString res(str);
//return res;
return "《"+res+"》";
}
void MyTableWidget::SetItemText(int row, int colunm, QString str)
{
QTableWidgetItem* item = m_table->item(row, colunm);
if(nullptr == item)
{
item = new QTableWidgetItem;
m_table->setItem(row, colunm, item);
}
item->setText(str);
}
void MyTableWidget::SetItemProgress(int row, int colunm, double value, double total)
{
QWidget *widget = m_table->cellWidget(row, colunm);
QProgressBar* bar = static_cast<QProgressBar*>(widget);
if(nullptr == bar)
{
bar = new QProgressBar;
bar->setObjectName("progress");
m_table->setCellWidget(row, colunm, bar);//设置后会自动show
bar->hide();//先hide 加载完会自动show
}
double percent =(double) value/total;
bar->setAlignment(Qt::AlignCenter);
bar->setRange(0,100);
bar->setValue(percent*100);
bar->setFormat(QString("%1%").arg(QString::number(percent*100,'f',1)));
}
有兴趣自己可以对比一下,我分享的和正常一次加载百万级别的数据到表格中,这俩种方式,看下程序内存,和加载速度,逻辑处理还是很快的 ,绘制UI确实比较慢,占内存。理论上,我分享的这种策略加载数据的大小上限,主要就跟数据存储方式和容量有关了,毕竟加载界面可视范围的Item还是比较快的.,但是比如你用容器map存储还是有上限的。还有就是线程那,要加 Qt::DirectConnection 在线程执行,具体了解更多Qt 信号和槽及第五个参数