Qt WORD/PDF(四)使用 QAxObject 对 Word 替换(QWidget)


关于QT Widget 其它文章请点击这里: QT Widget

国际站点 GitHub: https://github.com/chenchuhan
国内站点 Gitee : https://gitee.com/chuck_chee

姊妹篇:
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 文档,使用键值对,对模板文件进行替换操作,导出相应的文档,特别适合输出报告。

本文采用 Qt Widget 纯代码的方式

环境:

QT5.15.2 + MSVC2019 + Widget

二、演示

实现功能:

  • 用户可以选择一个模板文件,并进行占位符的批量替换。
  • 用户可以设置替换后文档的保存路径。
  • 支持通过界面交互实现选择文件、显示信息以及执行替换操作。
  • 使用 QAxObject 实现了与 Word 的 COM 接口的交互,允许直接操作 Word 文档中的内容。

三、代码

完整代码

mainwindow.cpp:

cpp 复制代码
#include "mainwindow.h"
// #include "ui_mainwindow.h"

#include <QFileDialog>
#include <QDebug>
#include <QMessageBox>


MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    // , ui(new Ui::MainWindow)
{
    // ui->setupUi(this);
    // 初始化 UI
    auto *centralWidget = new QWidget(this);
    auto *mainLayout = new QVBoxLayout;

    mainLayout->setContentsMargins(10, 10, 10, 10); // 设置布局边距
    mainLayout->setSpacing(10); // 设置控件之间的间距
    // setMinimumSize(600, 520); // 设置窗口最小宽度为600,高度为400

    // 模板文件选择
    auto *templateLayout = new QHBoxLayout;
    auto *templateLabel = new QLabel("打开模板:", this);
    templatePathEdit = new QLineEdit(this);
    auto *browseTemplateButton = new QPushButton("浏览", this);
    templateLayout->addWidget(templateLabel);
    templateLayout->addWidget(templatePathEdit);
    templateLayout->addWidget(browseTemplateButton);
    connect(browseTemplateButton, &QPushButton::clicked, this, &MainWindow::browseTemplateFile);

    // 输出文件选择
    auto *outputLayout = new QHBoxLayout;
    auto *outputLabel = new QLabel("输出路径:", this);
    outputPathEdit = new QLineEdit(this);
    auto *browseOutputButton = new QPushButton("浏览", this);
    outputLayout->addWidget(outputLabel);
    outputLayout->addWidget(outputPathEdit);
    outputLayout->addWidget(browseOutputButton);
    connect(browseOutputButton, &QPushButton::clicked, this, &MainWindow::browseOutputFile);

    // 键值对表格
    auto *placeholdersLabel = new QLabel("键值对替换:", this);
    placeholdersTable = new QTableWidget(this);
    placeholdersTable->setColumnCount(2);
    placeholdersTable->setHorizontalHeaderLabels({"占位符", "替换值"});
    placeholdersTable->setRowCount(5); // 默认三行

    // 设置默认值
    placeholdersTable->setItem(0, 0, new QTableWidgetItem("[A]"));
    placeholdersTable->setItem(0, 1, new QTableWidgetItem("柯布"));

    placeholdersTable->setItem(1, 0, new QTableWidgetItem("[B]"));
    placeholdersTable->setItem(1, 1, new QTableWidgetItem("阿瑟"));

    placeholdersTable->setItem(2, 0, new QTableWidgetItem("[C]"));
    placeholdersTable->setItem(2, 1, new QTableWidgetItem("杜拉"));

    placeholdersTable->setItem(3, 0, new QTableWidgetItem("[D]"));
    placeholdersTable->setItem(3, 1, new QTableWidgetItem("伊姆斯"));

    // 替换按钮
    replaceButton = new QPushButton("执行替换", this);
    connect(replaceButton, &QPushButton::clicked, this, &MainWindow::replaceInWord);


    // 布局整合
    mainLayout->addLayout(templateLayout);
    mainLayout->addLayout(outputLayout);
    mainLayout->addWidget(placeholdersLabel);
    mainLayout->addWidget(placeholdersTable);
    mainLayout->addWidget(replaceButton);

    centralWidget->setLayout(mainLayout);
    setCentralWidget(centralWidget);
    setWindowTitle("Word 替换工具");

    resize(1000, 600); // 初始窗口大小
}

