QT报表/QT生成docx文档

本文介绍了一种根据用户输入的查询日期,自动从数据库中提取数据并生成包含基本参数、定位图和波形图的Word报告的方法。通过Qt界面设计,实现了日期选择、数据查询、坐标标记、截图生成等功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、需求分析:
根据项目给定的word模板自动生成相应的word文件。
给定的word模板如下图(有些参数做了打码,不影响):

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

该文档中的内容都需要自动生成。根据用户输入的查询日期,自动搜索数据库内容同时显示定位坐标和读取波形文件,其中第一部分基本参数的内容是来自数据库,第二部分的定位图是在.dwg图中标记定位点后再转为.jgp图片,然后插入word里面,第三部分的波形文件是自动读取.csv文件然后自动截图,将截图插入word文件里面。(对该段说明不理解的朋友,可以去看我的前几篇博客)
因此,我们可以分析出下面几个要点:
1、获取用户输入的查询日期,然后在数据库中获取该时间范围内的数据
2、在获取的数据中提取出定位坐标,并在dwg图中标记出来(该部分可以参考之前发的博客)
3、将标记好定位点的dwg图转为jpg
4、在获取的数据中提取出记录事件数据的csv文件,然后自动读取(该部分可参考之前发的博客)
5、将读取的csv波形数据自动截图
6、后台调用wps中的word程序自动写入获取的数据内容以及插入所有截图
二、具体实现
1、界面设计

在这里插入图片描述

2、代码实现(有部分内容在前几篇博客提到过,这里不再说)

(1)QDateEdit设置以及获取输入日期

    ui->startDate->setMinimumDate(QDate::currentDate().addDays(-3650));   // -3650天
    ui->startDate->setCalendarPopup(true);                                // 日历弹出
    ui->endDate->setMaximumDate(QDate::currentDate().addDays(3650));      // +3650天
    ui->endDate->setCalendarPopup(true);
     startDate = ui->startDate->date().toString("yyyy-MM-dd");
     endDate = ui->endDate->date().toString("yyyy-MM-dd");

数据库查询

    QMap<QString,QList<QVariant>> mapParameter;
    QList<QString> listOfFilePath;    //保存查询到的文件路径表列,用于自动截图读取文件路径
//以下执行相关sql语句
     QSqlQuery query;
     QString sql = "select quackTime,kind,xData,yData,zData,quackGrade,Parrival,panfu,nengliang,wenjianming from mine_quack_results where quackTime>='"+
             startDate+" 00:00:00' and quackTime<='"+endDate+" 23:59:59' ";
     query.exec(sql);            // 执行查询操作

     mapParameter.clear();           //每次查询时要把上一次查询的数据库结果清除
     ui->csvList->clearContents();   //只清除表中数据,不清除表头内容

     QString quackTime;
     QString kind;
     double xData;
     double yData;
     double zData;
     double quackGrade;
     double Parrival;
     QString panfu;
     double nengliang;
     QString wenjianming;

     QMap<QString,QList<double>> map ;
     QList<double> list;
     QList<double> coordinates;
     QList<QVariant> dataParameters;

     int index = 0;           //tablewidget的索引,用来插入wenjianming和panfu

     //query.next()指向查找到的第一条记录,然后每次后移一条记录
     while(query.next())
     {
         quackTime = query.value(0).toString();
         dataParameters.append(quackTime);

         kind = query.value(1).toString();
         dataParameters.append(kind);

         xData = query.value(2).toDouble();
         dataParameters.append(xData);

         yData = query.value(3).toDouble();
         dataParameters.append(yData);

         zData = query.value(4).toDouble();
         dataParameters.append(zData);

         quackGrade = query.value(5).toDouble();
         dataParameters.append(quackGrade);

         Parrival = query.value(6).toDouble();
         dataParameters.append(Parrival);

         panfu = query.value(7).toString();
         dataParameters.append(panfu);

         nengliang = query.value(8).toDouble();
         dataParameters.append(nengliang);

         wenjianming = query.value(9).toString();
         dataParameters.append(wenjianming);

         list.append(xData);
         list.append(yData);

         map.insertMulti(kind,list);
         list.clear();
         qDebug()<<"kind = "<<kind<<" xData="<<xData<<" yData="<<yData;

         mapParameter.insert(quackTime,dataParameters);

         for(int i=0;i<dataParameters.size();i++)
         qDebug()<<"param:"<<dataParameters.at(i);
         dataParameters.clear();

         if(!listOfFilePath.contains(wenjianming)){
             listOfFilePath.append(wenjianming);
         }
         ui->csvList->setItem(index,0,new QTableWidgetItem(wenjianming));
         ui->csvList->setItem(index,1,new QTableWidgetItem(panfu));

         index ++;           //行数加一
     }

