Qt使用QAxObject读写Excel 的方法

参考资料:

Qt导出Excel的简单实现

QT界面开发-QAxObject 读写excel(COM组件)

浅谈 Excel 对象模型

ActiveX Objects

Excel VBA 参考

一、简介

QAxObject对COM对象进行了封装,QAxObject派生自QAxBase,而后者提供了一组API通过IUnknown指针直接访问COM对象,excel程序也是一个COM对象,因此可以通过QAxObject来操作它。

二、QAxObject的主要方法

QAxObject是一个QObject,可以接收事件并连接到信号和插槽。QAxObject还从QAxBase继承了大部分与ActiveX相关的功能,特别是dynamicCall() 和querySubObject()。

2.1) dynamicCall()

  • 多用于调用方法:  

            QAxObject *ExcelSpeech = ExcelApp.querySubObject("Speech");

            ExcelSpeech->dynamicCall("Speak(QString txt)", "hello world");

  • 可调用设置属性:

            ExcelSheet->dynamicCall("Value", 5);

但是一般使用不用,改用 QObject::property() and QObject::setProperty() 方法,速度会更快

2.2)querySubObject()

如果该对象有子对象,使用此方法获取子对象,比如:

      ExcelBooks = ExcelApp->querySubObject("WorkBooks");  //获取工作簿集合

2.3)构造函数

一般常用的构造函数

  • QAxObject(IUnknown *iface, QObject *parent )        // IUnknown 接口
  • QAxObject(const QString &c, QObject *parent )        // ProgID

       QAxObject * ExcelApp = new QAxObject("Excel.Application");     //连接excel

2.4)对象属性的读写

QObject::property() 和 QObject::setProperty() ,用这两个方法 来读写对象的值。

         ExcelApp->setProperty("DisplayAlerts", false);     //是否弹出警告窗口

2.5)初始化和释放资源函数

一般放在构造和析构函数中​

 // 在Qt下,由于 QApplication 中需要使用系统 粘贴板,已经调用过 CoInitialize,所以很容易被忽略这个问题。

void init() {

 //  多线程使用 CoInitializeEx ,但是会有告警信息 ,可无视

 //  CoInitializeEx(NULL, COINIT_MULTITHREADED);

 // 单线程使用OleInitialize()方法

 HRESULT r = OleInitialize(0);

 if (r != S_OK && r != S_FALSE) {

   qWarning("Qt:初始化Ole失败(error %x)", (unsigned int)r);

 }

}

void release() {

 // 对于每次调用 OleInitialize,必须对 OleUninitialize进行相应的调用。

 OleUninitialize();

}

三、excel的对象的主要层次结构:

image.png

  • Excel应用程序就是一个Application,全局的对象比如菜单,工具条都属于Application对象;
  • Application有一个Workbooks,包含已经打开了的工作簿,是工作簿Workbook的集合容器。
  • Workbook包含一个Worksheets,包含工作表,是工作表的集合容器。
  • Workbook还可以包含很多Shapes对象。工作表中还可以包含一些图表,标记,注释,控件等,这些都是浮在Sheet页上的,这些统称为Shapes,其中我们接触的最多的是图表(Charts),这条了解就行。
  • 一个WorkSheet可以包含很多个Range对象。具体而言,一个工作表里面有很多个单元格,单元格范围用Range表示,Range可以表示一个单元格,也可以表示多个单元格。

其中最重要的几个对象为Application,Workbook,Worksheet 和Range对象。

image.png

可以通过VBA的开发工具, 看到所有的属性和方法

四、Application对象常用的方法和属性

Application对象 代表了Excel应用程序本身。

4.1) 方法:

    • 构造方法 QAxObject Application("Excel.Application");
    • Quit() : 退出

4.2) 属性:

    • Caption Microsoft Excel 主窗口标题栏中显示的名称
    • Visible         程序是否可见
    • DisplayAlerts      是否发出告警
    • ActiveWorkbook  返回一个活动工作簿对象,表示活动窗口中的工作簿。
    • ActiveSheet  返回表示活动工作簿或指定工作簿中的活动工作表(顶部工作表)的对象。

   //连接excel

   QAxObject Application("Excel.Application");

   

   //是否可视化excel

   Application.setProperty("Visible", true);

   

   //是否弹出警告窗口

   Application.setProperty("DisplayAlerts", false);

   

   // 关闭excel 程序

   Application.dynamicCall ("Quit()");

