QT excel操作

本文主要参考博客:Qt之操作ExcelEXCEL_VBA完全手册,为公司项目设计了一个”Report to Excel“的功能。本文浅谈了几点我对Qt操作Excel编程的一些体会。

一、什么是VBA

Visual Basic Application是一种自动化语言(过去称为“宏语言”),可以用它是常用的过程或进程自动化,可以创建自定义的解决方案。目前,主要用它来扩展Window office的功能。

要了解VBA,有两个简单的办法:

1,查看“EXCEL帮助(F1)" -> ”搜索“ -> ”开发人员参考“ -> ”VB语言“

 


 

2,查看宏录制

这类的”宏“指一系列EXCEL能够执行的名字保存的命令。

参考EXCEL_VBA完全手册的第一节。或通过右击worksheet,查看源码的方式进入。同过宏录制,查看源代码,获知VBA的一些属性值和操作方法,如左对齐(xlLeft):-4131 ;居中(xlCenter):-4108 ;右对齐(xlRight):-4152  ;上对齐(xlTop):-4160 ;居中(xlCenter):4108 ;下对齐(xlBottom):-4107等等。

 

 

二、Qt是如何来操作Excel?

Qt操作Excel主要通过QAxObject + Excel VBA来实现。查看Qt的帮助文档:

The QAxObject class provides a QObject that wraps a COM object.

 

A QAxObject can be instantiated as an empty object, with the name of the COM object it should wrap, or with a pointer to the IUnknown that represents an existing COM object. If the COM object implements the IDispatch interface, the properties, methods and events of that object become available as Qt properties, slots and signals. The base class, QAxBase, provides an API to access the COM object directly through the IUnknown pointer.

 

QAxObject is a QObject and can be used as such, e.g. it can be organized in an object hierarchy, receive events and connect to signals and slots.

 

QAxObject also inherits most of its ActiveX-related functionality from QAxBase, notably dynamicCall() and querySubObject().

这段的大意就是:QAxObject是对COM对象的封装。COM Object的属性、方法和事件与Qt的属性、slot方法和信号对应。其中,最重要的两个函数是:dynamicCall() 和 querySubObject()。

此外,可以查看“QAxbase”,它提供了COM type和Qt property的对照表,并提供了dynamicCall() 和 querySubObject()函数以及 QObject::property() and QObject::setProperty()函数(Note that it is faster to get and set properties using QObject::property() and QObject::setProperty())的说明。

 

函数querySubObject()实现对象关联或对象转换(从COM Object转为Qt Object),如:QAxObject * workBooks = excel.querySubObject("WorkBooks");

函数dynamicCall() 实现跨QT到COM的函数调用,如:workBooks->dynamicCall("Open(const QString&)", QString("D:/Development/Trunk09/test.xlsx"));

函数property() 获取属性,函数setProperty()设置属性。

 

 

三、示例代码

1,读Excel文件

新建一个“Qt console”应用程序,在“.pro文件”中添加(查看QAxObject即可知),QT += axcontainer,然后在main文件中添加如下代码:

#include

#include

#include

#include    // 使用 CoInitializeEx(NULL, COINIT_MULTITHREADED);

 

int main(int argc, char *argv[])

