QT使用QAxObject读取Excel教程-全网最全

2023-05-16

文章目录

    • 一、背景
    • 二、介绍
      • 基本操作方法
        • 获取对象
        • 调用动态方法
        • 设置和获取属性
        • 更多相关
    • 三、使用要求
        • 添加模块
        • 与excel com连接的方法
        • Excel基本操作
    • 四、具体使用说明
    • 五、项目实战
      • 实战项目1
      • 实战项目2
      • 实战项目3
      • 实战项目4
      • 实战项目5-封装好的库
        • 头文件 `excelmanager.h`
        • 实现文件 `excelmanager.cpp`
        • 其他
      • 项目实战6-封装好的库
    • 六、一些小技巧
      • 读取excel中的数据

一、背景

QT中没有操作Excel的官方库,本文章介绍的是Windows系统的ActiveX对象(QAxObject)操作Excel。

当然还有一些开源库可以用来操作Excel,详见:Qt处理Excel的一些库

本文只对QAxObject读取Excel作说明。

二、介绍

首先,我们先来解释一些概念,方面我们去理解Qt操作excel的基本流程。
Qt中,QAxObject对COM对象进行封装,QAObject派生自QAxBase,QAxBase提供了一组API通过IUnknown指针直接访问COM对象。具体结构如下图。

这里写图片描述

我们要操作的Excel也是一个COM对象,因此,可以采用上述方式实现对Excel的操作。

在具体操作之前,我们先了解一下excel的层次结构,如图所示,Application对象–>Workbook对象–>Worksheet对象–>Range对象。
1个excel有一个Application对象,1个Application对象有多个workbook对象组成,这些workbook对象由workbooks对象统一管理,workbook对象下包含若干个worksheet,这些worksheet对象由worksheets对象统一管理,worksheet下面的range对象,对应这worksheet里面的表格单元了。

这里写图片描述

基本操作方法

获取对象

上述对象获取某个子对象一般通过 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 的基本类型。

更多相关

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

三、使用要求

添加模块

在Qt Creator中使用QAxObject需要先在pro中添加:
QT += axcontainer

包含头文件ActiveQt/QAxObject

#include <QAxObject>

与excel com连接的方法

QAxObject *excel = new QAxObject("Excel.Application"); //!建立excel操作对象,并连接Excel控件
excel->dynamicCall("SetVisible (bool Visible)",  "false"); //!  设置为不显示窗体
excel->setProperty("DisplayAlerts", false); //! 不显示任何警告信息, 如关闭时的是否保存提示
excel->dynamicCall("Quit(void)");  //! 关闭excel程序,操作完后记着关闭,由于是隐藏在后台进程中,不关闭进程会有很多excel.exe。
workbook->dynamicCall("Close(Boolean)", false); //! 关闭exce程序先关闭.xls文件

Excel基本操作

只介绍简单的读写操作,需要修改单元格格式等操作,请"Excel VBA参考手册.chm"

excel文件操作

获取当前工作簿的集合,这里需要注意,工作簿就是Excel文件。

QAxObject *workbooks =  excel->querySubObject("Workbooks"); //! 获取工作簿(excel文件)集合

新建一个工作簿,新建一个工作簿就是新建一个Excel文件

workbooks->synamicCall("Add"); //新建一个工作簿
QAxObject *workbook = excel->querySubObject("ActiveWorkBook"); //! 获取当前工作簿

打开一个已有的工作簿,就是打开一个Excel文件

QString filename = "e:/123.xlsx";
QAxObject* workbook = workbooks->querySubObject("Open(const QString&)", filename);

保存工作簿

workbook->dynamicCall("Save()");   //!保存文件
workbook->dynamicCall("Close(Boolean)", false);  //! 关闭文件 
excel->dynamicCall("Quit()"); //! 关闭excel

另存为工作簿

QDir::toNativeSeparators,将路径中的"/"转换为"\",否则无法保存,"/"只是qt中可以识别
workbook->dynamiCall("SaveAs(const QString&)",  QDit::toNativeSeparators(filename));
workbook->synamicCall("Close(Boolean)", false); //! 关闭文件
excel->dynamicCall("Quit()"); //! 关闭excel

Sheet工作表操作

获取所有工作表

QAxObject *worksheets = workbook->querySubObject("Sheets"):

根据序号获取某个工作表,序号顺序就是excel 打开后下方的排序

QAxObject *worksheet = worksheets->querySubObejct("Item(int)", 1);

获取表中的行数列数

QAxObject* usedrange = worksheet->querySubObject("UsedRange"); //!  sheet 范围
int intRowStart = usedrange->property("Row").toInt(); //!  起始行数
int intColStart = usedrange->property("Column").toInt(); //!  起始列数 
QAxObject *rows, *columns;
rows = usedrange->querySubObject("Rows"): //! 行 
columns = usedrange->querySubObject("Columns"); //! 列
int intRow = rows->property("Count").toInt(); //! 行数
int intCol = columns->property("Count").toInt();  //!  列数

内容操作

数据内容操作–获取单元格–基于坐标

QAxObject *cell = worksheet->querySubObject("Cells(int, int)", i,  j);

数据内容操作–获取单元格–基于行列名称

QAxObject *cell = worksheet->querySubObject("Range(QVariant, QVariant)", "A1");

数据内容操作–读单元格内容

QVariant cell_value = cell->property("Value");

数据内容操作-- 写单元格内容

cell->setProperty("Value",  "内容");

大数据量读取

读取所有单元格内容-数据量大,只需要进行一次操作即可读取所有内容,避免重复对每个单元格进行QAxObect操作

QVariant var;
QAxObject * usedRange = sheet->querySubObject("UseRange"); //! 获取用户区域范围
if(NULL == usedRange || usedRange->isNull())
{
    return  var;
}
var = usedRange->dynamicCall("Value"); // 读取区域内所有值
delete usedRange;

此时结果以QVariant保存,需要自行转化为QList<QList>

QList<QList<QVariant>> excel_list;
auto rows = var.toList();

for(auto row:rows) 
{
    excel_list.append(row.toList());
}

大数据写入

以QList<QList>存储,需要限定范围

QAxObject *user_rang = this->sheet->querySubObject("Rang(const QString&)", "A1:D100");

写入数据

rang->setProperty("Value", var); 

四、具体使用说明

一般我们使用QAxObject操作Excel分为以下的步骤:

  1. 连接控件Excel
  2. 打开工作簿(新建或打开Excel文件)
  3. 打开sheet
  4. 获取行数,列数
  5. 读和写
  6. 设置样式
  7. 保存文件
  8. 另存为
  9. 关闭文件

下面我们就具体的说明一下怎么完成上面的操作。

1:连接控件Excel
2: 打开工作簿(新建或打开Excel文件)
3: 打开sheet
4: 获取行数,列数
5: 读和写
6:设置样式
7: 保存文件
8:另存为
9:关闭文件
 
1:连接控件Excel
QAxObject excel("Excel.Application");//连接Excel控件
excel.setProperty("Visible", false);// 不显示窗体
excel->setProperty("DisplayAlerts", false);  // 不显示任何警告信息。如果为true, 那么关闭时会出现类似"文件已修改,是否保存"的提示
 