五、WorkBooks 集合对象的常用方法和属性

当前在Microsoft Excel应用程序中打开的所有 Workbook 工作簿的集合。

5.1)从excel程序获取工作簿集合:

QAxObject *WorkBooks = Application.querySubObject("WorkBooks");

5.2)常用方法:

    • Add (Template)          增加新的工作簿
      ​Template 是一个枚举: -4109 图表 ;-4167 Worksheet,默认为 工作表
      ​使用 Add 方法可创建一个新的空工作簿,并将其添加到集合中。
    •  Close( ) 关闭所有打开的工作簿
    • Open (FileNameUpdateLinksReadOnlyFormatPasswordWriteResPasswordIgnoreReadOnlyRecommendedOriginDelimiterEditableNotifyConverterAddToMruLocalCorruptLoad)

5.3) 属性

    • Item (Index) index : 对象的名称或索引号。

 //获取工作簿集合

 QAxObject *WorkBooks = ExcelApp.querySubObject("WorkBooks");

//打开一个工作簿

 WorkBooks->dynamicCall("Open(const QString&)", QDir::toNativeSeparators(filePath));

 //新建一个空白工作簿

 WorkBooks->dynamicCall("Add");  // 图表

 // 关闭 工作簿

 WorkBooks->dynamicCall("close()");

5.4)没啥用的Demo

1.gif

#include <QAxObject>
#include <QtWidgets>

#include "DebugTool.h"

// 在Qt下,由于 QApplication 中需要使用系统 粘贴板,已经调用过 CoInitialize,所以很容易被忽略这个问题。
void init() {
  //  多线程使用 CoInitializeEx ,但是会有告警信息,但是可以在多线程中使用
  //  CoInitializeEx(NULL, COINIT_MULTITHREADED);

  // 单线程使用OleInitialize()方法 完美
  HRESULT r = OleInitialize(0);
  if (r != S_OK && r != S_FALSE) {
    qWarning("Qt:初始化Ole失败(error %x)", (unsigned int)r);
  }
}

void release() {
  // 对于每次调用 OleInitialize,必须对 OleUninitialize进行相应的调用。
  OleUninitialize();
}

int main(int argc, char *argv[]) {

  QApplication a(argc,argv);

//  QAxObject ExcelApp("Excel.Application");
//  QAxObject *ExcelSpeech = ExcelApp.querySubObject("Speech");
//  ExcelSpeech->dynamicCall("Speak(QString txt)", "hello world");
//  ExcelApp.dynamicCall ("FindFile");

  //连接excel
  QAxObject Application("Excel.Application");

  //是否可视化excel
  Application.setProperty("Visible", true);

  //是否弹出警告窗口
  Application.setProperty("DisplayAlerts", false);

  //获取工作簿集合
  QAxObject *WorkBooks = Application.querySubObject("WorkBooks");

  //新建一个工作簿
  WorkBooks->dynamicCall("Add(int)", -4109);

  QTimer::singleShot (3000,qApp,SLOT (quit()));
  a.exec ();
  WorkBooks->dynamicCall("close()");
  Application.dynamicCall ("Quit()");

  return 0;
}

六、WorkBook对象

一个Microsoft Excel工作簿。

6.1) 获取对象

可以使用Workbooks (index)返回单个工作簿对象,其中index是工作簿名称或索引号。

一般都是从 Application.ActiveWorkbook 来获取活动工作簿

 //获取活动工作簿

 WorkBook = ExcelApp->querySubObject("ActiveWorkBook");

6.2)方法

    • Save
    • SaveAs 

WorkBook->dynamicCall("SaveAs(const QString &)", QDir::toNativeSeparators(savePath));

七、WorkSheets 对象

指定工作簿或活动工作簿中所有工作表对象的集合。每个工作表对象表示一个工作表。

7.1) 获取 从工作簿中获取

 //获取工作表格集合

 WorkSheets = WorkBook->querySubObject("Sheets");

7.2) 方法

    • Add 创建新的工作表、图表或宏工作表。新工作表将成为活动工作表。
    • Add2 此方法仅对Charts集合对象有用
    • Copy
    • Delete
    • Move

