其實前面使用多線程進行寫過一個動态加載,原先我自己以為沒問題,但是後面看了下任務管理器,100萬條資料背景加載到5個g還在激增。這樣下去當記憶體超出一定範圍,就會當機,後來檢視代碼就發現這個機制是一直向你的表格中一直插入,并不會釋放掉。後來,經過幾天的沉澱,網上大部分說使用tableview以及自定model,效率比qtablewidget效率高。其實自定義的model無非就是背景處理資料,我給qtablewidget單獨開出一條線程,也不會有任何卡頓。
下面來說下簡易思路,然後配合代碼細講。
簡單的思路:比如你有一個檔案,裡面有100萬行資料,我們想将他加載到qtablewidget,qtablewidget加載100條,1000條資料都沒啥問題。你不會有感覺卡頓什麼的,但是當你加載到一萬條資料以上,卡頓就會越來越明顯,(重點來了),那該如何加載這100萬條資料呢。首先一個窗體是有限的,也就是說,不論你将視窗放的再大,你也不可能同時看到100萬條資料,其實有限的視野也就是同時看到100條左右。首先我們需要單獨開出一個線程來進行加載100萬條資料,目的是為了不卡住主界面,使用一個自定義結構體來存儲你的資料,當我滑鼠滾輪滾動的時候,(比如向下滾動滾輪,我就向下加載5條資料),但是我的界面始終隻有100條資料,是以你的界面始終不會卡頓,記憶體也不會存在溢出的情況。
好,下面結合代碼細說
我們先看自定義tablewidget:
構造:
connect(this->verticalScrollBar(),&QScrollBar::valueChanged,this,&MyTableWgt::OnVertivalValueChanged);
m_pDeal= new MyDeal();
m_pDeal->moveToThread(&m_thread);
connect(this,&MyTableWgt::startRunning,m_pDeal,&MyDeal::CreateData);
connect(&m_thread,&QThread::finished,m_pDeal,&QObject::deleteLater);
connect(m_pDeal,&MyDeal::sSendFinished,this,&MyTableWgt::ReceiverData);
connect(m_pDeal,&MyDeal::sSendValueChanaged,this,&MyTableWgt::ReceiverChanged);
m_thread.start();
構造中mydeal就是我單獨開出來的線程,用作資料處理。線程我就不細說了,自己可以網上搜尋得知。
connect我們知道是信号和槽,能傳輸資料,但是你自定義的結構體是不可以進行傳輸的,是以我們要在資料加載完成時候發出一個信号。讓tablewidget進行顯示資料
滑鼠滾輪事件:
void MyTableWgt::wheelEvent(QWheelEvent *event)
{
int delta= event->delta();
int row=m_pDeal->GetCurrentRow();
if(delta>0)
{
if(row<5)
{
return;
}
else
{
row=row-5; //每次滾動滾輪,向上加載5個
}
}
else
{
if(row>m_pDeal->GetDataSize()-100)
{
row=m_pDeal->GetDataSize()-100;
}
else
{
row=row+5;
}
}
m_pDeal->LoadData(row);
}
我們擷取到目前滾輪是向上或向下滾動,然後從處理資料類中擷取目前是第幾個,如果滑鼠向下滾動就是在原有基礎上向下加載5個。反之亦然。
拖動滾動條:
void MyTableWgt::OnVertivalValueChanged(int i_value)
{
if(i_value==this->verticalScrollBar()->maximum())
{
int row=m_pDeal->GetCurrentRow();
if(row>m_pDeal->GetDataSize()-200)
{
row=m_pDeal->GetDataSize()-100;
}
else
{
row=row+100;
this->verticalScrollBar()->setValue(this->verticalScrollBar()->maximum()*0.9);
}
m_pDeal->LoadData(row);
}
if(i_value==this->verticalScrollBar()->minimum())
{
int row=m_pDeal->GetCurrentRow();
if(row>=100)
{
row=row-100;
if(row<100)
{
row=0;
}
this->verticalScrollBar()->setValue(this->verticalScrollBar()->maximum()*0.1);
}
m_pDeal->LoadData(row);
}
}
原理就是和wheelevent事件原理差不多,但是需要注意的是,不能讓滾動為最大值或最小值,不然就不能繼續加載資料了。
第一次加載資料:
void MyTableWgt::ReceiverData()
{
this->ClearAllRow();
QList<myData> dataList=m_pDeal->GetFirstData();
this->setRowCount(dataList.size());
for(int i=0;i<dataList.size();i++) //首次加載100個
{
myData data=dataList[i];
this->setItem(i,0,new QTableWidgetItem(data.str0));
this->setItem(i,1,new QTableWidgetItem(data.str1));
this->setItem(i,2,new QTableWidgetItem(data.str2));
this->setItem(i,3,new QTableWidgetItem(data.str3));
this->setItem(i,4,new QTableWidgetItem(data.str4));
this->setItem(i,5,new QTableWidgetItem(data.str5));
this->setItem(i,6,new QTableWidgetItem(data.str6));
this->setItem(i,7,new QTableWidgetItem(data.str7));
this->setItem(i,8,new QTableWidgetItem(data.str8));
this->setItem(i,9,new QTableWidgetItem(data.str9));
this->setItem(i,10,new QTableWidgetItem(data.str10));
}
}
預設界面就是顯示100個,因為加載100萬資料是需要時間的,是以當資料又100個的時候,我就會進行加載一次。
當滾動滾輪或滑動滾動條
void MyTableWgt::ReceiverChanged()
{
this->ClearAllRow();
this->setRowCount(100);
//int currentRowcount=this->rowCount();
QList<myData> dataList=m_pDeal->GetSendData();
this->setRowCount(100);
int k=0;
for(int i=0;i<dataList.size();i++)
{
myData data=dataList[i];
this->setItem(i,0,new QTableWidgetItem(data.str0));
this->setItem(i,1,new QTableWidgetItem(data.str1));
this->setItem(i,2,new QTableWidgetItem(data.str2));
this->setItem(i,3,new QTableWidgetItem(data.str3));
this->setItem(i,4,new QTableWidgetItem(data.str4));
this->setItem(i,5,new QTableWidgetItem(data.str5));
this->setItem(i,6,new QTableWidgetItem(data.str6));
this->setItem(i,7,new QTableWidgetItem(data.str7));
this->setItem(i,8,new QTableWidgetItem(data.str8));
this->setItem(i,9,new QTableWidgetItem(data.str9));
this->setItem(i,10,new QTableWidgetItem(data.str10));
k++;
}
}
記住一句畫,界面始終顯示100條資料
加載資料類:
初始加載資料:
void MyDeal::CreateData(QString i_path)
{
m_CurrentRow=0;
QFile file(i_path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return;
int i=0;
while (!file.atEnd())
{
QString strLine=file.readLine();
QStringList strList=strLine.split("&");
myData data;
data.str0=strList[0];
data.str1=strList[1];
data.str2=strList[2];
data.str3=strList[3];
data.str4=strList[4];
data.str5=strList[5];
data.str6=strList[6];
data.str7=strList[7];
data.str8=strList[8];
data.str9=strList[9];
data.str10=strList[10];
m_DataList.append(data);
if(i<=100)
{
m_FirstData.append(data);
}
if(i==100)
{
emit sSendFinished();
}
i++;
}
m_DataSize=m_DataList.size();
}
mydata是自定義資料類,就是簡單的存儲資料。
當資料改變時候:
m_SendData.clear();
for(int i=row;i<row+100;i++)
{
myData data=m_DataList[i];
m_SendData.append(data);
}
m_CurrentRow=row;
emit sSendValueChanaged();
先拿到需要改變的資料,然後将資料發送給界面,界面進行重新整理。
看下運作效果:
這是我需要加載的檔案,3萬行,不算多。後面可能會有百萬行。這套方法絕對承受的住百萬條資料!!
可以看到資料一直在往下加載,但沒有消耗任何的記憶體。
全部代碼到這裡下載下傳。
ヾ( ̄▽ ̄)ByeBye