2: 打开工作簿(新建或打开Excel文件)
  QAxObject* workbooks = excel->querySubObject("WorkBooks"); // 获取工作簿集合
  2.1新建
  workbooks->dynamicCall("Add"); // 新建一个工作簿
  QAxObject* workbook = excel->querySubObject("ActiveWorkBook"); // 获取当前工作簿
  2.2打开
  QAxObject* workbook = workbooks->querySubObject("Open(const QString&)", ("C:/Users/lixc/Desktop/tt2.xlsx"));//Excel文件地址

3: 打开sheet
QAxObject* worksheet = workbook->querySubObject("WorkSheets(int)", 1); // 获取工作表集合的工作表1, 即sheet1

4: 获取行数,列数
	QAxObject* usedrange = worksheet->querySubObject("UsedRange"); // sheet范围
    int intRowStart = usedrange->property("Row").toInt(); // 起始行数   为1
    int intColStart = usedrange->property("Column").toInt();  // 起始列数 为1
 
    QAxObject *rows, *columns;
    rows = usedrange->querySubObject("Rows");  // 行
    columns = usedrange->querySubObject("Columns");  // 列
 
    int intRow = rows->property("Count").toInt(); // 行数
    int intCol = columns->property("Count").toInt();  // 列数
    qDebug()<<"intRowStart:"<<intRowStart<<"\t intColStart"<<intColStart;
    qDebug()<<"intRow"<<intRow<<"\t intCol"<<intCol;

5: 读和写
	5.1读取单元格方式1
	for(int i=intRowStart;i<intRow+intRowStart;i++)
    {
        for(int j=intColStart;j<intCol+intColStart;j++)
        {
            QAxObject* cell = worksheet->querySubObject("Cells(int, int)", i, j);  //获单元格值
            qDebug() << i << j << cell->dynamicCall("Value2()").toString();
        }
    }

	5.2读取单元格方式2
	QString X = "A2"; //设置要操作的单元格,A1
    QAxObject* cellX = worksheet->querySubObject("Range(QVariant, QVariant)", X); //获取单元格
    qDebug() << cellX->dynamicCall("Value2()").toString();

	5.3写单元格方式1
	cellX->dynamicCall("SetValue(conts QVariant&)", 100); // 设置单元格的值

	5.4写单元格方式2
	QAxObject *cell_5_6 = worksheet->querySubObject("Cells(int,int)", 5, 6);
    cell_5_6->setProperty("Value2", "Java");

6:设置样式(未测试)
	//获得单元格对象
	QAxObject* cell = worksheet->querySubObject("Cells(int, int)", i, j);  
	6.1设置单元格内容的显示setProperty()
	   cell->setProperty("Value", "Java C++ C# PHP Perl Python Delphi Ruby");  //设置单元格值
	   cell->setProperty("RowHeight", 50);  //设置单元格行高
	   cell->setProperty("ColumnWidth", 30);  //设置单元格列宽
	   cell->setProperty("HorizontalAlignment", -4108); //左对齐(xlLeft):-4131  居中(xlCenter):-4108  右对齐(xlRight):-4152
	   cell->setProperty("VerticalAlignment", -4108);  //上对齐(xlTop)-4160 居中(xlCenter):-4108  下对齐(xlBottom):-4107
	   cell->setProperty("WrapText", true);  //内容过多,自动换行
	   cell->dynamicCall("ClearContents()");  //清空单元格内容

	6.2设置单元格的样式	QAxObject* interior = cell->querySubObject("Interior");
	   QAxObject* interior = cell->querySubObject("Interior");
	   interior->setProperty("Color", QColor(0, 255, 0));   //设置单元格背景色(绿色)
       QAxObject* border = cell->querySubObject("Borders");
       border->setProperty("Color", QColor(0, 0, 255));   //设置单元格边框色(蓝色)
       QAxObject *font = cell->querySubObject("Font");  //获取单元格字体
       font->setProperty("Name", QStringLiteral("华文彩云"));  //设置单元格字体
       font->setProperty("Bold", true);  //设置单元格字体加粗
       font->setProperty("Size", 20);  //设置单元格字体大小
       font->setProperty("Italic", true);  //设置单元格字体斜体
       font->setProperty("Underline", 2);  //设置单元格下划线
       font->setProperty("Color", QColor(255, 0, 0));  //设置单元格字体颜色(红色)

	6.3合并拆分单元格
	   QString merge_cell;
       merge_cell.append(QChar(3 - 1 + 'A'));  //初始列
       merge_cell.append(QString::number(5));  //初始行
       merge_cell.append(":");
       merge_cell.append(QChar(5 - 1 + 'A'));  //终止列
       merge_cell.append(QString::number(8));  //终止行
       QAxObject *merge_range = work_sheet->querySubObject("Range(const QString&)", merge_cell);
	   //merge_range 可以设置单元格属性
	   merge_range->setProperty("MergeCells", true);  //合并单元格
       //merge_range->setProperty("MergeCells", false);  //拆分单元格

7: 保存文件
	7.1保存当前文件(根据已经打开的工作簿,如果工作簿是新建的文件保存默认存储于Document文件夹中)
	 workbook->dynamicCall("Save()");  //保存文件

    7.2指定目录存储
	QString fileName = QFileDialog::getSaveFileName(NULL, QStringLiteral("保存文件"), QStringLiteral("excel名称"), QStringLiteral("EXCEL(*.xlsx)"));
    //QString fileName=QStringLiteral("C:/Users/lixc/Desktop/excel名称.xlsx");
    workbook->dynamicCall("SaveAs(const QString&)", QDir::toNativeSeparators(fileName)); //保存到filepath
    // 注意一定要用QDir::toNativeSeparators, 将路径中的"/"转换为"\", 不然一定保存不了

9:关闭文件
	 workbook->dynamicCall("Close (Boolean)", false);  //关闭文件
	 excel.dynamicCall("Quit(void)");  //退出
	 如果excel存在于堆上,注意手动释放
	 delete excel;
注意:调试过程中出现异常:需要打开任务管理器,结束Excle进程
//获得单元格对象
QAxObject* cell = worksheet->querySubObject("Cells(int, int)", i, j);  

五、项目实战

在了解了大致的使用步骤后,我们可以通过实际项目来综合运用了。

实战项目1

项目代码如下:

//#include <QtGui/QApplication>
#include <QApplication>
#include "mainwindow.h"
#include <QAxObject>
#include <QAxWidget>
#include <QTextCodec>
#include <QtDebug>

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

