Qt 实践——如何用 Qt 操作 Excel

Qt 实践——如何用 Qt 操作 Excel

原理

用到的库和类

一般 Qt 操作 Excel 的方法都是基于 QAxObject,也就是采用 COM 接口。

QAxObject 继承自 QAxBase,其中提供了许多 API 通过对象指针访问 COM 对象,而 Excel 本身也是一个 COM 对象,因此可以用这个类操作 Excel。

层次结构

Excel 一般的主要层次结构分为:

  • Application 对象。
    • Workbook 对象。
      • Worksheet 对象。
        • Range 对象。

Worksheet 对象相当于每个 Excel 文件的工作表,Range 对象则是其中的表格单元。

基本操作方法

获取对象

上述对象获取某个子对象一般通过 QAxObjectquerySubObject() 方法,比如:

QAxObject *excel = new QAxObject("Excel.Application");
QAxObject *workbooks = excel->querySubObject("WorkBooks");
QAxObject *workbook = workbooks->querySubObject("Open(QString&)", path);
QAxObject *sheets = workbook->querySubObject("Sheets");
QAxObject *sheet = sheets->querySubObject("Item(int)", 1);
QAxObject *range = sheet->querySubObject("Cells(int,int)", row, col);

这其中依次得到的对象分别是:

  • Excel 的 Application 对象。
  • 管理 Workbook 对象的 Workbooks 对象。
  • 路径为 path 的 Excel 文件对应的 Workbook 对象。
  • 管理其中工作表 SheetSheets 对象。
  • 第一张工作表对应的 Sheet 对象。
  • 其中第 row 行,第 col 列的表格单元的 range 对象。

调用动态方法

还可以通过 dynamicCall() 方法调用一些动作,比如:

workbook->dynamicCall("SaveAs(const QString &)", QDir::toNativeSeparators(path));

这是调用另存为 path 路径。

workbook->dynamicCall("Save()");

这是调用保存。

range->dynamicCall("Value", value);

这时设置单元格的值。

excel->dynamicCall("SetVisible(bool)", false);

这是设置打开 Excel 时不可见(也就是后台进行)。

workbooks->dynamicCall("Add");

这是新建一个 Excel 文件。

workbooks->dynamicCall("Close()");
excel->dynamicCall("Quit()");

这是关闭 Excel 应用。

除此之外,还有很多类似的方法。

设置和获取属性

一般通过 setProperty() 方法设置属性,比如:

range->setProperty("VerticalAlignment", -4108);
range->setProperty("HorizontalAlignment", -4108);
range->setProperty("WrapText", true);
range->setProperty("MergeCells", true);
range->setProperty("Bold", isBold);

分别为设置单元格:

  • 竖直居中。
  • 水平居中。
  • 文本自动换行。
  • 单元格合并。
  • 字体加粗。

而如果想获取属性就可以通过 property() 方法,会返回一个 QVariant 对象,可以根据需求通过 toString()toInt() 等方法转为 Qt 的基本类型。

更多相关

除了上面提到的,更多的方法可以直接到微软官网查看文档

简单封装代码

头文件 excelmanager.h

#ifndef EXCELMANGER_H
#define EXCELMANGER_H

#include <QWidget>
#include <QString>
#include <QAxObject>
#include <QDialog>

class ExcelManger : public QWidget
{
    Q_OBJECT
public:
    explicit ExcelManger(QWidget *parent = nullptr);
    ~ExcelManger();

protected:
    static QAxObject *excel;
    static QAxObject *workbooks;
    static QAxObject *workbook;
    static int count;

    void new_excel(const QString&);
    void open_excel(const QString&);
    QString get_cell_value(QAxObject*, int, int);
    QVariant get_range(QAxObject*, const QString&);
    void set_cell_value(QAxObject*, int, int, const QString&);
    void merge_cells(QAxObject*, const QString&);
    void set_cell_font_bold(QAxObject *sheet, const QString &cell, bool isBold = true);
    void set_cell_font_center(QAxObject *sheet, const QString &cell);
    void set_rows_autofit(QAxObject *sheet, const QString &rows);
    void set_cols_autofit(QAxObject *sheet, const QString &cols);
    void save_excel();
    void save_excel_as(const QString&);
    void close();
    void free_excel();

signals:

};

#endif // EXCELMANGER_H

实现文件 excelmanager.cpp

#include "excelmanger.h"
#include <QDebug>
#include <QVariant>
#include <QFile>
#include <QDir>
#ifdef Q_OS_WIN
#include <windows.h>
#endif