{

   // QCoreApplication a(argc, argv);

 

    // 在后台线程中使用QAxObject必须先初始化

    CoInitializeEx(NULL, COINIT_MULTITHREADED);

 

    QAxObject excel("Excel.Application");

    excel.setProperty("Visible", true);    // 调试的时候设为true,release模式设为false,不可见

    QAxObject * workBooks = excel.querySubObject("WorkBooks");

    workBooks->dynamicCall("Open(const QString&)", QString("D:/Development/Trunk09/test.xlsx"));

    QVariant titleValue = excel.property("Caption");    // 获取标题

    qDebug() << "excel title : " << titleValue;

    QAxObject * workBook = excel.querySubObject("ActiveWorkBook");

    QAxObject * workSheets = workBook->querySubObject("Sheets");    // Sheets也可换作WorkSheets

 

    int sheetCount = workSheets->property("Count").toInt();    // 获取工作表数目

    qDebug() << "sheet count : " << sheetCount;

    for (int i = 1; i <= sheetCount; i++)

    {

        QAxObject *workSheet = workBook->querySubObject("Sheets(int)", i);    // 可以用WorkSheets(int)

        QString sheetName = workSheet->property("Name").toString();    // 获取工作表名称

        qDebug() << "sheet" << QString::number(i, 10) << " name: " << sheetName;

    }

 

    if (sheetCount > 0)

    {

        QAxObject *workSheet = workBook->querySubObject("Sheets(int)", 1);

        QAxObject *usedRange = workSheet->querySubObject("UsedRange");

        QAxObject *rows = usedRange->querySubObject("Rows");

        QAxObject *columns = usedRange->querySubObject("Columns");

        int startingRow = usedRange->property("Row").toInt();

        int startingColumn = usedRange->property("Column").toInt();

        int rowCount = rows->property("Count").toInt();

        int columnCount = columns->property("Count").toInt();

 

 

        for (int i = startingRow; i <= rowCount; i++)

        {

            for(int j = startingColumn; j <= columnCount; j++)

            {

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

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

                qDebug() << "row-" << QString::number(i,10) << "-column-" << QString::number(j,10) << ": " << cellValue;

            }

        }

    }

 

    workBook->dynamicCall("Close(Boolen)", false);    // 关闭文件

    excel.dynamicCall("Quit(void)");    // 退出

 

    //return a.exec();

}

注:QT中的文件路径,既可以用两个反斜杠,其中一个是转义字符,也可以用一个斜杠。

 

2,写Excel文件

新建一个“Qt console”应用程序,在“.pro文件”中添加(查看QAxObject即可知),QT += axcontainer,然后在main文件中添加如下代码:

 

#include

#include

#include

#include

#include

 

int main(int argc, char *argv[])

{

    //QCoreApplication a(argc, argv);

    // 在后台线程中使用QAxObject必须先初始化

    CoInitializeEx(NULL, COINIT_MULTITHREADED);

 

    QAxObject excel("Excel.Application");

    excel.setProperty("Visible", true);

    QAxObject * workBooks = excel.querySubObject("WorkBooks");

    workBooks->dynamicCall("Open(const QString&)", QString("D:\\Development\\Trunk09\\testw.xlsx"));

 

    QVariant titleValue = excel.property("Caption");    // 获取标题

    qDebug() << "excel title : " << titleValue;

    QAxObject * workBook = excel.querySubObject("ActiveWorkBook");

    QAxObject * workSheets = workBook->querySubObject("Sheets");    // Sheets也可换作WorkSheets

 

    // 删除工作表(删除第一个)

    QAxObject * firstSheet = workSheets->querySubObject("Item(int)", 1);

    firstSheet->dynamicCall("delete");

 

    // 插入工作表(插入到最后一行)

    int sheetCount = workSheets->property("Count").toInt();    // 获取工作表数目

    QAxObject * lastSheet = workSheets->querySubObject("Item(int)", sheetCount);

    QAxObject * workSheet = workSheets->querySubObject("Add(QVariant", lastSheet->asVariant());

    lastSheet->dynamicCall("Move(QVariant)", workSheet->asVariant());

 

    // 设置工作表名称

    workSheet->setProperty("Name", "Qt Sheet");

 

    // 操作单元格(第二行第二列)

    QAxObject * cell = workSheet->querySubObject("Cells(int, int)", 2, 2);

    cell->setProperty("Value", "Java C++ C# PHP Perl Python Delphi Ruby");    // 设单元格的值

    cell->setProperty("RowHight", 50);

    cell->setProperty("ColumnWidth", 30);

    cell->setProperty("HoriontalAligment", -4108);    // 左对齐(xlLeft):-4131 ;居中(xlCenter):-4108 ;右对齐(xlRight):-4152

    cell->setProperty("VerticalAligment", -4108);    // 上对齐(xlTop):-4160 ;居中(xlCenter):4108 ;下对齐(xlBottom):-4107

    cell->setProperty("WrapText", true);    // 内容过多,自动换行

    //cell->dynamicCall("ClearContents()");    // 清空单元格内容

 

    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));  //设置单元格字体颜色(红色)

 

    //设置单元格内容,并合并单元格(第5行第3列-第8行第5列)

    QAxObject *cell_5_6 = workSheet->querySubObject("Cells(int,int)", 5, 3);

    cell_5_6->setProperty("Value", "Java");  //设置单元格值

    QAxObject *cell_8_5 = workSheet->querySubObject("Cells(int,int)", 8, 5);

    cell_8_5->setProperty("Value", "C++");

 

    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 = workSheet->querySubObject("Range(const QString&)", merge_cell);

    merge_range->setProperty("HorizontalAlignment", -4108);

    merge_range->setProperty("VerticalAlignment", -4108);

    merge_range->setProperty("WrapText", true);

    merge_range->setProperty("MergeCells", true);  //合并单元格

    //merge_range->setProperty("MergeCells", false);  //拆分单元格

 

    //work_book->dynamicCall("Save()");  //保存文件(为了对比test与下面的test2文件,这里不做保存操作) work_book->dynamicCall("SaveAs(const QString&)", "E:\\test2.xlsx");  //另存为另一个文件

    workBook->dynamicCall("Close(Boolean)", false);  //关闭文件

    excel.dynamicCall("Quit(void)");  //退出

 

 

    //return a.exec();

}

 

 