//    QTextCodec::setCodecForCStrings(QTextCodec::codecForName("GB18030"));
//    QTextCodec::setCodecForLocale(QTextCodec::codecForName("GB18030"));
//    QTextCodec::setCodecForTr(QTextCodec::codecForName("GB18030"));

    QAxWidget excel("Excel.Application");
    excel.setProperty("Visible", true);
    QAxObject * workbooks = excel.querySubObject("WorkBooks");
    if (!workbooks)  return 1;    //错误返回,用 if(!excel)则不行
    workbooks->dynamicCall("Open (const QString&)", QString("E:\\wenjian\\cs\\code\\QT\\testExcel2\\test.xlsx"));
    QAxObject * workbook = excel.querySubObject("ActiveWorkBook");      //5) 获取活动工作簿:
    QAxObject * worksheets = workbook->querySubObject("WorkSheets");    //获取所有的工作表:
    int intCount = worksheets->property("Count").toInt();               //获取工作表数量:
    qDebug() << "工作表数量" << intCount;
    for (int i = 1; i <= intCount; i++)
    {
       int intVal;
       QAxObject * worksheet = workbook->querySubObject("Worksheets(int)", i);     //获取第i个工作表:
       qDebug() << i << worksheet->property("Name").toString();
       QAxObject * range = worksheet->querySubObject("Cells(1,1)");    //获取cell的值:
       intVal = range->property("Value").toInt();
       range->setProperty("Value", QVariant(intVal+1));
       QAxObject * range2 = worksheet->querySubObject("Range(C1)");
       intVal = range2->property("Value").toInt();
       range2->setProperty("Value", QVariant(intVal+1));
    }

    QAxObject * worksheet = workbook->querySubObject("Worksheets(int)", 1);
    QAxObject * usedrange = worksheet->querySubObject("UsedRange");
    QAxObject * rows = usedrange->querySubObject("Rows");
    QAxObject * columns = usedrange->querySubObject("Columns");
    int intRowStart = usedrange->property("Row").toInt();
    int intColStart = usedrange->property("Column").toInt();

    int intCols = columns->property("Count").toInt();
    int intRows = rows->property("Count").toInt();
    qDebug() << "表格行数" << intRows;
    qDebug() << "表格列数" << intCols;
//    for (int i = intRowStart; i < intRowStart + intRows; i++)
//    {
//       for (int j = intColStart; j <= intColStart + intCols; j++)
//       {
//           QAxObject * range = worksheet->querySubObject("Cells(int,int)", i, j );
//           qDebug() << i << j << range->dynamicCall("Value2()").toString();  //property("Value");不行
//       }
//    }

    for (int i = intRowStart; i < intRows; i++)
    {
       for (int j = intColStart; j < intCols; j++)
       {
           QAxObject * range = worksheet->querySubObject("Cells(int,int)", i, j );
           qDebug() << i << j << range->dynamicCall("Value2()").toString();  //property("Value");不行
       }
    }

    excel.setProperty("DisplayAlerts", 0);
    //qDebug("here?");
    workbook->dynamicCall("SaveAs (const QString&)", QString("E:\\wenjian\\cs\\code\\QT\\testExcel2\\temp.xls"));//这里只能用相对路径!!!用绝对路径就报下面的错误。
    excel.setProperty("DisplayAlerts", 1);
    workbook->dynamicCall("Close (Boolean)", false);
    excel.dynamicCall("Quit (void)");

    MainWindow w;
    w.show();
    return a.exec();
}

test.xlsx中内容如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VWR6lCEv-1677385787835)(C:\Users\10521\AppData\Roaming\Typora\typora-user-images\image-20230226101051423.png)]

执行代码会打印如下信息:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZMXaMuYv-1677385787836)(C:\Users\10521\AppData\Roaming\Typora\typora-user-images\image-20230226101140437.png)]

说明代码把Excel表格中的内容读取并打印出来了。

实战项目2

具体的操作流程如下:

QAxWidget excel(“Excel.Application”);

  1. 显示当前窗口:
    excel.setProperty(“Visible”, true);
  2. 更改 Excel 标题栏:
    excel.setProperty(“Caption”, “Invoke Microsoft Excel”);
  3. 添加新工作簿:
    QAxObject * workbooks = excel.querySubObject(“WorkBooks”);
    workbooks->dynamicCall(“Add”);
  4. 打开已存在的工作簿:
    workbooks->dynamicCall(“Open (const QString&)”, QString(“c:/test.xls”));
  5. 获取活动工作簿:
    QAxObject * workbook = excel.querySubObject(“ActiveWorkBook”);
  6. 获取所有的工作表:
    QAxObject * worksheets = workbook->querySubObject(“WorkSheets”);
  7. 获取工作表数量:
    int intCount = worksheets->property(“Count”).toInt();
  8. 获取第一个工作表:
    QAxObject * worksheet = workbook->querySubObject(“Worksheets(int)”, 1);
  9. 获取cell的值:
    QAxObject * range = worksheet->querySubObject(“Cells(int,int)”, 1, 1 );

项目代码如下:

#include <QApplication>
#include <QDir>
#include <QDebug>
//#include <QAxBase>
#include "mainwindow.h"
#include <QAxObject>

int main(int argc, char **argv)
{
    QApplication a(argc, argv);

    QAxObject excel("Excel.Application");
    excel.setProperty("Visible", false);
    QAxObject * workbooks = excel.querySubObject("WorkBooks");
    workbooks->dynamicCall("Open (const QString&)", QString("E:\\wenjian\\cs\\code\\QT\\testExcel3\\test.xlsx"));
    QAxObject * workbook = excel.querySubObject("ActiveWorkBook");
    QAxObject * worksheets = workbook->querySubObject("WorkSheets");
    int intCount = worksheets->property("Count").toInt();
    QAxObject * worksheet = workbook->querySubObject("Worksheets(int)", 1);
    QAxObject * usedrange = worksheet->querySubObject("UsedRange");
    QAxObject * rows = usedrange->querySubObject("Rows");
    QAxObject * columns = usedrange->querySubObject("Columns");
    int intRowStart = usedrange->property("Row").toInt();
    int intColStart = usedrange->property("Column").toInt();
    int intCols = columns->property("Count").toInt();
    int intRows = rows->property("Count").toInt();
    qDebug() << "开始行数" << intRowStart;
    qDebug() << "开始列数" << intColStart;
    qDebug() << "总行数" << intRows;
    qDebug() << "总列数" << intCols;

    for (int i = intRowStart; i < intRowStart + intRows; i++)
    {
        for (int j = intColStart; j < intColStart + intCols; j++)
        {
            QAxObject * range = worksheet->querySubObject("Cells(int,int)", i, j );
            qDebug() << i << j << range->property("Value");
        }
    }
    excel.setProperty("DisplayAlerts", 0);
    workbook->dynamicCall("SaveAs (const QString&)", QDir::toNativeSeparators("E:\\wenjian\\cs\\code\\QT\\testExcel3\\temp.xlsx"));
    excel.setProperty("DisplayAlerts", 1);
    workbook->dynamicCall("Close (Boolean)", false);
    excel.dynamicCall("Quit (void)");
    return a.exec();
}

被操作的Excel文件和实战项目1中的一致。

执行代码打印信息如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OIat4Y1r-1677385787836)(C:\Users\10521\AppData\Roaming\Typora\typora-user-images\image-20230226102643579.png)]

实战项目3

代码如下:

// 参考内容: 	https://blog.csdn.net/qq319923400/article/details/80149367
//			    https://www.cnblogs.com/lifexy/p/10743316.html
//			    https://www.cnblogs.com/lifexy/p/10743352.html

#include "widget.h"

