当前位置: 首页 > news >正文

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 的操作,而是将内容以纯文本形式写入文件。它通过 QFileQTextStream 写入文本内容。

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;
}

流程:

  1. 创建 Word 应用程序:每个函数中都通过 QAxObject("Word.Application") 来创建 Word 应用程序的 COM 对象,随后可以通过该对象访问文档、光标(Selection)等。
  2. 插入和替换文本
    • 插入文本是通过 TypeText 方法来实现的。这个方法会在光标位置插入指定的文本。
    • createFromTemplate 函数中,使用 Find 对象查找占位符,替换文本的过程是循环执行的,直到文档中的所有占位符都被替换为新内容。
  3. 操作文档
    • SaveAs 方法用于保存文档,Close 方法用于关闭文档,Quit 方法退出 Word 应用程序。
    • Word.ApplicationVisible 属性设置为 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 的 QMLC++ 后端,目的是让用户通过图形界面操作生成 Word 文档。它允许用户通过键值对形式的输入替换 Word 模板中的占位符,并将替换后的内容保存为 Word 文件。

QML 界面结构

(1) OfficeExporter 实例

在 QML 中,OfficeExporter 是一个 C++ 类的 QML 组件

OfficeExporter {    id: exporter}

(2) 输入区:键值对

通过 ListViewTextField,用户可以输入一组键值对。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. 工作流程总结

  1. QML 界面:用户通过 QML 界面输入键值对,选择模板文件并选择保存路径。
  2. C++ 后端:当用户确认选择后,C++ 后端通过 QAxObject 与 Word 应用进行交互,打开模板文件并进行占位符替换。
  3. 生成 Word 文档:生成的新 Word 文档被保存到用户选择的位置。

注意:

  1. pro 工程文件中加入 : QT += axcontainer 模块
  2. c++中注册: qmlRegisterType(“com.example.OfficeExporter”, 1, 0, “OfficeExporter”);
  3. 另外在 Word 中不方便预览和打印,可结合姊妹篇的(一)和(二),输出为PDF后预览和打印会方便很多!

商务合作请加我: 19976699725

关于QGC地面站其它文章请点击这里:     QT Widget


http://www.mrgr.cn/news/80415.html

相关文章:

  • 智慧政务数据中台建设及运营解决方案
  • C# 探险之旅:第三十八节 - 类型class之Base与This关键字大冒险
  • Python(动态语言)和C++(静态语言)运行时和编译时比较:中英双语
  • 编写php项目所需环境
  • 微信小程序横屏页面跳转后,自定义navbar样式跑了?
  • 【机器学习】手写数字识别的最优解:CNN+Softmax、Sigmoid与SVM的对比实战
  • 【系统分析师】-收官整理-已考过
  • Day13洛谷 2043+2042+2040+2035+2034+2033+2030+2027+2031+2029
  • selenium工作原理
  • Python 参数配置使用 XML 文件的教程 || Python打包 || 模型部署
  • 规则引擎drools(一)-技术要点
  • 【软件工程】简答题系列(一)(山东大学·软院考试专属)
  • 【爬虫一】python爬虫基础合集一
  • ubuntu下anconda装pytorch
  • 业务观测:从定义到场景化分析
  • Linux栈帧
  • DALL·E 2(内含扩散模型介绍)-生成式模型【学习笔记】
  • elasticsearch 使用enrich processor填充数据
  • es中段是怎么合并的
  • java中的List、数组和set
  • 电脑显示器选购指南2024
  • 如何在繁忙的生活中找到自己的节奏?
  • M3DM的autodl环境构建过程笔记
  • 【开源】使用环信UIKit for uniapp 做一个IM即时聊天应用
  • monorepo代码管理框架
  • Linux NVIDIA GPU linpack 测试