7.3) 属性

    • Count
    • Item
    • Visible

八、WorkSheet 对象

8.1)获取

使用WorkSheets(index)返回单个工作表对象,其中index是工作表索引编号或名称。

一般都是从 Application.ActiveSheet  来获取活动工作表

//从工作表集合中使用index获取,即sheet1

 Worksheet = WorkSheets->querySubObject("Item(int)", 1);

​

//获取当前工作表格1,即sheet1

QAxObject* WorkSheet = Application.querySubObject("ActiveSheet");

8.2)方法

  • Activate
  • Copy
  • Delete
  • Move
  • Paste
  • SaveAs

8.3)属性

  • Cells       工作表上的所有单元格
  • Columns     所有列的范围对象
  • Comments    所有注释的集合
  • Index       集合中的索引号
  • Name        对象名称的字符串值
  • Next        下一个工作表对象
  • Rows        所有行的范围对象
  • Visible     是否可见
  • UsedRange   工作表上使用的范围    这个属性使用次数最多

九、Range 对象

表示一个单元格、一行、一列、包含一个或多个连续单元格块的单元格范围。

9.1)获取对象:

1.1)直接使用 Worksheet.Cells 属性 ,返回一个Range 对象

常用的方法:
QAxObject* Range = Worksheet->querySubObject("Cells(int,int)", row, column);  // 某个单元格
QAxObject *Range = WorkSheet->querySubObject("Range(QString)", "A1:C2");  // A1:C2的范围
QAxObject *Range = WorkSheet->querySubObject("Range(QString,QString)", "A1","C2");  // A1:C2的范围
QAxObject *Range = WorkSheet->querySubObject("Range(QString)", "A:C"); // A:C的整列
QAxObject *Range = WorkSheet->querySubObject("Cells(int)", 1);         // 第一个单元格
// QAxObject *Range = WorkSheet->querySubObject("Cells");              // 所有单元格

1.2)可以间接的调整 使用 Resize 属性进行调整,最后获得Range对象

  Range = WorkSheet->querySubObject ("Range(QString)","A1");
  Range = Range->querySubObject("Resize(int,int)", 2000,3); // 返回一个A1 :C2000 的区域

9.2)属性:

  • Address         返回地址
  • Column          指定范围内第一个区域中第一列的编号
  • Columns         指定范围内的列。
  • Count             多少
  • EntireColumn    指定范围的整个列
  • EntireRow       指定范围的整个行
  • Item            指定索引的单元格
  • Offset
  • Resize          调整指定范围的大小
  • Row             指定范围内第一个区域中第一行的编号
  • Rows            指定范围内的行
  • Value : 返回或设置一个 Variant 型,它代表指定单元格的值
  • Value2 :此属性和Value属性的唯一区别在于Value2属性不使用Currency和Date数据类型。 可以通过使用 Double 数据类型,以浮点数形式返回这些数据类型格式的数值。
Range->Property("Value");               // 取值
Range->setProperty("Value", "hello");   // 赋值
  • Rows 和 Columns 子对象属性
    通过这两个子对象 获取 区域的行数 和 列数
  int rows = Range->querySubObject ("Rows")->property ("Count").toInt ();
  int cols = Range->querySubObject ("Columns")->property ("Count").toInt ();
  cout << rows << cols;

9.3)多个单元格的范围值的读写

  • 注意:如果区域包含多个单元格的区域, Value 虽然只返回一个Variant ,但是它包含了整个区域的每个单元格中的值的,本身是个二维数组。如下所示:
QAxObject *Range = WorkSheet->querySubObject("Range(QString)", "A1:C2");  // A1:C2的范围
QVariant values = Range->property ("Value"); // 返回的 QVariant

QVariant(
    QVariantList, (
        QVariant(
            QVariantList, (
                QVariant(QString, "hello"), 
                QVariant(QString, "hello"), 
                QVariant(QString, "hello")
            )
        ),
        QVariant(
            QVariantList, (
                QVariant(QString, "hello"), 
                QVariant(QString, "hello"), 
                QVariant(QString, "hello")
            )
        )
    )
)

9.3.1)范围读取

  QVariant values = Range->property ("Value");

  QVariantList tempVarList = values.value<QVariantList>();
  // 读取Range的数据
  for (int i = 0; i < tempVarList.size(); ++i) {
    QVariantList tempVarRow = tempVarList [i].value<QVariantList>();
    for(int j=0; j < tempVarRow .size(); ++j)
      cout<<tempVarRow[j].toString();
  }