#include <QApplication>
#include <QAxObject>		// .pro需要添加: QT += axcontainer
#include <QFileDialog>
#include <QDebug>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;

    QAxObject excel("Excel.Application");		// 关联excel
    excel.setProperty("Visible", true);			// 运行程序时要不要通过excel打开当前编辑的表格
    excel.setProperty("DisplayAlerts", false);		// excel关闭时是否询问要不要保存
    //excel.setProperty("Caption", "Qt Excel");		// 类似于文件类型吧,感觉这个名称意义不大

    QAxObject* workbooks = excel.querySubObject("WorkBooks");
    workbooks->dynamicCall("Add");			// 增加一个excel表格,写两遍会打开两个不同的excel表格
    QAxObject* workbook = excel.querySubObject("ActiveWorkBook");
    QAxObject* worksheet = workbook->querySubObject("WorkSheets(QString)", "Sheet1");  	// 通过sheet名获取sheet1
    // QAxObject* worksheet = workbook->querySubObject("WorkSheets(int)", "1");  	// 通过编号获取sheet1, 结果和上面一样
    worksheet->setProperty("Name", "Shadow3D_1");			// 修改sheet名

    // 行列相关查询
    QAxObject* usedrange = worksheet->querySubObject("UsedRange");
    int intRowStart = usedrange->property("Row").toInt();
    int intColStart = usedrange->property("Column").toInt();
    // 注意: 这里的行和列都是从1开始计数的
    qDebug() << "intRowStart: " << intRowStart << "\tintColStart: " << intColStart;

    // 查看已经使用的最大行数和最大列数
    int intRow = usedrange->querySubObject("Rows")->property("Count").toInt();
    int intCol = usedrange->querySubObject("Columns")->property("Count").toInt();
    qDebug() << "Rows: " << intRow << "\tColumns: " << intCol;

    // 写单元格
    for(int i = 1; i < 10; i++)
        for(int j = 1; j < 10; j++)
            worksheet->querySubObject("Cells(int, int)", i, j)->setProperty("Value2", i+j);

    // 查看已经使用的最大行数和最大列数
    usedrange = worksheet->querySubObject("UsedRange");			// 当重新修改sheet后,必须重新获取UsedRange值才能得到最新的行数和列数。
    QAxObject* rows = usedrange->querySubObject("Rows");
    QAxObject* columns = usedrange->querySubObject("Columns");
    qDebug() << rows << columns;				// 不知道这个rows和columns的值表示什么意义,每次运行都不一样。。。
    intRow = rows->property("Count").toInt();
    intCol = columns->property("Count").toInt();
    qDebug() << "Rows: " << intRow << "\tColumns: " << intCol;

    // 读单元格
    QAxObject* cell = worksheet->querySubObject("Cells(int, int)", 5, 5);	// 获取单元格对象
    QString cellStr = cell->dynamicCall("Value2()").toString();
    qDebug() << "cell: " << cellStr;					// "10"
    qDebug() << "cell: " << cellStr.toUtf8().data();	// 10	( QString 类型转换为 QByteArray,再转换成Char* )

    // 单元格格式
    cell = worksheet->querySubObject("Cells(int, int)", 11, 11);
    cell->setProperty("Value", "bool setProperty(const char *name, const QVariant &value");
    cell->setProperty("RowHeight", 50);
    cell->setProperty("ColumnWidth", 30);
    cell->setProperty("HorizontalAlignment", -4108);	// left:-4131 center:-4108 right:-4152
    cell->setProperty("VerticalAlignment", -4108);	// left:-4161 center:-4108 right:-4107
    cell->setProperty("WrapText", true);	// 单元格内容多时自动换行
    // cell>dynamicCall("ClearContents()");	// 清空单元格内容

    // 设置颜色,字体
    cell = worksheet->querySubObject("Cells(int, int)", 12, 12);	// 获取单元格对象
    cell->setProperty("Value", "Text");
    QAxObject* interior = cell->querySubObject("Interior");
    interior->setProperty("Color", QColor(0, 255, 0));	// 背景颜色: Green
    QAxObject* border = cell->querySubObject("Borders");
    border->setProperty("Color", QColor(0, 0, 255));	// 边框颜色: Blue
    QAxObject* font = cell->querySubObject("Font");
    font->setProperty("Name", QStringLiteral("华文彩云"));
    font->setProperty("Bold", true);
    font->setProperty("Size", 20);
    font->setProperty("Italic", true);
    font->setProperty("Underline", 3);		// 下划线:2	双下划线:3
    font->setProperty("Color", QColor(255, 0, 0));		// 字体颜色: Red

    // 合并拆分单元格
    QString merge_cell;
    merge_cell.append(QChar('A'+20));			// 从第(20+1)列开始,因为'A'表示第一列
    merge_cell.append(QString::number(15));		// 从第15行开始
    merge_cell.append(":");
    merge_cell.append(QChar('A'+21));			// 到(21+1)列结束
    merge_cell.append(QString::number(16));		// 到第16列结束
    QAxObject* merge_range = worksheet->querySubObject("Range(const QString&)", merge_cell);
    merge_range->setProperty("MergeCells", true);		// 合并单元格
//    merge_range->setProperty("MergeCells", false);	// 拆分单元格


    // 文件保存
    QString fileName = QFileDialog::getSaveFileName(NULL, QStringLiteral("Save File As"), QStringLiteral("Shadow3D"), QStringLiteral("EXCEL(*.xlsx | *.xls)"));
    workbook->dynamicCall("SaveAs(conse QString&)", QDir::toNativeSeparators(fileName));

    // 关闭文件
    workbook->dynamicCall("Close(Boolean)", false);
    // 退出excel
    excel.dynamicCall("Quit(void)");
    // delete excel;	// 如果excel是通过new方式建立在堆上,要记得释放。

    w.show();
    return a.exec();
}

实战项目4

widget.h

#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <ActiveQt/QAxObject>

QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE

class Widget : public QWidget
{
    Q_OBJECT

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();

    void OpenExcel();
    void AddNewExcel();
    void SaveAndClose();
    int GetRowsCount();
    QString GetCell(int row, int column);
    QString GetCell(QString numer);
    void SetCell(int row, int column, QString value);
    void SetCell(QString number, QString value);
    void SetCellColor(int row, int column, QColor color);

private:
    Ui::Widget *ui;

    QAxObject *m_pExcel;
    QAxObject *m_pWorkBooks;
    QAxObject *m_pWorkBook;
    QAxObject *m_pWorkSheets;
    QAxObject *m_pWorkSheet;
};
#endif // WIDGET_H

widget.cpp

#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QDir>

Widget::Widget(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::Widget)
    , m_pExcel(nullptr)
    , m_pWorkBooks(nullptr)
    , m_pWorkBook(nullptr)
    , m_pWorkSheets(nullptr)
    , m_pWorkSheet(nullptr)
{
    ui->setupUi(this);

    // 连接excel 控件
    m_pExcel = new QAxObject("Excel.Application");
    // m_pExcel->setControl("Excel.Applicatio");

    // 设置操作excel时不打开excel窗体
    m_pExcel->dynamicCall("SetVisible(bool Visible)",false);

    // 设置不显示任何警告信息
    m_pExcel->setProperty("DisplayAlert",false);

}