MainWindow::~MainWindow()
{
    // delete ui;
}

void MainWindow::browseTemplateFile() {
    QString filePath = QFileDialog::getOpenFileName(this, "选择模板文件", QString(), "Word 文件 (*.docx *.doc)");
    if (!filePath.isEmpty()) {
        templatePathEdit->setText(filePath);

         wordApp = new QAxObject("Word.Application");
        if (wordApp->isNull()) {
            qDebug() << "Failed to initialize Word.Application.";
            delete wordApp;
            return ;
        }

        // 隐藏 Word 窗口
        wordApp->setProperty("Visible", true);

        //打开指定文档
        QAxObject *documents = wordApp->querySubObject("Documents");
        QAxObject *document = documents->querySubObject("Open(const QString&)", filePath);
        if (document == nullptr) {
            QMessageBox::critical(this, "错误", "无法打开 Word 文件!");
            return;
        }
    }
}

void MainWindow::browseOutputFile() {
    QString filePath = QFileDialog::getSaveFileName(this, "选择输出文件", QString(), "Word 文件 (*.docx *.doc)");
    if (!filePath.isEmpty()) {
        outputPathEdit->setText(filePath);
    }
}

void MainWindow::replaceInWord() {
    QString templatePath = templatePathEdit->text();
    QString outputPath = outputPathEdit->text();

    if (templatePath.isEmpty() || outputPath.isEmpty()) {
        QMessageBox::warning(this, "错误", "请填写模板路径和输出路径!");
        return;
    }

    QMap<QString, QString> placeholders;
    for (int row = 0; row < placeholdersTable->rowCount(); ++row) {
        QString key = placeholdersTable->item(row, 0) ? placeholdersTable->item(row, 0)->text() : QString();
        QString value = placeholdersTable->item(row, 1) ? placeholdersTable->item(row, 1)->text() : QString();
        if (!key.isEmpty()) {
            placeholders.insert(key, value);
        }
    }

    if (placeholders.isEmpty()) {
        QMessageBox::warning(this, "错误", "请填写至少一个占位符和替换值!");
        return;
    }

    if (replaceMultiple(templatePath, outputPath, placeholders)) {
        QMessageBox::information(this, "成功", "替换完成!");
    } else {
        QMessageBox::critical(this, "失败", "替换失败!");
    }
}

