Qt通過QAxObject調用Excel[解決在非主線程無法調用的問題]


  各大論壇有非常多的關於如何Qt調用Excel的方法,也有源碼可以直接拿來用,改一改完全可以用,但有時也會遇到一些特有的問題,和我們自己做的項目需求和設計有關。

  本博文主要講兩個方面的內容:

  • VS2010內開發一個簡單的通過QAxObject調用excel的程序。
  • 如何解決非主線程無法調用excel的問題。

  本博文目的是分享和記錄自己的編程路上的點點滴滴,有淺嘗輒止之嫌,歡迎批評指點。


 一、VS2010內開發一個簡單的通過QAxObject調用excel的程序

  開發環境VS2010+qt4.8.3。

  1. 在VS2010中創建【Qt Application】工程,之后的操作都可以默認。有一個地方需要注意,因為是QAxObject調用,需要在Qt4項目創建向導中勾選【ActiveQt container library】:
  2. 通過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
View Code

 

 

          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);  
} 
View Code

 

 

二、解決非主線程無法調用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

CoInitializeEx function (COM)

 

 

 

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM