您的位置:首页 > 编程语言 > C语言/C++

用dumpcpp工具生成的excel.h/excel.cpp来操纵Excel

2013-04-08 19:33 369 查看
上一篇:在Qt中用QAxObject来操作Excel
1.系统环境:Qt 4.7+MinGW、Win7、QtCreator、Excel 2007
2.生成excel.h、excel.cpp、office.h和office.cpp


dumpcpp ---- ActiveQt模块的命令行工具,可以导出Qt能识别的COM组件的接口。这个工具在Qt库的bin目录下可以找到。如果没有,也可以在Qt库\tools\activeqt\dumpcpp文件夹中找到源码,自己编译一下就有了。在命令行中进入某个目录,比如D:\,然后执行dumpcpp {00020813-0000-0000-C000-000000000046}就可以在D:\下生成excel.hexcel.cpp文件了。这两个文件中就包含了Qt导出的操纵Excel的类和申明。

如果找不到dumpcpp这个工具,可能是Qt库的bin目录没有加入到环境变量path中,或者bin目录下没有这个工具。如果dumpcpp提示 dumpcpp: type library '' not found 或者QAxBase::setControl: requested control {00020813-0000-0000-C000-000000000046} could not be instantiated,那么可以将 {00020813-0000-0000-C000-000000000046}改成Excel.Application或者{00024500-0000-0000-C000-000000000046}试试。如果还不行,就去注册表HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\中找吧。。。比如HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{00020813-0000-0000-C000-000000000046}\1.6\0\win32下有个键值C:\Program
Files\Microsoft Office\Office12\EXCEL.EXE,那么 {00020813-0000-0000-C000-000000000046} 就是正确的CSSID了。

有了excel.h和excel.cpp还不行,加入到工程,编译后会发现其中很多类型不全,会出现诸如excel.h:26: 错误:use of enum 'MsoCalloutDropType' without previous declaration之类的很多错误。这是因为还需要office.hoffice.cpp。同样用dumpcpp,执行dumpcpp
{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}就可得到office.h和office.cpp文件。这是Microsoft Office 12.0 Object Library,在注册表中位置是HKEY_LOCAL_MACHINE\SOFTWARE\Classes\TypeLib\{2DF8D04C-5BFA-101B-BDE5-00AA0044DE52}\2.4\0\win32。若提示找不到qpixmap类,则在excel.h的头文件中添加#include
<QtGui/qpixmap.h>。

将office.h和office.cpp也包含进工程,并在excel.h中添加#include"office.h"后再次编译,还是不行。会出现:office.h:1666: 警告:this decimal constant is unsigned only in ISO C90这样的错误。这是由编译器引起的。找到这个常数,在常数后面增加一个UL标识,或者ULL表示,如4294967295UL,这样就不会报警了。再次编译,会出现:
In file included from ..\testExcel\/excel.h:20,
                 from ..\testExcel\main.cpp:4:
..\testExcel\/office.h: In copy constructor 'Office::ChartColorFormat::ChartColorFormat(const Office::ChartColorFormat&)':
..\testExcel\/office.h:25460: note: synthesized method 'QAxObject::QAxObject(const QAxObject&)' first required here 
In file included from ..\..\Qt\4.7.2\include/QtCore/qmetatype.h:1,
                 from ..\..\Qt\4.7.2\include/QtCore/../../src/corelib/kernel/qvariant.h:48,
                 from ..\..\Qt\4.7.2\include/QtCore/qvariant.h:1,
                 from ..\..\Qt\4.7.2\include/ActiveQt/../../src/activeqt/container/qaxbase.h:47,
                 from ..\..\Qt\4.7.2\include/ActiveQt/qaxbase.h:1,
                 from ..\..\Qt\4.7.2\include\ActiveQt/../../src/activeqt/container/qaxobject.h:44,
                 from ..\..\Qt\4.7.2\include\ActiveQt/qaxobject.h:1,
                 from ..\..\Qt\4.7.2\include\ActiveQt/QAxObject:1,
                 from ..\testExcel\main.cpp:2:
..\..\Qt\4.7.2\include/QtCore/../../src/corelib/kernel/qmetatype.h: In function 'void* qMetaTypeConstructHelper(const T*) [with T = Office::ChartColorFormat]':
..\..\Qt\4.7.2\include/QtCore/../../src/corelib/kernel/qmetatype.h:142: note: synthesized method 'Office::ChartColorFormat::ChartColorFormat(const Office::ChartColorFormat&)' first required here 
mingw32-make[1]: *** [release/main.o] Error 1
这样的错误。找到office.h的52460行,将函数inline Office::ChartColorFormat* ChartFillFormat::BackColor()和函数Office::ChartColorFormat* ChartFillFormat::ForeColor()中的前三行注释掉。例如:
inline Office::ChartColorFormat* ChartFillFormat::ForeColor() const
{
/*    Office::ChartColorFormat* qax_pointer = 0;
    qRegisterMetaType("ChartColorFormat*", &qax_pointer);
    qRegisterMetaType("ChartColorFormat", qax_pointer);*/
    QVariant qax_result = property("ForeColor");
    if (!qax_result.constData()) return 0;
    Q_ASSERT(qax_result.isValid());
    return *(Office::ChartColorFormat**)qax_result.constData();
}


这样应该就可以正常编译了。
3.例子
我在QtCreator中新建了一个工程testExcel2作为测试之用。注意要在.pro文件中添加CONFIG += qaxcontainer。该工程中所有文件均为 uft8 格式。
//testExcel2.pro
#-------------------------------------------------
#
# Project created by QtCreator 2013-04-02T21:09:33
#
#-------------------------------------------------

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = testExcel2
TEMPLATE = app

CONFIG += qaxcontainer

SOURCES += main.cpp\
        mainwindow.cpp \
    office.cpp \
    excel.cpp

HEADERS  += mainwindow.h \
    excel.h \
    office.h


//mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QPushButton>
#include "excel.h"
#include <QAxObject>
#include <QFileDialog>
#include <QFileinfo>
#include <QDir>
#include <qmath.h>
#include <QDebug>

class MainWindow : public QPushButton
{
    Q_OBJECT
    
public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
public slots:
    void click_pushbutton();
};

#endif // MAINWINDOW_H


//main.cpp

#include "mainwindow.h"
#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    
    return a.exec();
}


//mainwindow.cpp

#include "mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QPushButton(parent)
{
    connect(this,SIGNAL(clicked()),this,SLOT(click_pushbutton()));
    this->setText(tr("Push Me"));
}

MainWindow::~MainWindow()
{
    
}

