参考资料:
Qt导出Excel的简单实现
QT界面开发-QAxObject 读写excel(COM组件)
浅谈 Excel 对象模型
ActiveX Objects
Excel VBA 参考
QAxObject对COM对象进行了封装,QAxObject派生自QAxBase,而后者提供了一组API通过IUnknown指针直接访问COM对象,excel程序也是一个COM对象,因此可以通过QAxObject来操作它。
QAxObject是一个QObject,可以接收事件并连接到信号和插槽。QAxObject还从QAxBase继承了大部分与ActiveX相关的功能,特别是dynamicCall() 和querySubObject()。
QAxObject *ExcelSpeech = ExcelApp.querySubObject("Speech"); ExcelSpeech->dynamicCall("Speak(QString txt)", "hello world"); |
ExcelSheet->dynamicCall("Value", 5); |
但是一般使用不用,改用 QObject::property() and QObject::setProperty() 方法,速度会更快
如果该对象有子对象,使用此方法获取子对象,比如:
ExcelBooks = ExcelApp->querySubObject("WorkBooks"); //获取工作簿集合 |
一般常用的构造函数
QAxObject * ExcelApp = new QAxObject("Excel.Application"); //连接excel |
QObject::property() 和 QObject::setProperty() ,用这两个方法 来读写对象的值。
ExcelApp->setProperty("DisplayAlerts", false); //是否弹出警告窗口 |
一般放在构造和析构函数中
// 在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应用程序就是一个Application,全局的对象比如菜单,工具条都属于Application对象;
- Application有一个Workbooks,包含已经打开了的工作簿,是工作簿Workbook的集合容器。
- Workbook包含一个Worksheets,包含工作表,是工作表的集合容器。
- Workbook还可以包含很多Shapes对象。工作表中还可以包含一些图表,标记,注释,控件等,这些都是浮在Sheet页上的,这些统称为Shapes,其中我们接触的最多的是图表(Charts),这条了解就行。
- 一个WorkSheet可以包含很多个Range对象。具体而言,一个工作表里面有很多个单元格,单元格范围用Range表示,Range可以表示一个单元格,也可以表示多个单元格。
其中最重要的几个对象为Application,Workbook,Worksheet 和Range对象。
可以通过VBA的开发工具, 看到所有的属性和方法
Application对象 代表了Excel应用程序本身。
//连接excel QAxObject Application("Excel.Application");
//是否可视化excel Application.setProperty("Visible", true);
//是否弹出警告窗口 Application.setProperty("DisplayAlerts", false);
// 关闭excel 程序 Application.dynamicCall ("Quit()"); |
当前在Microsoft Excel应用程序中打开的所有 Workbook 工作簿的集合。
QAxObject *WorkBooks = Application.querySubObject("WorkBooks"); |
Template 是一个枚举: -4109 图表 ;-4167 Worksheet,默认为 工作表
使用 Add 方法可创建一个新的空工作簿,并将其添加到集合中。
//获取工作簿集合 QAxObject *WorkBooks = ExcelApp.querySubObject("WorkBooks");
//打开一个工作簿 WorkBooks->dynamicCall("Open(const QString&)", QDir::toNativeSeparators(filePath));
//新建一个空白工作簿 WorkBooks->dynamicCall("Add"); // 图表
// 关闭 工作簿 WorkBooks->dynamicCall("close()"); |
#include
#include
#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;
}
一个Microsoft Excel工作簿。
可以使用Workbooks (index)返回单个工作簿对象,其中index是工作簿名称或索引号。
一般都是从 Application.ActiveWorkbook 来获取活动工作簿
//获取活动工作簿 WorkBook = ExcelApp->querySubObject("ActiveWorkBook"); |
WorkBook->dynamicCall("SaveAs(const QString &)", QDir::toNativeSeparators(savePath)); |
指定工作簿或活动工作簿中所有工作表对象的集合。每个工作表对象表示一个工作表。
//获取工作表格集合 WorkSheets = WorkBook->querySubObject("Sheets"); |
使用WorkSheets(index)返回单个工作表对象,其中index是工作表索引编号或名称。
一般都是从 Application.ActiveSheet 来获取活动工作表
//从工作表集合中使用index获取,即sheet1
Worksheet = WorkSheets->querySubObject("Item(int)", 1);
//获取当前工作表格1,即sheet1
QAxObject* WorkSheet = Application.querySubObject("ActiveSheet");
表示一个单元格、一行、一列、包含一个或多个连续单元格块的单元格范围。
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 的区域
Range->Property("Value"); // 取值
Range->setProperty("Value", "hello"); // 赋值
int rows = Range->querySubObject ("Rows")->property ("Count").toInt ();
int cols = Range->querySubObject ("Columns")->property ("Count").toInt ();
cout << rows << cols;
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")
)
)
)
)
QVariant values = Range->property ("Value");
QVariantList tempVarList = values.value();
// 读取Range的数据
for (int i = 0; i < tempVarList.size(); ++i) {
QVariantList tempVarRow = tempVarList [i].value();
for(int j=0; j < tempVarRow .size(); ++j)
cout<
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); // 可以设置属性的方法写入
二百万的数据,写入只要2秒左右,还是挺快的
#include
#include
#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();
// 读取Range的数据
for (int i = 0; i < tempVarList.size(); ++i) {
QVariantList tempVarRow = tempVarList [i].value();
for(int j=0; j < tempVarRow .size(); ++j)
cout<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里用起来还是很方便,其他的属性和方法也会有适合的地方。