四、项目实践

将主界面的数据保存到excel,由于保存时间比较长,所以开线程执行该操作。

void SaveReportThread::run()

{

    // 在后台线程中使用QAxObject必须先初始化

    CoInitializeEx(NULL, COINIT_MULTITHREADED);

 

    //SaveToExcel();

    SaveToWord();

 

    emit SIGNAL_SaveReportFinish();

}

 

void SaveReportThread::SaveToExcel()

{

    QAxObject* pExcelApp = new QAxObject("Excel.Application");  // 创建EXCEL对象,启动Excel.exe进程

    pExcelApp->setProperty("DisplayAlerts", false);             // 保存时不提示覆盖

    //pExcelApp->setProperty("Visible", true);                  // 操作时可以看见EXCEL窗口,调试用

 

    // 打开测试报告模板

    QAxObject* pWorkBooks = pExcelApp->querySubObject("WorkBooks");

    pWorkBooks->dynamicCall("Open(const QString&)", QCoreApplication::applicationDirPath() + "/TestReportTemplate.xlsx");

    QAxObject* pWorkBook = pExcelApp->querySubObject("ActiveWorkBook");

 

    // 如果要保存全部测试报告,先在模板文件中复制多份sheet模板

    QAxObject* pWorkSheet = pWorkBook->querySubObject("WorkSheets(int)", 1);

    if (m_bSaveAll)

    {

        for (int i = 1; i < m_pMainwindow->m_vecItemList.size(); i++)

            pWorkSheet->dynamicCall("Copy(const QVariant&)", pWorkSheet->asVariant());

    }

 

    // for循环流程兼容保存单个和全部报告

    for (int i = 0; i < m_pMainwindow->m_vecItemList.size(); i++)

    {

        if (m_bSaveAll)

            emit SIGNAL_ItemSelected(i);   // 通知UI切换测试项,同步操作

 

        // 1.修改sheet名称

        pWorkSheet = pWorkBook->querySubObject("WorkSheets(int)", i + 1);

        pWorkSheet->dynamicCall("Select()");    // 必须先select当前sheet

        pWorkSheet->setProperty("Name", m_pMainwindow->m_strCurrentItemName);

 

        // 2.填写测试基本信息

        int iRow = 2;

        pWorkSheet->querySubObject("Cells(int,int)", iRow++, 2)->setProperty("Value", QDate::currentDate().toString("yyyy-MM-dd"));

        pWorkSheet->querySubObject("Cells(int,int)", iRow++, 2)->setProperty("Value", QTime::currentTime().toString());

        pWorkSheet->querySubObject("Cells(int,int)", iRow++, 2)->setProperty("Value", "user");

        foreach (QString strInfo, m_listBaseInfo)

            pWorkSheet->querySubObject("Cells(int,int)", iRow++, 3)->setProperty("Value", strInfo);

 

        // 3.填写测试项名称和最终结果

        iRow = 18;

        pWorkSheet->querySubObject("Cells(int,int)", iRow, 1)->setProperty("Value", m_pMainwindow->m_strCurrentItemName);

        pWorkSheet->querySubObject("Cells(int,int)", iRow, 2)->setProperty("Value", m_pMainwindow->m_vecItemList[m_pMainwindow->m_iCurrentItemIndex]->GetTestPassOrFail());

 

        // 4.填写静态参数

        iRow = 21;

        int iCount = m_pMainwindow->ui->listStaticParams->count();

        // 有需要手工输入的静态参数也写进测试报告中

        QVector vecUserParams;

        if (m_pMainwindow->ui->doubleSpinBoxPower->isVisible())

        {

            QStringList listPower;

            listPower.append(C_PARA_Power);

            listPower.append(m_pMainwindow->ui->doubleSpinBoxPower->text());

            vecUserParams.push_back(listPower);

            iCount++;

        }

        if (m_pMainwindow->ui->doubleSpinBoxFrequency->isVisible())

        {

            QStringList listFreq;

            listFreq.append(C_PARA_Freq);

            listFreq.append(m_pMainwindow->ui->doubleSpinBoxFrequency->text());

            vecUserParams.push_back(listFreq);

            iCount++;

        }

 

        for (int i = 0; i < iCount; i++)

        {

            QStringList listParam;

            if (i < m_pMainwindow->ui->listStaticParams->count())

                listParam = m_pMainwindow->ui->listStaticParams->item(i)->text().split("=");   // 静态参数框控件中的内容

            else

                listParam = vecUserParams[i - m_pMainwindow->ui->listStaticParams->count()];   // 手工输入的参数

 

            if (i > 0)

            {

                // 除了第一行,每次都要复制前一行再插入一行,因为报告模板中有第一行的模板

                QAxObject* pRow = pWorkSheet->querySubObject("Rows(int)", iRow);

                pRow->dynamicCall("Select()");

                pRow->dynamicCall("Copy()");

                pRow = pWorkSheet->querySubObject("Rows(int)", ++iRow);

                pRow->dynamicCall("Select()");

                pRow->dynamicCall("Insert()");

            }

            pWorkSheet->querySubObject("Cells(int,int)", iRow, 1)->setProperty("Value", listParam[0]);

            pWorkSheet->querySubObject("Cells(int,int)", iRow, 2)->setProperty("Value", listParam[1]);

        }

 

        // 5.填写结果列表

        iRow += 3;

        for (int i = 0; i < m_pMainwindow->ui->tableResult->rowCount(); i++)

        {

            if (i > 0)

            {

                // 除了第一行,每次都要复制前一行再插入一行,因为报告模板中有第一行的模板

                QAxObject* pRow = pWorkSheet->querySubObject("Rows(int)", iRow);

                pRow->dynamicCall("Select()");

                pRow->dynamicCall("Copy()");

                pRow = pWorkSheet->querySubObject("Rows(int)", ++iRow);

                pRow->dynamicCall("Select()");

                pRow->dynamicCall("Insert()");

            }

 

            if (m_pMainwindow->ui->tableResult->item(i, 0) != NULL)

                pWorkSheet->querySubObject("Cells(int,int)", iRow, 1)->setProperty("Value", m_pMainwindow->ui->tableResult->item(i, 0)->text());

            if (m_pMainwindow->ui->tableResult->item(i, 1) != NULL)

                pWorkSheet->querySubObject("Cells(int,int)", iRow, 2)->setProperty("Value", m_pMainwindow->ui->tableResult->item(i, 1)->text());

            if (m_pMainwindow->ui->tableResult->item(i, 2) != NULL)

                pWorkSheet->querySubObject("Cells(int,int)", iRow, 3)->setProperty("Value", m_pMainwindow->ui->tableResult->item(i, 2)->text());

            if (m_pMainwindow->ui->tableResult->item(i, 3) != NULL)

                pWorkSheet->querySubObject("Cells(int,int)", iRow, 4)->setProperty("Value", m_pMainwindow->ui->tableResult->item(i, 3)->text());

            if (m_pMainwindow->ui->tableResult->item(i, 4) != NULL)

                pWorkSheet->querySubObject("Cells(int,int)", iRow, 5)->setProperty("Value", m_pMainwindow->ui->tableResult->item(i, 4)->text());

            if (m_pMainwindow->ui->tableResult->item(i, 5) != NULL)

                pWorkSheet->querySubObject("Cells(int,int)", iRow, 6)->setProperty("Value", m_pMainwindow->ui->tableResult->item(i, 5)->text());

        }

 

        // 6.插入成功率图

        iRow += 2;

        QString strImagePath = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/pic.png"); // 必须把路径中的'/'转换成'\\'

        emit SIGNAL_ExportPicture(strImagePath);   // 通知UI线程保存成功率图片,同步操作

 

        if (QFile::exists(strImagePath))

        {

            QImage image(strImagePath);

            double dRowHeight = pWorkSheet->querySubObject("Cells(int,int)", iRow, 1)->property("Height").toDouble();   // 每一行的高度,单位磅

            double width = image.width() * 0.75;       // 图片相对于左上角偏移的宽度,单位磅

            double height = image.height() * 0.75;     // 图片相对于左上角偏移的高度,单位磅

            QAxObject* pShapes = pWorkSheet->querySubObject("Shapes");

            pShapes->dynamicCall("AddPicture(QString&,bool,bool,double,double,double,double)", strImagePath, false, true, 0, iRow * dRowHeight, width, height);

            QFile::remove(strImagePath);

        }

 

        // 调整列宽

        QAxObject* pAllCells = pWorkSheet->querySubObject("Cells()");

        pAllCells->dynamicCall("Select()");

        pAllCells->querySubObject("EntireColumn()")->dynamicCall("AutoFit()");

        pWorkSheet->querySubObject("Cells(int,int)", 1, 1)->dynamicCall("Select()");    // 默认选中第一个单元格

 

        if (!m_bSaveAll)

            break;

    }

 

    // 默认选中第一个sheet

    pWorkSheet = pWorkBook->querySubObject("WorkSheets(int)", 1);

    pWorkSheet->dynamicCall("Select()");

 

    // 另存为测试报告文件

    QString ReportPath = QDir::toNativeSeparators(QCoreApplication::applicationDirPath());  // 必须把路径中的'/'转换成'\\'

    if (m_bSaveAll)

        ReportPath += "\\TestReport_All.xlsx";

    else

        ReportPath += "\\TestReport_" + m_pMainwindow->m_strCurrentItemName + ".xlsx";

    pWorkBook->dynamicCall("SaveAs(const QString&)", ReportPath);

    pWorkBook->dynamicCall("Close()");

    pWorkBooks->dynamicCall("Close()");

 

    delete pExcelApp;

    pExcelApp = NULL;

}

 