void MainWindow::click_pushbutton(){
    Excel::Application *excel = new Excel::Application(this);
    excel->SetVisible(false);//不显示Excel窗体
    excel->SetDisplayAlerts(false);//不显示警告

    Excel::Workbooks *workbooks = excel->Workbooks();//工作簿集合
    Excel::Workbook *workbook = workbooks->Add();//添加工作簿
	
    Excel::Sheets *worksheets = workbook->Sheets();//工作表集合
	//这里若写Excel::Worksheets *worksheets = workbook->Sheets();
	//会出现cannot convert 'Excel::Sheets*' to 'Excel::Worksheets*' in initialization这样的错误
	
    Excel::Worksheet* worksheet = new Excel::Worksheet(new Excel::_Worksheet(worksheets->Item(1)));//获取工作表
	//这里若写Excel::Worksheet *worksheet = worksheets->Item(1);
	//会出现cannot convert 'IDispatch*' to 'Excel::Worksheet*' in initialization这样的错误
	//因为worksheets->Item(1)返回的是IDispatch*类型。要将IDispatch*转换为Excel的对象,就得用上面的
	//new的方法。若Excel空间中包含_XXXX这样的类(如Excel::_Worksheet),则生成XXXX对象
	//(如Excel::Worksheet)时,就用new Excel::XXXX(new Excel_XXXX(IDispatch*));这样的方法,例如
	//Excel::Worksheet* worksheet = new Excel::Worksheet(new Excel::_Worksheet(worksheets->Item(1)));
	//如果某对象XXXX没有_XXXX这样的类,那么将IDispatch*转换为该对象的指针时只用new XXXX(IDispatch*)
	//这样的方法就行了,例如下面的
	//Excel::SeriesCollection *seriesCollection = new Excel::SeriesCollection(chart->SeriesCollection());

    //因为源码是utf8格式的,所以这里用QString::fromUtf8(...),否则Excel中会出现乱码
    worksheet->SetName(QString::fromUtf8("【测试】"));//设置中文表名

    QString picpath = QFileDialog::getOpenFileName(this,tr("Open"),".",tr("picture file (*.jpg | *.png)"));
    if(!picpath.isEmpty()){
        worksheet->SetBackgroundPicture(QDir::toNativeSeparators(picpath));//为工作表设置背景图片
        //有关字体的Background属性,参见
		//http://msdn.microsoft.com/en-us/library/office/bb220875(v=office.12).aspx
        worksheet->Cells()->Font()->SetBackground(Excel::xlBackgroundAutomatic);//设置背景图片和文字重叠时的显示效果
        worksheet->Cells()->Font()->SetBold(false);//不要粗体
    }

    Excel::Range *cell;
    Excel::Range *cells = worksheet->Cells();//获取工作表中的所有单元格
    for(int i=0;i<10;i++){
        cells->SetItem(i+1,i+1,(i+1)*(i+1));//设置第i+1行,第i+1列的单元格的值

        QString range = QString('A'+i)+QString::number(i+1);
        cell = worksheet->Range(range);获取第i+1行,第i+1列的单元格
        cell->Font()->SetItalic(true);//设置字体为斜体
        cell->Font()->SetSize(25);//设置字号
        cell->Font()->SetColor(QRgb("#FF0000")+i*30);//设置字体颜色,每次循环颜色都不同
    }

    //测试中文输入
    cells->SetItem(3,5,QString::fromUtf8("输入中文"));//在第3行,第5列的单元格中输入中文字符

    //测试Excel的函数1
    cells->SetItem(2,1,5);
    cell = worksheet->Range("A3");//获取A3单元格
    cell->SetFormula("=sum(A1:A2)");//求取A1:A2单元格中值的和
    //测试Excel的函数1
    cells->SetItem(3,2,10);
    cells->SetItem(4,2,"=sum(B1:B3)");//求取B1:B3单元格中值的和
	//以上两种方式说明用setFormula和setItem来设置函数,效果是一样的

    //Qt的dumpcpp工具没有生成Cells(int,int)这种用法,所以用以下方法
	//间接调用Cells(int,int)函数
    QAxObject* range = worksheet->querySubObject("Cells(int,int)",4,6);
    cell = (Excel::Range*)range;
    cell->SetValue(QString::fromUtf8("因为dumcpp没有自动生成的函数Cells(int,int),故用querySubObject(\"Cells(int,int),\",4,6)的方式调用"));

    //由于Cells(int,int)十分常用,所以我在excel.h的Worksheet类中添加了Excel::Range* Worksheet::Cells(int,int)函数
	//在excel.h中搜索class EXCEL_EXPORT Worksheet就可以找到Worksheet类
	//然后在其中添加函数Cells(int rowIndex,int colIndex)即可。如:
	//inline Excel::Range* Cells(int rowIndex,int colIndex){
    //    QAxObject* range = this->querySubObject("Cells(int,int)",rowIndex,colIndex);
    //    return (Excel::Range*)range;
    //}
    cell = worksheet->Cells(5,6);
    cell->SetValue(QString::fromUtf8("手动在类Excel::Worksheet中添加函数Cells(int,int)后,可直接调用"));

    //测试图表
    //生成数据
    Excel::Range* cellX;
    Excel::Range* cellY;
    double y;
    for(int x=0;x<37;x++){
        y=sin(2*3.141592653/36*x);
        cellX = worksheet->Cells(x+1,12);
        cellY = worksheet->Cells(x+1,13);
        cellX->SetValue(x);
        cellY->SetValue(y);
    }
    Excel::Range *xValues = worksheet->Range("L1:L37");
    Excel::Range *yValues = worksheet->Range("M1:M37");
    //先选择一列,则待会儿在Chart中会创建一个Series,否则会创建多个Series
    xValues->Select();
	//用workbook->Charts()->Add()添加一个图表工作表。由于返回的是IDispatch*类型
	//所以需用如下方法,转为Excel::Chart*类型。这与前面获取Worksheet*时类似。
    Excel::Chart *chart = new Excel::Chart(new Excel::_Chart(workbook->Charts()->Add()));
    chart->SetChartType(Excel::xlXYScatterSmooth);//设置图表类型为带点迹的平滑散点图
    chart->SetName(QString::fromUtf8("【测试图表】"));//设置表名
    //获取Series集合。由于chart->SeriesCollection()返回的是IDispatch*类型,
	//故用如下方法转换为Excel::SeriesCollection*
	Excel::SeriesCollection *seriesCollection = new Excel::SeriesCollection(chart->SeriesCollection());
    Excel::Series *series = seriesCollection->Item(1);//获取第1条曲线
    series->SetName(QString::fromUtf8("正弦"));
    series->SetXValues(xValues->asVariant());//设置横坐标
    series->SetValues(yValues->asVariant());//设置对应的纵坐标

	//在工作表内插入图表
	//获取图表对象集合。同样,由于worksheet->ChartObjects()返回的是IDispatch*,
	//故要用new的方式来转换为Excel::ChartObjects*
    Excel::ChartObjects *chartObjects = new Excel::ChartObjects(worksheet->ChartObjects());
	//添加图表,Left=800,Top=10,Height=500,Width=500,单位磅
    Excel::ChartObject *chartObject = chartObjects->Add(800,10,500,500);
    Excel::Chart *innerChart = chartObject->Chart();
	//使用xValues作为数据源,Excel::xlColumns表示每列都为一组数据,即1个Series
    innerChart->SetSourceData(xValues,Excel::xlColumns);
    innerChart->SetHasTitle(true);//显示标题
    innerChart->SetChartType(Excel::xlXYScatterSmoothNoMarkers);//图表为不包含数据点的散点图
	//获取散点图的系列(Series)集合
    Excel::SeriesCollection *innerSeriesCollection = new Excel::SeriesCollection(innerChart->SeriesCollection());
	//选取系列1,因为我只有1个系列,即一条曲线
    Excel::Series *innerSeries = innerSeriesCollection->Item(1);
    innerSeries->SetName(QString::fromUtf8("正弦"));//设置系列名
    innerSeries->SetXValues(xValues->asVariant());//设置横坐标
    innerSeries->SetValues(yValues->asVariant());//设置对应的纵坐标

    delete innerSeriesCollection;innerSeriesCollection=NULL;
    delete chartObjects;chartObjects=NULL;
    delete seriesCollection;seriesCollection=NULL;
    delete chart;chart=NULL;

    //QFileDialog::DontConfirmOverwrite表示如果遇到文件已存在,不提示,直接覆盖
    QString filepath = QFileDialog::getSaveFileName(this,tr("Save"),".",tr("Microsoft Office 2007 (*.xlsx)\n"
                                                                           "Microsoft Office 2003 (*.xls)"),0,QFileDialog::DontConfirmOverwrite);
    //Excel::xlLocalSessionChanges表示遇到冲突时覆盖,且不提示
    //SaveAs的详细用法参见http://msdn.microsoft.com/zh-cn/library/ff195820(v=office.15).aspx
    if(QFileInfo(filepath).suffix() == "xls"){
        workbook->SaveAs(QDir::toNativeSeparators(filepath),Excel::xlExcel8,"","",false,false,Excel::xlNoChange,Excel::xlLocalSessionChanges);
    }
    else{
        workbook->SaveAs(QDir::toNativeSeparators(filepath),Excel::xlOpenXMLWorkbook,"","",false,false,Excel::xlNoChange,Excel::xlLocalSessionChanges);
    }

    workbook->Close();
    excel->Quit();
    delete worksheet;worksheet=NULL;
    delete excel;excel=NULL;
}


