使用Qt的AxContainer模块操作Office文档(一)

在Windows环境下,在Qt里面操作Office文档,可以通过AxContainer模块调用Office全家桶的Com组件。

完成这个操作,需要使用到AxContainer模块中的QAxObject。

使用这种方法操作Office文件,好处是可以非常精确,精确到Word文档的每一个字符、PowerPoint的每一个图片以及Excel文档的每一行一列……那种程度。

但是麻烦的是,太精确了,就没有那么简便。当我们想实现简单地预览、统计、输出等只读功能的时候,会比较复杂。而且,雪上加霜的是,同样一个小需求,对不同类型的文档,都需要使用不同的类,不同的方法,不同的属性。

下面进入正题,先说说如何使用。

AxContainer模块

首先,确保安装了Qt的AxContainer模块。这个模块只有在Windows下的Qt开发环境才有,Linux等系统中没有,当然也不需要。

在qt的安装过程里,选择模块的那一页,其中有一个复现框内容是QtActive,就是我们需要的模块。确保安装qt环境的时候,选择了QtActive。

其次,需要把它的头文件包含到项目中,另外连接它的库文件。它的CMake文件并不叫QtActive,而是叫AxContainer:

FIND_PACKAGE(Qt6AxContainer REQUIRED)  
INCLUDE_DIRECTORIES(${Qt6AxContainer_INCLUDE_DIRES})

QAxObject类

把Qt6Container的头文件目录加入编译环境之后,就可以直接包含QtAxContainer这个头文件,使用QtAxContainer中的类QAxObject了。

#include <QtAxContainer>

QAxObject最常用的构造函数是:

QAxObject(const QString &c, QObject *parent = nullptr);

其中,c指的是Com的名称。比如,要操作Word文档,就传入Word.Application。

如:

auto application = new QAxObject("Word.Application");

QAxObject继承自QAxBase,QAxBase的方法主要有dynamicCall与querySubObject。

dynamicCall用于执行方法调用,它的原型如下:

QVariant    dynamicCall(const char *function, const QVariant &var1 = QVariant(), const QVariant &var2 = QVariant(), const QVariant &var3 = QVariant(), const QVariant &var4 = QVariant(), const QVariant &var5 = QVariant(), const QVariant &var6 = QVariant(), const QVariant &var7 = QVariant(), const QVariant &var8 = QVariant())

QVariant 	dynamicCall(const char *function, QList<QVariant> &vars)

querySubObject用于返回对象的子对象,它的原型如下:


QAxObject *	querySubObject(const char *name, const QVariant &var1 = QVariant(), const QVariant &var2 = QVariant(), const QVariant &var3 = QVariant(), const QVariant &var4 = QVariant(), const QVariant &var5 = QVariant(), const QVariant &var6 = QVariant(), const QVariant &var7 = QVariant(), const QVariant &var8 = QVariant())

QAxObject *	querySubObject(const char *name, QList<QVariant> &vars)

这两个方法的第一个参数,都是字符串,dynamicCall需要写需要执行的VBA函数原型,而querySubObject需要写子对象名字。

如:

selection->dynamicCall ("GoTo(int, int, int, const QVariant&)", 1, 1, 1,  page->property ("Start"));

调用了Office VBA的Goto方法。

Office VBA中,Word文档的Selection对象的Goto方法原型为:

Syntax

expression. GoTo( _What_ , _Which_ , _Count_ , _Name_ )

expression Required. A variable that represents a Selection object.

可以对照着上面,体会QAxObject到Office VBA的关系。

基本上,掌握了QAxObject的调用方法,再结合Office VBA的参考手册就可以随心所欲地做很多事情了。

下面尝试一下操作Word文档。

操作Word文档

操作Word文档,需要使用"Word.Application"这个Com组件,使用QAxObject构造这个组件之后,使用组件之中的Documents对象来打开Word文档对象Document。

所以,从开头到能够操作一个具体的Word文档,需要经过三个“东西”:Word.Application、Documents、Document。

而使用Documents打开Word文档可以任选两个API其中之一,分别是Open和OpenNoRepairDialog:

expression.Open (FileName, ConfirmConversions, ReadOnly, AddToRecentFiles, PasswordDocument, PasswordTemplate, Revert, WritePasswordDocument, WritePasswordTemplate, Format, Encoding, Visible, OpenConflictDocument, OpenAndRepair, DocumentDirection, NoEncodingDialog)

expression.OpenNoRepairDialog (FileName, ConfirmConversions, ReadOnly, AddToRecentFiles, PasswordDocument, PasswordTemplate, Revert, WritePasswordDocument, WritePasswordTemplate, Format, Encoding, Visible, OpenAndRepair, DocumentDirection, NoEncodingDialog, XMLTransform)

使用Open的话,如果文档没有正常关闭,而且OpenAndRepai参数又没有置为假的话,会弹出来一个是否修复的对话框,使用OpenNoRepairDialog就可以简单地避免这个困扰。

但是,PowerPoint与Excel的Com里却并没有这个函数,只能使用复杂的Open函数,而且传入的参数还不一样。

Document对象

把上面的内容合在一起,实现一个打开一个Word文档的函数:

// 为演示方便,下述代码没有进行错误处理以及资源清理
QVariant  
loadWord (const QString &filename)  
{  
  auto application = new QAxObject ("Word.Application");  
  application->setProperty ("Visible", false);  
  auto documents = application->querySubObject ("Documents");  
  auto doc = documents->querySubObject (  
      "OpenNoRepairDialog(const QString&, bool, bool, bool)", qname, false,  
      true, false);  
  return doc;  
}

取得Range对象

Range是Document的子对象,有多种方法构造,通过Document的Content方法是其中之一。

QVariant
wordContent (QVariant doc)  
{  
  auto range = doc->querySubObject ("Content");  
  return range;
}

Range对象有很多方法,我们先使用Information方法,查询一下这个Word文档有多少页:

int  
wordPageCount (QVariant doc)  
{  
  auto range = doc->querySubObject ("Content");  
  auto pages = range->dynamicCall ("Information(wdNumberOfPagesInDocument)");  
  return pages.toInt ();
}

下面,我们把上面的东西再组合起来,把Word文档的其中一页复制到剪贴板里。

// 注意,还是没有资源清理等操作 !!!
bool
wordPageCopy (QVariant doc, int page_num)
{
  // 跳转到第page_num页。
  auto pageRange = doc->querySubObject ("GoTo(int, int, int, const QVariant&)",
                                        1, 1, page_num);
  // 跳转到第page_num的下一页,以便找到page_num的结尾。
  auto endRange = mDoc->querySubObject ("GoTo(int, int, int, const QVariant&)",
                                        1, 1, page_num + 1);
  
  int endPosition;
  // 如果page_num的下一页没有,表示文档到了末尾,使用Document的Content的结尾
  if (endRange->property ("Start").toInt () == 1)
    {
      auto content = doc->querySubObject ("Content");
      endPosition = content->property ("End").toInt ();
    }
  else
    {
      endPosition = endRange->property ("Start").toInt () - 1;
    }

  pageRange->setProperty ("End", endPosition);

  // 执行复制方法
  pageRange->dynamicCall ("Copy()");

  // 还可以执行CopyAsPicture方法,这个方法可以把当前的页面保存成图片
  // pageRange->dynamicCall ("CopyAsPicture()");
}

之后,剪贴板里面就有了这篇Word文档的内容,可以在支持粘贴的应用中粘贴了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值