// 打开已有的excel
void Widget::OpenExcel()
{
    QString strExcelPath = "C:\\Users\\Qcx\\Desktop\\test.xlsx";
    // 获取当前工作簿
    m_pWorkBooks = m_pExcel->querySubObject("WorkBooks");
    // 打开指定工作簿
    m_pWorkBook = m_pWorkBooks->querySubObject("Open(const QString&)",strExcelPath);

    if(m_pWorkBook)
    {
        qDebug()<<"Open Excel Success!";
    }

    // 获取sheets
    m_pWorkSheets = m_pWorkBook->querySubObject("Sheets");
    // 获取某个sheet
    m_pWorkSheet = m_pWorkSheets->querySubObject("Item(int)",1);
}

// 创建新的Excel
void Widget::AddNewExcel()
{
    // 获取当前工作簿
    m_pWorkBooks = m_pExcel->querySubObject("WorkBooks");

    m_pWorkBooks->dynamicCall("Add");
    m_pWorkBook = m_pExcel->querySubObject("ActiveWorkBook");
    m_pWorkSheets = m_pWorkBook->querySubObject("Sheets");
    m_pWorkSheet = m_pWorkSheets->querySubObject("Item(int)",1);
}

// 保存并关闭Excel
void Widget::SaveAndClose()
{
    QString strSavePath = "C:\\Users\\Qcx\\Desktop\\temp.xlsx";
    // 保存文件,一定要将路径中的'/'转为'\\',前者只能被Qt识别
    m_pWorkBook->dynamicCall("SaveAs(const QString&)", QDir::toNativeSeparators(strSavePath));
    // 关闭文件
    m_pWorkBook->dynamicCall("Close()");
    // 关闭excel
    m_pExcel->dynamicCall("Quit()");
    delete m_pExcel;
    m_pExcel = nullptr;
}

// 获取行数
int Widget::GetRowsCount()
{
    int iRows = 0;
    QAxObject *pRows = m_pWorkSheet->querySubObject("Rows");
    iRows = pRows->property("Count").toInt();
    return  iRows;
}

// 获取单元格内容,行号+列号
QString Widget::GetCell(int row, int column)
{
    QAxObject *pCell = m_pWorkSheet->querySubObject("Range(int, int)", row, column);
    return  pCell->property("Value").toString();
}

// 获取单元格内容,单元格标号如: A1,C5
QString Widget::GetCell(QString number)
{
    QAxObject *pCell = m_pWorkSheet->querySubObject("Range(QString)", number);
    return  pCell->property("Value").toString();
}

// 设置单元格内容,行号+列号
void Widget::SetCell(int row, int column, QString value)
{
    QAxObject *pCell = m_pWorkSheet->querySubObject("Range(int, int)", row, column);
    pCell->setProperty("Value", value);    
}

// 设置单元格内容,单元格标号如: A1,C5
void Widget::SetCell(QString number, QString value)
{
    QAxObject *pCell = m_pWorkSheet->querySubObject("Range(QString)", number);
    pCell->setProperty("Value", value);   
}

// 设置单元格颜色的方式与设置值的方式一样,都是先获取到单元格,再设置属性
void Widget::SetCellColor(int row, int column, QColor color)
{
    QAxObject *pCell = m_pWorkSheet->querySubObject("Range(int, int)", row, column);
    QAxObject *pInterior = pCell->querySubObject("Interior");
    pInterior->setProperty("Color", color);    
}

Widget::~Widget()
{
    delete ui;
}

可能遇到的坑

在进行关闭操作时,明明执行没有任何错误,指针也没有异常,但就是Excel进程没有被杀死,我遇到的情况是电脑安装了福昕阅读器,会占用Excel的com接口。有两种解决方案:
1、暴力简单,直接卸载福昕阅读器;
2、打开Excel --> 选项 --> 加载项 --> 最下方的管理 --> 选中COM加载项 --> 转到 --> 取消FoxitReader PDF Creator COM Add-in。

实战项目5-封装好的库

简单封装代码

头文件 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每个对象能够得到正确的析构

项目实战6-封装好的库

exceloperator.h

#ifndef EXCELOPERATOR_H
#define EXCELOPERATOR_H

#include <QObject>
#include <ActiveQt/QAxObject>
#include <QDebug>
#include <QDir>

class ExcelOperator : public QObject
{
    Q_OBJECT
public:
    explicit ExcelOperator(QObject *parent = nullptr);
    ~ExcelOperator();
    //打开文件
    bool open(QString path);
    //关闭文件
    bool close();
    //获取工作表数量
    int getSheetsCount();
    //根据名称创建工作表
    QAxObject* addSheet(QString name);
    //根据名称删除工作表
    bool delSheet(QString name);
    //根据编号删除工作表
    bool delSheet(int index);
    //根据名称获取工作表
    QAxObject* getSheet(QString name);
    //根据编号获取工作表
    QAxObject* getSheet(int index);
    //获取行对象
    QAxObject* getRows(QAxObject* pSheet);
    //获取行数
    int getRowsCount(QAxObject* pSheet);
    //获取列对象
    QAxObject* getColumns(QAxObject* pSheet);
    //获取列数
    int getColumnsCount(QAxObject* pSheet);
    //根据行列值获取单元格值, 如: 3行,5列
    QString getCell(QAxObject* pSheet, int row, int column);
    //根据行列编号获取单元格值, 如: "F6"
    QString getCell(QAxObject* pSheet, QString number);
    //根据行列值设置单元格值
    bool setCell(QAxObject* pSheet, int row, int column, QString value);
    //根据行列编号设置单元格值
    bool setCell(QAxObject* pSheet, QString number, QString value);


signals:

public slots:
private:
    QAxObject*      m_pExcel;
    QAxObject*      m_pWorksheets;
    QAxObject*      m_pWorkbook;
    QString         m_strPath;
};

#endif // EXCELOPERATOR_H

exceloperator.c

#include "exceloperator.h"
#include <objbase.h>

ExcelOperator::ExcelOperator(QObject *parent) : QObject(parent)
  , m_pExcel(NULL)
  , m_pWorksheets(NULL)
  , m_pWorkbook(NULL)
{

}

ExcelOperator::~ExcelOperator()
{
    close();
}

bool ExcelOperator::open(QString path)
{
    m_strPath = path;
    QAxObject *pWorkbooks = NULL;
    CoInitializeEx(NULL, COINIT_MULTITHREADED);
    m_pExcel = new(std::nothrow) QAxObject();
    if (NULL == m_pExcel) {
        qCritical()<<"创建Excel对象失败...";
        return false;
    }
    try {
        m_pExcel->setControl("Excel.Application");
        m_pExcel->dynamicCall("SetVisible(bool)", false); //true 表示操作文件时可见,false表示为不可见
        m_pExcel->setProperty("DisplayAlerts", false);
        pWorkbooks = m_pExcel->querySubObject("WorkBooks");
        pWorkbooks->dynamicCall("Add");
        m_pWorkbook = m_pExcel->querySubObject("ActiveWorkBook");
        qDebug()<<"excel path: "<<m_strPath;

        // 获取打开的excel文件中所有的工作sheet
        m_pWorksheets = m_pWorkbook->querySubObject("WorkSheets");
    } catch (...) {
        qCritical()<<"打开文件失败...";
        return false;
    }

    return true;
}

