AFai

AFai
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

GDAL/QT 算法进度条

Posted on 2013-06-08 13:52  阿Fai  阅读(2022)  评论(0编辑  收藏  举报

在开始博文之前,先介绍下背景。这篇博文在写作过程中参考了以下资料:

李明录先生博客:

http://blog.csdn.net/liminlu0314/article/details/7276954

http://blog.csdn.net/liminlu0314/article/details/6127755

yhexie博客

http://www.cnblogs.com/yhlx125/archive/2012/12/22/2816049.html

这三篇博文详细清晰,但我之所以还会重新写一遍,原因在于我想梳理思路。本文代码分析尽可能详尽,也会详尽叙述每一过程,期望能者多指缺陷,后来者多有收获。

代码实现环境:

VS2010/gdal1.9.1_vs2010/QT4.8.4

1、首先建立QT Application程序,取名SatImg

2、实现菜单栏,创建File-Open选项,这一过程比较简单

具体参考http://www.cnblogs.com/carfield/archive/2013/06/04/3117337.html

3、通过以上两步,我们实现了菜单,在菜单实现的槽函数slotOpenFile里面,我们只是简单的实现了一些功能,不要亦可。接下来,我们来进行进度条的生成。

4、首先,添加类——C++类,类名取为基类CProcessBase,这时候会生成.h与.cpp文件,我们在这里是要定义一个基类,不要.cpp文件,删除即可。

在头文件里,我们需要申明基类中的函数,变量等,并在结构函数里赋初值。

CProcessBase.h代码

#pragma once
#include "qstring.h"

class CProcessBase
{
public:
    //构造函数,赋初值
    CProcessBase()
    {
        m_dPosition = 0.0;  
        m_iStepCount = 100;  
        m_iCurStep = 0;  
        m_bIsContinue = true;
    }
    ~CProcessBase(void)
    {}

    /**
    * @brief 设置进度信息 
    * @param pszMsg      进度信息 
    */ 
    virtual void SetMessage(const char* pszMsg) = 0;  
    
    /** 
    * @brief 设置进度值 
    * @param dPosition      进度值 
    * @return 返回是否取消的状态,true为不取消,false为取消 
    */  
    virtual bool SetPosition(double dPosition) = 0;  
  
    /** 
    * @brief 进度条前进一步,返回true表示继续,false表示取消 
    * @return 返回是否取消的状态,true为不取消,false为取消 
    */  
    virtual bool StepIt() = 0;  
  
    /** 
    * @brief 设置进度个数 
    * @param iStepCount     进度个数 
    */  
    virtual void SetStepCount(int iStepCount)  
    {  
        ReSetProcess();   
        m_iStepCount = iStepCount;  
    }  
  
    /** 
    * @brief 获取进度信息 
    * @return 返回当前进度信息 
    */  
    QString GetMessage()  
    {  
        return m_strMessage;  
    }  
  
    /** 
    * @brief 获取进度值 
    * @return 返回当前进度值 
    */  
    double GetPosition()  
    {  
        return m_dPosition;  
    }  
  
    /** 
    * @brief 重置进度条 
    */  
    void ReSetProcess()  
    {  
        m_dPosition = 0.0;  
        m_iStepCount = 100;  
        m_iCurStep = 0;  
        m_bIsContinue = true;  
    }  
  
    //进度信息
    QString m_strMessage;
    //进度值
    double m_dPosition;       
    //进度个数  
    int m_iStepCount;         
    //进度当前个数
    int m_iCurStep;           
    //是否取消,值为false时表示计算取消 
    bool m_bIsContinue;  
};

5、新建类CProcessDlg,新建类型为QT4Class,继承的基类类型为QProgressDialog,Constructor signature为QWidget *parent,这个类还需要继承之前生成的基类,所以在.h文件继承代码处中需要添加public CProcessBase,包括相应的头文件。同时CProcessDlg(QWidget *parent);改成CProcessDlg(QWidget *parent=0);按理说默认的就是等于0,但是如果不改的话会提示 “CProcessDlg”: 没有合适的默认构造函数可用

这里还要进行全局函数int STD_API ALGTermProgress的申明,为什么要这个,可以参见文章开头列出的李明录博文

CProcessDlg.h代码

#ifndef CPROCESSDLG_H
#define CPROCESSDLG_H

#include <QProgressDialog>
#include "CProcessBase.h"


#ifndef STD_API  
#define STD_API __stdcall  
#endif  
/** 
*全局函数,在类之外声明
* \brief 调用GDAL进度条接口 
* 
* 该函数用于将GDAL算法中的进度信息导出到CProcessBase基类中,供给界面显示 
* 
* @param dfComplete 完成进度值,其取值为 0.0 到 1.0 之间 
* @param pszMessage 进度信息 
* @param pProgressArg   CProcessBase的指针 
* 
* @return 返回TRUE表示继续计算,否则为取消 
*/  
int STD_API ALGTermProgress( double dfComplete, const char *pszMessage, void * pProgressArg ); 

class CProcessDlg :
    public QProgressDialog,
    public CProcessBase
{
    Q_OBJECT

public:
    //parent=0,如果为0(默认),新的窗口部件将是一个顶层部件部件。如果不是,它将会使parent的一个孩子,并且被parent的几何形状所强迫(除非你指定Qt.Window作为视窗标识)。
    CProcessDlg(QWidget *parent=0);
    ~CProcessDlg();

    /** 
    * @brief 设置进度信息 
    * @param pszMsg         进度信息 
    */  
    void SetMessage(const char* pszMsg);  
  
    /** 
    * @brief 设置进度值 
    * @param dPosition      进度值 
    */  
    bool SetPosition(double dPosition);  
  
    /** 
    * @brief 进度条前进一步 
    */  
    bool StepIt();  
  
    public slots:  
        void updateProgress(int); 
};

#endif // CPROCESSDLG_H

6、在相应的.cpp文件中进行实现:

下面这段代码中的函数并不是所有都需要用到的,例如StepIt,在GDAL提供的函数中就用不到,如果在自己编写的函数中就需要使用

#include "CProcessDlg.h"
#include "QCoreApplication"

CProcessDlg::CProcessDlg(QWidget *parent)
    : QProgressDialog(parent)
{
    m_dPosition = 0.0;  
    m_iStepCount = 100;  
    m_iCurStep = 0;

    //对进度窗口进行设计,之后还可以修改
    setModal(true);  
    setLabelText(tr("处理中..."));  
    setAutoClose(false);  
    setAutoReset(false);  
    setCancelButtonText(tr("取消"));  
    setWindowTitle(tr("进度"));
    //禁用关闭按钮  
    setWindowFlags(Qt::WindowTitleHint | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
}

CProcessDlg::~CProcessDlg()
{

}


/**
* \brief 调用GDAL进度条接口
*
* 该函数用于将GDAL算法中的进度信息导出到CProcessBase基类中,供给界面显示
*
* @param dfComplete    完成进度值,其取值为 0.0 到 1.0 之间
* @param pszMessage    进度信息
* @param pProgressArg   CProcessBase的指针
*
* @return 返回TRUE表示继续计算,否则为取消
*/
int STD_API ALGTermProgress( double dfComplete, const char *pszMessage, void * pProgressArg )
{
    if(pProgressArg != NULL)
    {
        CProcessBase * pProcess = (CProcessBase*) pProgressArg;
        pProcess->m_bIsContinue = pProcess->SetPosition(dfComplete);

        if(pProcess->m_bIsContinue)
            return TRUE;
        else
            return FALSE;
    }
    else
        return TRUE;
}

/** 
* @brief 设置进度信息 
* @param pszMsg         进度信息 
*/  
void CProcessDlg::SetMessage(const char* pszMsg)  
{  
    if (pszMsg != NULL)  
    {  
        m_strMessage = pszMsg;  
        setLabelText(QString(pszMsg));  
    }  
}  

/** 
* @brief 设置进度值 
* @param dPosition      进度值 
*/  
bool CProcessDlg::SetPosition(double dPosition)  
{  
    m_dPosition = dPosition;  

    setValue( std::min( 100u, ( uint )( m_dPosition*100.0 ) ) );  
    QCoreApplication::instance()->processEvents();     

    if(this->wasCanceled())  
        return false;  

    return true;  
}  

/** 
* @brief 进度条前进一步,返回false表示终止操作 
*/  
bool CProcessDlg::StepIt()  
{  
    m_iCurStep ++;  
    m_dPosition = m_iCurStep*1.0 / m_iStepCount;  

    setValue( std::min( 100u, ( uint )( m_dPosition*100.0 ) ) );  
    QCoreApplication::instance()->processEvents();     

    if(this->wasCanceled())  
        return false;  

    return true;  
}  

void CProcessDlg::updateProgress(int step)  
{  
    this->setValue(step);  
    QCoreApplication::instance()->processEvents();     
}

7、在satimg.cpp文件中编写实现金字塔的代码

代码编写完后,在实现过程中,出现两个问题,

第一、进度条会进行三次1-100,后来我想到ERDAS也是三次,应该是金字塔文件创建过程中的三个步骤吧,可以用其他算法验证一下,我暂时没有验证,等验证了再更新博客

第二、有时候打开文件不一定会出现进度条,当然也不进行金字塔处理。这点不知道为什么

//*************************************************
//*************************************************

//*************************************************
//*************************************************
 
#include "satimg.h"
#include <QFileDialog>
#include <QMessageBox>
#include "gdal.h"
#include "gdal_priv.h"
#include "CProcessBase.h"
#include "CProcessDlg.h"


SatImg::SatImg(QWidget *parent, Qt::WFlags flags)
    : QMainWindow(parent, flags)
{
    ui.setupUi(this);
    //UI先setupUi之后才能实现其他操作
    setWindowTitle(tr("SatImgFrame"));
    creatActions();
    creatMenus();
}

SatImg::~SatImg()
{

}


void SatImg::creatMenus()
{
    //文件菜单
    menuFile = menuBar()->addMenu(tr("File"));
    //menuFile->addAction(actionNewfile);
    menuFile->addAction(actionOpenFile);
    //menuFile->addAction(actionSaveFileSave);
    //menuFile->addAction(actionExit);
}

void SatImg::creatActions()
{
    //open file action
    //actionOpenFile= new QAction(QIcon(":/images/open.png"),tr("Open"),this);
    actionOpenFile= new QAction(tr("Open"),this);
    actionOpenFile->setShortcut(QKeySequence(tr("Ctrl+O")));
    actionOpenFile->setStatusTip(tr("open file"));
    //信号函数和槽函数必须要有相同的参数,这样信号和槽函数才能成功连接。
    connect(actionOpenFile,SIGNAL(triggered()),this,SLOT(slotOpenFile()));
}

void SatImg::slotOpenFile()
{
    QString slotFileName=QFileDialog::getOpenFileName(this,tr("Open Image"),".", tr("Image Files(*.jpg *.png *.tif)"));
    if(slotFileName.length() == 0) 
    { 
        QMessageBox::information(NULL, tr("Path"), tr("You didn't select any files.")); 
    }    
    if (slotFileName!=NULL)
    {
        //QString convert to const char*
        std::string str=std::string(slotFileName.toAscii().data());
        const char *ConstCharFileName = str.c_str();
        CProcessDlg *pPro = new CProcessDlg();  
        pPro->setWindowTitle(tr("正在执行金字塔"));  
        pPro->show(); 
        int irev=CreatePyramids(ConstCharFileName,pPro);
        delete pPro;
    }
    
    
}

/*
*Create Pyramids
*pszFileName:影像文件文件名(const char*)
*/
int SatImg::CreatePyramids(const char* pszFileName,CProcessBase *pProcess=NULL)
{
    GDALAllRegister();
    CPLSetConfigOption("USE_RRD","YES");//创建Erdas格式的字塔文件

    GDALDatasetH hDataset;
    hDataset = GDALOpen( pszFileName, GA_ReadOnly ); 


    /* -------------------------------------------------------------------- */  
    /*      Open data file.                                                 */  
    /* -------------------------------------------------------------------- */
    GDALDriverH hDriver = GDALGetDatasetDriver(hDataset);
    const char* pszDriver = GDALGetDriverShortName(hDriver);
    if (EQUAL(pszDriver, "HFA") || EQUAL(pszDriver, "PCIDSK"))
    {
        GDALClose(hDataset);    //如果文件是Erdas的img或者PCI的pix格式,创建内金字塔,其他的创建外金字塔 
        hDataset = GDALOpen( pszFileName, GA_Update ); 
    }
    if( hDataset == NULL )
    {
        return 0; 
    } 

    /* -------------------------------------------------------------------- */  
    /*      Get File basic infomation                                       */  
    /* -------------------------------------------------------------------- */ 
    int iWidth = GDALGetRasterXSize(hDataset);  
    int iHeigh = GDALGetRasterYSize(hDataset);  
    int iPixelNum = iWidth * iHeigh;    //图像中的总像元个数 
    int iTopNum = 4096;                 //顶层金字塔大小,64*64  
    int iCurNum = iPixelNum / 4; 
    int anLevels[1024] = { 0 }; 
    int nLevelCount = 0;                //金字塔级数
    do    //计算金字塔级数,从第二级到顶层  
    { 
        anLevels[nLevelCount] = static_cast<int>(pow(2.0, nLevelCount+2));  
        nLevelCount ++;  
        iCurNum /= 4;  
    } while (iCurNum > iTopNum);  

    const char *pszResampling = "nearest"; //采样方式  
    GDALProgressFunc pfnProgress=ALGTermProgress;
    /* -------------------------------------------------------------------- */  
    /*      Generate overviews.                                             */  
    /* -------------------------------------------------------------------- */ 
    if (nLevelCount > 0 &&     GDALBuildOverviews( hDataset,pszResampling, nLevelCount, anLevels,0, NULL, pfnProgress, pProcess ) != CE_None )  
    {   
        return 0;  
    }  

    /* -------------------------------------------------------------------- */  
    /*      Cleanup                                                         */  
    /* -------------------------------------------------------------------- */   
    GDALClose(hDataset);  
    GDALDestroyDriverManager();     
    return 1;  
}

相应的头文件

#ifndef SATIMG_H
#define SATIMG_H
/*
第1和第2句定义头文件包含卫哨
目的是防止重复包含头文件
这两句与结尾"#endif"42句结合在一起使用才是完整的
*/

#include <QtGui/QMainWindow>
#include "ui_satimg.h"
#include "CProcessBase.h"
#include "CProcessDlg.h"

//前置声明
class QAction;
class QMenu;

class SatImg : public QMainWindow
{
    Q_OBJECT

public:
    SatImg(QWidget *parent = 0, Qt::WFlags flags = 0);
    ~SatImg();

    void creatMenus();
    void creatActions();


private:
    Ui::SatImgClass ui;

public slots:
    void slotOpenFile();

private:
    QMenu *menuFile;
    QAction *actionOpenFile;

public:
    int CreatePyramids(const char* pszFileName,CProcessBase *pProgress);

         
};

#endif // SATIMG_H

最后,为了让控件支持显示中文字符

main.cpp

#include "satimg.h"
#include <QtGui/QApplication>
#include <QTextCodec>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    QTextCodec::setCodecForTr(QTextCodec::codecForLocale());
    SatImg w;
    w.show();
    return a.exec();
}