• 引言
  • 一使用ActiveQt模块
  • 二子线程中使用
  • 三准备word模板
  • 四代码
    • 插入书签位置
    • 批量插入
    • 插入表格
      • 方法一利用Range对象定位后插入表格
      • 方法二利用bookmark定位后插入表格
  • 五其他
  • 参考

引言

项目中需要生成word格式的报告文件,初探了Qt5通过word模板生成报告的方法,整理了使用时的环境配置、子线程中使用时的注意事项以及常用的操作方法,于此记录。

环境:vs2012+Qt5.2+word2016

一、使用ActiveQt模块

注意:ActiveQt只适用于windows平台下,linux和macOS版本的Qt中是没有这个库的

首先需要添加库文件,可以直接在VS2012菜单栏Qt5->Qt Project Setting->勾选Active Qt

勾选之后再次查看Qt Project Setting,发现自动勾上了Active Qt server,同时“项目属性->配置属性->链接器->输入->附加依赖性”中自动加入了Qt5AxContainerd.lib;Qt5AxBased.lib
此后便可成功include头文件

#include <QAxWidget>
#include <QAxObject>

二、子线程中使用

在使用过程中发现调用word过程比较耗时,会阻塞GUI线程,于是将保存报告操作移到子线程中.
不过在子线程中使用QAxWidget会报错ASSERT failure in QWidget: "Widgets must be created in the GUI thread."这是由于线程里面不能创建GUI对象。
解决方案是用 QAxObjec取代QAxWidget,初始化过程如下:

bool Report::Open(QString Dir)
{// 新建一个word应用程序,并设置为不可见//m_WordFile = new QAxWidget("Word.Application", 0, Qt::MSWindowsOwnDC);m_WordFile = new QAxObject();//取代QAxWidget,使其在子线程中可用bool bFlag = m_WordFile->setControl( "word.Application" );if(NULL == m_WordFile){// 尝试用wps打开bFlag = m_WordFile->setControl( "kwps.Application" );if(!bFlag){return false;}}m_WordFile->setProperty("Visible", false);// 获取所有的工作文档QAxObject *Documents = m_WordFile->querySubObject("Documents");if(NULL == Documents){return false;}// 以文件template.dot为模版新建一个文档Documents->dynamicCall("Add(QString)", Dir);// 获取当前激活的文档m_Document = m_WordFile->querySubObject("ActiveDocument");if(NULL == m_Document){return false;}m_bInit = true;return true;
}

同时由于在QApplication的主线程中,会自动初始化COM库,而新开辟的子线程不会自动初始化COM库,所以需要我们手动来初始化,方法如下:
添加头文件:

#include <windows.h>  

构造函数中初始化COM库:

Report::Report(QObject *parent): QObject(parent)
{HRESULT result = OleInitialize(0);if (result != S_OK && result != S_FALSE){qDebug()<<QString("Could not initialize OLE (error %x)").arg((unsigned int)result);}//moveToThread方法产生线程this->moveToThread(&m_thread);m_thread.start();
}

析构函数中释放:

Report::~Report()
{OleUninitialize();m_thread.quit();m_thread.wait();
}

三、准备word模板

在word文档中手动添加书签(bookmark)后保存为dot格式

四、代码

1.插入书签位置

QString outFileName = QFileDialog::getSaveFileName(this, QStringLiteral("请输入要保存的名字:"),".", "Microsoft Word 97-2003(*.doc);;Microsoft Word 2007-2013(*.docx)");if (outFileName.isEmpty()) {QMessageBox::warning(this, tr("警告"),tr("输入的文件名为空!"),QMessageBox::Ok);return ;}// 新建一个word应用程序,并设置为不可见  QAxWidget *word=new QAxWidget("Word.Application", 0, Qt::MSWindowsOwnDC);  word->setProperty("Visible", false);  // 获取所有的工作文档  QAxObject * documents = word->querySubObject("Documents");// 以文件testTemplate.dot为模版新建一个文档,注意这里的路径为绝对路径QDir dir(".");documents->dynamicCall("Add(QString)",QString("%1/testTemplate.dot").arg(dir.absolutePath()));  // 获取当前激活的文档  QAxObject *document=word->querySubObject("ActiveDocument");  // 获取文档中名字为TSName_1_1的标签  QString bookmakrName="TSName_1_1";QAxObject*bookmark_text=document->querySubObject(QString("Bookmarks(%1)").arg(bookmakrName).toLocal8Bit().data());  // 选中标签,将字符插入到标签位置  if(!bookmark_text->isNull())  {  bookmark_text->dynamicCall("Select(void)");  bookmark_text->querySubObject("Range")->setProperty("Text",QStringLiteral("测试输入"));  }  // 将文件另存为outFileName,关闭工作文档,退出应用程序  document->dynamicCall("SaveAs (const QString&)", outFileName);  document->dynamicCall("Close (boolean)", true);  //关闭文本窗口word->dynamicCall("Quit(void)");  //退出worddelete bookmark_text; delete document;  delete documents;  delete word;  

