1. 程式人生 > >Qt 讀寫Excel,並顯示到tablewidget

Qt 讀寫Excel,並顯示到tablewidget

<pre name="code" class="cpp">#include "excelengine.h"
#include "qt_windows.h"

ExcelEngine::ExcelEngine()
{
    pExcel     = NULL;
    pWorkbooks = NULL;
    pWorkbook  = NULL;
    pWorksheet = NULL;

    sXlsFile     = "";
    nRowCount    = 0;
    nColumnCount = 0;
    nStartRow    = 0;
    nStartColumn = 0;

    bIsOpen     = false;
    bIsValid    = false;
    bIsANewFile = false;
    bIsSaveAlready = false;

    HRESULT r = OleInitialize(0);
    if (r != S_OK && r != S_FALSE)
    {
        qDebug("Qt: Could not initialize OLE (error %x)", (unsigned int)r);
    }
}

ExcelEngine::ExcelEngine(QString xlsFile)
{
    pExcel     = NULL;
    pWorkbooks = NULL;
    pWorkbook  = NULL;
    pWorksheet = NULL;

    sXlsFile     = xlsFile;
    nRowCount    = 0;
    nColumnCount = 0;
    nStartRow    = 0;
    nStartColumn = 0;

    bIsOpen     = false;
    bIsValid    = false;
    bIsANewFile = false;
    bIsSaveAlready = false;

    HRESULT r = OleInitialize(0);
    if (r != S_OK && r != S_FALSE)
    {
        qDebug("Qt: Could not initialize OLE (error %x)", (unsigned int)r);
    }
}

ExcelEngine::~ExcelEngine()
{
    if ( bIsOpen )
    {
        //析構前,先儲存資料,然後關閉workbook
        Close();
    }
    OleUninitialize();
}

/**
  *@brief 開啟sXlsFile指定的excel報表
  *@return true : 開啟成功
  *        false: 開啟失敗
  */
bool ExcelEngine::Open(UINT nSheet, bool visible)
{

    if ( bIsOpen )
    {
        //return bIsOpen;
        Close();
    }

    nCurrSheet = nSheet;
    bIsVisible = visible;

    if ( NULL == pExcel )
    {
        pExcel = new QAxObject("Excel.Application");
        if ( pExcel )
        {
            bIsValid = true;
        }
        else
        {
            bIsValid = false;
            bIsOpen  = false;
            return bIsOpen;
        }

        pExcel->dynamicCall("SetVisible(bool)", bIsVisible);
    }

    if ( !bIsValid )
    {
        bIsOpen  = false;
        return bIsOpen;
    }

    if ( sXlsFile.isEmpty() )
    {
        bIsOpen  = false;
        return bIsOpen;
    }

    /*如果指向的檔案不存在,則需要新建一個*/
    QFile f(sXlsFile);
    if (!f.exists())
    {
        bIsANewFile = true;
    }
    else
    {
        bIsANewFile = false;
    }

    if (!bIsANewFile)
    {
        pWorkbooks = pExcel->querySubObject("WorkBooks"); //獲取工作簿
        pWorkbook = pWorkbooks->querySubObject("Open(QString, QVariant)",sXlsFile,QVariant(0)); //開啟xls對應的工作簿
    }
    else
    {
        pWorkbooks = pExcel->querySubObject("WorkBooks");     //獲取工作簿
        pWorkbooks->dynamicCall("Add");                       //新增一個新的工作薄
        pWorkbook  = pExcel->querySubObject("ActiveWorkBook"); //新建一個xls
    }

    pWorksheet = pWorkbook->querySubObject("WorkSheets(int)", nCurrSheet);//開啟第一個sheet

    //至此已開啟,開始獲取相應屬性
    QAxObject *usedrange = pWorksheet->querySubObject("UsedRange");//獲取該sheet的使用範圍物件
    QAxObject *rows = usedrange->querySubObject("Rows");
    QAxObject *columns = usedrange->querySubObject("Columns");

    //因為excel可以從任意行列填資料而不一定是從0,0開始,因此要獲取首行列下標
    nStartRow    = usedrange->property("Row").toInt();    //第一行的起始位置
    nStartColumn = usedrange->property("Column").toInt(); //第一列的起始位置

    nRowCount    = rows->property("Count").toInt();       //獲取行數
    nColumnCount = columns->property("Count").toInt();    //獲取列數

    bIsOpen  = true;
    return bIsOpen;
}