bool ExcelOperator::close()
{
    qDebug()<<"excel close...";
    if (m_pExcel)
    {
        qDebug()<<"closing...";
        m_pWorkbook->dynamicCall("SaveAs(const QString&)", QDir::toNativeSeparators(m_strPath));
        m_pWorkbook->dynamicCall("Close()");
        m_pExcel->dynamicCall("Quit()");
        delete m_pExcel;
        m_pExcel = NULL;
    }
    return true;
}

int ExcelOperator::getSheetsCount()
{
    int count =  0;
    count = m_pWorksheets->property("Count").toInt();
    return count;
}


QAxObject* ExcelOperator::addSheet(QString name)
{
    QAxObject *pWorkSheet = NULL;
    try {
        int count = m_pWorksheets->property("Count").toInt();  //获取工作表数目
        QAxObject *pLastSheet = m_pWorksheets->querySubObject("Item(int)", count);
        pWorkSheet = m_pWorksheets->querySubObject("Add(QVariant)", pLastSheet->asVariant());
        pLastSheet->dynamicCall("Move(QVariant)", pWorkSheet->asVariant());
        pWorkSheet->setProperty("Name", name);  //设置工作表名称
    } catch (...) {
        qCritical()<<"创建sheet失败...";
    }
    return pWorkSheet;
}

bool ExcelOperator::delSheet(QString name)
{
    try {
        QAxObject *pFirstSheet = m_pWorksheets->querySubObject("Item(QString)", name);
        pFirstSheet->dynamicCall("delete");
    } catch (...) {
        qCritical()<<"删除sheet失败...";
        return false;
    }
    return true;
}

bool ExcelOperator::delSheet(int index)
{
    try {
        QAxObject *pFirstSheet = m_pWorksheets->querySubObject("Item(int)", index);
        pFirstSheet->dynamicCall("delete");
    } catch (...) {
        qCritical()<<"删除sheet失败...";
        return false;
    }
    return true;
}

QAxObject* ExcelOperator::getSheet(QString name)
{
    QAxObject* pWorkSheet = NULL;
    try {
        pWorkSheet = m_pWorksheets->querySubObject("Item(QString)", name);
    } catch (...) {
        qCritical()<<"获取sheet失败...";
    }
    return pWorkSheet;
}

QAxObject* ExcelOperator::getSheet(int index)
{
    QAxObject* pWorkSheet = NULL;
    try {
        pWorkSheet = m_pWorksheets->querySubObject("Item(int)", index);
    } catch (...) {
        qCritical()<<"获取sheet失败...";
    }
    return pWorkSheet;
}

QAxObject* ExcelOperator::getRows(QAxObject* pSheet)
{
    QAxObject* pRows = NULL;
    try {
        pRows = pSheet->querySubObject("Rows");
    } catch (...) {
        qCritical()<<"获取行失败...";
    }
    return pRows;
}

int ExcelOperator::getRowsCount(QAxObject* pSheet)
{
    int rows = 0;
    try {
        QAxObject* pRows = getRows(pSheet);
        rows = pRows->property("Count").toInt();
    } catch (...) {
        qCritical()<<"获取行数失败...";
    }
    return rows;
}

QAxObject* ExcelOperator::getColumns(QAxObject* pSheet)
{
    QAxObject* pColumns = NULL;
    try {
        pColumns = pSheet->querySubObject("Columns");
    } catch (...) {
        qCritical()<<"获取列失败...";
    }
    return pColumns;
}

int ExcelOperator::getColumnsCount(QAxObject* pSheet)
{
    int columns = 0;
    try {
        QAxObject* pColumns = getColumns(pSheet);
        columns = pColumns->property("Count").toInt();
    } catch (...) {
        qCritical()<<"获取列数失败...";
    }
    return columns;
}

QString ExcelOperator::getCell(QAxObject* pSheet, int row, int column)
{
    QString strCell = "";
    try {
        QAxObject* pCell = pSheet->querySubObject("Cells(int, int)", row, column);
        strCell = pCell->property("Value").toString();
    } catch (...) {
        qCritical()<<"获取单元格信息失败...";
    }

    return strCell;
}

QString ExcelOperator::getCell(QAxObject* pSheet, QString number)
{
    QString strCell = "";
    try {
        QAxObject* pCell = pSheet->querySubObject("Range(QString)", number);
        strCell = pCell->property("Value").toString();
    } catch (...) {
        qCritical()<<"获取单元格信息失败...";
    }

    return strCell;
}

bool ExcelOperator::setCell(QAxObject* pSheet, int row, int column, QString value)
{
    try {
        QAxObject* pCell = pSheet->querySubObject("Cells(int, int)", row, column);
        pCell->setProperty("Value", value);
    } catch (...) {
        qCritical()<<"写入单元格信息失败...";
        return false;
    }
    return true;
}

bool ExcelOperator::setCell(QAxObject* pSheet, QString number, QString value)
{
    try {
        QAxObject* pCell = pSheet->querySubObject("Range(QString)", number);
        pCell->setProperty("Value", value);
    } catch (...) {
        qCritical()<<"写入单元格信息失败...";
        return false;
    }
    return true;
}

六、一些小技巧

读取excel中的数据

前面说过了怎么保存Qt中的数据到excel,现在再来说一说怎么读取excel中的数据到Qt(无论是读取到表格中,还是保存到数据模型中,核心的一点都是将excel中的数据读取到Qt的容器中,然后只要能读取容器中的数据,那么后面是保存到表格还是模型,都是很简单的事了)。

假设我们已经有这么一个由Qt保存好的excel表格,现在想要读取其中的数据

img

代码如下:

void Widget::on_readbtn_clicked()
{
    QAxObject *excel = new QAxObject(this);//建立excel操作对象
    excel->setControl("Excel.Application");//连接Excel控件
    excel->setProperty("Visible", false);//显示窗体看效果,选择ture将会看到excel表格被打开
    excel->setProperty("DisplayAlerts", true);//显示警告看效果
    QAxObject *workbooks = excel->querySubObject("WorkBooks");//获取工作簿(excel文件)集合
    QString str = QFileDialog::getOpenFileName(this,"打开",
                                                    QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation),"Excel 文件(*.xls *.xlsx)");
    workbooks->dynamicCall("Open(const QString&)", str);//打开刚才选定的excel
    QAxObject *workbook = excel->querySubObject("ActiveWorkBook");
    QAxObject *worksheet = workbook->querySubObject("WorkSheets(int)",1);
    QAxObject *usedRange = worksheet->querySubObject("UsedRange");//获取表格中的数据范围

    QVariant var = usedRange->dynamicCall("Value");//将所有的数据读取刀QVariant容器中保存
    QList<QList<QVariant>> excel_list;//用于将QVariant转换为Qlist的二维数组
    QVariantList varRows=var.toList();
    if(varRows.isEmpty())
     {
         return;
     }
    const int row_count = varRows.size();
    QVariantList rowData;
    for(int i=0;i<row_count;++i)
    {
        rowData = varRows[i].toList();
        excel_list.push_back(rowData);
    }//转换完毕
    qDebug()<<excel_list.at(3).at(1).toInt();
 
    //然后将二维数组Qlist<Qlist<QVariant>>中的数据读取出来,到表格或者数据模型中,具体在这里过程省略
    
    workbook->dynamicCall( "Close(Boolean)", false );
    excel->dynamicCall( "Quit(void)" );
    delete excel;//因为前面选择了不显示excel窗口,如果这里不删除excel的话,excel依然是打开的,只是我们看不见(因为被隐藏起来了)
}