(2)定位点标记,利用mxdraw提供的api

 QString pointPath=QDir::currentPath() + "/centerpoint.png";
     QString pointPath2=QDir::currentPath() + "/centerpoint2.png";
     QString pointPath3=QDir::currentPath() + "/centerpoint3.png";

     QMap<QString,QList<double>>::Iterator it=map.begin();
     while(it!=map.end()){
         //qDebug()<<it.key()<<"\t"<<it.value();

         if(it.key().compare("three") == 0){
            QColor color1(255,0,0);
            ui->axWidget->dynamicCall("SetDrawColor(QColor)", color1);

            coordinates = it.value();
            //qDebug()<<"coordinates x:"<<coordinates.at(0)<<"coordinates y:"<<coordinates.at(1);
            //X坐标,Y坐标,文字,文字高度,旋转角度,水平对齐(0=kTextLeft,1=kTextCenter,2=kTextRight ),垂直对齐(1=kTextBottom,2=kTextVertMid,3=kTextTop )
            qlonglong result = ui->axWidget->dynamicCall("DrawText(double, double, QString, double, double, int, int)",
                                                             coordinates.at(0),coordinates.at(1),"three",250,0,0,1).toLongLong();
            textId->enqueue(result);

            qlonglong result2 = ui->axWidget->dynamicCall("DrawCircle(double, double, double)",coordinates.at(0),coordinates.at(1),400).toLongLong();
            blinkId->enqueue(result2);

            qlonglong result3 = ui->axWidget->dynamicCall("DrawImage(double, double, double, double, QString)",
                                                          coordinates.at(0)-50,coordinates.at(1)-50,2,0,pointPath).toLongLong();
            pointId->enqueue(result3);
         }else if(it.key().compare("five") == 0){
            QColor color2(0,255,0);
            ui->axWidget->dynamicCall("SetDrawColor(QColor)", color2);
            coordinates = it.value();
            qlonglong result =ui->axWidget->dynamicCall("DrawText(double, double, QString, double, double, int, int)",
                                                             coordinates.at(0),coordinates.at(1),"five",250,0,0,1).toLongLong();
            textId->enqueue(result);

            qlonglong result2 = ui->axWidget->dynamicCall("DrawCircle(double, double, double)",coordinates.at(0),coordinates.at(1),400).toLongLong();
            blinkId->enqueue(result2);

            qlonglong result3 = ui->axWidget->dynamicCall("DrawImage(double, double, double, double, QString)",
                                                          coordinates.at(0)-50,coordinates.at(1)-50,1,0,pointPath2).toLongLong();
            pointId->enqueue(result3);
         }else if(it.key().compare("PSO") == 0){
            QColor color3(0,0,255);
            ui->axWidget->dynamicCall("SetDrawColor(QColor)", color3);
            coordinates = it.value();
            qlonglong result = ui->axWidget->dynamicCall("DrawText(double, double, QString, double, double, int, int)",
                                                             coordinates.at(0),coordinates.at(1),"PSO",250,0,0,1).toLongLong();
            textId->enqueue(result);

            qlonglong result2 = ui->axWidget->dynamicCall("DrawCircle(double, double, double)",coordinates.at(0),coordinates.at(1),400).toLongLong();
            blinkId->enqueue(result2);

            qlonglong result3 = ui->axWidget->dynamicCall("DrawImage(double, double, double, double, QString)",
                                                          coordinates.at(0)-50,coordinates.at(1)-50,1,0,pointPath3).toLongLong();
            pointId->enqueue(result3);
         }
         it++;
     }

dwg转jpg

 QString locationPath=QDir::currentPath() + "/locationresult.jpg";
    ui->axWidget->dynamicCall("SaveJpgFile(QString, int, int, int)",locationPath,-1,-1,0);

(3)读取csv数据,并自动截图