bool MainWindow::replaceMultiple(const QString &templatePath, const QString &outputPath, const QMap<QString, QString> &placeholders) {

    qDebug() << "Received data:" << placeholders;


    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" ;
    
    // 打开模板文件
    QAxObject *documents = wordApp->querySubObject("Documents");
    QAxObject *document = documents->querySubObject("Open(const QString&)", templatePath);

    // 查找占位符并替换
    //使用 Find.Execute 查找占位符,使用 TypeText 方法替换为新内容
    QAxObject *selection = wordApp->querySubObject("Selection");

    // 获取 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();

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

mainwindow.h:

cpp 复制代码
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include <QTableWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QMap>
#include <QAxObject>
#include <QAxWidget>

QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void browseTemplateFile();
    void browseOutputFile();
    void replaceInWord();

private:
    QLineEdit *templatePathEdit;
    QLineEdit *outputPathEdit;
    QTableWidget *placeholdersTable;
    QPushButton *replaceButton;
    bool replaceMultiple(const QString &templatePath, const QString &outputPath, const QMap<QString, QString> &placeholders);
    QAxObject *wordApp = nullptr;
    QAxWidget *wordPreview = nullptr;

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

pro 中需要增加对 QAxObject 的支持

bash 复制代码
QT       += core gui axcontainer 

四、分析:

这段Qt C++代码实现了一个简单的"Word 文件替换工具",允许用户通过一个图形界面选择Word模板文件、设置输出路径,并在Word文档中进行占位符的替换操作。

1. 类的构造函数 (MainWindow::MainWindow)

  • UI设置:
    • 使用 QWidget 创建中央窗口,QVBoxLayout 为主布局,内部包含多个控件(如标签、输入框、按钮等)。
    • 通过 QHBoxLayout 设置了模板文件选择区域(输入框和浏览按钮)、输出路径选择区域(输入框和浏览按钮)、以及键值对表格用于占位符替换。
    • 还创建了一个 QTableWidget 来管理占位符和替换值的键值对。初始化了5行默认数据。
  • 控件连接:
    • 点击"浏览"按钮会触发文件选择对话框,并通过信号槽机制连接相应的函数(browseTemplateFilebrowseOutputFile)。
    • 替换按钮 (replaceButton) 连接到 replaceInWord 函数。
  • Word预览:
    • wordPreview 是一个 QAxWidget 控件,允许通过 ActiveX 技术与 Word 应用进行交互。它在 browseTemplateFile 中初始化并用于打开 Word 文件。

2. 浏览模板文件 (browseTemplateFile)

  • 打开文件对话框 (QFileDialog::getOpenFileName) 选择模板文件,文件路径显示在 templatePathEdit 中。
  • 使用 QAxWidget 来查询 Word 的应用对象 wordApp,并打开用户选择的文件。
  • 通过 Word.Application 创建 ActiveX 对象,加载模板文件并将其显示在打印预览模式。
  • 如果没有成功加载Word文件,会弹出错误提示。

3. 浏览输出文件 (browseOutputFile)

  • 打开保存文件对话框 (QFileDialog::getSaveFileName) 选择输出文件路径,并将路径显示在 outputPathEdit 中。

4. 替换操作 (replaceInWord)

  • 从 UI 中获取模板文件路径和输出文件路径,如果路径为空,弹出警告。
  • 获取表格中的占位符及其对应替换值,并构建一个 QMap 来存储这些键值对。
  • 调用 replaceMultiple 函数进行批量替换操作。

5. 替换逻辑 (replaceMultiple)

  • 文件存在性检查: 使用 QFile::exists 检查模板文件是否存在。
  • 打开Word文档: 使用 QAxObject 打开模板文件,获取 Selection 对象和 Find 对象来查找占位符。
  • 查找和替换: 遍历占位符的键值对,使用 Find.Execute 查找占位符,并通过 TypeText 替换为新内容。替换过程中使用了 while 循环,确保文档中所有的占位符都能被替换(即使它们重复出现)。
  • 保存文件: 替换完成后,使用 SaveAs 保存文件到指定的输出路径。
  • 关闭和退出: 完成替换后,关闭文档并退出Word应用。

总结

该程序使用 Qt 的 QAxObject 来与 Microsoft Word 进行交互,实现了以下功能:

  • 用户可以选择一个模板文件,并进行占位符的批量替换。
  • 用户可以设置替换后文档的保存路径。
  • 支持通过界面交互实现选择文件、显示信息以及执行替换操作。
  • 使用 QAxObject 实现了与 Word 的 COM 接口的交互,允许直接操作 Word 文档中的内容。

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

相关推荐
看到我,请让我去学习2 小时前
Qt— 布局综合项目(Splitter,Stacked,Dock)
开发语言·qt
创想未来CTF2 小时前
Qt同步处理业务并禁用按钮
qt
伊织code2 小时前
pdfminer.six
python·pdf·图片·提取·文本·pdfminer·pdfminer.six
HAPPY酷6 小时前
给纯小白的Python操作 PDF 笔记
开发语言·python·pdf
谱写秋天7 小时前
Qt 5.5 的安装与配置(使用 VSCode编辑)
开发语言·vscode·qt
前端市界1 天前
前端视角: PyQt6+Vue3 跨界开发实战
前端·qt·pyqt
Eternity_GQM1 天前
【Word VBA Zotero 引用宏错误分析与改正指南】【解决[21–23]参考文献格式插入超链接问题】
开发语言·c#·word
誰能久伴不乏1 天前
Qt 动态属性(Dynamic Property)详解
开发语言·qt
枫叶丹41 天前
【Qt开发】常用控件(四)
开发语言·qt
代码AI弗森1 天前
PDF OCR + 大模型:让文档理解不止停留在识字
pdf·ocr