参考资料:
一、简介
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的对象的主要层次结构:
- Excel应用程序就是一个Application,全局的对象比如菜单,工具条都属于Application对象;
- Application有一个Workbooks,包含已经打开了的工作簿,是工作簿Workbook的集合容器。
- Workbook包含一个Worksheets,包含工作表,是工作表的集合容器。
- Workbook还可以包含很多Shapes对象。工作表中还可以包含一些图表,标记,注释,控件等,这些都是浮在Sheet页上的,这些统称为Shapes,其中我们接触的最多的是图表(Charts),这条了解就行。
- 一个WorkSheet可以包含很多个Range对象。具体而言,一个工作表里面有很多个单元格,单元格范围用Range表示,Range可以表示一个单元格,也可以表示多个单元格。
其中最重要的几个对象为Application,Workbook,Worksheet 和Range对象。
可以通过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 方法可创建一个新的空工作簿,并将其添加到集合中。
- Add (Template) 增加新的工作簿
-
- Close( ) 关闭所有打开的工作簿
- Open (FileName, UpdateLinks, ReadOnly, Format, Password, WriteResPassword, IgnoreReadOnlyRecommended, Origin, Delimiter, Editable, Notify, Converter, AddToMru, Local, CorruptLoad)
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
#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里用起来还是很方便,其他的属性和方法也会有适合的地方。