//自动截图
void ReportForm::captureByRobotClicked()
{
    isManual = false;

    int i = 1;
    for(QString filePath:listOfFilePath){
        if(readCSVFileOfZ(filePath)){
            QRect rect = ui->gridLayout->geometry();
            QPixmap p = this->grab(rect);
            QString filePathName = QDir::currentPath() + "/captures/"+startDate + "-"+endDate+"capture"+QString::number(i);
            //filePathName += QDateTime::currentDateTime().toString("yyyy-MM-dd hh-mm-ss-zzz");
            filePathName += ".png";
            if(!p.save(filePathName,"png")){
                ui->statusOfProgramme->setText("自动截图"+filePathName+"保存失败");
                qDebug()<<"save widget screen failed";
            }
            i++;
            if((i-1)== listOfFilePath.size()){
                //记得要清掉保存的路径,不然下次再查询会重复添加
                listOfFilePath.clear();
                QMessageBox::warning(this,tr("完成"),tr("自动截图已完成,保存至/captures"),QMessageBox::Yes);
            }
        }else{
            QMessageBox *msg = new QMessageBox(this);
            msg->setWindowTitle(tr("错误"));
            msg->setText(tr("在自动截图过程中,发生异常"));
            msg->setIcon(QMessageBox::Warning);
            QPushButton *conntinueButton = msg->addButton(tr("继续执行"), QMessageBox::ActionRole);
            QPushButton *abortButton = msg->addButton(tr("退出"),QMessageBox::RejectRole);
            msg->exec();
            if (msg->clickedButton() == conntinueButton) {
                continue;
            } else {
                return;
            }
        }
    }
}
说明:readCSVFileOfZ是读取csv数据,代码比较长,而且和之前的qt读取csv文件的博客相似,这里就不贴了
另外:
 QRect rect = ui->gridLayout->geometry();
 QPixmap p = this->grab(rect);
 这两句是QT的截图功能,可以截图某个布局内所有控件的显示内容

(4)调用word的com程序,自动生成word文档