其中标红的那一行代码可以用来测试是否能成功读取出二维数组中的数据。

当输入 qDebug()<<excel_list.at(3);时,打印的结果为img

因为excel中一共有24列,但是只有6行中有数据,因此可以看到这其实是把表格中第4行的数据(因为是从零开始0,1,2,3)全部读取出来了。

当输入 qDebug()<<excel_list.at(3).at(3);时,打印结果为

img

img

即打印出了第4行第4列的数据.。

当输入qDebug()<<excel_list.at(3).at(3).toInt();时,打印的结果就是数字3。

由此,读取excel工作基本完成,至于怎么保存到表格中或者模型中,就看自己喜好啦。

*Tips: 由于excel中保存的数据读取到Qlist的数组中之后,这不一定是个规则的二维数组,因此在必要的情况下可以将excel的第一二排留出来用于保存信息,这些信息可以用来判断读取时按什么规则来读取。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

QT使用QAxObject读取Excel教程-全网最全 的相关文章

  • 为什么我身边有人说我没有眼力见,眼力见是什么,应该怎么做?

    眼力见 主要描述一个人善于察言观色 为人殷勤 xff0c 还很有礼貌的素养 其实 xff0c 只要把自己的 姿态放低 xff0c 眼力见 自然就见长 大家有没有这种感觉 xff0c 工作中不少领导 前辈经常把 眼力见 这个词挂在嘴边 xff
  • 反应慢,没有眼力劲,看起来给别人一种老实的感觉,做起事情不太利索,接触让别人感觉有点傻。怎么办?

    内向的人 xff0c 缺乏社交 xff0c 就缺乏来自从小到大关系网的 经验叠加 比如 xff0c 你没钱 xff0c 但你有个哥们儿是富二代 xff0c 你们经常一起混 xff0c 那么你会从他身上得到很多 富有家庭对待事务的态度和想法
  • 遇事没有眼力见,反应不灵活,该怎么改善?

    说实话 xff0c 我也属于这种类型的人 说白了就是头脑简单 这个跟成长环境有关系 xff0c 没有经历过需要你去思考太多 xff0c 分析太多 xff0c 最后根据情况作出当时应该 需要做出的回应 和经历有关 如果你现在认为这个事情非常重
  • 如何快速读懂开源代码?

    文章目录 RUN起来 调试 把控关键数据结构和函数 从小的开始 关注一个模块 工具 一 阅读开源代码存在的一些误区 二 阅读代码的心态 三 阅读源码与 辅助材料 四 如何阅读开源代码 gdb 高级调试实战教程 电子书下载链接 xff1a 1
  • 关于我转行嵌入式的那些事

    文章目录 为什么想转行了 xff1f 一 工作环境问题 二 无休止的出差加班和混乱的作息时间 三 工作压力大 四 薪资上限低 xff0c 行业前景差 为什么选择嵌入式 转行前的学习 一 单片机开发 二 Linux应用开发 三 Linux驱动
  • QT的UDP通信详解

  • 这一年我的书单!

    已剪辑自 https mp weixin qq com s Uy3hsbQQY3U4h43rdWr8qA 昨天写了2022年的一些感悟 xff1a 我这一年的感悟 xff0c 在文章里我提到读书在精不在多 xff0c 能指导生活工作中实践的
  • 如何专业地命名嵌入式软件版本?

    已剪辑自 https mp weixin qq com s F XhvYy0IjTrdHIu2BLhNA 不知道大家发布软件的时候 xff0c 版本号是怎么命名的 xff1f 最常见的就是V1 0 0这种简单的形式命名 甚至有些同事直接用V
  • 万字长文细说 Code Review 的正确姿势

    已剪辑自 https mp weixin qq com s GWLlRkF1b6LnyIYZi NSdQ 随着研发团队规模的逐步扩大 xff0c 新项目及新成员越来越多 xff0c 如何做好 code review xff0c 把控研发人员
  • 50条C语言奇技淫巧,精品干货!

    已剪辑自 https mp weixin qq com s vvdvVMVmx3i 6eXjUUYfBQ 本文汇总了50条C语言奇技淫巧 xff0c 希望能对大家有所帮助 01 宏定义用do while 0 如果定义的宏函数后面有多条语句
  • FreeRTOS学习(一)

    裸机与RTOS对比 裸机 xff1a 又称为前后台系统 xff0c 前台系统指的是中断服务函数 xff0c 后台系统指的大循环 xff0c 即应用程序 实时性差 xff1a xff08 应用程序轮流执行 xff09 delay xff1a
  • 如何画架构图?

    在我们做系统架构设计时 xff0c 如何快速的向外界传达我们的设计思路 4 43 1试图适合我们厘清思路 表达自己的想法 在我们汇报 xff0c 争取领导层的认同支持更适合用架构图来表述我们的观点 架构图包括总体架构 逻辑架构 应用架构 技
  • 怎么做串口调试软件?

    嗯 说一下我自己写的串口助手吧 xff0c 名字叫 Bittly xff0c 样子呢长下面这个样子 Bittly 指令调试界面 1 需求确认 一开始使用的是类似于XCOM或者SSCOM之类的串口调试助手 xff0c 他们的优点是体积小 xf
  • 【需求专题】如何写好需求——INCOSE需求编写指南(1)

    已剪辑自 https mp weixin qq com s Z5VBTyV6j07JylDdOsFSxQ 编者按 如何写好需求是INCOSE 需求工作组编写的需求文本化表达指南 本指南是专门讲述如何在系统工程中对需求进行文本化表达 xff0
  • 怎么提高自己的系统设计和架构理论水平?

    文章目录 前言 1 无锁化 1 1 串行无锁 1 2 结构无锁 2 零拷贝 2 1 内存映射 2 2 零拷贝 3 序列化 3 1 分类 3 2 性能指标 3 3 选型考量 4 池子化 4 1 内存池 4 2 线程池 4 3 连接池 4 4
  • 30+男生程序员中年如何破局

    已剪辑自 https zhuanlan zhihu com p 596751971 1 最顶级的程序员根据自己的经验拼paper 拼专利 xff0c 成为不可替代的专家 最厉害的程序员拼的不是代码写的多牛逼 而是有多少paper多少顶尖专利
  • 为啥AI难落地?

    总在说AI落地难 xff0c 那为啥难落地 xff1f 以最典型的智慧城市业务来说 xff0c 就是接入网络摄像头 xff0c 然后识别里面的人 xff0c 判断是不是抽烟 打架 闯红灯 不带安全帽等 首先是连接网络摄像机 xff0c GB
  • 搞技术,如何写好技术文档?

    已剪辑自 https mp weixin qq com s OtSwtMyeifoc7ED35a vEA 嵌入式方案设计文档 xff0c 到底应该怎么写 xff1f 你是不是从来没有想过这个问题 xff1f 很多技术人自己非常轻视技术文档的
  • 用125行C语言编写一个简单的16位虚拟机

    已剪辑自 https mp weixin qq com s ikrpGtssoKpumHXhrQdh8Q 博文地址 xff1a 改博文用图文代码的方式详细描述了实现的具体过程 xff0c 包含每一条指令的含义 系统虚拟机 xff0c 可完全
  • RT-Thread操作系统的FreeRTOS兼容层

    已剪辑自 https mp weixin qq com s 2BjJyieMr97NQhO76DQ3hw Github地址 https github com RT Thread packages FreeRTOS Wrapper 本项目是2

