QTableWidget加载大量数据不卡顿解决办法
最近在模仿网易云音乐的UI,积累自己的代码能力,在使用QTabbleWidget的时候发现加载大量数据会导致卡顿,这个不能忍。
原因
- 设置了自适应宽度
// item 水平表头自适应大小
tab->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
// item 垂直表头自适应大小
ui->tableWidget->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
//试着设置为: QHeaderView::Fixed 看看是否还会卡顿
- 加载的数据太大
解决方案
- QTableView 配合 Model (推荐)
- 我们知道 QTableWidget 每次显示的数据有限(屏幕只有这么大),我们利用这一点来实现动态加载item,解决卡顿的问题
- 使用事件过滤器,或者 重写鼠标事件,
//需要重写的虚函数
virtual void wheelEvent(QWheelEvent* event)
//事件过滤器
virtual bool eventFilter(QObject* obj, QEvent* event)
举例说明
1.我们首先先加载一定量的数据,这样就不会卡了。
2.为了防止越界,每次循环我们都应该判断一下
3.用一个 curtableindex 来记录当前数据的位置,方便之后加载剩余的数据
void SongMenu::loadData()
{
int len = taglsit.length();
for (int i = 0; i != 20; ++i) {
if (i >= len)return;
ui->tab_SongTable->insertRow(i);
//添加窗口小部件
ui->tab_SongTable->setCellWidget(i, 0, base->setItemWidget(1));
QTableWidgetItem* item1 = new QTableWidgetItem(taglsit.at(i).Title);
QTableWidgetItem* item2 = new QTableWidgetItem(taglsit.at(i).Artist);
QTableWidgetItem* item3 = new QTableWidgetItem(taglsit.at(i).Ablue);
QTableWidgetItem* item4 = new QTableWidgetItem(taglsit.at(i).Duration);
ui->tab_SongTable->setItem(i, 1, item1);
ui->tab_SongTable->setItem(i, 2, item2);
ui->tab_SongTable->setItem(i, 3, item3);
ui->tab_SongTable->setItem(i, 4, item4);
}
curtableindex = 20;
//下面例子是加载大量数据会卡顿,原因:一次性把所有的数据都添加了且设置了自适应宽度
/*foreach(const Temptag & rhs, taglsit) {
ui->tab_SongTable->insertRow(index);
ui->tab_SongTable->setCellWidget(index, 0, base->setItemWidget(1));
QTableWidgetItem* item1 = new QTableWidgetItem(rhs.Artist);
QTableWidgetItem* item2 = new QTableWidgetItem(rhs.Title);
QTableWidgetItem* item3 = new QTableWidgetItem(rhs.Ablue);
QTableWidgetItem* item4 = new QTableWidgetItem(rhs.Duration);
ui->tab_SongTable->setItem(index, 1, item1);
ui->tab_SongTable->setItem(index, 2, item2);
ui->tab_SongTable->setItem(index, 3, item3);
ui->tab_SongTable->setItem(index, 4, item4);
}*/
}
这里并没有加载全部的数据。
重新鼠标事件
函数原型:
virtual void wheelEvent(QWheelEvent* event)
wheelEvent() 实现方法
void Base::wheelEvent(QWheelEvent* event)
{
//滑动一次 y() == 120
//y() > 0 鼠标滚轮向前滑动, y() < 0 鼠标滚轮向自己滑动
if (event->angleDelta().y() < 0) {
emit loadNextPage();
}
}
事件过滤器
函数原型:
virtual bool eventFilter(QObject* obj, QEvent* event)
eventFilter() 实现方法
bool Base::eventFilter(QObject* obj, QEvent* event)
{
if (obj == tab) {
if (event->type() == QEvent::Wheel) {
QWheelEvent* wheel = static_cast<QWheelEvent*>(event);
//y() < 0 鼠标滚轮向自己滑动
if (wheel->angleDelta().y() < 0) {
//发射信号,然后处理数据
emit loadNextPage();
}
}
}
return QTableWidget::eventFilter(obj,event);
}
//然后在 构造函数中安装事件过滤器
ui->tab_SongTable->installEventFilter(this);
实现滑动加载
前面已经实现了事件过滤器,和重写鼠标事件,接下来就是加载数据了,方法很简单,只要连接信号,然后加载数据就行了
//加载剩余的数据
connect(base, &Base::loadNextPage, this, [&]() {
//每次滚动到底部都加载5条数据,和前面一样,检查边界,以防越界
for (int i = 0; i != 5; i++) {
if (curtableindex >= taglsit.length()) {
return;
}
else
{
//拿到ui->tab_SongTable尾部的索引,之后往尾部添加数据
int currow = ui->tab_SongTable->rowCount();
ui->tab_SongTable->insertRow(currow);
ui->tab_SongTable->setCellWidget(currow, 0, base->setItemWidget(1));
QTableWidgetItem* item1 = new QTableWidgetItem(taglsit.at(currow).Title);
QTableWidgetItem* item2 = new QTableWidgetItem(taglsit.at(currow).Artist);
QTableWidgetItem* item3 = new QTableWidgetItem(taglsit.at(currow).Ablue);
QTableWidgetItem* item4 = new QTableWidgetItem(taglsit.at(currow).Duration);
ui->tab_SongTable->setItem(currow, 1, item1);
ui->tab_SongTable->setItem(currow, 2, item2);
ui->tab_SongTable->setItem(currow, 3, item3);
ui->tab_SongTable->setItem(currow, 4, item4);
}
//累加索引,防止越界
++curtableindex ;
}
});
总结:
这里提供了三种解决办法,更具自己的实际情况选择合适的方法,达到自己的目的如果你有更好的解决方案,欢迎分享