各大論壇有非常多的關於如何Qt調用Excel的方法,也有源碼可以直接拿來用,改一改完全可以用,但有時也會遇到一些特有的問題,和我們自己做的項目需求和設計有關。
本博文主要講兩個方面的內容:
- VS2010內開發一個簡單的通過QAxObject調用excel的程序。
- 如何解決非主線程無法調用excel的問題。
本博文目的是分享和記錄自己的編程路上的點點滴滴,有淺嘗輒止之嫌,歡迎批評指點。
一、VS2010內開發一個簡單的通過QAxObject調用excel的程序
開發環境VS2010+qt4.8.3。
- 在VS2010中創建【Qt Application】工程,之后的操作都可以默認。有一個地方需要注意,因為是QAxObject調用,需要在Qt4項目創建向導中勾選【ActiveQt container library】:
- 通過QAxObject調用的方式也很簡單,這里提供兩個相關的源代碼,原作者不詳(但是這里感謝他的分享),有一些些的改動。 借助Qt的QAxObject技術,以下幾句可以訪問到Excel的工作簿:
如果想了解Excel有哪些函數(上圖中的Add()函數)可以調用,參考Microsoft Excel Visual Basic,谷歌搜索VBAXL10.CHM
主類中調用方式為:
QExcel *excel = new QExcel; excel->selectSheet(1); excel->setCellVariant(1, 1, "excel"); QString fileName = "F:/Desktop/test.xlsx"; excel->saveAs(fileName); excel->close();
QExcel.h

#ifndef QEXCEL_H #define QEXCEL_H #include <QString> #include <QVariant> #include <QDir> #include <Qdebug> class QAxObject; class QExcel { public: QExcel(); ~QExcel(); public: QAxObject *getWorkBooks(); QAxObject *getWorkBook(); QAxObject *getWorkSheets(); QAxObject *getWorkSheet(); public: /**************************************************************************/ /* 工作表 */ /**************************************************************************/ void selectSheet(const QString& sheetName); //sheetIndex 起始於 1 void selectSheet(int sheetIndex); void deleteSheet(const QString& sheetName); void deleteSheet(int sheetIndex); void insertSheet(QString sheetName); int getSheetsCount(); //在 selectSheet() 之后才可調用 QString getSheetName(); QString getSheetName(int sheetIndex); /**************************************************************************/ /* 單元格 */ /**************************************************************************/ void setCellVariant(int row, int column, const QVariant& value); //cell 例如 "A7" void setCellVariant(const QString& cell, const QVariant& value); //range 例如 "A5:C7" void mergeCells(const QString& range); void mergeCells(int topLeftRow, int topLeftColumn, int bottomRightRow, int bottomRightColumn); QVariant getCellValue(int row, int column); void clearCell(int row, int column); void clearCell(const QString& cell); /**************************************************************************/ /* 布局格式 */ /**************************************************************************/ void getUsedRange(int *topLeftRow, int *topLeftColumn, int *bottomRightRow, int *bottomRightColumn); void setColumnWidth(int column, int width); void setRowHeight(int row, int height); void setCellTextCenter(int row, int column); void setCellTextCenter(const QString& cell); void setCellTextWrap(int row, int column, bool isWrap); void setCellTextWrap(const QString& cell, bool isWrap); void setAutoFitRow(int row); void mergeSerialSameCellsInAColumn(int column, int topRow); int getUsedRowsCount(); void setCellFontBold(int row, int column, bool isBold); void setCellFontBold(const QString& cell, bool isBold); void setCellFontSize(int row, int column, int size); void setCellFontSize(const QString& cell, int size); /**************************************************************************/ /* 透視圖表 */ /**************************************************************************/ void QExcel::setPivotTable(const QString& rowField, const QString& dataField); //注:rowField,選擇某個字段,設置為xlRowField(行字段); // dataField,選擇某個字段,設置為xlDataField(行字段); //以上參考VBA orientation屬性,有xlRowField(行字段)xlColumnField(列字段)xlDataField(數值字段)等。 /**************************************************************************/ /* 文件 */ /**************************************************************************/ void saveAs(const QString& fileName); void close(); private: QAxObject *excel; QAxObject *workBooks; QAxObject *workBook; QAxObject *sheets; QAxObject *sheet; }; #endif
QExcel.cpp

