用dumpcpp工具生成的excel.h/excel.cpp來操縱Excel

上一篇:在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.源碼下載
百度網盤:
新浪網盤(最近把網盤整理了一下,不知這個失效沒):
        7.其他類
        在Qt的天池項目裏也有處理Excel文件的類,不過我還沒用過。大家有興趣可以看看。地址:http://www.qtcn.org/bbs/thread-htm-fid-105.html
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章