QT快速操作Excel的实现介绍及操作类封装

简介: QT快速操作Excel的实现介绍及操作类封装

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

相关文章
|
3月前
Qt类结构分析
Qt类结构分析
60 3
|
4月前
|
Java BI 数据处理
如何在Java中实现Excel操作
如何在Java中实现Excel操作
|
2月前
|
设计模式 前端开发 安全
Qt注册类对象单例与单类型区别
在进行开发时,应当根据具体的应用场景和需求来选择使用单例模式或是单类型。如果是全局服务或状态管理,可能需要单例模式;如果是为了使QML环境下的不同组件能够访问到同一个后端服务对象,则可能需要使用单类型。
34 2
|
3月前
|
编解码 开发框架
【Qt 学习笔记】Qt窗口 | Qt窗口介绍 | QMainwindow类及各组件介绍
【Qt 学习笔记】Qt窗口 | Qt窗口介绍 | QMainwindow类及各组件介绍
239 3
|
3月前
|
容器
【Qt 学习笔记】Qt常用控件 | 容器类控件 | Group Box的使用及说明
【Qt 学习笔记】Qt常用控件 | 容器类控件 | Group Box的使用及说明
242 3
|
3月前
|
容器
【Qt 学习笔记】Qt常用控件 | 容器类控件 | Tab Widget的使用及说明
【Qt 学习笔记】Qt常用控件 | 容器类控件 | Tab Widget的使用及说明
83 2
|
3月前
【Qt 学习笔记】Qt常用控件 | 输入类控件 | Slider的使用及说明
【Qt 学习笔记】Qt常用控件 | 输入类控件 | Slider的使用及说明
383 2
|
3月前
【Qt 学习笔记】Qt常用控件 | 输入类控件 | Dial的使用及说明
【Qt 学习笔记】Qt常用控件 | 输入类控件 | Dial的使用及说明
140 2
|
3月前
|
数据可视化
【Qt 学习笔记】Qt常用控件 | 输入类控件 | Date/Time Edit的使用及说明
【Qt 学习笔记】Qt常用控件 | 输入类控件 | Date/Time Edit的使用及说明
353 2
|
3月前
【Qt 学习笔记】Qt常用控件 | 按钮类控件 | Radio Button的使用及说明
【Qt 学习笔记】Qt常用控件 | 按钮类控件 | Radio Button的使用及说明
520 1