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();
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章