Qt5生成Word格式报告

news/2024/12/1 20:36:58/

  • 引言
  • 一使用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的问题


http://www.ppmy.cn/news/496792.html

相关文章

第六十回: 如何创建垂直版Slider

文章目录 概念介绍创建方法RotatedBoxTransform 示例代码 我们在上一章回中介绍了 Slider Widget相关的内容&#xff0c;本章回中将介绍如何创建垂直版Slider.闲话休提&#xff0c;让我们一起Talk Flutter吧。 概念介绍 正常的Slider组件是沿着水平方向滑动&#xff0c;或者叫…

Windows下Mathtype7安装及其在Word中的加载方法

最近购置了新电脑&#xff0c;激活Office后需对其进行安装Mathtype7&#xff0c;在配置过程中遇到许多问题&#xff0c;并借鉴了CSDN中的许多解决方案&#xff0c;现对其进行整理归纳&#xff0c;以避免大家后续踩坑。 ps&#xff1a;由于后续涉及到Mathtype的**&#xff0c;后…

MathType如何导入word

目标&#xff1a;分别安装好office 和 Mathtype。Mathtype激活不激活对这个没有影响。如何将MathType 成 功以及如何导入word 中。 出现失败的原因&#xff1a; 1.版本兼容性问题 我用的是win10 office2016 mathtype 7 一般问题不大 2.确定受信任位置 &#xff08;这个很重要…

Word2Vec解释

Word2Vec解释 一、Word2Vec梗概 字面意思&#xff1a;即Word to Vector&#xff0c;由词到向量的方法。 专业解释&#xff1a;Word2Vec使用一层神经网络将one-hot&#xff08;独热编码&#xff09;形式的词向量映射到分布式形式的词向量。使用了Hierarchical softmax&#x…

编程实现操作word文档 c#

建立一个自己的类 //word 类, /* 1. 添加引用COM里面的 Microsoft Word 12.0 Object. Library 引用(12.0表示Word 2007版本) 2. 导命名空间 using Word =Microsoft.Office.Interop.Word; using System.IO; using System.Reflection; 3. 把引用中的Microsoft.Office.…

VBA word 常用代码及注释

(1) Option Explicit ‘强制对模块内所有变量进行声明 (2) Option Base 1 ‘指定数组的第一个下标为1 (3) On Error Resume Next ‘忽略错误继续执行VBA代码,避免出现错误消息 (4) On Error GoTo 100 ‘当错误发生时跳转到过程中的某个位置 (5) On Error GoTo 0 ‘恢复正常…

word 安装

亲测可用&#xff0c;记录一下 &#x1f606;安装word步骤&#x1f606; office tool plus ❤️1. 下载安装&#xff1a;&#x1f9e1;2. 解压&#x1f49b;3. 移除&#x1f49a;4. 安装部署&#x1f499;5. 激活&#x1f90d; 总结 ❤️1. 下载安装&#xff1a; 官网下载offic…

PCB设计实验|第二周|谐波振荡电路实验|3月6日

目录 实验二 谐波振荡电路实验 一、实验原理 二、实验环境 三、实验结果及分析 四、实验总结 实验二 谐波振荡电路实验 一、实验原理 利用深度正反馈&#xff0c;通过阻容耦合使两个电子器件交替导通与截止&#xff0c;从而自激产生方波输出的振荡器&#xff0c;常用作…