2.批量插入

QString outFileName = QFileDialog::getSaveFileName(this, QStringLiteral("请输入要保存的名字:"),".", "Microsoft Word 97-2003(*.doc);;Microsoft Word 2007-2013(*.docx)");if (outFileName.isEmpty()) {QMessageBox::warning(this, tr("警告"),tr("输入的文件名为空!"),QMessageBox::Ok);return ;}word = new QAxWidget("Word.Application", 0, Qt::MSWindowsOwnDC);word->setProperty("Visible", false);word->setProperty("DisplayAlerts", true);QAxObject *docs = word->querySubObject("Documents");if (!docs) {QMessageBox::warning(this, tr("警告"), tr("无法获得Documents对象!"),QMessageBox::Ok);return ;}QStringList items;QStringList sometexts;//items按顺序依次是“待匹配标签名”和“插入的内容”items<<"TSName_1_2"<<"TSName222"<<"TSName_1_1"<<"TSName111"<<"555";sometexts<<"111"<<"222"<<"333"<<"444"<<"555";editBookMarks(docs, sometexts, items, outFileName);word->dynamicCall("Quit(boolean)", true);delete word;
void testword::editBookMarks(QAxObject *docs, QStringList sometexts, QStringList &itemList, QString outFileName)
{QDir dir(".");docs->dynamicCall("Add(QString)", QString("%1/testTemplate.dot").arg(dir.absolutePath()));QAxObject *currentDoc = word->querySubObject("ActiveDocument");if(!currentDoc){QMessageBox::warning(this, QStringLiteral("警告"), QStringLiteral("无法获取当前打开文件对象!"),QMessageBox::Ok);return;}QAxObject *allBookmarks = currentDoc->querySubObject("Bookmarks");if (!allBookmarks) {QMessageBox::warning(this, QStringLiteral("警告"), QStringLiteral("无法获取模板中的书签,请先插入书签!"), QMessageBox::Ok);return ;}int count = allBookmarks->property("Count").toInt();/* 填写模板中的书签  */for (int i = count; i > 0; --i) {QAxObject *bookmark = allBookmarks->querySubObject("Item(QVariant)", i);QString name= bookmark->property("Name").toString();int j=0;foreach(QString itemName , itemList){if (name == itemName) {QAxObject *curBM = currentDoc->querySubObject("Bookmarks(QString)", name);curBM->querySubObject("Range")->setProperty("Text", itemList.at(j+1));break;}j++;}if (j == itemList.length()) {//如果遍历itemList,未找到匹配的书签,提示输入QString text = QInputDialog::getText(this, QStringLiteral("请输入"), QStringLiteral("%1").arg(name));bookmark->querySubObject("Range")->setProperty("Text", text);itemList.append(name);itemList.append(text);}}//依次插入sometexts中内容while(!sometexts.isEmpty()){QAxObject *currentRange = currentDoc->querySubObject("Range()");int rangeEnd = currentRange->property("End").toInt();currentRange->dynamicCall("setRange(QVariant, QVariant)", rangeEnd, rangeEnd);currentRange->dynamicCall("InsertAfter(QString)", QStringLiteral("\n%1-%3\n").arg(sometexts[0]).arg(1));sometexts.removeAt(0);}currentDoc->dynamicCall("SaveAs(QString&)", outFileName);currentDoc->dynamicCall("Close()");
}

效果如下:

3.插入表格

方法一:利用Range对象定位后插入表格

/******************************************************************************* 函数:intsertTable* 功能:创建表格* 参数:nStart 开始位置; nEnd 结束位置; row hang; column 列* 返回值: void*****************************************************************************/
void WordEngine::intsertTable(int nStart, int nEnd, int row, int column)
{   QAxObject* ptst = m_wordDocuments->querySubObject( "Range( Long, Long )",nStart, nEnd );QAxObject* pTable = m_wordDocuments->querySubObject( "Tables" );QVariantList params;params.append(ptst->asVariant());params.append(row);params.append(column);if( pTable ){pTable->dynamicCall( "Add(QAxObject*, Long ,Long )",params);}
//       QAxObject* table = selection->querySubObject("Tables(1)");
//       table->setProperty("Style", "网格型");
}

方法二:利用bookmark定位后插入表格

QAxObject *WordEngine::insertTable(QString sLabel, int row, int column)
{  QAxObject *bookmark = m_pWorkDocument->querySubObject("Bookmarks(QVariant)", sLabel);  if(bookmark)  {  bookmark->dynamicCall("Select(void)");  QAxObject *selection = m_pWord->querySubObject("Selection");  selection->dynamicCall("InsertAfter(QString&)", "\n");  //selection->dynamicCall("MoveLeft(int)", 1);  selection->querySubObject("ParagraphFormat")->dynamicCall("Alignment", "wdAlignParagraphCenter");  //selection->dynamicCall("TypeText(QString&)", "Table Test");//设置标题  QAxObject *range = selection->querySubObject("Range");  QAxObject *tables = m_pWorkDocument->querySubObject("Tables");  QAxObject *table = tables->querySubObject("Add(QVariant,int,int)",range->asVariant(),row,column);  for(int i=1;i<=6;i++)  {  QString str = QString("Borders(-%1)").arg(i);  QAxObject *borders = table->querySubObject(str.toAscii().constData());  borders->dynamicCall("SetLineStyle(int)",1);  }  return table;  }
} 

插入表格,修改列宽等是后来看到的,可参考这个github项目中的WordEngine实现

五、其他

用以下方法往bookmark插入内容:

QString bookmakrName="TSName1_1_2";//假设dot文件中并没有这个bookmarkQAxObject* bookmark_text=document->querySubObject("Bookmarks(const QString&)", bookmakrName);  if(NULL == bookmark_text)//注意这个判断不可少,否则下面调用isNull()时会出错{return;}// 选中标签,将字符插入到标签位置  if(!bookmark_text->isNull())  //如果没有匹配到对应的bookmark,直接判断会出错,所以要提前返回{  bookmark_text->dynamicCall("Select(void)");  bookmark_text->querySubObject("Range")->setProperty("Text",QStringLiteral("测试输入"));  }

如果dot文件中并没有这个bookmark,会报如下错误:

QAxBase: Error calling IDispatch member Bookmarks: Exception thrown by serverCode       : 5941Source     : Microsoft WordDescription: ????????????Help       : wdmain11.chm [25421]Connect to the exception(int,QString,QString,QString) signal to catch this exception

这时判断NULL == bookmark_text返回即可,如果要避免匹配不存在的bookmark,可以在前面处理,比如上文“2.批量插入”中所示的先利用QAxObject *allBookmarks = currentDoc->querySubObject("Bookmarks");获取所有Bookmarks,然后在进行处理。

最后附上相关源码:demo-Qt5生成Word格式报告(demo是最开始写的,未包含插入表格,多线程等方法实现,比较简单)

参考

Qt利用ActiveX生成Word文档
qt中如何使用ActiveX读写word
github-试卷自动生成系统
github-QTScada
QT在子线程中使用QAxWidget需要初始化COM的问题

Qt5生成Word格式报告相关推荐

  1. python3根据excel表数据自动生成word格式数据报告

    目录 1. 完成xlsx数据读取 2.对数据做一些处理 3.生成图表 4.创建word文件 5.将分析结果保存入word文件中 本文主要是讲如何根据简单的xlsx数据,生成word数据报告,首先需要先 ...

  2. apipost生成word格式的接口文档,接口文档合并操作

    一.Apipost导出单个接口word 1.Apipost在分享完网址链接之后,有一个导出离线文档的功能.有导出HTML.导出MarkDown和导出word格式. 2.选择导出word文档 下载的为压 ...

  3. 【技巧】ApiPost生成word格式的接口文档,接口文档合并操作

    一.Apipost导出单个接口word 1.Apipost在分享完网址链接之后,有一个导出离线文档的功能.有导出HTML.导出MarkDown和导出word格式. 2.选择导出word文档 下载的为压 ...

  4. 【DevOps】总结下容器方式构建Grafana-reporter生成PDF格式报告

    目录 Grafana-reporter 1.编写Dockerfile 2.构建镜像 3.运行Grafana-reporter 4.在Grafana配置Link 即文章:[DevOps]Promethe ...

  5. 【2.5万字】详解 Python-docx 自动生成word图文报告

    目录 推荐:[python自动化办公--python操作Excel.Word.PDF集合大全](https://blog.csdn.net/weixin_41261833/article/detail ...

  6. StarkSoft题库管理系统(二)--生成word格式试卷

    一.功能介绍     1.自定义试题库管理系统目录.难易程度,题型,知识库等.     2.试题录入.     3.强大的试题编辑功能,并与通常应用编辑工具有共通.     4.灵活的试卷构造功能,用 ...

  7. StarkSoft题库管理系统--生成word格式试卷

    一.功能介绍     1.自定义试题库管理系统目录.难易程度,题型,知识库等.     2.试题录入.     3.强大的试题编辑功能,并与通常应用编辑工具有共通.     4.灵活的试卷构造功能,用 ...

  8. 【Power Automate】如何自动生成Word与PDF文件[上]

    上半年已经悄悄溜走,因为疫情,大家似乎也很习惯于在家办公.作为业务人员,如何汇报自己的工作,让自己更多地学习和掌握数字化办公技巧至关重要.那么今天我们就来看一下在不使用代码的情况下,如何通过Power ...

  9. nmon结果分析工具_Nmon实时监控并生成HTML监控报告

    之前的文章介绍了服务端监控工具:Nmon使用方法,最近在github找到了一个nmon自动监控并生成HTML格式报告的工具:easyNmon,使用体验蛮不错的,这里介绍下它的安装及使用方法. 一.关于 ...

最新文章

  1. 获取oracle数据库war报告,Oracle 数据库开发及SQL基础实战
  2. Mac 技术篇-通过AppScript编写脚本实现设置快捷键打开指定程序实例演示
  3. Java开发语句和代码块模板
  4. 自动局部变量 与 静态局部变量 的区别与用途
  5. win8改win7 教程
  6. 归纳一下:C#线程同步的几种方法
  7. [Vue.js] 基础 -- Vue实例
  8. SQL COUNT() 语法总结及用法【原创】
  9. Cannot modify the value of a static config: spark.sql.queryExecutionListeners
  10. python可打印字符_测试一个python字符串是否可打印
  11. 如何检查linux 下是否安装java(jdk)环境
  12. MySpace:.Net架构网站的王者
  13. Windows 10下adb、fastboot工具的配置
  14. 使用Unicode字符集出现乱码问题
  15. SEO关键词产出之词频分析(关键词工具篇)
  16. firefox插件下载失败
  17. 《c语言程序设计》第一次网上作业,精编国家开放大学电大《C语言程序设计》《劳动与社会保障法》网络课形考网考作业(合集)答案...
  18. 教你怎样激励自己做好每一件事
  19. 什么是Excel的快速编号功能?
  20. 益丰大药房互联网医院,积极推动中国大健康产业发展变革

热门文章

  1. Mi_Smart_Band_6(小米手环6)心跳数值PC显示
  2. Axure仿淘宝搜索原型
  3. Unity SKFramework框架(二十五)、RSA算法加密、签名工具 RSA Crypto
  4. android使用popupwindow仿微信点赞功能
  5. 虹科案例应用激光雷达研究北卡罗来纳州沿海地区风暴影响
  6. 我的十几年技术总结(一):从疯狂到无奈
  7. postman的使用-----号码归属地查询接口测试
  8. 用于针对DNN中后门攻击的蒸馏对策和中毒数据的去除
  9. P3386 【模板】二分图最大匹配(匈牙利算法,网络流)
  10. json解析格式化工具