天天看點

qtablewidget解決加載百萬條資料卡頓(不會存在記憶體溢出,界面也不會卡頓)動态加載

其實前面使用多線程進行寫過一個動态加載,原先我自己以為沒問題,但是後面看了下任務管理器,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();
           

先拿到需要改變的資料,然後将資料發送給界面,界面進行重新整理。

看下運作效果:

qtablewidget解決加載百萬條資料卡頓(不會存在記憶體溢出,界面也不會卡頓)動态加載

這是我需要加載的檔案,3萬行,不算多。後面可能會有百萬行。這套方法絕對承受的住百萬條資料!!

qtablewidget解決加載百萬條資料卡頓(不會存在記憶體溢出,界面也不會卡頓)動态加載

可以看到資料一直在往下加載,但沒有消耗任何的記憶體。

全部代碼到這裡下載下傳。

ヾ( ̄▽ ̄)ByeBye