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

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:

#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:

#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 的支持

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


http://www.kler.cn/a/441637.html

相关文章:

  • 【2024年终总结】我与CSDN的一年
  • 【Red Hat8】:搭建FTP服务器
  • c++学习第七天
  • (三)线性代数之二阶和三阶行列式详解
  • < OS 有关 > 阿里云:轻量应用服务器 的使用 安装 Tailscale 后DNS 出错, 修复并替换 apt 数据源
  • 网络安全(渗透)
  • VScode:常见问题的原因及其解决方案
  • Linux高性能服务器编程 | 读书笔记 | 9.定时器
  • Git简介和特点
  • Web 毕设篇-适合小白、初级入门练手的 Spring Boot Web 毕业设计项目:教室信息管理系统(前后端源码 + 数据库 sql 脚本)
  • Unity全局光照详解
  • 基于Spring Boot的摄影师分享交流社区
  • 了解ARM的千兆以太网——RK3588
  • AI技术赋能电商行业:创新应用与未来展望
  • linux 添加默认网关
  • 25上半年软考《系统架构设计师》,备考大纲已出!
  • Qt Q_ENUM enum 转 QString 枚举字符串互转; C++模板应用
  • Rust关键字实例解析
  • 虚拟机安装+XS hell+Xfit(安装方法大致都相同,若不一样,可看其他的)
  • PVE——OpenWRT 硬盘 size单位的调整
  • 【计算机网络】期末考试预习复习|上
  • Java全体系精华(上):从基础到框架,构建坚实开发技能
  • MySQL(七)---C/C++连接MySQL
  • Roslyn 是什么?
  • 从 CephFS 到 JuiceFS:同程旅行亿级文件存储平台构建之路
  • STM32F407ZGT6-UCOSIII笔记6:UCOS-III软件定时器