#include <QAxObject> #include <QFile> #include <QStringList> #include <QDebug> #include "Qexcel.h" QExcel::QExcel() { excel = 0; workBooks = 0; workBook = 0; sheets = 0; sheet = 0; excel = new QAxObject("Excel.Application"); workBooks = excel->querySubObject("Workbooks"); workBooks->dynamicCall("Add()"); workBook = excel->querySubObject("ActiveWorkBook"); sheets = workBook->querySubObject("WorkSheets"); } QExcel::~QExcel() { close(); } void QExcel::close() { excel->dynamicCall("Quit()"); delete sheet; delete sheets; delete workBook; delete workBooks; delete excel; excel = 0; workBooks = 0; workBook = 0; sheets = 0; sheet = 0; } void QExcel::clearCell(const QString& cell) { QAxObject *range = sheet->querySubObject("Range(const QString&)", cell); range->dynamicCall("ClearContents()"); } void QExcel::deleteSheet(const QString& sheetName) { QAxObject * a = sheets->querySubObject("Item(const QString&)", sheetName); a->dynamicCall("delete"); } void QExcel::deleteSheet(int sheetIndex) { QAxObject * a = sheets->querySubObject("Item(int)", sheetIndex); a->dynamicCall("delete"); } QAxObject *QExcel::getWorkBooks() { return workBooks; } QAxObject *QExcel::getWorkBook() { return workBook; } QAxObject *QExcel::getWorkSheets() { return sheets; } QAxObject *QExcel::getWorkSheet() { return sheet; } QVariant QExcel::getCellValue(int row, int column) { QAxObject *range = sheet->querySubObject("Cells(int,int)", row, column); return range->property("Value"); } int QExcel::getSheetsCount() { return sheets->property("Count").toInt(); } QString QExcel::getSheetName() { return sheet->property("Name").toString(); } QString QExcel::getSheetName(int sheetIndex) { QAxObject * a = sheets->querySubObject("Item(int)", sheetIndex); return a->property("Name").toString(); } void QExcel::getUsedRange(int *topLeftRow, int *topLeftColumn, int *bottomRightRow, int *bottomRightColumn) { QAxObject *usedRange = sheet->querySubObject("UsedRange"); *topLeftRow = usedRange->property("Row").toInt(); *topLeftColumn = usedRange->property("Column").toInt(); QAxObject *rows = usedRange->querySubObject("Rows"); *bottomRightRow = *topLeftRow + rows->property("Count").toInt() - 1; QAxObject *columns = usedRange->querySubObject("Columns"); *bottomRightColumn = *topLeftColumn + columns->property("Count").toInt() - 1; } void QExcel::insertSheet(QString sheetName) { sheets->querySubObject("Add()"); QAxObject * a = sheets->querySubObject("Item(int)", 1); a->setProperty("Name", sheetName); } void QExcel::mergeCells(const QString& cell) { QAxObject *range = sheet->querySubObject("Range(const QString&)", cell); range->setProperty("VerticalAlignment", -4108);//xlCenter range->setProperty("WrapText", true); range->setProperty("MergeCells", true); } void QExcel::mergeCells(int topLeftRow, int topLeftColumn, int bottomRightRow, int bottomRightColumn) { QString cell; cell.append(QChar(topLeftColumn - 1 + 'A')); cell.append(QString::number(topLeftRow)); cell.append(":"); cell.append(QChar(bottomRightColumn - 1 + 'A')); cell.append(QString::number(bottomRightRow)); QAxObject *range = sheet->querySubObject("Range(const QString&)", cell); range->setProperty("VerticalAlignment", -4108);//xlCenter range->setProperty("WrapText", true); range->setProperty("MergeCells", true); } void QExcel::mergeSerialSameCellsInAColumn(int column, int topRow) { int a,b,c,rowsCount; getUsedRange(&a, &b, &rowsCount, &c); int aMergeStart = topRow, aMergeEnd = topRow + 1; QString value; while(aMergeEnd <= rowsCount) { value = getCellValue(aMergeStart, column).toString(); while(value == getCellValue(aMergeEnd, column).toString()) { clearCell(aMergeEnd, column); aMergeEnd++; } aMergeEnd--; mergeCells(aMergeStart, column, aMergeEnd, column); aMergeStart = aMergeEnd + 1; aMergeEnd = aMergeStart + 1; } } void QExcel::setPivotTable(const QString& rowField, const QString& dataField) { sheet->dynamicCall("PivotTableWizard()"); sheet = sheets->querySubObject("Item(const QString&)", "Sheet4"); QAxObject *pvtTables = sheet->querySubObject("PivotTables"); //int pvtTableCount = pvtTables->property("Count").toInt(); //qDebug() << pvtTableCount; QAxObject *pvtTable = sheet->querySubObject("PivotTables(int)", 1); QAxObject *pvtRowField = pvtTable->querySubObject("PivotFields(const QString&)", rowField); pvtRowField->setProperty("Orientation", "xlRowField"); QAxObject *pvtDataField = pvtTable->querySubObject("PivotFields(const QString&)", dataField); pvtDataField->setProperty("Orientation", "xlDataField"); //嵌入圖表 QAxObject *chObjects = sheet->querySubObject("ChartObjects()"); chObjects->dynamicCall("Add(int, int, int, int)", 150, 20, 550, 320); int chartCount = chObjects->property("Count").toInt(); //QAxObject *chObject = sheet->querySubObject("ActiveChartObject"); } void QExcel::selectSheet(const QString& sheetName) { sheet = sheets->querySubObject("Item(const QString&)", sheetName); } void QExcel::selectSheet(int sheetIndex) { sheet = sheets->querySubObject("Item(int)", sheetIndex); } void QExcel::setCellVariant(int row, int column, const QVariant& value) { QAxObject *range = sheet->querySubObject("Cells(int,int)", row, column); range->dynamicCall("SetValue(const QVariant&)", value); } void QExcel::setCellFontBold(int row, int column, bool isBold) { QString cell; cell.append(QChar(column - 1 + 'A')); cell.append(QString::number(row)); QAxObject *range = sheet->querySubObject("Range(const QString&)", cell); range = range->querySubObject("Font"); range->setProperty("Bold", isBold); } void QExcel::setCellFontSize(int row, int column, int size) { QString cell; cell.append(QChar(column - 1 + 'A')); cell.append(QString::number(row)); QAxObject *range = sheet->querySubObject("Range(const QString&)", cell); range = range->querySubObject("Font"); range->setProperty("Size", size); } void QExcel::saveAs(const QString& fileName) { workBook->dynamicCall("SaveAs(const QString&)", QDir::toNativeSeparators(fileName)); } void QExcel::setColumnWidth(int column, int width) { QString columnName; columnName.append(QChar(column - 1 + 'A')); columnName.append(":"); columnName.append(QChar(column - 1 + 'A')); QAxObject * col = sheet->querySubObject("Columns(const QString&)", columnName); col->setProperty("ColumnWidth", width); } void QExcel::setCellTextCenter(int row, int column) { QString cell; cell.append(QChar(column - 1 + 'A')); cell.append(QString::number(row)); QAxObject *range = sheet->querySubObject("Range(const QString&)", cell); range->setProperty("HorizontalAlignment", -4108);//xlCenter } void QExcel::setCellTextWrap(int row, int column, bool isWrap) { QString cell; cell.append(QChar(column - 1 + 'A')); cell.append(QString::number(row)); QAxObject *range = sheet->querySubObject("Range(const QString&)", cell); range->setProperty("WrapText", isWrap); } void QExcel::setAutoFitRow(int row) { QString rowsName; rowsName.append(QString::number(row)); rowsName.append(":"); rowsName.append(QString::number(row)); QAxObject * rows = sheet->querySubObject("Rows(const QString &)", rowsName); rows->dynamicCall("AutoFit()"); } void QExcel::clearCell(int row, int column) { QString cell; cell.append(QChar(column - 1 + 'A')); cell.append(QString::number(row)); QAxObject *range = sheet->querySubObject("Range(const QString&)", cell); range->dynamicCall("ClearContents()"); } int QExcel::getUsedRowsCount() { QAxObject *usedRange = sheet->querySubObject("UsedRange"); int topRow = usedRange->property("Row").toInt(); QAxObject *rows = usedRange->querySubObject("Rows"); int bottomRow = topRow + rows->property("Count").toInt() - 1; return bottomRow; } void QExcel::setCellVariant(const QString& cell, const QVariant& value) { QAxObject *range = sheet->querySubObject("Range(const QString&)", cell); range->dynamicCall("SetValue(const QVariant&)", value); } void QExcel::setCellFontSize(const QString &cell, int size) { QAxObject *range = sheet->querySubObject("Range(const QString&)", cell); range = range->querySubObject("Font"); range->setProperty("Size", size); } void QExcel::setCellTextCenter(const QString &cell) { QAxObject *range = sheet->querySubObject("Range(const QString&)", cell); range->setProperty("HorizontalAlignment", -4108);//xlCenter } void QExcel::setCellFontBold(const QString &cell, bool isBold) { QAxObject *range = sheet->querySubObject("Range(const QString&)", cell); range = range->querySubObject("Font"); range->setProperty("Bold", isBold); } void QExcel::setCellTextWrap(const QString &cell, bool isWrap) { QAxObject *range = sheet->querySubObject("Range(const QString&)", cell); range->setProperty("WrapText", isWrap); } void QExcel::setRowHeight(int row, int height) { QString rowsName; rowsName.append(QString::number(row)); rowsName.append(":"); rowsName.append(QString::number(row)); QAxObject * r = sheet->querySubObject("Rows(const QString &)", rowsName); r->setProperty("RowHeight", height); }
二、解決非主線程無法調用excel的問題
問題情境:這段代碼可能要執行很長時間,那么需要單獨開一個線程完成這項工作。那么我們可能會繼承QThread類重寫run()方法:
void Thread::run(){ QExcel *excel = new QExcel; excel->selectSheet(1); excel->setCellVariant(1, 1, "excel"); QString fileName = "F:/Desktop/test.xlsx"; excel->saveAs(fileName); excel->close(); }
然后調用:
Thread* thread = new Thread; thread->start();
但問題出現了,
workBooks是空指針,單步調試發現excel.exe根本沒有啟動。
解決方法:需要調用一個Windows API函數初始化COM Library,注意包含頭文件“windows.h”:
CoInitializeEx(NULL, COINIT_MULTITHREADED);
此方面更多信息參考:http://msdn.microsoft.com/en-us/library/ms695279(v=vs.85).aspx
添加這一句后,之前的代碼變為:
void Thread::run(){ CoInitializeEx(NULL, COINIT_MULTITHREADED); QExcel *excel = new QExcel; excel->selectSheet(1); excel->setCellVariant(1, 1, "excel"); QString fileName = "F:/Desktop/test2.xlsx"; excel->saveAs(fileName); excel->close(); }
附一些可以參考的文章並再次感謝他們的分享:
[SOLVED] MS Excel and QThread | Qt Project forums | Qt Project
在Qt中用QAxObject來操作Excel - 瀟的專欄 - 博客頻道 - CSDN.NET