Qt WORD/PDF(三)使用 QAxObject 对 Word 替换(QML)
关于QT Widget 其它文章请点击这里: QT Widget
GitHub 源码: QWidgetLearningPro (暂未更新)
姊妹篇:
Qt WORD/PDF(一)使用 QtPdfium库实现 PDF 操作
Qt WORD/PDF(二)使用 QtPdfium库实现 PDF 预览、打印等
Qt WORD/PDF(三)使用 QAxObject 对 Word 替换(QML)
Qt WORD/PDF(四)使用 QAxObject 对 Word 替换(QWidget)
一、QAxObject 简介
QAxObject 是 Qt 提供的一个类,它用于与 COM(Component Object Model)对象进行交互。COM 是一种微软的技术,广泛用于各种应用程序之间的通信,尤其在 Windows 平台上,很多软件和系统组件都是基于 COM 构建的。QAxObject 类提供了一个 Qt 风格的接口,简化了与这些 COM 对象的交互。
本文主要使用 QAxObject 操作 word 文档,使用键值对,对模板文件进行替换操作,导出相应的文档,特别适合输出报告。
本文采用 QML + C++ 分离的方式:
QML 界面:用户通过 QML 界面输入键值对,选择模板文件并选择保存路径。
C++ 后端:当用户确认选择后,C++ 后端通过 QAxObject 与 Word 应用进行交互,打开模板文件并进行占位符替换,生成的新 Word 文档被保存到用户选择的位置。
环境:
QT5.15.2 + MSVC2019 + QML
二、演示
基本流程:
输入替换键值对——>选择模板文件——>选择输出文件夹——>开始替换并导出;
三、代码与分析
完整代码
OfficeExporter.cpp:
#include "OfficeExporter.h"
#include <QFile>
#include <QTextStream>
#include <QAxObject>
#include <QDebug>OfficeExporter::OfficeExporter(QObject *parent): QObject(parent) {}bool OfficeExporter::exportWord(const QString &filePath, const QString &content) {// 使用 Qt-Office 或 QTextDocument API 生成文档QFile file(filePath);if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {return false;}QTextStream stream(&file);stream << content;file.close();return true;
}//测试:创造和保存
bool OfficeExporter::createAndSaveWordDocument(const QString &filePath, const QString &content)
{// 创建 Word 应用程序对象QAxObject *wordApp = new QAxObject("Word.Application");if (!wordApp->isNull()) {wordApp->setProperty("Visible", false); // 隐藏 Word 窗口// 创建一个新的文档QAxObject *documents = wordApp->querySubObject("Documents");QAxObject *document = documents->querySubObject("Add()");// 获取文档中的选择区域(光标位置)QAxObject *selection = wordApp->querySubObject("Selection");// 插入内容selection->dynamicCall("TypeText(const QString&)", content);selection->dynamicCall("TypeParagraph()"); // 换行// 保存文件document->dynamicCall("SaveAs(const QString&)", filePath);// 关闭文档document->dynamicCall("Close()");wordApp->dynamicCall("Quit()");delete wordApp;return true;}delete wordApp;return false;
}//测试:替换
bool OfficeExporter::createFromTemplate(const QString &templatePath, const QString &outputPath, const QVariantMap &data) //const QMap<QString, QString> &placeholder)
{qDebug() << "Received data:" << data;// 转换 QVariantMap 为 QMap<QString, QString>QMap<QString, QString> placeholders;for (auto it = data.begin(); it != data.end(); ++it) {placeholders[it.key()] = it.value().toString();}qDebug() << "Template Path:" << templatePath;qDebug() << "Output Path:" << outputPath;// 检查文件路径if (!QFile::exists(templatePath)) {qDebug() << "Template file does not exist:" << templatePath;return false;}qDebug() << "QFile::exists ok" ;// 创建 Word 应用程序对象QAxObject *wordApp = new QAxObject("Word.Application");if (wordApp->isNull()) {qDebug() << "Failed to initialize Word.Application.";delete wordApp;return false;}qDebug() << "wordApp ok" ;wordApp->setProperty("Visible", false); // 隐藏 Word 窗口// 打开模板文件QAxObject *documents = wordApp->querySubObject("Documents");QAxObject *document = documents->querySubObject("Open(const QString&)", templatePath);// 查找占位符并替换//使用 Find.Execute 查找占位符,使用 TypeText 方法替换为新内容QAxObject *selection = wordApp->querySubObject("Selection");qDebug() << "selection ok" ;// 获取 Find 对象QAxObject *find = selection->querySubObject("Find");qDebug() << "start placeholde";// 遍历占位符键值对, 替换未成功,则有问题for(auto it = placeholders.begin(); it != placeholders.end(); ++it) {QString placeholder = it.key();QString newContent = it.value();// 重置光标到文档开头
// selection->dynamicCall("HomeKey(Unit:=6)"); // Move to the start of the document//适应于多目标的查找,全部替换bool isFound = true;//可替换多个,且重复的while (isFound) {// 查找目标文本并替换
// isFound = find->dynamicCall("Execute(const QString&)", placeholder).toBool();isFound = find->dynamicCall("Execute(QString, bool, bool, bool, bool, bool, bool, int)",placeholder, // 要查找的字符串false, // 区分大小写false, // 完整单词false, // 使用通配符false, // 忽略标点符号false, // 忽略空格true, // 向前查找1).toBool(); // 查找范围:整个文档if (isFound) {// 替换文本selection->dynamicCall("TypeText(const QString&)", newContent);}}}qDebug() << "All Find operation succeed!";document->dynamicCall("SaveAs(const QString&)", outputPath);// 关闭文档document->dynamicCall("Close()");wordApp->dynamicCall("Quit()");delete wordApp;return true;
}
OfficeExporter.h:
#ifndef OFFICEEXPORTER_H
#define OFFICEEXPORTER_H#include <QObject>
#include <QString>class OfficeExporter : public QObject {Q_OBJECT
public:explicit OfficeExporter(QObject *parent = nullptr);Q_INVOKABLE bool exportWord(const QString &filePath, const QString &content);// 创建并保存 Word 文档Q_INVOKABLE bool createAndSaveWordDocument(const QString &filePath, const QString &content);Q_INVOKABLE bool createFromTemplate(const QString &templatePath, const QString &outputPath, const QVariantMap &data);bool replaceMultiple(const QString &templatePath, const QString &outputPath, const QMap<QString, QString> &placeholders);};
#endif // OFFICEEXPORTER_H
pro 中需要增加对 QAxObject 的支持
QT += core gui axcontainer
简要分析
这段代码展示了如何使用 Qt 和 QAxObject
类与 Word 进行交互,主要分为三个功能:导出 Word 文档、创建并保存 Word 文档、以及从模板生成并替换占位符内容。
1. exportWord
函数
该函数将内容保存为纯文本文件。它并不直接涉及 Word 的操作,而是将内容以纯文本形式写入文件。它通过 QFile
和 QTextStream
写入文本内容。
bool OfficeExporter::exportWord(const QString &filePath, const QString &content) {QFile file(filePath);if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {return false;}QTextStream stream(&file);stream << content;file.close();return true;
}
2. createAndSaveWordDocument
函数
此函数使用 QAxObject
创建一个新的 Word 文档,插入给定内容,并保存为指定路径。通过 QAxObject
创建 Word 应用程序并与之交互:
Word.Application
通过QAxObject
实现。- 插入文本通过
Selection
对象的TypeText
方法进行。 - 保存文件通过
SaveAs
方法,关闭文档并退出 Word。
bool OfficeExporter::createAndSaveWordDocument(const QString &filePath, const QString &content) {QAxObject *wordApp = new QAxObject("Word.Application");if (!wordApp->isNull()) {wordApp->setProperty("Visible", false); // 隐藏 Word 窗口QAxObject *documents = wordApp->querySubObject("Documents");QAxObject *document = documents->querySubObject("Add()");QAxObject *selection = wordApp->querySubObject("Selection");selection->dynamicCall("TypeText(const QString&)", content);selection->dynamicCall("TypeParagraph()");document->dynamicCall("SaveAs(const QString&)", filePath);document->dynamicCall("Close()");wordApp->dynamicCall("Quit()");delete wordApp;return true;}delete wordApp;return false;
}
3. createFromTemplate
函数
此函数通过加载一个现有的 Word 模板文件,并在文档中查找并替换占位符。主要步骤包括:
- 通过
Find
对象查找占位符。 - 使用
TypeText
方法替换占位符为给定数据。 - 使用
Execute
方法执行查找,并通过for
循环迭代器确保替换所有出现的占位符。
bool OfficeExporter::createFromTemplate(const QString &templatePath, const QString &outputPath, const QVariantMap &data) {// Convert QVariantMap to QMap<QString, QString>QMap<QString, QString> placeholders;for (auto it = data.begin(); it != data.end(); ++it) {placeholders[it.key()] = it.value().toString();}// Open template fileQAxObject *wordApp = new QAxObject("Word.Application");if (wordApp->isNull()) {delete wordApp;return false;}wordApp->setProperty("Visible", false);QAxObject *documents = wordApp->querySubObject("Documents");QAxObject *document = documents->querySubObject("Open(const QString&)", templatePath);QAxObject *selection = wordApp->querySubObject("Selection");QAxObject *find = selection->querySubObject("Find");for (auto it = placeholders.begin(); it != placeholders.end(); ++it) {QString placeholder = it.key();QString newContent = it.value();bool isFound = true;while (isFound) {isFound = find->dynamicCall("Execute(QString, bool, bool, bool, bool, bool, bool, int)",placeholder, false, false, false, false, false, true, 1).toBool();if (isFound) {selection->dynamicCall("TypeText(const QString&)", newContent);}}}document->dynamicCall("SaveAs(const QString&)", outputPath);document->dynamicCall("Close()");wordApp->dynamicCall("Quit()");delete wordApp;return true;
}
流程:
- 创建 Word 应用程序:每个函数中都通过
QAxObject("Word.Application")
来创建 Word 应用程序的 COM 对象,随后可以通过该对象访问文档、光标(Selection
)等。 - 插入和替换文本:
- 插入文本是通过
TypeText
方法来实现的。这个方法会在光标位置插入指定的文本。 - 在
createFromTemplate
函数中,使用Find
对象查找占位符,替换文本的过程是循环执行的,直到文档中的所有占位符都被替换为新内容。
- 插入文本是通过
- 操作文档:
SaveAs
方法用于保存文档,Close
方法用于关闭文档,Quit
方法退出 Word 应用程序。Word.Application
的Visible
属性设置为false
使得 Word 在后台运行,用户看不到界面。
main.qml:
import QtQuick.Window 2.12
import QtQuick.Dialogs 1.3
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import com.example.OfficeExporter 1.0Window {visible: truewidth: 640height: 480title: qsTr("Word Export Demo")OfficeExporter {id: exporter}ColumnLayout {anchors.fill: parentspacing: 10anchors.margins: 10// 表单标题Text {text: "请输入键值对:"font.pixelSize: 20anchors.horizontalCenter: parent.horizontalCenter}// 键值对输入区ListView {id: listViewanchors.horizontalCenter: parent.horizontalCenterLayout.fillWidth: trueLayout.fillHeight: truemodel: ListModel {ListElement { key: "[A]"; value: "柯布" }ListElement { key: "[B]"; value: "阿瑟" }ListElement { key: "[C]"; value: "杜拉" }ListElement { key: "[D]"; value: "伊姆斯" }}delegate: RowLayout {spacing: 10TextField {id: keyFieldplaceholderText: "键 (Key)"text: model.keyLayout.fillWidth: trueonTextChanged: model.key = text}TextField {id: valueFieldplaceholderText: "值 (Value)"text: model.valueLayout.fillWidth: trueonTextChanged: model.value = text}Button {text: "删除"onClicked: model.remove(index)}}}// 添加键值对按钮Button {text: "添加键值对"Layout.alignment: Qt.AlignHCenteronClicked: listView.model.append({key: "", value: ""})}// 生成文档按钮Button {text: "选择模板并导出 Word 文档"Layout.alignment: Qt.AlignHCenteronClicked: {dialog.open();}}Item {width: 1height: 1}}FileDialog {id: dialogtitle: "选择 Word 模板文件"selectExisting: truenameFilters: [ "Word 文档 (*.docx)", "All files (*)" ]onAccepted: {fileDialog.open();}}//templatePath.slice(8), 通常是为了去掉路径中的前缀部分,比如 file:///FileDialog {id: fileDialogtitle: "选择保存路径"selectExisting: falsenameFilters: [ "Word 文档 (*.docx)", "All files (*)" ]onAccepted: {let data = {};for (let i = 0; i < listView.model.count; i++) {let item = listView.model.get(i);data[item.key] = item.value;}console.log("用户输入的键值对: ", JSON.stringify(data));var templatePath = dialog.fileUrl.toString();var filePath = fileUrl.toString();if (!filePath.endsWith(".docx")) {filePath += ".docx";}if ( exporter.createFromTemplate(templatePath.slice(8), filePath.slice(8), data)) {console.log("Word 文档已导出到:" + filePath);} else {console.log("导出失败");}}}
}
这段代码结合了 Qt 的 QML 和 C++ 后端,目的是让用户通过图形界面操作生成 Word 文档。它允许用户通过键值对形式的输入替换 Word 模板中的占位符,并将替换后的内容保存为 Word 文件。
QML 界面结构
(1) OfficeExporter 实例
在 QML 中,OfficeExporter
是一个 C++ 类的 QML 组件
OfficeExporter { id: exporter}
(2) 输入区:键值对
通过 ListView
和 TextField
,用户可以输入一组键值对。model
用于管理键值对的列表,delegate
负责定义如何显示每一对键值。用户可以修改键值对的内容,并且可以删除不需要的项。
ListView {model: ListModel {ListElement { key: "[A]"; value: "柯布" }ListElement { key: "[B]"; value: "阿瑟" }ListElement { key: "[C]"; value: "杜拉" }ListElement { key: "[D]"; value: "伊姆斯" }}delegate: RowLayout {TextField {text: model.keyonTextChanged: model.key = text}TextField {text: model.valueonTextChanged: model.value = text}Button {text: "删除"onClicked: model.remove(index)}}
}
(3) 调用c++替换程序
最终通过 createFromTemplate 接口,输入键值对,调用 C++ 后端程序
if ( exporter.createFromTemplate(templatePath.slice(8), filePath.slice(8), data)) {···
}
3. 工作流程总结
- QML 界面:用户通过 QML 界面输入键值对,选择模板文件并选择保存路径。
- C++ 后端:当用户确认选择后,C++ 后端通过
QAxObject
与 Word 应用进行交互,打开模板文件并进行占位符替换。 - 生成 Word 文档:生成的新 Word 文档被保存到用户选择的位置。
注意:
- pro 工程文件中加入 : QT += axcontainer 模块
- c++中注册: qmlRegisterType(“com.example.OfficeExporter”, 1, 0, “OfficeExporter”);
- 另外在 Word 中不方便预览和打印,可结合姊妹篇的(一)和(二),输出为PDF后预览和打印会方便很多!
商务合作请加我: 19976699725
关于QGC地面站其它文章请点击这里: QT Widget