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博客