一、前言
做數據導出,少量的數據比如10W級別以下的,基本上直接佔用主線程也是很快的就可以處理完,上了百萬級別的數據量以後,就會發現性能極速下降,很容易卡主整體界面,於是這部分處理必須要用到線程,本數據導出到xls組件採用xml格式的數據,固定的頭部和尾部數據,中間是一行行的數據,於是需要把這部分移到線程執行,用戶主動初始化類以後調用open方法打開文件,並先輸出好頭部數據,然後開啓線程,調用append方法逐行輸出數據,百萬千萬級別也可以慢慢輸出,線程排隊輸出到文件,最後用戶主動調用close關閉文件,輸出尾部數據,形成最終的文件。
數據導出到xls基本步驟:
- 打開文件,採用QTextStream輸出數據到文件。
- 輸出固定頭部信息。
- 輸出文檔信息。
- 輸出邊框樣式。
- 輸出表結構。
- 輸出字段名稱。
- 逐行輸出數據。
- 輸出固定尾部信息。
二、功能特點
- 組件同時集成了導出數據到csv、xls、pdf和打印數據。
- 所有操作全部提供靜態方法無需new,數據和屬性等各種參數設置採用結構體數據,極爲方便。
- 同時支持QTableView、QTableWidget、QStandardItemModel、QSqlTableModel等數據源。
- 提供靜態方法直接傳入QTableView、QTableWidget控件,自動識別列名、列寬和數據內容。
- 每組功能都提供單獨的完整的示例,註釋詳細,非常適合各階段Qter程序員。
- 原創導出數據機制,不依賴任何office組件或者操作系統等第三方庫,支持嵌入式linux。
- 速度超快,9個字段10萬行數據只需要2秒鐘完成。
- 只需要四個步驟即可開始急速導出海量數據比如100W條記錄到Excel。
- 同時提供直接寫入數據接口和多線程寫入數據接口,不卡主界面。
- 可設置標題、副標題、表名。
- 可設置導出數據的字段名、列名、列寬。
- 可設置末尾列自動拉伸填充,默認拉伸更美觀。
- 可設置是否啓用校驗過濾數據,啓用後符合規則的數據特殊顏色顯示。
- 可指定校驗的列、校驗規則、校驗值、校驗值數據類型。
- 校驗規則支持 精確等於==、大於>、大於等於>=、小於<、小於等於<=、不等於!=、包含contains。
- 校驗值數據類型支持 整型int、浮點型float、雙精度型double,默認文本字符串類型。
- 可設置隨機背景顏色及需要隨機背景色的列集合。
- 支持分組輸出數據,比如按照設備分組輸出數據,方便查看。
- 可設置csv分隔符、行內容分隔符、子內容分隔符。
- 可設置邊框寬度、自動填數據類型,默認自動數據類型開啓。
- 可設置是否開啓數據單元格樣式,默認不開啓,不開啓可以節約大概30%的文件體積。
- 可設置橫向排版、紙張邊距等,比如導出到pdf以及打印數據。
- 支持圖文混排導出數據到pdf以及打印數據,自動分頁。
- 靈活性超高,可自由更改源碼設置對齊方式、文字顏色、背景顏色等。
- 支持任意excel表格軟件,包括但不限於excel2003-2021、wps、openoffice等。
- 純Qt編寫,支持任意Qt版本+任意編譯器+任意系統。
三、體驗地址
- 體驗地址:https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A 提取碼:o05q 文件名:bin_dataout.zip
- 國內站點:https://gitee.com/feiyangqingyun
- 國際站點:https://github.com/feiyangqingyun
- 個人主頁:https://blog.csdn.net/feiyangqingyun
- 知乎主頁: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();
}