Qt Windows 下快速读写Excel指南

很多人搜如何读写excel都会看到用QAxObject来进行操作,很多人试了之后都会发现一个问题,就是慢,非常缓慢!因此很多人得出结论是QAxObject读写excel方法不可取,效率低。 后来我曾试过用ODBC等数据库类型的接口进行读写,遇到中文嗝屁不说,超大的excel还是会读取速度慢。 最后,看了一些开源的代码后发现,Windows下读取excel,还是用QAxObject最快!没错,就是用QAxObject读写最快!!! 大家以后读取excel时(win下),不用考虑别的方法,用QAxObject就行,速度杠杠的,慢是你操作有误!下面就说说咋能提高其读取效率。

读取excel慢的原因

这里不说如何打开或生成excel,着重说说如何快速读取excel。 网上搜到用Qt操作excel的方法,读取都是使用类似下面这种方法进行:

QVariant ExcelBase::read(int row, int col)
{
    QVariant ret;
    if (this->sheet != NULL && ! this->sheet->isNull())
    {
        QAxObject* range = this->sheet->querySubObject("Cells(int, int)", row, col);
        //ret = range->property("Value");
        ret = range->dynamicCall("Value()");
        delete range;
    }
    return ret;
}

读取慢的根源就在于sheet->querySubObject("Cells(int, int)", row, col)

试想有10000个单元就得调用10000次querySubObject,网络上90%的教程都没说这个querySubObject产生的QAxObject*最好进行手动删除,虽然在它的父级QAxObject会管理它的内存,但父级不析构,子对象也不会析构,若调用10000次,就会产生10000个QAxObject对象 得益于QT快速读取数据量很大的Excel文件此文,下面总结如何快速读写excel

快速读取excel文件

原则是一次调用querySubObject把所有数据读取到内存中 VBA中可以使用UsedRange把所有用到的单元格范围返回,并使用属性Value把这些单元格的所有值获取。

这时,获取到的值是一个table,但Qt把它变为一个变量QVariant来储存,其实实际是一个QList<QList<QVariant> >,此时要操作里面的内容,需要把这个QVariant转换为QList<QList<QVariant> >

先看看获取整个单元格的函数示意(这里ExcelBase是一个读写excel的类封装):

QVariant ExcelBase::readAll()
{
    QVariant var;
    if (this->sheet != NULL && ! this->sheet->isNull())
    {
        QAxObject *usedRange = this->sheet->querySubObject("UsedRange");
        if(NULL == usedRange || usedRange->isNull())
        {
            return var;
        }
        var = usedRange->dynamicCall("Value");
        delete usedRange;
    }
    return var;
}

代码中this->sheet是已经打开的一个sheet,再获取内容时使用this->sheet->querySubObject("UsedRange");即可把所有范围都获取。

下面这个castVariant2ListListVariant函数把QVariant转换为QList<QList<QVariant> >

///
/// \brief 把QVariant转为QList<QList<QVariant> >
/// \param var
/// \param res
///
void ExcelBase::castVariant2ListListVariant(const QVariant &var, QList<QList<QVariant> > &res)
{
    QVariantList varRows = var.toList();
    if(varRows.isEmpty())
    {
        return;
    }
    const int rowCount = varRows.size();
    QVariantList rowData;
    for(int i=0;i<rowCount;++i)
    {
        rowData = varRows[i].toList();
        res.push_back(rowData);
    }
}

这样excel的所有内容都转换为QList<QList<QVariant> >保存,其中QList<QList<QVariant> >QList<QVariant>为每行的内容,行按顺序放入最外围的QList中。

对于如下如的excel

 

读取后的QList<QList<QVariant> >结构如下所示:

 

 

下面看看此excel的读取速度有多高 这里有个excel,有1000行,100列,共计十万单元格,打开使用了一些时间,读取10万单元格耗时229毫秒, 读取的代码如下:(完整源代码见后面)

void MainWindow::on_action_open_triggered()
{
    QString xlsFile = QFileDialog::getOpenFileName(this,QString(),QString(),"excel(*.xls *.xlsx)");
    if(xlsFile.isEmpty())
        return;
    QElapsedTimer timer;
    timer.start();
    if(m_xls.isNull())
        m_xls.reset(new ExcelBase);
    m_xls->open(xlsFile);
    qDebug()<<"open cost:"<<timer.elapsed()<<"ms";timer.restart();
    m_xls->setCurrentSheet(1);
    m_xls->readAll(m_datas);
    qDebug()<<"read data cost:"<<timer.elapsed()<<"ms";timer.restart();
    QVariantListListModel* md = qobject_cast<QVariantListListModel*>(ui->tableView->model());
    if(md)
    {
        md->updateData();
    }
    qDebug()<<"show data cost:"<<timer.elapsed()<<"ms";timer.restart();
}