/**
  *@brief Open()的過載函式
  */
bool ExcelEngine::Open(QString xlsFile, UINT nSheet, bool visible)
{
    sXlsFile = xlsFile;
    nCurrSheet = nSheet;
    bIsVisible = visible;

    return Open(nCurrSheet,bIsVisible);
}

/**
  *@brief 儲存表格資料,把資料寫入檔案
  */
void ExcelEngine::Save()
{
    if ( pWorkbook )
    {
        if (bIsSaveAlready)
        {
            return ;
        }

        if (!bIsANewFile)
        {
            pWorkbook->dynamicCall("Save()");
        }
        else /*如果該文件是新建出來的,則使用另存為COM介面*/
        {
            pWorkbook->dynamicCall("SaveAs (const QString&,int,const QString&,const QString&,bool,bool)",
                      sXlsFile,56,QString(""),QString(""),false,false);

        }

        bIsSaveAlready = true;
    }
}

/**
  *@brief 關閉前先儲存資料,然後關閉當前Excel COM物件,並釋放記憶體
  */
void ExcelEngine::Close()
{
    //關閉前先儲存資料
    Save();

    if ( pExcel && pWorkbook )
    {
        pWorkbook->dynamicCall("Close(bool)", true);
        pExcel->dynamicCall("Quit()");

        delete pExcel;
        pExcel = NULL;

        bIsOpen     = false;
        bIsValid    = false;
        bIsANewFile = false;
        bIsSaveAlready = true;
    }
}

/**
  *@brief 把tableWidget中的資料儲存到excel中
  *@param tableWidget : 指向GUI中的tablewidget指標
  *@return 儲存成功與否 true : 成功
  *                  false: 失敗
  */
bool ExcelEngine::SaveDataFrTable(QTableWidget *tableWidget)
{
    if ( NULL == tableWidget )
    {
        return false;
    }
    if ( !bIsOpen )
    {
        return false;
    }

    int tableR = tableWidget->rowCount();
    int tableC = tableWidget->columnCount();

    //獲取表頭寫做第一行
    for (int i=0; i<tableC; i++)
    {
        if ( tableWidget->horizontalHeaderItem(i) != NULL )
        {
            this->SetCellData(1,i+1,tableWidget->horizontalHeaderItem(i)->text());
        }
    }

    //寫資料
    for (int i=0; i<tableR; i++)
    {
        for (int j=0; j<tableC; j++)
        {
            if ( tableWidget->item(i,j) != NULL )
            {
                this->SetCellData(i+2,j+1,tableWidget->item(i,j)->text());
            }
        }
    }

    //儲存
    Save();

    return true;
}

/**
  *@brief 從指定的xls檔案中把資料匯入到tableWidget中
  *@param tableWidget : 執行要匯入到的tablewidget指標
  *@return 匯入成功與否 true : 成功
  *                   false: 失敗
  */
bool ExcelEngine::ReadDataToTable(QTableWidget *tableWidget)
{
    if ( NULL == tableWidget )
    {
        return false;
    }

    //先把table的內容清空
    int tableColumn = tableWidget->columnCount();
    tableWidget->clear();
    for (int n=0; n<tableColumn; n++)
    {
        tableWidget->removeColumn(0);
    }

    int rowcnt    = nStartRow + nRowCount;
    int columncnt = nStartColumn + nColumnCount;

    //獲取excel中的第一行資料作為表頭
    QStringList headerList;
    for (int n = nStartColumn; n<columncnt; n++ )
    {
        QAxObject * cell = pWorksheet->querySubObject("Cells(int,int)",nStartRow, n);
        if ( cell )
        {
            headerList<<cell->dynamicCall("Value2()").toString();
        }
    }

    //重新建立表頭
    tableWidget->setColumnCount(nColumnCount);
    tableWidget->setHorizontalHeaderLabels(headerList);


    //插入新資料
    for (int i = nStartRow+1, r = 0; i < rowcnt; i++, r++ )  //行
    {
        tableWidget->insertRow(r); //插入新行
        for (int j = nStartColumn, c = 0; j < columncnt; j++, c++ )  //列
        {
            QAxObject * cell = pWorksheet->querySubObject("Cells(int,int)", i, j );//獲取單元格

            //在r新行中新增子項資料
            if ( cell )
            {
                tableWidget->setItem(r,c,new QTableWidgetItem(cell->dynamicCall("Value2()").toString()));
            }
        }
    }

    return true;
}