在上例中,我测试了单元格的使用、函数的使用、图表的使用。还添加了Cells(int,int)函数,插入表格背景图片、中文输入无乱码,xls和xlsx两种版本的保存。
4.其他问题
运用过程中可能还会出现其他问题:
1)在Excel::Application *excel = new Excel::Application(this);这句出现undefined reference to `vtable for Excel::Application' 或 undefined reference to `Excel::Sheets::staticMetaObject'这样的错误。
解决办法:将外部引用的*.h文件在头文件中包含,不要包含在*.cpp中。在Office.h中包含<QMetaObject>。
2)出现QAxBase::setControl: requested control {00020813-0000-0000-C000-000000000046} could not be instantiated

QAxBase::qt_metacall: Object is not initialized, or initialization failed

QAxBase::qt_metacall: Object is not initialized, or initialization failed

QAxBase::qt_metacall: Object is not initialized, or initialization failed这样的错误
解决办法:在整个工程中查找 {00020813-0000-0000-C000-000000000046},将其替换为 Excel.Application
3)出现 'Excel::XlFileFormat' is not a class or namespace这样的错误
解决办法:xlFileFormat是枚举类型,如果要用其中的某个枚举值,直接写成 Excel::xlOpenXMLWorkbook,不要写成 Excel::XlFileFormat::xlOpenXMLWorkbook
5.样例图片







6.源码下载
百度网盘:
3个压缩包均在:http://pan.baidu.com/share/link?shareid=2465785956&uk=4128141257
新浪网盘(最近把网盘整理了一下,不知这个失效没):
1)testExcel2_仅包含修改后的excel,h、excel.cpp、office.h和office.cpp
2)testExcel2_包含所有源码,不包含Qt运行库
3)testExcel2_包含所有源码和Qt运行库
7.其他类
在Qt的天池项目里也有处理Excel文件的类,不过我还没用过。大家有兴趣可以看看。地址:http://www.qtcn.org/bbs/thread-htm-fid-105.html
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: