QT中操作Excel还是比较简单的,Qt提供了QAxObject,包装COM组件的类,通过COM通过COM操作使用QAxObject类,使用此类,需要在pro文件中添加"QT += axcontainer "。
基本流程介绍
QAxObject:
QAxObject是Qt提供给程序员从代码中访问Office的对象类,其本质上是一个面向微软操作系统的COM接口,它操作Excel的基本流程如下:
QAxObject将所有Office的工作簿、表格、文档等都作为其子对象,程序员通过调用querySubObject()这个统一的方法来获取各个子对象的实例,再用dynamicCall()方法执行各对象上的具体操作。QT中Excel的使用,在工程配置上需要QT += axcontainer
具体过程
1.启动Excel进程,获取Excel工作簿集
创建Excel进程使用如下语句:
QAxObject *myexcel = new QAxObject("Excel.Application");
其中,myexcel为进行的实例对象名,该名称由用户自己定义,整个程序中引用一致即可。
通过进程获取Excel工作簿集,语句为:
QAxObject *myworks = myexcel->querySubObject("WorkBooks");
其中,myworks是工作簿集的引用,用户可根据需要定义其名称,同样,在程序中也要求引用一致。
有了Excel进程和工作簿集的引用,就可以使用它们对Excel进行一系列文档级别的操作。例如:
myworks->dynamicCall("Add"); //添加一个工作簿
myexcel->querySubObject("ActiveWorkBook"); //获取当前活动的工作簿
2.获取电子表格集
每个Excel工作簿中都可以包含若干电子表格(Sheet),通过打开的当前工作簿获取其所有电子表格的程序语句为:
QAxObject *mysheets = workbook->querySubObject("Sheets");
其中,workbook也是一个QAxObject对象,引用的是当前正在操作的一个活动工作簿。
同理,在获取了电子表格集后,就可以像操作工作簿文档那样,对其中的表格执行各种操作。例如:
mysheets->dynamicCall("Add"); //添加一个表格
workbook->querySubObject("ActiveSheet") //获取工作簿中当前活动表格
sheet->setProperty("Name",字符串) //给表格命名
3.操作单元格及其数据
对Excel的操作最终要落实到对某个电子表格单元格中数据信息的读写上,在Qt中的Excel单元格同样是作为QAxObject对象来看待的,对它的操作通过其所在表格的QAxObject对象句柄执行,如下:
QAxObject *cell = sheet->querySubObject("Range(QVariant, QVariant)", 单元格编号);
cell->dynamicCall("SetValue(const QVariant&)", QVariant(字符串));
这样,就实现了对Excel各个级别对象的灵活操作和使用。
为避免资源无谓消耗和程序死锁,通常在编程结束时还必须通过语句释放该Excel进程所占据的系统资源,如下:
workbook->dynamicCall("Close()"); //关闭工作簿
myexcel->dynamicCall("Quit()"); //退出进程
两种写入方法举例
方法一,直观,但写入低效。
bool MainWindow::saveToExcel(QString &filepath)
{
if(!filepath.isEmpty()){
QAxObject *excel = new QAxObject(this);
excel->setControl("Excel.Application");//连接Excel控件
excel->dynamicCall("SetVisible (bool Visible)","false");//不显示窗体
excel->setProperty("DisplayAlerts", false);//不显示任何警告信息。如果为true那么在关闭是会出现类似“文件已修改,是否保存”的提示
QAxObject *workbooks = excel->querySubObject("WorkBooks");//获取工作簿集合
workbooks->dynamicCall("Add");//新建一个工作簿
//QAxObject *workbook = workbooks->querySubObject("Open(QString&)",filepath);
QAxObject *workbook = excel->querySubObject("ActiveWorkBook");//获取当前工作簿
QAxObject *worksheets = workbook->querySubObject("Sheets");//获取工作表集合
QAxObject *worksheet = worksheets->querySubObject("Item(int)",1);//获取工作表集合的工作表1,即sheet1
QAxObject *cellA,*cellB,*cellC,*cellD,*cellE,*cellF;
//设置标题
int cellrow=1;
QString A="A"+QString::number(cellrow);//设置要操作的单元格,如A1
QString B="B"+QString::number(cellrow);
QString C="C"+QString::number(cellrow);
QString D="D"+QString::number(cellrow);
QString E="E"+QString::number(cellrow);
QString F="F"+QString::number(cellrow);
cellA = worksheet->querySubObject("Range(QVariant, QVariant)",A);//获取单元格
cellB = worksheet->querySubObject("Range(QVariant, QVariant)",B);
cellC=worksheet->querySubObject("Range(QVariant, QVariant)",C);
cellD=worksheet->querySubObject("Range(QVariant, QVariant)",D);
cellE=worksheet->querySubObject("Range(QVariant, QVariant)",E);
cellF=worksheet->querySubObject("Range(QVariant, QVariant)",F);
cellA->dynamicCall("SetValue(const QVariant&)",QVariant("通道号"));//设置单元格的值
cellB->dynamicCall("SetValue(const QVariant&)",QVariant(""));
cellC->dynamicCall("SetValue(const QVariant&)",QVariant("通道1"));
cellD->dynamicCall("SetValue(const QVariant&)",QVariant("通道2"));
cellE->dynamicCall("SetValue(const QVariant&)",QVariant("通道3"));
cellF->dynamicCall("SetValue(const QVariant&)",QVariant("通道4"));
cellrow++;
A="A"+QString::number(cellrow);//设置要操作的单元格,如A2
B="B"+QString::number(cellrow);
C="C"+QString::number(cellrow);
D="D"+QString::number(cellrow);
E="E"+QString::number(cellrow);
F="F"+QString::number(cellrow);
cellA = worksheet->querySubObject("Range(QVariant, QVariant)",A);//获取单元格
cellB = worksheet->querySubObject("Range(QVariant, QVariant)",B);
cellC=worksheet->querySubObject("Range(QVariant, QVariant)",C);
cellD=worksheet->querySubObject("Range(QVariant, QVariant)",D);
cellE=worksheet->querySubObject("Range(QVariant, QVariant)",E);
cellF=worksheet->querySubObject("Range(QVariant, QVariant)",F);
cellA->dynamicCall("SetValue(const QVariant&)",QVariant("量纲类型"));//设置单元格的值
cellB->dynamicCall("SetValue(const QVariant&)",QVariant(""));
cellC->dynamicCall("SetValue(const QVariant&)",QVariant("MPa"));
cellD->dynamicCall("SetValue(const QVariant&)",QVariant("MPa"));
cellE->dynamicCall("SetValue(const QVariant&)",QVariant("MPa"));
cellF->dynamicCall("SetValue(const QVariant&)",QVariant("L/M"));
cellrow++;
int rows=mTbData.recList.size();
for(int i=0;i<rows;i++){
A="A"+QString::number(cellrow);//设置要操作的单元格,如A6
B="B"+QString::number(cellrow);
C="C"+QString::number(cellrow);
D="D"+QString::number(cellrow);
E="E"+QString::number(cellrow);
F="F"+QString::number(cellrow);
cellA = worksheet->querySubObject("Range(QVariant, QVariant)",A);//获取单元格
cellB = worksheet->querySubObject("Range(QVariant, QVariant)",B);
cellC=worksheet->querySubObject("Range(QVariant, QVariant)",C);
cellD=worksheet->querySubObject("Range(QVariant, QVariant)",D);
cellE=worksheet->querySubObject("Range(QVariant, QVariant)",E);
cellF=worksheet->querySubObject("Range(QVariant, QVariant)",F);
cellA->dynamicCall("SetValue(const QVariant&)",QVariant(i+1));
cellB->dynamicCall("SetValue(const QVariant&)",QVariant(mTbData.recList.at(i).value("collect_date")));
cellC->dynamicCall("SetValue(const QVariant&)",QVariant(mTbData.recList.at(i).value("shelf_value0")));
cellD->dynamicCall("SetValue(const QVariant&)",QVariant(mTbData.recList.at(i).value("shelf_value1")));
cellE->dynamicCall("SetValue(const QVariant&)",QVariant(mTbData.recList.at(i).value("shelf_value2")));
cellF->dynamicCall("SetValue(const QVariant&)",QVariant(mTbData.recList.at(i).value("X_Liu_Value3")));
cellrow++;
}
//workbook->dynamicCall("Save()"); //!保存文件
workbook->dynamicCall("SaveAs(const QString&)",QDir::toNativeSeparators(filepath));//保存至filepath,注意一定要用QDir::toNativeSeparators将路径中的"/"转换为"\",不然一定保存不了。
workbook->dynamicCall("Close()");
excel->dynamicCall("Quit()");
if (excel)
{
delete excel;
excel = NULL;
}
return true;
//
}
return false;
}
方法二,写入效率高,推荐使用这种。
void MainWindow::on_btn_export_clicked()
{
QString filepath=QFileDialog::getSaveFileName(this,tr("保存数据"),".",tr("Microsoft Office 2007 (*.xlsx)"));//获取保存路径
auto ret = saveToExcel_fast(filepath);
if(ret){
QMetaObject::invokeMethod(qApp, []{
QMessageBox::information(0 ,"提示" ,"数据保存成功", QMessageBox::Ok | QMessageBox::Default , 0 );
});
}
/*
QFuture<void> future = QtConcurrent::run([this,filepath](){
QString fpath = filepath;
auto ret = saveToExcel_fast(fpath);
if(ret){
QMetaObject::invokeMethod(qApp, []{
QMessageBox::information(0 ,"提示" ,"数据保存成功", QMessageBox::Ok | QMessageBox::Default , 0 );
});
}
});*/
}
bool MainWindow::saveToExcel_fast(QString &filepath)
{
if(!filepath.isEmpty()){
QAxObject *excel = new QAxObject(this);
excel->setControl("Excel.Application");//连接Excel控件
excel->dynamicCall("SetVisible (bool Visible)","false");//不显示窗体
excel->setProperty("DisplayAlerts", false);//不显示任何警告信息。如果为true那么在关闭是会出现类似“文件已修改,是否保存”的提示
QAxObject *workbooks = excel->querySubObject("WorkBooks");//获取工作簿集合
workbooks->dynamicCall("Add");//新建一个工作簿
//QAxObject *workbook = workbooks->querySubObject("Open(QString&)",filepath);
QAxObject *workbook = excel->querySubObject("ActiveWorkBook");//获取当前工作簿
QAxObject *worksheets = workbook->querySubObject("Sheets");//获取工作表集合
QAxObject *worksheet = worksheets->querySubObject("Item(int)",1);//获取工作表集合的工作表1,即sheet1
QAxObject *cellA,*cellB,*cellC,*cellD,*cellE,*cellF;
//设置标题
int cellrow=1;
QString A="A"+QString::number(cellrow);//设置要操作的单元格,如A1
QString B="B"+QString::number(cellrow);
QString C="C"+QString::number(cellrow);
QString D="D"+QString::number(cellrow);
QString E="E"+QString::number(cellrow);
QString F="F"+QString::number(cellrow);
cellA = worksheet->querySubObject("Range(QVariant, QVariant)",A);//获取单元格
cellB = worksheet->querySubObject("Range(QVariant, QVariant)",B);
cellC=worksheet->querySubObject("Range(QVariant, QVariant)",C);
cellD=worksheet->querySubObject("Range(QVariant, QVariant)",D);
cellE=worksheet->querySubObject("Range(QVariant, QVariant)",E);
cellF=worksheet->querySubObject("Range(QVariant, QVariant)",F);
cellA->dynamicCall("SetValue(const QVariant&)",QVariant("通道号"));//设置单元格的值
cellB->dynamicCall("SetValue(const QVariant&)",QVariant(""));
cellC->dynamicCall("SetValue(const QVariant&)",QVariant("通道1"));
cellD->dynamicCall("SetValue(const QVariant&)",QVariant("通道2"));
cellE->dynamicCall("SetValue(const QVariant&)",QVariant("通道3"));
cellF->dynamicCall("SetValue(const QVariant&)",QVariant("通道4"));
cellrow++;
A="A"+QString::number(cellrow);//设置要操作的单元格,如A2
B="B"+QString::number(cellrow);
C="C"+QString::number(cellrow);
D="D"+QString::number(cellrow);
E="E"+QString::number(cellrow);
F="F"+QString::number(cellrow);
cellA = worksheet->querySubObject("Range(QVariant, QVariant)",A);//获取单元格
cellB = worksheet->querySubObject("Range(QVariant, QVariant)",B);
cellC=worksheet->querySubObject("Range(QVariant, QVariant)",C);
cellD=worksheet->querySubObject("Range(QVariant, QVariant)",D);
cellE=worksheet->querySubObject("Range(QVariant, QVariant)",E);
cellF=worksheet->querySubObject("Range(QVariant, QVariant)",F);
cellA->dynamicCall("SetValue(const QVariant&)",QVariant("量纲类型"));//设置单元格的值
cellB->dynamicCall("SetValue(const QVariant&)",QVariant(""));
cellC->dynamicCall("SetValue(const QVariant&)",QVariant("MPa"));
cellD->dynamicCall("SetValue(const QVariant&)",QVariant("MPa"));
cellE->dynamicCall("SetValue(const QVariant&)",QVariant("MPa"));
cellF->dynamicCall("SetValue(const QVariant&)",QVariant("L/M"));
cellrow++;
QList<QVariant> rowdata;
int rows=mTbData.recList.size();
for (int i = 0; i < rows;i++) {
QList<QVariant> aline;
aline.append(QVariant(i+1));
aline.append(QVariant(mTbData.recList.at(i).value("collect_date")));
aline.append(QVariant(mTbData.recList.at(i).value("shelf_value0")));
aline.append(QVariant(mTbData.recList.at(i).value("shelf_value1")));
aline.append(QVariant(mTbData.recList.at(i).value("shelf_value2")));
aline.append(QVariant(mTbData.recList.at(i).value("X_Liu_Value3")));
QVariant conv(aline);
rowdata.append(conv);
}
QVariant d(rowdata);
QString nA = "A" + QString::number(cellrow) + ":" + "F" + QString::number(rows+cellrow);
qDebug() << nA;
QAxObject *range = worksheet->querySubObject("Range(QString)", nA);
if(NULL == range || range->isNull())
{
return false;
}
range->setProperty("Value", d);
//workbook->dynamicCall("Save()"); //!保存文件
workbook->dynamicCall("SaveAs(const QString&)",QDir::toNativeSeparators(filepath));//保存至filepath,注意一定要用QDir::toNativeSeparators将路径中的"/"转换为"\",不然一定保存不了。
workbook->dynamicCall("Close()");
excel->dynamicCall("Quit()");
if (excel)
{
delete excel;
excel = NULL;
}
return true;
//
}
return false;
}
操作Excel类简单封装
#ifndef MYEXCEL_H
#define MYEXCEL_H
#include <QString>
#include <QVariant>
#include <QColor>
#include <QAxObject>
/*excel操作*/
enum class ColumnType{
ColumnA = 1,
ColumnB = 2,
ColumnC = 3,
ColumnD = 4,
ColumnE = 5,
ColumnF = 6,
ColumnG = 7,
ColumnH = 8,
ColumnI = 9
};
class MyExcel
{
public:
MyExcel();
/**
* @brief writeOneTable 批量写入数据 效率高
* @param filepath 文件路径
* @param startRow 起始的列
* @param table 要保存的表(二维数据)
* @return
*/
bool writeOneTable(const QString &filepath, int startRow,QVector<QVector<QVariant>> & table);
/**
* @brief saveAs 另存为 对话框
* @return
*/
QString saveAs();
/**
* @brief setOneCell 设置单行Excel数据,效率低
* @param worksheet
* @param column
* @param row
* @param color
* @param text
*/
void setOneCell(QAxObject *worksheet,ColumnType column,int row,QColor color,QString text);
private:
void convertToColName(int data, QString& res);
QString to26AlphabetString(int data);
};
#endif // MYEXCEL_H
#include "myexcel.h"
#include <QStandardPaths>
#include <QFileDialog>
#include <QRegExp>
MyExcel::MyExcel()
{
}
QString MyExcel::saveAs()
{
QString file;
QString filter;
//如果版本低于QT5,则需要将:
// QStandardPaths::writableLocation(QStandardPaths::DesktopLocation),
//改为:QDesktopServices::storageLocation(QDesktopServices::DesktopLocation),
file = QFileDialog::getSaveFileName (
NULL,
"save",
QStandardPaths::writableLocation(QStandardPaths::DesktopLocation), //设置路径, .表示当前路径,./表示更目录
"Excel(*.xlsx)", //过滤器
&filter);
return file.replace("/","\\");
}
void MyExcel::setOneCell(QAxObject *worksheet, ColumnType column, int row, QColor color, QString text)
{
QAxObject *cell = worksheet->querySubObject("Cells(int,int)", row, column);
cell->setProperty("Value", text);
QAxObject *font = cell->querySubObject("Font");
font->setProperty("Color", color);
}
bool MyExcel::writeOneTable(const QString &filepath, int startRow,QVector<QVector<QVariant>> &table)
{
if (!table.isEmpty() && !filepath.isEmpty())
{
//新建excel表
QAxObject excel("Excel.Application");
excel.dynamicCall("SetVisible (bool Visible)", "false");
excel.setProperty("DisplayAlerts", false);
QAxObject* workbooks = excel.querySubObject("WorkBooks");
workbooks->dynamicCall("Add");
QAxObject* workbook = excel.querySubObject("ActiveWorkBook");
QAxObject* sheet = workbook->querySubObject("WorkSheets(int)", 1);
//数据转换
QVariantList tdata;
QVariant var;
int row = table.count();
int col = table[0].count();
for (int i = 0; i < row; i++)
{
//tdata << table[i].toList(); //这个地方一定要注意!!!这种写法是错误的
tdata << QVariant(table[i].toList());//ok
}
var = tdata; //注意!
//例如写入单元格范围A1:L2243
QString crange;
convertToColName(col, crange);
crange = QString("A%1:%2%3").arg(startRow).arg(crange).arg(row+startRow);
//写入数据
QAxObject* range = sheet->querySubObject("Range(const QString &)", crange);
QVariant res = range->setProperty("Value", var);
delete range;
//退出
workbook->dynamicCall("SaveAs(const QString&)", filepath);
workbook->dynamicCall("Close(Boolean)", false);
excel.dynamicCall("Quit(void)");
return true;
}
return false;
}
1->A 26->Z 27->AA
void MyExcel::convertToColName(int data, QString &res)
{
Q_ASSERT(data > 0 && data < 65535);
int tempData = data / 26;
if (tempData > 0)
{
int mode = data % 26;
convertToColName(mode, res);
convertToColName(tempData, res);
}
else
{
res = (to26AlphabetString(data) + res);
}
}
QString MyExcel::to26AlphabetString(int data)
{
QChar ch = data + 0x40;//A对应0x41
return QString(ch);
}
其他资源
Qt Xlsx使用教程、Qt操作Excel、Qt生成Excel图表、跨平台不依赖Office_qtxlsx_超级大洋葱806的博客-CSDN博客
Qt实战案例(4)——利用Qt读取Excel表格_wendy_ya的博客-CSDN博客
QT学习一:利用QT QAxObject读取Excel表格数据的两种方法比较_qt读取表格文件_兄弟李德胜的博客-CSDN博客
QT关于excel文件的简单操作整理_qt excel文件操作_Xavier_TXHXH的博客-CSDN博客
Qt之excel 操作使用说明_qt操作excel_音视频开发老舅的博客-CSDN博客