9.3.2)范围写入

int rows = 2000;
int cols = 3;  

QVariantList mList;     // 准备写入的值
  // 写入Range的数据
  for (int i = 0; i < rows; ++i) {
    QVariantList tempVarRow;        // 每一行的QVariantList
    for (int j = 0; j < cols; ++j) {
      tempVarRow << QString("%1,%2").arg (i).arg (j);
      cout << i << j;
    }
    mList << QVariant(tempVarRow);  // 通过QVariant加入mList
  }

  cout << mList;

  Range = WorkSheet->querySubObject ("Range(QString)","A1");
  Range = Range->querySubObject("Resize(int,int)", rows,cols);

  Range->setProperty ("Value",mList);       // 可以设置属性的方法写入

9.3.3 )算是一个完整的Demo

二百万的数据,写入只要2秒左右,还是挺快的

#include <QAxObject>
#include <QtWidgets>

#include "DebugTool.h"

// 在Qt下,由于 QApplication 中需要使用系统 粘贴板,已经调用过 CoInitialize,所以很容易被忽略这个问题。
void init() {
  //  多线程使用 CoInitializeEx ,但是会有告警信息,可无视
  //  CoInitializeEx(NULL, COINIT_MULTITHREADED);

  // 单线程使用OleInitialize()方法 完美
  HRESULT r = OleInitialize(0);
  if (r != S_OK && r != S_FALSE) {
    qWarning("Qt:初始化Ole失败(error %x)", (unsigned int)r);
  }
}

void release() {
  // 对于每次调用 OleInitialize,必须对 OleUninitialize进行相应的调用。
  OleUninitialize();
}

int main(int argc, char *argv[]) {
//  init();
  QApplication a(argc, argv);

  //  QAxObject ExcelApp("Excel.Application");
  //  QAxObject *ExcelSpeech = ExcelApp.querySubObject("Speech");
  //  ExcelSpeech->dynamicCall("Speak(QString txt)", "hello world");
  //  ExcelApp.dynamicCall ("FindFile");

  //连接excel
  QAxObject Application("Excel.Application");

  //是否可视化excel
  Application.setProperty("Visible", false);


  //是否弹出警告窗口
  Application.setProperty("DisplayAlerts", false);

  //获取工作簿集合
  QAxObject *WorkBooks = Application.querySubObject("WorkBooks");

  //新建一个工作簿
  WorkBooks->dynamicCall("Add");

  QAxObject *WorkSheet = Application.querySubObject("ActiveSheet");

  QAxObject *Range = WorkSheet->querySubObject("Range(QString)", "A1:C2");  // A1:C2的范围

  Range->setProperty("Value", "hello");

  QVariant values = Range->property ("Value");

  QVariantList tempVarList = values.value<QVariantList>();
  // 读取Range的数据
  for (int i = 0; i < tempVarList.size(); ++i) {
    QVariantList tempVarRow = tempVarList [i].value<QVariantList>();
    for(int j=0; j < tempVarRow .size(); ++j)
      cout<<tempVarRow[j].toString();
  }


  int rows = 200000;
  int cols = 10;
  cout << rows << cols;

  QElapsedTimer timer;
  timer.start ();
  QVariantList mList;   // 写入Range的数据
  for (int i = 0; i < rows; ++i) {
    QVariantList tempVarRow;
    for (int j = 0; j < cols; ++j) {
      tempVarRow << i*cols + j + 1;
//      cout << i << j;
    }
    mList.append (QVariant(tempVarRow));
  }

//  cout << mList;

  Range = WorkSheet->querySubObject ("Range(QString)","A1");
  Range = Range->querySubObject("Resize(int,int)", rows,cols);

  Range->setProperty ("Value",mList);
  //是否可视化excel
  Application.setProperty("Visible", true);

  cout << timer.elapsed ();
//  release();
  return 0;
}

十、总结

有些属性可能用不到,但是既然已经整理出来,就懒得删除了,而且Resize这个属性就是在整理的时候发现的,在Qt里用起来还是很方便,其他的属性和方法也会有适合的地方。

  • 29
    点赞
  • 146
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值