一、需求分析:
根据项目给定的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是在另一台电脑生成的,因为有些数据没有...