1. 程式人生 > 其它 >Qt資料庫應用5-海量資料多執行緒匯出

Qt資料庫應用5-海量資料多執行緒匯出

一、前言

做資料匯出,少量的資料比如10W級別以下的,基本上直接佔用主執行緒也是很快的就可以處理完,上了百萬級別的資料量以後,就會發現效能極速下降,很容易卡主整體介面,於是這部分處理必須要用到執行緒,本資料匯出到xls元件採用xml格式的資料,固定的頭部和尾部資料,中間是一行行的資料,於是需要把這部分移到執行緒執行,使用者主動初始化類以後呼叫open方法開啟檔案,並先輸出好頭部資料,然後開啟執行緒,呼叫append方法逐行輸出資料,百萬千萬級別也可以慢慢輸出,執行緒排隊輸出到檔案,最後使用者主動呼叫close關閉檔案,輸出尾部資料,形成最終的檔案。

資料匯出到xls基本步驟:

  • 開啟檔案,採用QTextStream輸出資料到檔案。
  • 輸出固定頭部資訊。
  • 輸出文件資訊。
  • 輸出邊框樣式。
  • 輸出表結構。
  • 輸出欄位名稱。
  • 逐行輸出資料。
  • 輸出固定尾部資訊。

二、功能特點

  1. 元件同時集成了匯出資料到csv、xls、pdf和列印資料。
  2. 所有操作全部提供靜態方法無需new,資料和屬性等各種引數設定採用結構體資料,極為方便。
  3. 同時支援QTableView、QTableWidget、QStandardItemModel、QSqlTableModel等資料來源。
  4. 提供靜態方法直接傳入QTableView、QTableWidget控制元件,自動識別列名、列寬和資料內容。
  5. 每組功能都提供單獨的完整的示例,註釋詳細,非常適合各階段Qter程式設計師。
  6. 原創匯出資料機制,不依賴任何office元件或者作業系統等第三方庫,支援嵌入式linux。
  7. 速度超快,9個欄位10萬行資料只需要2秒鐘完成。
  8. 只需要四個步驟即可開始急速匯出海量資料比如100W條記錄到Excel。
  9. 同時提供直接寫入資料介面和多執行緒寫入資料介面,不卡主介面。
  10. 可設定標題、副標題、表名。
  11. 可設定匯出資料的欄位名、列名、列寬。
  12. 可設定末尾列自動拉伸填充,預設拉伸更美觀。
  13. 可設定是否啟用校驗過濾資料,啟用後符合規則的資料特殊顏色顯示。
  14. 可指定校驗的列、校驗規則、校驗值、校驗值資料型別。
  15. 校驗規則支援 精確等於==、大於>、大於等於>=、小於<、小於等於<=、不等於!=、包含contains。
  16. 校驗值資料型別支援 整型int、浮點型float、雙精度型double,預設文字字串型別。
  17. 可設定隨機背景顏色及需要隨機背景色的列集合。
  18. 支援分組輸出資料,比如按照裝置分組輸出資料,方便檢視。
  19. 可設定csv分隔符、行內容分隔符、子內容分隔符。
  20. 可設定邊框寬度、自動填資料型別,預設自動資料型別開啟。
  21. 可設定是否開啟資料單元格樣式,預設不開啟,不開啟可以節約大概30%的檔案體積。
  22. 可設定橫向排版、紙張邊距等,比如匯出到pdf以及列印資料。
  23. 支援圖文混排匯出資料到pdf以及列印資料,自動分頁。
  24. 靈活性超高,可自由更改原始碼設定對齊方式、文字顏色、背景顏色等。
  25. 支援任意excel表格軟體,包括但不限於excel2003-2021、wps、openoffice等。
  26. 純Qt編寫,支援任意Qt版本+任意編譯器+任意系統。

三、體驗地址

  1. 體驗地址:https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A 提取碼:o05q 檔名:bin_dataout.zip
  2. 國內站點:https://gitee.com/feiyangqingyun
  3. 國際站點:https://github.com/feiyangqingyun
  4. 個人主頁:https://blog.csdn.net/feiyangqingyun
  5. 知乎主頁:https://www.zhihu.com/people/feiyangqingyun/

四、效果圖

五、相關程式碼

void frmDataThread::append()
{
    //先判斷是否已經輸出完成資料
    if (currentCount >= AppConfig::RowThread) {
        QString info;
        if (AppConfig::TypeThread == 0) {
            info = QString("%1 輸出完成 (共 %2 條/總計 %3 條) 用時 %4 秒").arg(TIMEMS)
                   .arg(AppConfig::RowThread).arg(AppConfig::RowThread).arg(xls->getUseTime());
            xls->stop();
            xls->close();
        } else {
            info = QString("%1 輸出完成 (共 %2 條/總計 %3 條) 用時 %4 秒").arg(TIMEMS)
                   .arg(AppConfig::RowThread).arg(AppConfig::RowThread).arg(pdf->getUseTime());
            pdf->stop();
        }

        ui->textEdit->setTextColor("#A279C5");
        ui->textEdit->append(info);
        ui->btnStart->setEnabled(true);
        ui->progressBar->setValue(ui->progressBar->maximum());

        return;
    }

    QElapsedTimer time;
    time.start();

    for (int i = 0; i < AppConfig::CountThread; i++) {
        QStringList list;
        for (int j = 0; j < AppConfig::ColumnThread; j++) {
            list << QString("%1_%2").arg(i + 1).arg(j + 1);
        }

        if (AppConfig::TypeThread == 0) {
            xls->append(list.join(";"));
        } else {
            pdf->append(list.join(";"));
        }
    }

    currentCount += AppConfig::CountThread;
    ui->progressBar->setValue(currentCount);
    QString msec = QString::number((float)time.elapsed() / 1000, 'f', 3);
    QString info = QString("%1 生成資料 (第 %2 條/總計 %3 條) 用時 %4 秒").arg(TIMEMS).arg(currentCount).arg(AppConfig::RowThread).arg(msec);
    ui->textEdit->setTextColor("#22A3A9");
    ui->textEdit->append(info);
}

void frmDataThread::openFile()
{
    QString info;
    if (AppConfig::TypeThread == 0) {
        info = QString("%1 匯出完成 (共 %2 條/總計 %3 條) 用時 %4 秒").arg(TIMEMS)
               .arg(AppConfig::RowThread).arg(AppConfig::RowThread).arg(xls->getUseTime());
    } else {
        info = QString("%1 匯出完成 (共 %2 條/總計 %3 條) 用時 %4 秒").arg(TIMEMS)
               .arg(AppConfig::RowThread).arg(AppConfig::RowThread).arg(pdf->getUseTime());
    }

    ui->textEdit->setTextColor("#FD8B28");
    ui->textEdit->append(info);
    QTimer::singleShot(1000, this, SLOT(openFile2()));
}

void frmDataThread::openFile2()
{
    QUIHelper::openFile(fileName, "匯出隨機資料");
}

void frmDataThread::appendFinshed(int count, int mesc)
{
    QString time = QString::number((double)mesc / 1000, 'f', 3);
    QString info = QString("%1 輸出資料 (共 %2 條/總計 %3 條) 用時 %4 秒").arg(TIMEMS).arg(count).arg(AppConfig::RowThread).arg(time);
    ui->textEdit->setTextColor("#D64D54");
    ui->textEdit->append(info);

    //完成一個以後繼續追加
    append();
}

void frmDataThread::on_btnStart_clicked()
{
    //設定進度條
    currentCount = 0;
    ui->progressBar->setRange(0, AppConfig::RowThread);
    ui->progressBar->setValue(currentCount);
    ui->btnStart->setEnabled(false);

    //隨機生成列名稱和寬度
    QList<QString> columnNames;
    QList<int> columnWidths;
    for (int j = 0; j < AppConfig::ColumnThread; j++) {
        columnNames << QString("列%1").arg(j + 1);
        columnWidths << 50;
    }

    //第一步:設定檔名標題等
    //第二步:開啟檔案
    //第三步:啟動執行緒執行

    DataContent dataContent;
    dataContent.sheetName = "測試名稱";
    dataContent.title = "測試標題";
    dataContent.columnNames = columnNames;
    dataContent.columnWidths = columnWidths;
    //dataContent.randomColor = true;

    if (AppConfig::TypeThread == 0) {
        fileName = QUIHelper::appPath() + "/db/dataout_thread.xls";
        dataContent.fileName = fileName;
        xls->setDataContent(dataContent);
        xls->init();
        xls->open();
        xls->start();
    } else {
        //匯出到pdf由於Qt自帶類QPrint效率比較低所以數量過大彈個提示
        if (AppConfig::RowThread >= 5000) {
            QUIHelper::showMessageBoxInfo("資料量比較大用時比較久, 建議耐心等待最後完成!");
        }
        fileName = QUIHelper::appPath() + "/db/dataout_thread.pdf";
        dataContent.fileName = fileName;
        pdf->setDataContent(dataContent);
        pdf->init();
        pdf->open(true);
        pdf->start();
    }

    //立即執行一次
    append();
}