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

相关推荐
牵牛老人41 分钟前
Qt生成随机数的方法
服务器·数据库·qt
haha_qasim1 小时前
如何将多张图片合并为一个pdf?多张图片合并成一个PDF文件的方法
pdf
xiaocherry11282 小时前
QT绘制同心扇形
qt
程序猿DD_2 小时前
一款可以完整保留排版的PDF翻译:GitHub增长第一、可私有化部署
pdf·github
界面开发小八哥2 小时前
QtitanChart组件——高效、灵活的Qt数据可视化解决方案
c++·qt·ui·信息可视化·界面控件
慧都小妮子3 小时前
Spire.PDF for .NET【页面设置】演示:向 PDF 文档添加页码
java·pdf·.net
Quz3 小时前
QScreen在Qt5.15与Qt6.8版本下的区别
c++·qt·ui
「QT(C++)开发工程师」4 小时前
如何在Qt中应用html美化控件
qt
抽风侠5 小时前
qt的QItemSelectionModel
开发语言·qt