美文网首页
Qt数据库应用5-海量数据多线程导出

Qt数据库应用5-海量数据多线程导出

作者: feiyangqingyun | 来源:发表于2022-01-21 09:37 被阅读0次

    一、前言

    做数据导出,少量的数据比如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();
    }
    

    相关文章

      网友评论

          本文标题:Qt数据库应用5-海量数据多线程导出

          本文链接:https://www.haomeiwen.com/subject/iwhehrtx.html