上面的m_xls和m_datas是成员变量:

QScopedPointer<ExcelBase> m_xls;
QList< QList<QVariant> > m_datas;

读取的耗时:

"D:\czy_blog\czyBlog\fastReadExcel\src\fastReadExcelInWindows\excelRWByCztr1988.xls"
open cost: 1183 ms
read data cost: 229 ms
show data cost: 14 ms

10万个也就0.2秒而已

快速写入excel文件

同理,能通过QAxObject *usedRange = this->sheet->querySubObject("UsedRange");实现快速读取,也可以实现快速写入

快速写入前需要些获取写入单元格的范围:Range(const QString&) 如excel的A1为第一行第一列,那么A1:B2就是从第一行第一列到第二行第二列的范围。

要写入这个范围,同样也是通过一个与之对应的QList<QList<QVariant> >,具体见下面代码:

///
/// \brief 写入一个表格内容
/// \param cells
/// \return 成功写入返回true
/// \see readAllSheet
///
bool ExcelBase::writeCurrentSheet(const QList<QList<QVariant> > &cells)
{
    if(cells.size() <= 0)
        return false;
    if(NULL == this->sheet || this->sheet->isNull())
        return false;
    int row = cells.size();
    int col = cells.at(0).size();
    QString rangStr;
    convertToColName(col,rangStr);
    rangStr += QString::number(row);
    rangStr = "A1:" + rangStr;
    qDebug()<<rangStr;
    QAxObject *range = this->sheet->querySubObject("Range(const QString&)",rangStr);
    if(NULL == range || range->isNull())
    {
        return false;
    }
    bool succ = false;
    QVariant var;
    castListListVariant2Variant(cells,var);
    succ = range->setProperty("Value", var);
    delete range;
    return succ;
}

此函数是把数据从A1开始写

函数中的convertToColName为把列数,转换为excel中用字母表示的列数,这个函数是用递归来实现的:

///
/// \brief 把列数转换为excel的字母列号
/// \param data 大于0的数
/// \return 字母列号,如1->A 26->Z 27 AA
///
void ExcelBase::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);
    }
}
///
/// \brief 数字转换为26字母
///
/// 1->A 26->Z
/// \param data
/// \return
///
QString ExcelBase::to26AlphabetString(int data)
{
    QChar ch = data + 0x40;//A对应0x41
    return QString(ch);
}

看看写excel的耗时:

void MainWindow::on_action_write_triggered()
{
    QString xlsFile = QFileDialog::getExistingDirectory(this);
    if(xlsFile.isEmpty())
        return;
    xlsFile += "/excelRWByCztr1988.xls";
    QElapsedTimer timer;
    timer.start();
    if(m_xls.isNull())
        m_xls.reset(new ExcelBase);
    m_xls->create(xlsFile);
    qDebug()<<"create cost:"<<timer.elapsed()<<"ms";timer.restart();
    QList< QList<QVariant> > m_datas;
    for(int i=0;i<1000;++i)
    {
        QList<QVariant> rows;
        for(int j=0;j<100;++j)
        {
            rows.append(i*j);
        }
        m_datas.append(rows);
    }
    m_xls->setCurrentSheet(1);
    timer.restart();
    m_xls->writeCurrentSheet(m_datas);
    qDebug()<<"write cost:"<<timer.elapsed()<<"ms";timer.restart();
    m_xls->save();
}

输出:

create cost: 814 ms 
"A1:CV1000" 
write cost: 262 ms 

写10万个数据耗时262ms,有木有感觉很快,很强大

结论

  • Qt在windows下读写excel最快速的方法还是使用QAxObject
  • 不要使用类似sheet->querySubObject("Cells(int, int)", row, col);的方式读写excel,这是导致低效的更本原因

源代码

--> 见 github

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值