随机推荐

  • 嵌入式开发既要代码小,又想速度快,该如何优化?

    已剪辑自 https mp weixin qq com s HaoPN0upS8OEheXpSHWBFA 素材来源 xff1a 网络素材 整理 xff1a 技术让梦想更伟大 李肖遥 对程序进行优化 xff0c 通常是指优化程序代码或程序执行
  • 小白学C语言编程(for语句无限制循环)

    问题 xff1a 怎么样无限制输出一个比1大的数 xff1f span class token macro property span class token directive keyword include span span clas
  • 嵌入式开发打印,我放弃了printf

    已剪辑自 https mp weixin qq com s GGZ38dUITlS6w9hnMbzsvg 对于printf xff0c 相信不用我过多介绍 xff0c 大家在初学C语言时用得最多的信息输出接口函数应该就是printf了 对于
  • 自动驾仿真测试平台干货内容梳理

    已剪辑自 https mp weixin qq com s Ftv2rgiGW6FGVQgMz4A9PQ 1 自动驾驶仿真平台的关键构成 自动驾驶仿真平台需支持车辆动力学仿真 环境感知传感器仿真 交通场景仿真等 xff1b 车辆动力学仿真
  • 自动驾驶域控制器开发和量产的挑战

    已剪辑自 https mp weixin qq com s Sh4ONJxrmvDbfWlcDnXYtQ 过去十多年的汽车智能化和信息化发展产生了一个显著结果就是ECU芯片使用量越来越多 从传统的引擎控制系统 安全气囊 防抱死系统 电动助力
  • STM32属于哈佛结构还是冯诺依曼结构?

    现代的CPU基本上归为冯诺伊曼结构 xff08 也称普林斯顿结构 xff09 和哈佛结构 我们常见的X86架构是冯 诺依曼结构 xff0c 而ARM架构是哈佛结构 一个广泛用于桌面端 xff08 台式 笔记本 服务器 工作站等 xff09
  • 2022年度复盘和2023年目标:在焦虑中探索,在体验中成长,在开放中升华

    文章目录 2022年度复盘工作 xff1a 焦虑 xff0c 认知 xff0c 提升个人工作 xff1a 工作态度需要提升团队工作 xff1a 尊重 真诚 准确清晰完善感悟 个人成长硬能力 xff1a 学习 博客软能力 xff1a 知乎 B
  • 技术部门Leader是不是一定要技术大牛担任?

    现在在腾讯做技术Leader xff0c 之前在阿里 xff0c 绿厂也带过技术团队 xff0c 每家的情况有共同点也有区别 现在总结下来 xff0c 除去特别通用的技术 责任心 沟通 主动性这些 xff0c 作为Leader很关键的个人素
  • 一个人该怎样找到自己真正热爱和擅长的事,并以此规划自己的人生?

    文章目录 一个人该怎样找到自己真正热爱和擅长的事 xff0c 并以此规划自己的人生 xff1f 一 有关擅长的4个错误认知 二 做好这3步 xff0c 拥有擅长之事 1 生成兴趣清单 2 缩小选择范围 3 练练看 三 写在最后 下面这张图
  • 一个适用于单片机的开源网络协议栈

    已剪辑自 https mp weixin qq com s Vpi4E9T5BUo cdCE692V A 移植及使用说明 协议栈支持主流的ARM Cortex系列MCU xff0c 支持Keil MDK IAR等常见IDE 移植的核心工作就
  • 嵌入式软件分层隔离的典范

    已剪辑自 https mp weixin qq com s T7EJEAuXo1CCJa5vPAPVvg 引言 xff1a 嵌入式软件开发分层 模块化是理想状态 xff0c 实际开发中因各种限制而有所取舍 xff0c 但这不妨碍学习参考优秀
  • 软件定时器库

    前后台系统和多任务操作系统 xff0c 在简单功能上差别不大 xff0c 唯一不顺手的就是前后台系统没有合适的软件定时器 封装一层软件定时器接口 xff0c 对后续功能开发事半功倍 定义结构体数组 xff0c 其基础成员如下 xff1a s
  • 解决Raspberry系统ssh默认关闭的问题

    问题描述 xff1a 最近 xff0c 自己动手给树莓派3B安装了一个最新的系统 xff0c 本想通过SSH远程登录系统的时候 xff0c 发现登录不了 xff0c 具体表现为能ping通 xff0c 但是Xshell就是连不上 xff0c
  • mbedtls 库基础及其应用

    文章目录 1 引言1 1 为什么要加密1 2 SSL TLS协议的历史 2 SSL TLS演化2 1 明文时代2 2 对称加密时代2 3 非对称加密时代2 4 公证时代2 5 TLS协议时代2 6 TLS的应用 3 mbedtls3 1 软
  • 动态内存管理及防御性编程

    概述 xff1a C语言的优势是可以直接访问内存地址 xff0c 也就是指针操作 xff0c 但其缺陷也是因为直接内存访问 如何通过防御性编程提前发现问题 xff0c 尽可能减少内存异常产生的后果 xff0c 就是本文的重点 1 内存划分
  • 基于RTOS的软件开发理论

    文章目录 1 RTOS的特点2 任务设计2 1 任务的特性2 2 任务划分的方法2 2 1 设备依赖性任务2 2 2 关键任务2 2 3 紧迫任务2 2 4 数据处理任务2 2 5 触发条件相同的任务2 2 6 运行周期相同的任务2 2 7
  • 面向对象类之间主要的几种关系

    已剪辑自 https mp weixin qq com s ClBuraVUIPhnWceI7m78Xg 嵌入式开发虽然平时C语言用的比较多 xff0c 但面向对象的思维应该是每一位嵌入式软件工程师必备的知识 之前给大家分享过用C语言实现面
  • 世界上最健康的程序员作息表!

    文章目录 7 307 30 8 008 00 8 308 30 9 009 3010 3011 0013 0014 30 15 3016 0017 00 19 0019 3021 4523 0023 30时间 健康的小常识 已剪辑自 htt
  • 30岁了,冒死说几句大实话!

    已剪辑自 https mp weixin qq com s j0yzonrhPPcemDRF6QBVkw 是的 xff0c 我 30 岁了 xff0c 还是周岁 就在这上个周末 xff0c 我度过了自己 30 岁的生日 都说三十而立 xff
  • QT使用QAxObject读取Excel教程-全网最全

    文章目录 一 背景二 介绍基本操作方法获取对象调用动态方法设置和获取属性更多相关 三 使用要求添加模块与excel com连接的方法Excel基本操作 四 具体使用说明五 项目实战实战项目1实战项目2实战项目3实战项目4实战项目5 封装好的