QAxObject* ExcelManger::excel = nullptr;
QAxObject* ExcelManger::workbooks = nullptr;
QAxObject* ExcelManger::workbook = nullptr;
int ExcelManger::count = 0;

ExcelManger::ExcelManger(QWidget *parent) : QWidget(parent)
{
    CoInitializeEx(nullptr, COINIT_MULTITHREADED);
    if ((count++) == 0)
    {
        excel = new QAxObject("Excel.Application", this->parent());
        excel->dynamicCall("SetVisible(bool)", false);
        workbooks = excel->querySubObject("WorkBooks");
    }
}

ExcelManger::~ExcelManger()
{
    if ((--count) == 0)
        free_excel();
}

void ExcelManger::new_excel(const QString &path)
{
    workbooks->dynamicCall("Add");
    workbook = excel->querySubObject("ActiveWorkBook");
    save_excel_as(path);
}

void ExcelManger::open_excel(const QString &path)
{
    close();
    QFile file(path);
    if (!file.exists())
        new_excel(path);
    else
        workbook = workbooks->querySubObject("Open(QString&)", path);
    file.close();
}

QString ExcelManger::get_cell_value(QAxObject *sheet, int row, int col)
{
    QAxObject *range = sheet->querySubObject("Cells(int,int)", row, col);
    return range->property("Value").toString();
}

QVariant ExcelManger::get_range(QAxObject *sheet, const QString &range)
{
    return sheet->querySubObject("Range(const QString&)", range)->property("value");
}

void ExcelManger::set_cell_value(QAxObject *sheet, int row, int col, const QString& value)
{
    QAxObject *range = sheet->querySubObject("Cells(int,int)", row, col);
    range->dynamicCall("Value", value);
}

void ExcelManger::merge_cells(QAxObject *sheet, const QString &cell)
{
    QAxObject *range = sheet->querySubObject("Range(const QString&)", cell);
    range->setProperty("VerticalAlignment", -4108);
    range->setProperty("WrapText", true);
    range->setProperty("MergeCells", true);
}

void ExcelManger::set_cell_font_bold(QAxObject *sheet, const QString &cell, bool isBold)
{
    QAxObject *range = sheet->querySubObject("Range(const QString&)", cell);
    range = range->querySubObject("Font");
    range->setProperty("Bold", isBold);
}

void ExcelManger::set_cell_font_center(QAxObject *sheet, const QString &cell)
{
    QAxObject *range = sheet->querySubObject("Range(const QString&)", cell);
    range->setProperty("HorizontalAlignment", -4108);
    range->setProperty("VerticalAlignment", -4108);
}

void ExcelManger::set_rows_autofit(QAxObject *sheet, const QString &rows)
{
    QAxObject *Rows = sheet->querySubObject("Rows(const QString &)", rows);
    Rows->dynamicCall("AutoFit()");
}

void ExcelManger::set_cols_autofit(QAxObject *sheet, const QString &cols)
{
    QAxObject *Cols = sheet->querySubObject("Columns(const QString &)", cols);
    Cols->dynamicCall("AutoFit()");
}

void ExcelManger::save_excel_as(const QString &path)
{
    if (workbook)
        workbook->dynamicCall("SaveAs(const QString &)", QDir::toNativeSeparators(path));
}

void ExcelManger::save_excel()
{
    if (workbook)
        workbook->dynamicCall("Save()");
}

void ExcelManger::close()
{
    if (workbook)
        workbook->dynamicCall("Close()");
}

void ExcelManger::free_excel()
{
    if (excel != nullptr)
    {
        workbooks->dynamicCall("Close()");
        excel->dynamicCall("Quit()");
        delete workbook;
        delete workbooks;
        delete excel;
        excel = nullptr;
        workbooks = nullptr;
        workbook = nullptr;
    }
}

其中现有的功能主要是:

  • new_excel():按照给定目录创建新表格文件。
  • open_excel():按照给定目录打开表格文件。
  • get_cell_value():获取指定工作表中某行某列的单元格值(返回类型为 QString)。
  • set_cell_value():设置指定工作表中某行某列的单元格值(设置类型为 QString)。
  • save_excel()save_excel_as():保存文件以及保存为路径。
  • close()free_excel():关闭文件和 Application
  • 以及一些基本的单元格样式设置,更多功能待补充。

其他

CoInitializeEx(nullptr, COINIT_MULTITHREADED);

需要在使用 COM 接口前使用此函数初始化

程序中使用了静态成员,是希望在有多个工作表被操纵的时候,保证后台只有一个 Excel 应用在运行。

当然为了实现这一目标,程序也需要保证在使用 ExcelManger每个对象能够得到正确的析构

 

点赞 0

No Comments

Add your comment