主要有两种方式生成word文档,
一种是利用word模板的方式,可以参考:
https://blog.csdn.net/qq_41605114/article/details/86596059 这种方式使用复杂,需要做好模板。
一种是利用html的方式,这种方式简便但同时也存在一些问题。
本文使用html的方式。用到的参考文章有:
https://blog.csdn.net/toby54king/article/details/79101303 
https://www.cnblogs.com/lpxblog/p/6042330.html
//该功能是利用html转word,使用简便
void ReportForm::generateWebDOCClicked()
{
    QString locationPath=QDir::currentPath() + "/locationresult.jpg";
    QString capturesPath=QDir::currentPath() + "/captures/";

    //下面这几句是过滤截图文件
    QQueue<QString> capturesOfPng ;
    QDir dir;
    QStringList filters;
    filters << "*.png";
    dir.setPath(capturesPath);
    if (!dir.exists()){
        qDebug()<<"captures目录不存在";
    }
    dir.setNameFilters(filters);
    QDirIterator iter(dir,QDirIterator::Subdirectories);
    while (iter.hasNext()){
        iter.next();
        QFileInfo info=iter.fileInfo();
        if (info.isFile()){
            //return info.absoluteFilePath().replace('/', '\\');
            QString temp = info.absoluteFilePath();
            if(temp.contains(startDate)&&temp.contains(endDate)){
                capturesOfPng.enqueue(temp);
            }
        }
    }

    //将CAD转JPG
    ui->axWidget->dynamicCall("SaveJpgFile(QString, int, int, int)",locationPath,-1,-1,0);

    QString quackTime;
    QString kind;
    double xData;
    double yData;
    double zData;
    double quackGrade;
    double Parrival;
    QString panfu;
    double nengliang;
    QString wenjianming;

    QString filename = QFileDialog::getSaveFileName(this,"Save File",startDate+"-"+endDate,"*.doc");
    QString html;

    //如果查询日期为一天那么标题中只写当天日期,否则标题中写起始和结束日期
    if(startDate.compare(endDate)==0){
        html +="<h3 align=\"center\"><font face=\"宋体\" > ";
        html += "XXX矿"+startDate;
        html += "矿震数据初步分析</font></h3>";
    }else{
        html += "<h3 align=\"center\"><font face=\"宋体\" >";
        html += "XXX矿"+startDate+"到"+endDate;
        html += "矿震数据初步分析</font></h3> ";
    }

    QFile outFile(filename);
    if(!outFile.open(QIODevice::WriteOnly | QIODevice::Append )){
        return ;
    }

    QTextStream ts(&outFile);

    //先把大标题和第一个子标题写上,然后将html置为空
    html += "<h5 align=\"left\"><font face=\"宋体\" >1.基本参数:</font></h5> ";
    ts<<html<<endl;
    html = "";

    //quackTime为主键
    QList<QString> paramKeys = mapParameter.keys();
    QString compareFile ="";
    int i = 0;                                   //表示事件计数
    for(QString singleKey:paramKeys){
        qDebug()<<"singleKey:"<<singleKey;
        QList<QList<QVariant>> valueList = mapParameter.values(singleKey);
        // qDebug()<<"valueList.size():"<<valueList.size();
        for(QList<QVariant> temp:valueList){
            quackTime = temp.at(0).toString();
            kind = temp.at(1).toString();
            xData = temp.at(2).toDouble();
            yData = temp.at(3).toDouble();
            zData = temp.at(4).toDouble();
            quackGrade = temp.at(5).toDouble();
            Parrival = temp.at(6).toDouble();
            panfu = temp.at(7).toString();
            nengliang = temp.at(8).toDouble();
            wenjianming = temp.at(9).toString();

            //如果查询查到多个事件,那么将事件写为事件1:quackTime、事件2:quackTime...这样的形式
            //wenjiangming不同就认为是一次事件
            if(compareFile != wenjianming){
                compareFile = wenjianming;
                html += "<p style=\"font-size:10;\"><b>事件";
                html += QString::number(i+1)+":"+quackTime;
                html += " 分析结果如下:";
                html += "</b></p>";
                i++;
            }

            html += "<p style=\"font-size:10;\">" ;
            html +=  "类型: ";
            html +=  kind;
            html +=  "</p>";

            html += "<p style=\"font-size:10;\">" ;
            html +=  "最早到时: ";

            //xxxxx/xxxxxx 2020-06-18 04-19-20`47.csv
            //到时为路径后边的日期
            QStringList wenjianmingDate = wenjianming.split(" ");

            QChar ch;
            for(int i=0;i<panfu.size();i++){
                ch = panfu.at(i);
                if(ch == 'r'){
                    //判断最早到时是哪个台站,因为panfu是按照到时顺序来的,所以索引为0的为最早到时
                    if(0 == i){
                        html +=  wenjianmingDate.at(1)+wenjianmingDate.at(2).split(".").at(0);
                        html +=  "</p>";
                    }
//                    html += "<p style=\"font-size:10;\">  " ;
//                    html +=  R+"R: s";
//                    html +=  "</p>";
                }
                if(ch == 's'){
                    if(0 == i){
                        html +=  wenjianmingDate.at(1)+wenjianmingDate.at(2).split(".").at(0);
                        html +=  "</p>";
                    }
//                    html += "<p style=\"font-size:10;\">  " ;
//                    html +=  S+"S: s";
//                    html +=  "</p>";
                }
                if(ch == 't'){
                    if(0 == i){
                        html +=  wenjianmingDate.at(1)+wenjianmingDate.at(2).split(".").at(0);
                        html +=  "</p>";
                    }
//                    html += "<p style=\"font-size:10;\">  " ;
//                    html +=  T+"T: s";
//                    html +=  "</p>";
                }
                if(ch == 'u'){
                    if(0 == i){
                        html +=  wenjianmingDate.at(1)+wenjianmingDate.at(2).split(".").at(0);
                        html +=  "</p>";
                    }
//                    html += "<p style=\"font-size:10;\">  " ;
//                    html +=  U+"U: s";
//                    html +=  "</p>";
                }
                if(ch == 'v'){
                    if(0 == i){
                        html +=  wenjianmingDate.at(1)+wenjianmingDate.at(2).split(".").at(0);
                        html +=  "</p>";
                    }
//                    html += "<p style=\"font-size:10;\">  " ;
//                    html +=  V+"V: s";
//                    html +=  "</p>";
                }
                if(ch == 'w'){
                    if(0 == i){
                        html +=  wenjianmingDate.at(1)+wenjianmingDate.at(2).split(".").at(0);
                        html +=  "</p>";
                    }
//                    html += "<p style=\"font-size:10;\">  " ;
//                    html +=  W+"W: s";
//                    html +=  "</p>";
                }
                if(ch == 'x'){
                    if(0 == i){
                        html +=  wenjianmingDate.at(1)+wenjianmingDate.at(2).split(".").at(0);
                        html +=  "</p>";
                    }
//                    html += "<p style=\"font-size:10;\">  " ;
//                    html +=  X+"X: s";
//                    html +=  "</p>";
                }
                if(ch == 'y'){
                    if(0 == i){
                        html +=  wenjianmingDate.at(1)+wenjianmingDate.at(2).split(".").at(0);
                        html +=  "</p>";
                    }
//                    html += "<p style=\"font-size:10;\">  " ;
//                    html +=  Y+"Y: s";
//                    html +=  "</p>";
                }
                if(ch == 'z'){
                    if(0 == i){
                        html +=  wenjianmingDate.at(1)+wenjianmingDate.at(2).split(".").at(0);
                        html +=  "</p>";
                    }
//                    html += "<p style=\"font-size:10;\">  " ;
//                    html +=  Z+"Z: s";
//                    html +=  "</p>";
                }

            }

            html += "<p style=\"font-size:10;color:#FF0000\">  定位坐标:x :" ;
            html += QString::number(xData,'g',13)+",y:"+QString::number(yData,'g',13);
            html +=  "</p>";
            html += "<p style=\"font-size:10;color:#FF0000\">  震源深度估计:z :" ;
            html +=  QString::number(zData,'g',13);
            html +=  "</p>";
            html += "<p style=\"font-size:10;\">  P波到时:" ;
            html +=  QString::number(Parrival,'g',13);
            html +=  "</p>";
            html += "<p style=\"font-size:10;color:#FF0000\">  震级:" ;
            html +=  QString::number(quackGrade);
            html +=  "</p>";
            html += "<p style=\"font-size:10;color:#FF0000\">  能量:" ;
            html +=  QString::number(nengliang);
            html +=  "</p>";
            html += "<p style=\"font-size:10;\">  发震时刻:" ;
            html +=  quackTime;
            html +=  "</p>";

            //每个事件的基本参数写完之后再将html置为空
            ts<<html<<endl;
            html = "";
        }

    }
    //如果每个事件的参数都写完之后,只需要在后边加一个定位图
    html += "<h5 align=\"left\">2.定位图:</h5> ";
    html += "<img align=\"middle\" src = \"" + locationPath + "\"  width=\"500\" height=\"500\" /><br>" ;

    //定位图后面加上波形图
    html += "<h5 align=\"left\">3、波形图:</h5> ";
    while(!capturesOfPng.isEmpty()){
        html += "<img align=\"middle\" src = \"" + capturesOfPng.dequeue() + "\"  width=\"500\" height=\"500\" /><br>" ;
    }

    ts<<html<<endl;
    html = "";
    outFile.close();
    QMessageBox::warning(this,tr("完成"),tr("文件已经保存"),QMessageBox::Yes);
}
刚才提到了使用该方式有一些问题,主要表现在:
1、因为是使用的html的格式生成的doc,所以用wps打开默认显示的是web板式,需要在视图中切换成页面板式,当然这并不影响内容的显示。
2、因为使用html插入图片的时候使用的是路径(不管是相对路径还是绝对路径),当环境变化的时候,比如将你生成的这个doc文档发给别人,别人打开的时候显示的只是一串html字符,并不会显示图片,所以针对这个问题个人觉得最好的方式就是打开生成的doc,copy一下里面的内容再新建一个文件copy过去就好了,但这一步增加了人工操作,后期可以调用word的一些命令让他自己copy。
3、当屏幕分辨率发生变化或者程序运行环境发生变化,生的doc文档中的字体及大小可能会不一样
3、结果展示

在这里插入图片描述

下面生成的doc是在另一台电脑生成的,因为有些数据没有...

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

我做的整个东西项目好像也不用了… 都得搞到java环境里,所以交给其他兄弟了…

在这里插入图片描述

水平有限,经验不足,错误之处,请多指点
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Not found

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值