/**
  *@brief 獲取指定單元格的資料
  *@param row : 單元格的行號
  *@param column : 單元格的列號
  *@return [row,column]單元格對應的資料
  */
QVariant ExcelEngine::GetCellData(UINT row, UINT column)
{
    QVariant data;

    QAxObject *cell = pWorksheet->querySubObject("Cells(int,int)",row,column);//獲取單元格物件
    if ( cell )
    {
        data = cell->dynamicCall("Value2()");
    }

    return data;
}

/**
  *@brief 修改指定單元格的資料
  *@param row : 單元格的行號
  *@param column : 單元格指定的列號
  *@param data : 單元格要修改為的新資料
  *@return 修改是否成功 true : 成功
  *                   false: 失敗
  */
bool ExcelEngine::SetCellData(UINT row, UINT column, QVariant data)
{
    bool op = false;

    QAxObject *cell = pWorksheet->querySubObject("Cells(int,int)",row,column);//獲取單元格物件
    if ( cell )
    {
        QString strData = data.toString(); //excel 居然只能插入字串和整型,浮點型無法插入
        cell->dynamicCall("SetValue(const QVariant&)",strData); //修改單元格的資料
        op = true;
    }
    else
    {
        op = false;
    }

    return op;
}

/**
  *@brief 清空除報表之外的資料
  */
void ExcelEngine::Clear()
{
    sXlsFile     = "";
    nRowCount    = 0;
    nColumnCount = 0;
    nStartRow    = 0;
    nStartColumn = 0;
}

/**
  *@brief 判斷excel是否已被開啟
  *@return true : 已開啟
  *        false: 未開啟
  */
bool ExcelEngine::IsOpen()
{
    return bIsOpen;
}

/**
  *@brief 判斷excel COM物件是否呼叫成功,excel是否可用
  *@return true : 可用
  *        false: 不可用
  */
bool ExcelEngine::IsValid()
{
    return bIsValid;
}

/**
  *@brief 獲取excel的行數
  */
UINT ExcelEngine::GetRowCount()const
{
    return nRowCount;
}

/**
  *@brief 獲取excel的列數
  */
UINT ExcelEngine::GetColumnCount()const
{
    return nColumnCount;
}



最近用Qt在幫別人最一個管理工具,需要操作Excel,在網上找了大半天,發現大多都是對excel進行讀操作,少有寫的,而我的需求是,既需要從excel中讀資料,也需要網excel中寫,同時需要和tablewidget關聯,也就是,最後實現,可從excel中讀取資料直接顯示到tablewidget中,並且,可把tablewidget中的資料匯出到excel報表中,網上找了半天,沒找到,多數都是抄來抄去的,於是就自己封裝了一個類。

最近修改:最近在繼續使用這個類操作Excel,發現向Excel中插入資料時,就然只能插入字串型別或者整型,浮點數型別無法插入,第二個問題是,線上程中無法使用這個類。
解決方案:關於第一個問題,我自己在類內部把插入的資料先toString成字串再插入,但是介面不變,還是原來的SetCellData(int row,int column,QVarient data);
第二個問題,搜了一下,發現是在GUI中使用COM時,Qt內部程式碼會幫我們初始化OLE環境,但是執行緒中不會,得我們自己初始化,所以,我在類的建構函式中添加了初始化的程式碼,在解構函式中清除環境。

經過改變後,類可以線上程中使用,也可以插入任何型別的資料

zz 最近修改:匯出檔案時,通過新建xls檔案方式建立檔案,原來的方法較土,是通過拷貝模板來建立


說明:該類使用COM操作excel,需要在.pro中加入CONFIG  += qaxcontainer