qt&gdal
2016-01-11 16:09
477 查看
转自:http://blog.csdn.net/deirjie/article/details/37872743
使用需要自行配置Qt和GDAL路径。
近期写了一个高光谱图像光谱曲线匹配的算法,想封装到软件当中方便观察效果,也便于做后期算法改进和实际应用,并且以后的算法可以直接集成上来。于是打算自己写一个基本的框架实现图像浏览的一些基本功能。在网上各种找,利用GDAL进行遥感图像显示的代码很多,但不是有问题就是写的不太清楚,不够简洁,并且大多基于MFC。经过几天的奋战,成功实现了利用Qt框架进行遥感图像显示的基本功能,于是分享出来,对自己是总结,并且希望对别人有所帮助。
开发环境:VS2010, Qt4.8.4,GDAL
1.10。
说明:这里并没有Qt和GDAL,以及C++的一些基本的东西,这里只关注功能的实现做说明。如果对于Qt和GDAL有问题的,请参见其他资料。并不高深,高手绕道!
重要说明:Qt发音叫做“Q特”,不是“Q替”,不要再读错了,官网有专门说明的!不要搞半天还说我发音有问题!!! 还有要写成Qt,不是QT,QT是QuickTime,不要混淆了。
OK,先说说程序的功能:
能够打开图像,并读取图像元数据信息;
能够实现图像的正确显示,包括灰度图和RGB真彩色图的显示;
支持图像的缩放,漫游;
![](http://img.blog.csdn.net/20140716102432359?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGVpcmppZQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
![](http://img.blog.csdn.net/20140716102719487?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvZGVpcmppZQ==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
功能非常简单基础,但是这当中很多细节是需要认真参透的。下面开始逐一进行讲解。(PS:UI的设计就不讲了。)
1.程序简单设计
其实这个程序的设计相对简单,除了显示图像的控件需要自定义,其他的都可以直接在Qt Designer里面拖放就行了。那就来说说图像显示控件吧。先看头文件:
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
//***********************************************************************
//Assembly : ImgTest
//Author : Jacory
//Created : 07-10-2014
//
// LastModified By : Jacory
// LastModified On : 07-10-2014
//***********************************************************************
//<copyright file="MapCanvas.h" company="">
// Copyright (c) . All rights reserved.
//</copyright>
//<summary>图像显示窗口类,负责图像的读取和显示等功能。</summary>
//***********************************************************************
#ifndefMAPCANVAS_H
#defineMAPCANVAS_H
#include<QtGui/QGraphicsView>
#include<QStandardItemModel>
#include<gdal_priv.h>
///<summary>
/// ClassMapCanvas.
///</summary>
classMapCanvas : public QGraphicsView
{
Q_OBJECT
public:
MapCanvas( QWidget *parent = 0 );
~MapCanvas();
void ReadImg( const QString imgPath );
void CloseCurrentImg();
/// <summary>
/// 返回图像元数据信息模型.
/// </summary>
///<returns>图像元数据信息模型.</returns>
QStandardItemModel* ImgMetaModel()
{
return imgMetaModel;
};
/// <summary>
/// 设置图像元数据信息模型
/// </summary>
/// <paramname="model">图像元数据信息模型.</param>
void SetMetaModel( QStandardItemModel*model )
{
this->imgMetaModel = model;
};
/// <summary>
/// 返回文件列表数据模型
/// </summary>
///<returns>文件列表数据模型.</returns>
QStandardItemModel* FileListModel()
{
return fileListModel;
};
/// <summary>
/// 设置fileListModel图像文件列表数据模型
/// </summary>
/// <paramname="model">文件列表数据模型.</param>
void SetFileListModel( QStandardItemModel*model )
{
this->fileListModel = model;
};
QSize sizeHint() const;
publicslots:
/// <summary>
/// 放大图像
/// </summary>
void ZoomIn()
{
ScaleImg( 1.2 );
};
/// <summary>
/// 缩小图像
/// </summary>
void ZoomOut()
{
ScaleImg( 0.8 );
};
protected:
void wheelEvent( QWheelEvent *event );
void mousePressEvent( QMouseEvent *event );
void mouseMoveEvent( QMouseEvent *event );
void mouseReleaseEvent( QMouseEvent *event);
private:
void ShowBand( GDALRasterBand* band );
void ShowImg( QList<GDALRasterBand*>*imgBand );
void ShowImgInfor( const QString filename);
void ShowFileList( const QString filename);
unsigned char* ImgSketch( float* buffer ,GDALRasterBand* currentBand, int size, double noValue );
/// <summary>
/// 图像缩放
/// </summary>
/// <paramname="factor">缩放因子</param>
void ScaleImg( double factor )
{
m_scaleFactor *= factor;
QMatrix matrix;
matrix.scale( m_scaleFactor,m_scaleFactor );
this->setMatrix( matrix );
};
/// <summary>
/// 图像元数据模型
/// </summary>
QStandardItemModel *imgMetaModel;
/// <summary>
/// 图像数据集
/// </summary>
GDALDataset *poDataset;
/// <summary>
/// 文件列表数据模型
/// </summary>
QStandardItemModel *fileListModel;
/// <summary>
/// 缩放系数
/// </summary>
float m_scaleFactor;
/// <summary>
/// 判断是否显示RGB彩色图像
/// </summary>
bool m_showColor;
/// <summary>
/// 上一个鼠标事件触发时鼠标的位置
/// </summary>
QPoint lastEventCursorPos;
};
#endif //MAPCANVAS_H
mapView类集成自QGraphicsView类。代码里面对于成员变量和inline函数我已经写了非常详细的注释了,这里再说明一下几个函数的功能。void
ReadImg( const QStringimgPath )是读图函数,通过UI来调用,它并不直接读取图像,而是将相应的参数传递给ShowBand、ShowImg、ShowImgInfor、ShowFileList来分别实现各自相应的功能。void
ShowBand( GDALRasterBand*band )函数是显示单个波段图像,当文件中的波段数不为3个时,没法组成RGB显示,默认就显示单个波段图像,事实上它只是把单个波段加入3次到QList<GDALRasterBand*>,然后把这个波段列表传递给ShowImg进行显示。而void
ShowImg( QList<GDALRasterBand*>*imgBand )是显示图像的核心函数,它的内部通过判断m_showColor的真假来决定将要显示的图像的组织方式,并通过unsigned
char* ImgSketch(float* buffer , GDALRasterBand* currentBand, int size, double noValue )函数对图像波段进行拉伸,详细说明放在后面,这里只要知道是什么用就行了。void ShowImgInfor(
constQString filename )、 void ShowFileList( const QString filename )这两个函数,分别是为了读取并显示图像文件的元数据信息和文件结构树,分别位于主界面的两侧。void
CloseCurrentImg()这个函数很简单,就是关闭当前图像。void wheelEvent( QWheelEvent*event )、void
mousePressEvent(QMouseEvent *event )、void mouseMoveEvent(QMouseEvent *event )、void
mouseReleaseEvent(QMouseEvent *event )这4个函数都是重写的鼠标事件,具体后面再讨论。
看完了头文件,已经基本知道这个MapView是一个什么样的对象了,事实上所有与图像显示有关的操作都属于这个类的职责,自然功能函数也就应该写在这个类里。下面我们根据需求来逐步实现这些功能函数。
2.打开图像并读取图像元数据信息;
这一步先不显示图像,只要求能够正确打开图像并读取出图像的基本信息即可。主要的函数就是ShowImgInfor和ShowFileList这两个函数,来看代码:
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
///<summary>
///显示图像基本信息
///</summary>
///<param name="filename">文件名</param>
voidMapCanvas::ShowImgInfor( const QString filename )
{
if ( filename == "" || poDataset== NULL )
{
return;
}
int row = 0; // 用来记录数据模型的行号
// 图像的格式信息
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Description" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetDescription() ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Meta Infor" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetMetadataItem( GDAL_DMD_LONGNAME) ) ) ;
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Data Type" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( GDALGetDataTypeName( ( poDataset->GetRasterBand( 1)->GetRasterDataType() ) ) ) );
// 图像的大小和波段个数
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "X Size" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterXSize() ) ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Y Size" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterYSize() ) ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Band Count" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterCount() ) ) );
// 图像的投影信息
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Projection" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetProjectionRef() ) );
// 图像的坐标和分辨率信息
double adfGeoTransform[6];
QString origin = "";
QString pixelSize = "";
if( poDataset->GetGeoTransform(adfGeoTransform ) == CE_None )
{
origin = QString::number(adfGeoTransform[0] ) + ", " + QString::number( adfGeoTransform[3] );
pixelSize = QString::number(adfGeoTransform[1] ) + ", " + QString::number( adfGeoTransform[5] );
}
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Origin" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( origin ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Pixel Size" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( pixelSize ) );
}
///<summary>
///显示文件结构树
///</summary>
///<param name="filename">文件名</param>
voidMapCanvas::ShowFileList( const QString filename )
{
if ( filename == "" || poDataset== NULL )
{
return;
}
QFileInfo fileInfo( filename );
QStandardItem *rootItem = newQStandardItem( fileInfo.fileName() );
for ( int i = 0; i <poDataset->GetRasterCount(); i++ )
{
QStandardItem *childItem = newQStandardItem( tr( "Band %1" ).arg( i + 1 ) );
rootItem->setChild( i, childItem );
}
fileListModel->setItem( 0, rootItem );
}
其实很简单,就是用了Qt的Model-View模型,显示的控件分别是QTableView和QTreeView,数据模型分别是这里的imgMetaModel和fileListModel,只用将模型适当的初始化并绑定到显示控件上就行了。以上初始化比较简单,这里就不啰嗦了,都是一些基本的GDAL数据模型和Qt的东西。
3.实现图像的显示功能
这一步就是核心了,我打算通过函数调用的顺序来逐步讲解这里牵涉到的功能函数。首先是UI选择文件后,将文件路径传递给ReadImg函数进行文件读取,那我们先来看看ReadImg函数的实现。
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
///<summary>
/// 读取图像文件
///</summary>
///<param name="imgPath">图像文件</param>
voidMapCanvas::ReadImg( const QString imgPath )
{
GDALAllRegister();
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO" );
poDataset = ( GDALDataset* )GDALOpen(imgPath.toStdString().c_str(), GA_ReadOnly );
if ( poDataset == NULL )
{
QMessageBox::critical( this, tr("Error!" ), tr( "Can not open file %1" ).arg( imgPath ) );
return;
}
ShowFileList( imgPath );
ShowImgInfor( imgPath );
// 如果图像文件并非三个波段,则默认只显示第一波段灰度图像
if ( poDataset->GetRasterCount() != 3 )
{
m_showColor = false;
ShowBand( poDataset->GetRasterBand(1 ) );
}
// 如果图像正好三个波段,则默认以RGB的顺序显示彩色图
else
{
m_showColor = true;
QList<GDALRasterBand*> bandList;
bandList.append(poDataset->GetRasterBand( 1 ) );
bandList.append(poDataset->GetRasterBand( 2 ) );
bandList.append(poDataset->GetRasterBand( 3 ) );
ShowImg( &bandList );
}
GDALClose( poDataset );
}
逻辑很清晰,调用ShowFileList和ShowImgInfor初始化两个model以供相应的显示View控件来显示。再判断如果图像文件波段数不为3,则默认显示第一波段灰度图像,否则显示彩色图。
再来看ShowBand函数:
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
///<summary>
///显示单波段图像
///</summary>
///<param name="band">图像波段</param>
voidMapCanvas::ShowBand( GDALRasterBand* band )
{
if ( band == NULL )
{
return;
}
QList<GDALRasterBand*> myBand;
myBand.append( band );
myBand.append( band );
myBand.append( band );
ShowImg( &myBand );
}
可以看到,它只是把单个波段用列表的方式存储了三次,传递给ShowImg来进行显示。那直接看ShowImg吧。
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
///<summary>
/// 显示图像
///</summary>
///<param name="imgBand">图像波段</param>
voidMapCanvas::ShowImg( QList<GDALRasterBand*> *imgBand )
{
if ( imgBand->size() != 3 )
{
return;
}
int imgWidth = imgBand->at( 0)->GetXSize();
int imgHeight = imgBand->at( 0)->GetYSize();
m_scaleFactor = this->height() * 1.0 /imgHeight;
int iScaleWidth = ( int )( imgWidth *m_scaleFactor - 1 );
int iScaleHeight = ( int )( imgHeight *m_scaleFactor - 1 );
GDALDataType dataType = imgBand->at( 0)->GetRasterDataType();
// 首先分别读取RGB三个波段
float* rBand = new float[iScaleWidth *iScaleHeight];
float* gBand = new float[iScaleWidth *iScaleHeight];
float* bBand = new float[iScaleWidth *iScaleHeight];
unsigned char *rBandUC, *gBandUC, *bBandUC;
// 根据是否显示彩色图像,判断RGB三个波段的组成方式,并分别读取
if ( m_showColor == true )
{
imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
imgBand->at( 1 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, gBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
imgBand->at( 2 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, bBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
// 分别拉伸每个波段并将Float转换为unsigned char
rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );
gBandUC = ImgSketch( gBand,imgBand->at( 1 ), iScaleWidth * iScaleHeight, imgBand->at( 1)->GetNoDataValue() );
bBandUC = ImgSketch( bBand,imgBand->at( 2 ), iScaleWidth * iScaleHeight, imgBand->at( 2)->GetNoDataValue() );
}
else
{
imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );
gBandUC = rBandUC;
bBandUC = rBandUC;
}
// 将三个波段组合起来
int bytePerLine = ( iScaleWidth * 24 + 31 )/ 8;
unsigned char* allBandUC = new unsignedchar[bytePerLine * iScaleHeight * 3];
for( int h = 0; h < iScaleHeight; h++ )
{
for( int w = 0; w < iScaleWidth; w++)
{
allBandUC[h * bytePerLine + w * 3 +0] = rBandUC[h * iScaleWidth + w];
allBandUC[h * bytePerLine + w * 3 +1] = gBandUC[h * iScaleWidth + w];
allBandUC[h * bytePerLine + w * 3 +2] = bBandUC[h * iScaleWidth + w];
}
}
// 构造图像并显示
QGraphicsPixmapItem *imgItem = newQGraphicsPixmapItem( QPixmap::fromImage(QImage( allBandUC, iScaleWidth, iScaleHeight, bytePerLine,QImage::Format_RGB888 ) ) );
QGraphicsScene *myScene = newQGraphicsScene();
myScene->addItem( imgItem );
this->setScene( myScene );
}
这个函数的核心在于构造QGraphicsPixmapItem,也就是这里的imgItem这个对象,之前的代码都是为了正确构造这个对象而做的工作。这里的imgWidth和imgHeight分别是图像文件的宽和高,而iScaleWidth和iScaleHeight是缩放后的宽和高,缩放的条件正是显示窗口的大小。读取图像时,采用分别读取RGB三个波段,最后再组合起来的方式,读取波段都是采用RasterIO函数,采用float的数组来进行读取避免一些格式问题带来的精度截断,读取之后,通过ImgSketch函数进行图像拉伸,把图像值拉伸到0~255灰度级。
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
///<summary>
/// 图像线性拉伸
///</summary>
///<param name="buffer">图像缓存</param>
///<param name="currentBand">当前波段</param>
///<param name="size">The size.</param>
///<param name="noValue">图像中的异常值</param>
///<returns>经过拉伸的8位图像缓存</returns>
unsignedchar* MapCanvas::ImgSketch( float* buffer , GDALRasterBand* currentBand, intbandSize, double noValue )
{
unsigned char* resBuffer = new unsignedchar[bandSize];
double max, min;
double minmax[2];
currentBand->ComputeRasterMinMax( 1,minmax );
min = minmax[0];
max = minmax[1];
if( min <= noValue && noValue<= max )
{
min = 0;
}
for ( int i = 0; i < bandSize; i++ )
{
if ( buffer[i] > max )
{
resBuffer[i] = 255;
}
else if ( buffer[i] <= max&& buffer[i] >= min )
{
resBuffer[i] =static_cast<uchar>( 255 - 255 * ( max - buffer[i] ) / ( max - min ) );
}
else
{
resBuffer[i] = 0;
}
}
return resBuffer;
}
这里的转换核心为
resBuffer[i] = static_cast<uchar>( 255 - 255 * ( max -buffer[i] ) / ( max - min ) );
转换公式也可以变为((buffer[i]-min)/(max-min))*255,是一样的,当然还有其他的拉伸方法,可以自行使用。
拉伸之后组合波段,需要用到int bytePerLine = (iScaleWidth * 24 + 31 ) / 8;这句,因为要显示的图像为8位的,需要把图像值变换为unsigned
char型,这个工作在imgSketch里面已经做了,但是由于要将3个波段组织正确,还需要字节对齐,也就是这里的bytePerLine,这个是byte型表示的这个图像的宽度,当然是包含了RGB三个波段。然后就是将每个波段的值一一对齐放入allBandUC这个数组中,最后根据这个组合后的数组构QImage,QPixmap以及QGraphicsPixmapItem,最后把这个Item加入到scene里面用MapView显示就行了。需要注意的是,这个过程看似简单,但里面有很多细节的地方需要认真对待,仔细思考,不然很容易得不到正确的结果。
4.实现图像的漫游功能
这一部分我在网上看了很多代码,大部分人都是从底层原理开始又来写一遍,但其实对于漫游的操作其实Qt在后台已经支持的非常好了,我们的MapView继承自QGraphicsView,这个父类已经把很多操作封装到位了,我们只要合理组织就好了,没有必要再去写一遍。那就很简单了,来分别看看实现代码吧。
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
///<summary>
///鼠标滚轮事件,实现图像缩放
///</summary>
///<param name="event">滚轮事件</param>
voidMapCanvas::wheelEvent( QWheelEvent *event )
{
// 滚轮向上滑动,放大图像
if ( event->delta() > 0 )
{
ZoomIn();
}
// 滚轮向下滑动,缩小图像
if ( event->delta() < 0 )
{
ZoomOut();
}
}
///<summary>
///鼠标按键按下事件
///</summary>
///<param name="event">鼠标事件.</param>
voidMapCanvas::mousePressEvent( QMouseEvent *event )
{
// 滚轮键按下,平移图像
if ( event->button() == Qt::MidButton )
{
this->setDragMode(QGraphicsView::ScrollHandDrag );
this->setInteractive( false );
lastEventCursorPos = event->pos();
}
}
///<summary>
/// 鼠标移动事件
///</summary>
///<param name="event">鼠标事件</param>
voidMapCanvas::mouseMoveEvent( QMouseEvent *event )
{
if ( this->dragMode() ==QGraphicsView::ScrollHandDrag )
{
QPoint delta = ( event->pos() -lastEventCursorPos ) / 10;
this->horizontalScrollBar()->setValue(this->horizontalScrollBar()->value() + ( isRightToLeft() ? delta.x() :-delta.x() ) );
this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() - delta.y() );
this->viewport()->setCursor(Qt::ClosedHandCursor );
}
}
///<summary>
///鼠标按键释放事件
///</summary>
///<param name="event">鼠标事件</param>
voidMapCanvas::mouseReleaseEvent( QMouseEvent *event )
{
if ( event->button() == Qt::MidButton )
{
this->setDragMode(QGraphicsView::NoDrag );
}
}
重写鼠标事件,这里需要说明的是,漫游的操作Qt采用鼠标左键按下并移动来实现视图平移,但是我这里强制定义为了鼠标中键按下,所以还比较麻烦相对来说,如果你只想用默认的漫游操作,直接把this->setDragMode(QGraphicsView::NoDrag
);这句加入到你的构造函数中就行了,非常简单。事实上我这个平移函数有问题,我后面再解释。这里我们先关注放大缩小功能,这部分代码我是通过inline的方式添加到头文件中的。
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
///<summary>
/// 放大图像
/// </summary>
void ZoomIn()
{
ScaleImg( 1.2 );
};
/// <summary>
/// 缩小图像
/// </summary>
void ZoomOut()
{
ScaleImg( 0.8 );
};
///<summary>
/// 图像缩放
/// </summary>
/// <paramname="factor">缩放因子</param>
void ScaleImg( double factor )
{
m_scaleFactor *= factor;
QMatrix matrix;
matrix.scale( m_scaleFactor,m_scaleFactor );
this->setMatrix( matrix );
};
图像缩放也非常简单,让你的QGraphicsView去处理细节,不要自己又去从底层计算然后写一大堆代码,还不一定就比Qt默认的方式效率高。
做到这里基本就搞定了,我刚刚说的平移函数那里,实际上我在mouseMoveEvent里采用计算当前坐标点和事件发生的坐标点差值的方式,然后去设置水平和竖直滑动条的值来实现平移
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
QPointdelta = ( event->pos() - lastEventCursorPos ) / 10;
this->horizontalScrollBar()->setValue(this->horizontalScrollBar()->value() + ( isRightToLeft() ? delta.x() :-delta.x() ) );
this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() - delta.y() );
我这样写后,我的平移实际上并不是非常流畅,而是有点像浏览网页时按住鼠标中键往下浏览的效果,但实际上很多GIS和遥感软件的平移方式都不是这样的。这里应该是我对Qt还有些不熟悉,也希望大家知道的话告诉我一下,我认为Qt应该是支持的。
好了,差不多了,其他的功能我也还在完善,先写到这里吧,希望对大家有所帮助。最后还是把MapView的实现文件整体粘上来。VS2010的工程文件我放到CSDN的资源里面大家可以下载。
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
![](https://code.csdn.net/assets/ico_fork.svg)
#include"MapCanvas.h"
#include<QtGui/QMessageBox>
#include<QtCore/QFileInfo>
#include<QtGui/QImage>
#include<QtGui/QPixmap>
#include<QtGui/QGraphicsPixmapItem>
#include<QtGui/QMatrix>
#include<QtGui/QWheelEvent>
#include<QtGui/QScrollBar>
MapCanvas::MapCanvas(QWidget *parent /*= 0 */ )
: QGraphicsView( parent )
{
poDataset = NULL;
m_scaleFactor = 1.0;
m_showColor = true;
imgMetaModel = new QStandardItemModel;
imgMetaModel->setColumnCount( 2 );
fileListModel = new QStandardItemModel;
QSizePolicy policy( QSizePolicy::Preferred,QSizePolicy::Preferred );
this->setSizePolicy( policy );
}
///<summary>
///Finalizes an instance of the <see cref="MapCanvas" /> class.
///</summary>
MapCanvas::~MapCanvas()
{
}
///<summary>
/// 读取图像文件
///</summary>
///<param name="imgPath">图像文件</param>
voidMapCanvas::ReadImg( const QString imgPath )
{
GDALAllRegister();
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO" );
poDataset = ( GDALDataset* )GDALOpen(imgPath.toStdString().c_str(), GA_ReadOnly );
if ( poDataset == NULL )
{
QMessageBox::critical( this, tr("Error!" ), tr( "Can not open file %1" ).arg( imgPath ) );
return;
}
ShowFileList( imgPath );
ShowImgInfor( imgPath );
// 如果图像文件并非三个波段,则默认只显示第一波段灰度图像
if ( poDataset->GetRasterCount() != 3 )
{
m_showColor = false;
ShowBand( poDataset->GetRasterBand(1 ) );
}
// 如果图像正好三个波段,则默认以RGB的顺序显示彩色图
else
{
m_showColor = true;
QList<GDALRasterBand*> bandList;
bandList.append(poDataset->GetRasterBand( 1 ) );
bandList.append(poDataset->GetRasterBand( 2 ) );
bandList.append(poDataset->GetRasterBand( 3 ) );
ShowImg( &bandList );
}
GDALClose( poDataset );
}
///<summary>
///关闭当前图像文件
///</summary>
voidMapCanvas::CloseCurrentImg()
{
poDataset = NULL;
imgMetaModel->clear();
fileListModel->clear();
}
///<summary>
///显示单波段图像
///</summary>
///<param name="band">图像波段</param>
voidMapCanvas::ShowBand( GDALRasterBand* band )
{
if ( band == NULL )
{
return;
}
QList<GDALRasterBand*> myBand;
myBand.append( band );
myBand.append( band );
myBand.append( band );
ShowImg( &myBand );
}
///<summary>
/// 显示图像
///</summary>
///<param name="imgBand">图像波段</param>
voidMapCanvas::ShowImg( QList<GDALRasterBand*> *imgBand )
{
if ( imgBand->size() != 3 )
{
return;
}
int imgWidth = imgBand->at( 0)->GetXSize();
int imgHeight = imgBand->at( 0)->GetYSize();
m_scaleFactor = this->height() * 1.0 /imgHeight;
int iScaleWidth = ( int )( imgWidth *m_scaleFactor - 1 );
int iScaleHeight = ( int )( imgHeight *m_scaleFactor - 1 );
GDALDataType dataType = imgBand->at( 0)->GetRasterDataType();
// 首先分别读取RGB三个波段
float* rBand = new float[iScaleWidth *iScaleHeight];
float* gBand = new float[iScaleWidth *iScaleHeight];
float* bBand = new float[iScaleWidth *iScaleHeight];
unsigned char *rBandUC, *gBandUC, *bBandUC;
// 根据是否显示彩色图像,判断RGB三个波段的组成方式,并分别读取
if ( m_showColor == true )
{
imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
imgBand->at( 1 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, gBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
imgBand->at( 2 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, bBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
// 分别拉伸每个波段并将Float转换为unsigned char
rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );
gBandUC = ImgSketch( gBand,imgBand->at( 1 ), iScaleWidth * iScaleHeight, imgBand->at( 1)->GetNoDataValue() );
bBandUC = ImgSketch( bBand,imgBand->at( 2 ), iScaleWidth * iScaleHeight, imgBand->at( 2)->GetNoDataValue() );
}
else
{
imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );
gBandUC = rBandUC;
bBandUC = rBandUC;
}
// 将三个波段组合起来
int bytePerLine = ( iScaleWidth * 24 + 31 )/ 8;
unsigned char* allBandUC = new unsignedchar[bytePerLine * iScaleHeight * 3];
for( int h = 0; h < iScaleHeight; h++ )
{
for( int w = 0; w < iScaleWidth; w++)
{
allBandUC[h * bytePerLine + w * 3 +0] = rBandUC[h * iScaleWidth + w];
allBandUC[h * bytePerLine + w * 3 +1] = gBandUC[h * iScaleWidth + w];
allBandUC[h * bytePerLine + w * 3 +2] = bBandUC[h * iScaleWidth + w];
}
}
// 构造图像并显示
QGraphicsPixmapItem *imgItem = newQGraphicsPixmapItem( QPixmap::fromImage(QImage( allBandUC, iScaleWidth, iScaleHeight, bytePerLine,QImage::Format_RGB888 ) ) );
QGraphicsScene *myScene = newQGraphicsScene();
myScene->addItem( imgItem );
this->setScene( myScene );
}
///<summary>
///显示图像基本信息
///</summary>
///<param name="filename">文件名</param>
voidMapCanvas::ShowImgInfor( const QString filename )
{
if ( filename == "" || poDataset== NULL )
{
return;
}
int row = 0; // 用来记录数据模型的行号
// 图像的格式信息
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Description" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetDescription() ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Meta Infor" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetMetadataItem( GDAL_DMD_LONGNAME) ) ) ;
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Data Type" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( GDALGetDataTypeName( ( poDataset->GetRasterBand( 1)->GetRasterDataType() ) ) ) );
// 图像的大小和波段个数
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "X Size" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterXSize() ) ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Y Size" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterYSize() ) ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Band Count" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterCount() ) ) );
// 图像的投影信息
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Projection" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetProjectionRef() ) );
// 图像的坐标和分辨率信息
double adfGeoTransform[6];
QString origin = "";
QString pixelSize = "";
if( poDataset->GetGeoTransform(adfGeoTransform ) == CE_None )
{
origin = QString::number(adfGeoTransform[0] ) + ", " + QString::number( adfGeoTransform[3] );
pixelSize = QString::number(adfGeoTransform[1] ) + ", " + QString::number( adfGeoTransform[5] );
}
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Origin" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( origin ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Pixel Size" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( pixelSize ) );
}
///<summary>
///显示文件结构树
///</summary>
///<param name="filename">文件名</param>
voidMapCanvas::ShowFileList( const QString filename )
{
if ( filename == "" || poDataset== NULL )
{
return;
}
QFileInfo fileInfo( filename );
QStandardItem *rootItem = newQStandardItem( fileInfo.fileName() );
for ( int i = 0; i <poDataset->GetRasterCount(); i++ )
{
QStandardItem *childItem = newQStandardItem( tr( "Band %1" ).arg( i + 1 ) );
rootItem->setChild( i, childItem );
}
fileListModel->setItem( 0, rootItem );
}
///<summary>
/// 图像线性拉伸
///</summary>
///<param name="buffer">图像缓存</param>
///<param name="currentBand">当前波段</param>
///<param name="size">The size.</param>
///<param name="noValue">图像中的异常值</param>
///<returns>经过拉伸的8位图像缓存</returns>
unsignedchar* MapCanvas::ImgSketch( float* buffer , GDALRasterBand* currentBand, intbandSize, double noValue )
{
unsigned char* resBuffer = new unsignedchar[bandSize];
double max, min;
double minmax[2];
currentBand->ComputeRasterMinMax( 1,minmax );
min = minmax[0];
max = minmax[1];
if( min <= noValue && noValue<= max )
{
min = 0;
}
for ( int i = 0; i < bandSize; i++ )
{
if ( buffer[i] > max )
{
resBuffer[i] = 255;
}
else if ( buffer[i] <= max&& buffer[i] >= min )
{
resBuffer[i] =static_cast<uchar>( 255 - 255 * ( max - buffer[i] ) / ( max - min ) );
}
else
{
resBuffer[i] = 0;
}
}
return resBuffer;
}
///<summary>
/// 控件大小
///</summary>
///<returns>QSize.</returns>
QSizeMapCanvas::sizeHint() const
{
return QSize( 1024, 768 );
}
///<summary>
///鼠标滚轮事件,实现图像缩放
///</summary>
///<param name="event">滚轮事件</param>
voidMapCanvas::wheelEvent( QWheelEvent *event )
{
// 滚轮向上滑动,放大图像
if ( event->delta() > 0 )
{
ZoomIn();
}
// 滚轮向下滑动,缩小图像
if ( event->delta() < 0 )
{
ZoomOut();
}
}
///<summary>
///鼠标按键按下事件
///</summary>
///<param name="event">鼠标事件.</param>
voidMapCanvas::mousePressEvent( QMouseEvent *event )
{
// 滚轮键按下,平移图像
if ( event->button() == Qt::MidButton )
{
this->setDragMode(QGraphicsView::ScrollHandDrag );
this->setInteractive( false );
lastEventCursorPos = event->pos();
}
}
///<summary>
/// 鼠标移动事件
///</summary>
///<param name="event">鼠标事件</param>
voidMapCanvas::mouseMoveEvent( QMouseEvent *event )
{
if ( this->dragMode() ==QGraphicsView::ScrollHandDrag )
{
QPoint delta = ( event->pos() -lastEventCursorPos ) / 10;
this->horizontalScrollBar()->setValue(this->horizontalScrollBar()->value() + ( isRightToLeft() ? delta.x() :-delta.x() ) );
this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() - delta.y() );
this->viewport()->setCursor(Qt::ClosedHandCursor );
}
}
///<summary>
///鼠标按键释放事件
///</summary>
///<param name="event">鼠标事件</param>
voidMapCanvas::mouseReleaseEvent( QMouseEvent *event )
{
if ( event->button() == Qt::MidButton )
{
this->setDragMode(QGraphicsView::NoDrag );
}
}
使用需要自行配置Qt和GDAL路径。
近期写了一个高光谱图像光谱曲线匹配的算法,想封装到软件当中方便观察效果,也便于做后期算法改进和实际应用,并且以后的算法可以直接集成上来。于是打算自己写一个基本的框架实现图像浏览的一些基本功能。在网上各种找,利用GDAL进行遥感图像显示的代码很多,但不是有问题就是写的不太清楚,不够简洁,并且大多基于MFC。经过几天的奋战,成功实现了利用Qt框架进行遥感图像显示的基本功能,于是分享出来,对自己是总结,并且希望对别人有所帮助。
开发环境:VS2010, Qt4.8.4,GDAL
1.10。
说明:这里并没有Qt和GDAL,以及C++的一些基本的东西,这里只关注功能的实现做说明。如果对于Qt和GDAL有问题的,请参见其他资料。并不高深,高手绕道!
重要说明:Qt发音叫做“Q特”,不是“Q替”,不要再读错了,官网有专门说明的!不要搞半天还说我发音有问题!!! 还有要写成Qt,不是QT,QT是QuickTime,不要混淆了。
OK,先说说程序的功能:
能够打开图像,并读取图像元数据信息;
能够实现图像的正确显示,包括灰度图和RGB真彩色图的显示;
支持图像的缩放,漫游;
功能非常简单基础,但是这当中很多细节是需要认真参透的。下面开始逐一进行讲解。(PS:UI的设计就不讲了。)
1.程序简单设计
其实这个程序的设计相对简单,除了显示图像的控件需要自定义,其他的都可以直接在Qt Designer里面拖放就行了。那就来说说图像显示控件吧。先看头文件:
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
//***********************************************************************
//Assembly : ImgTest
//Author : Jacory
//Created : 07-10-2014
//
// LastModified By : Jacory
// LastModified On : 07-10-2014
//***********************************************************************
//<copyright file="MapCanvas.h" company="">
// Copyright (c) . All rights reserved.
//</copyright>
//<summary>图像显示窗口类,负责图像的读取和显示等功能。</summary>
//***********************************************************************
#ifndefMAPCANVAS_H
#defineMAPCANVAS_H
#include<QtGui/QGraphicsView>
#include<QStandardItemModel>
#include<gdal_priv.h>
///<summary>
/// ClassMapCanvas.
///</summary>
classMapCanvas : public QGraphicsView
{
Q_OBJECT
public:
MapCanvas( QWidget *parent = 0 );
~MapCanvas();
void ReadImg( const QString imgPath );
void CloseCurrentImg();
/// <summary>
/// 返回图像元数据信息模型.
/// </summary>
///<returns>图像元数据信息模型.</returns>
QStandardItemModel* ImgMetaModel()
{
return imgMetaModel;
};
/// <summary>
/// 设置图像元数据信息模型
/// </summary>
/// <paramname="model">图像元数据信息模型.</param>
void SetMetaModel( QStandardItemModel*model )
{
this->imgMetaModel = model;
};
/// <summary>
/// 返回文件列表数据模型
/// </summary>
///<returns>文件列表数据模型.</returns>
QStandardItemModel* FileListModel()
{
return fileListModel;
};
/// <summary>
/// 设置fileListModel图像文件列表数据模型
/// </summary>
/// <paramname="model">文件列表数据模型.</param>
void SetFileListModel( QStandardItemModel*model )
{
this->fileListModel = model;
};
QSize sizeHint() const;
publicslots:
/// <summary>
/// 放大图像
/// </summary>
void ZoomIn()
{
ScaleImg( 1.2 );
};
/// <summary>
/// 缩小图像
/// </summary>
void ZoomOut()
{
ScaleImg( 0.8 );
};
protected:
void wheelEvent( QWheelEvent *event );
void mousePressEvent( QMouseEvent *event );
void mouseMoveEvent( QMouseEvent *event );
void mouseReleaseEvent( QMouseEvent *event);
private:
void ShowBand( GDALRasterBand* band );
void ShowImg( QList<GDALRasterBand*>*imgBand );
void ShowImgInfor( const QString filename);
void ShowFileList( const QString filename);
unsigned char* ImgSketch( float* buffer ,GDALRasterBand* currentBand, int size, double noValue );
/// <summary>
/// 图像缩放
/// </summary>
/// <paramname="factor">缩放因子</param>
void ScaleImg( double factor )
{
m_scaleFactor *= factor;
QMatrix matrix;
matrix.scale( m_scaleFactor,m_scaleFactor );
this->setMatrix( matrix );
};
/// <summary>
/// 图像元数据模型
/// </summary>
QStandardItemModel *imgMetaModel;
/// <summary>
/// 图像数据集
/// </summary>
GDALDataset *poDataset;
/// <summary>
/// 文件列表数据模型
/// </summary>
QStandardItemModel *fileListModel;
/// <summary>
/// 缩放系数
/// </summary>
float m_scaleFactor;
/// <summary>
/// 判断是否显示RGB彩色图像
/// </summary>
bool m_showColor;
/// <summary>
/// 上一个鼠标事件触发时鼠标的位置
/// </summary>
QPoint lastEventCursorPos;
};
#endif //MAPCANVAS_H
mapView类集成自QGraphicsView类。代码里面对于成员变量和inline函数我已经写了非常详细的注释了,这里再说明一下几个函数的功能。void
ReadImg( const QStringimgPath )是读图函数,通过UI来调用,它并不直接读取图像,而是将相应的参数传递给ShowBand、ShowImg、ShowImgInfor、ShowFileList来分别实现各自相应的功能。void
ShowBand( GDALRasterBand*band )函数是显示单个波段图像,当文件中的波段数不为3个时,没法组成RGB显示,默认就显示单个波段图像,事实上它只是把单个波段加入3次到QList<GDALRasterBand*>,然后把这个波段列表传递给ShowImg进行显示。而void
ShowImg( QList<GDALRasterBand*>*imgBand )是显示图像的核心函数,它的内部通过判断m_showColor的真假来决定将要显示的图像的组织方式,并通过unsigned
char* ImgSketch(float* buffer , GDALRasterBand* currentBand, int size, double noValue )函数对图像波段进行拉伸,详细说明放在后面,这里只要知道是什么用就行了。void ShowImgInfor(
constQString filename )、 void ShowFileList( const QString filename )这两个函数,分别是为了读取并显示图像文件的元数据信息和文件结构树,分别位于主界面的两侧。void
CloseCurrentImg()这个函数很简单,就是关闭当前图像。void wheelEvent( QWheelEvent*event )、void
mousePressEvent(QMouseEvent *event )、void mouseMoveEvent(QMouseEvent *event )、void
mouseReleaseEvent(QMouseEvent *event )这4个函数都是重写的鼠标事件,具体后面再讨论。
看完了头文件,已经基本知道这个MapView是一个什么样的对象了,事实上所有与图像显示有关的操作都属于这个类的职责,自然功能函数也就应该写在这个类里。下面我们根据需求来逐步实现这些功能函数。
2.打开图像并读取图像元数据信息;
这一步先不显示图像,只要求能够正确打开图像并读取出图像的基本信息即可。主要的函数就是ShowImgInfor和ShowFileList这两个函数,来看代码:
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
///<summary>
///显示图像基本信息
///</summary>
///<param name="filename">文件名</param>
voidMapCanvas::ShowImgInfor( const QString filename )
{
if ( filename == "" || poDataset== NULL )
{
return;
}
int row = 0; // 用来记录数据模型的行号
// 图像的格式信息
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Description" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetDescription() ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Meta Infor" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetMetadataItem( GDAL_DMD_LONGNAME) ) ) ;
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Data Type" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( GDALGetDataTypeName( ( poDataset->GetRasterBand( 1)->GetRasterDataType() ) ) ) );
// 图像的大小和波段个数
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "X Size" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterXSize() ) ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Y Size" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterYSize() ) ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Band Count" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterCount() ) ) );
// 图像的投影信息
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Projection" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetProjectionRef() ) );
// 图像的坐标和分辨率信息
double adfGeoTransform[6];
QString origin = "";
QString pixelSize = "";
if( poDataset->GetGeoTransform(adfGeoTransform ) == CE_None )
{
origin = QString::number(adfGeoTransform[0] ) + ", " + QString::number( adfGeoTransform[3] );
pixelSize = QString::number(adfGeoTransform[1] ) + ", " + QString::number( adfGeoTransform[5] );
}
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Origin" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( origin ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Pixel Size" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( pixelSize ) );
}
///<summary>
///显示文件结构树
///</summary>
///<param name="filename">文件名</param>
voidMapCanvas::ShowFileList( const QString filename )
{
if ( filename == "" || poDataset== NULL )
{
return;
}
QFileInfo fileInfo( filename );
QStandardItem *rootItem = newQStandardItem( fileInfo.fileName() );
for ( int i = 0; i <poDataset->GetRasterCount(); i++ )
{
QStandardItem *childItem = newQStandardItem( tr( "Band %1" ).arg( i + 1 ) );
rootItem->setChild( i, childItem );
}
fileListModel->setItem( 0, rootItem );
}
其实很简单,就是用了Qt的Model-View模型,显示的控件分别是QTableView和QTreeView,数据模型分别是这里的imgMetaModel和fileListModel,只用将模型适当的初始化并绑定到显示控件上就行了。以上初始化比较简单,这里就不啰嗦了,都是一些基本的GDAL数据模型和Qt的东西。
3.实现图像的显示功能
这一步就是核心了,我打算通过函数调用的顺序来逐步讲解这里牵涉到的功能函数。首先是UI选择文件后,将文件路径传递给ReadImg函数进行文件读取,那我们先来看看ReadImg函数的实现。
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
///<summary>
/// 读取图像文件
///</summary>
///<param name="imgPath">图像文件</param>
voidMapCanvas::ReadImg( const QString imgPath )
{
GDALAllRegister();
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO" );
poDataset = ( GDALDataset* )GDALOpen(imgPath.toStdString().c_str(), GA_ReadOnly );
if ( poDataset == NULL )
{
QMessageBox::critical( this, tr("Error!" ), tr( "Can not open file %1" ).arg( imgPath ) );
return;
}
ShowFileList( imgPath );
ShowImgInfor( imgPath );
// 如果图像文件并非三个波段,则默认只显示第一波段灰度图像
if ( poDataset->GetRasterCount() != 3 )
{
m_showColor = false;
ShowBand( poDataset->GetRasterBand(1 ) );
}
// 如果图像正好三个波段,则默认以RGB的顺序显示彩色图
else
{
m_showColor = true;
QList<GDALRasterBand*> bandList;
bandList.append(poDataset->GetRasterBand( 1 ) );
bandList.append(poDataset->GetRasterBand( 2 ) );
bandList.append(poDataset->GetRasterBand( 3 ) );
ShowImg( &bandList );
}
GDALClose( poDataset );
}
逻辑很清晰,调用ShowFileList和ShowImgInfor初始化两个model以供相应的显示View控件来显示。再判断如果图像文件波段数不为3,则默认显示第一波段灰度图像,否则显示彩色图。
再来看ShowBand函数:
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
///<summary>
///显示单波段图像
///</summary>
///<param name="band">图像波段</param>
voidMapCanvas::ShowBand( GDALRasterBand* band )
{
if ( band == NULL )
{
return;
}
QList<GDALRasterBand*> myBand;
myBand.append( band );
myBand.append( band );
myBand.append( band );
ShowImg( &myBand );
}
可以看到,它只是把单个波段用列表的方式存储了三次,传递给ShowImg来进行显示。那直接看ShowImg吧。
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
///<summary>
/// 显示图像
///</summary>
///<param name="imgBand">图像波段</param>
voidMapCanvas::ShowImg( QList<GDALRasterBand*> *imgBand )
{
if ( imgBand->size() != 3 )
{
return;
}
int imgWidth = imgBand->at( 0)->GetXSize();
int imgHeight = imgBand->at( 0)->GetYSize();
m_scaleFactor = this->height() * 1.0 /imgHeight;
int iScaleWidth = ( int )( imgWidth *m_scaleFactor - 1 );
int iScaleHeight = ( int )( imgHeight *m_scaleFactor - 1 );
GDALDataType dataType = imgBand->at( 0)->GetRasterDataType();
// 首先分别读取RGB三个波段
float* rBand = new float[iScaleWidth *iScaleHeight];
float* gBand = new float[iScaleWidth *iScaleHeight];
float* bBand = new float[iScaleWidth *iScaleHeight];
unsigned char *rBandUC, *gBandUC, *bBandUC;
// 根据是否显示彩色图像,判断RGB三个波段的组成方式,并分别读取
if ( m_showColor == true )
{
imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
imgBand->at( 1 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, gBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
imgBand->at( 2 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, bBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
// 分别拉伸每个波段并将Float转换为unsigned char
rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );
gBandUC = ImgSketch( gBand,imgBand->at( 1 ), iScaleWidth * iScaleHeight, imgBand->at( 1)->GetNoDataValue() );
bBandUC = ImgSketch( bBand,imgBand->at( 2 ), iScaleWidth * iScaleHeight, imgBand->at( 2)->GetNoDataValue() );
}
else
{
imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );
gBandUC = rBandUC;
bBandUC = rBandUC;
}
// 将三个波段组合起来
int bytePerLine = ( iScaleWidth * 24 + 31 )/ 8;
unsigned char* allBandUC = new unsignedchar[bytePerLine * iScaleHeight * 3];
for( int h = 0; h < iScaleHeight; h++ )
{
for( int w = 0; w < iScaleWidth; w++)
{
allBandUC[h * bytePerLine + w * 3 +0] = rBandUC[h * iScaleWidth + w];
allBandUC[h * bytePerLine + w * 3 +1] = gBandUC[h * iScaleWidth + w];
allBandUC[h * bytePerLine + w * 3 +2] = bBandUC[h * iScaleWidth + w];
}
}
// 构造图像并显示
QGraphicsPixmapItem *imgItem = newQGraphicsPixmapItem( QPixmap::fromImage(QImage( allBandUC, iScaleWidth, iScaleHeight, bytePerLine,QImage::Format_RGB888 ) ) );
QGraphicsScene *myScene = newQGraphicsScene();
myScene->addItem( imgItem );
this->setScene( myScene );
}
这个函数的核心在于构造QGraphicsPixmapItem,也就是这里的imgItem这个对象,之前的代码都是为了正确构造这个对象而做的工作。这里的imgWidth和imgHeight分别是图像文件的宽和高,而iScaleWidth和iScaleHeight是缩放后的宽和高,缩放的条件正是显示窗口的大小。读取图像时,采用分别读取RGB三个波段,最后再组合起来的方式,读取波段都是采用RasterIO函数,采用float的数组来进行读取避免一些格式问题带来的精度截断,读取之后,通过ImgSketch函数进行图像拉伸,把图像值拉伸到0~255灰度级。
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
///<summary>
/// 图像线性拉伸
///</summary>
///<param name="buffer">图像缓存</param>
///<param name="currentBand">当前波段</param>
///<param name="size">The size.</param>
///<param name="noValue">图像中的异常值</param>
///<returns>经过拉伸的8位图像缓存</returns>
unsignedchar* MapCanvas::ImgSketch( float* buffer , GDALRasterBand* currentBand, intbandSize, double noValue )
{
unsigned char* resBuffer = new unsignedchar[bandSize];
double max, min;
double minmax[2];
currentBand->ComputeRasterMinMax( 1,minmax );
min = minmax[0];
max = minmax[1];
if( min <= noValue && noValue<= max )
{
min = 0;
}
for ( int i = 0; i < bandSize; i++ )
{
if ( buffer[i] > max )
{
resBuffer[i] = 255;
}
else if ( buffer[i] <= max&& buffer[i] >= min )
{
resBuffer[i] =static_cast<uchar>( 255 - 255 * ( max - buffer[i] ) / ( max - min ) );
}
else
{
resBuffer[i] = 0;
}
}
return resBuffer;
}
这里的转换核心为
resBuffer[i] = static_cast<uchar>( 255 - 255 * ( max -buffer[i] ) / ( max - min ) );
转换公式也可以变为((buffer[i]-min)/(max-min))*255,是一样的,当然还有其他的拉伸方法,可以自行使用。
拉伸之后组合波段,需要用到int bytePerLine = (iScaleWidth * 24 + 31 ) / 8;这句,因为要显示的图像为8位的,需要把图像值变换为unsigned
char型,这个工作在imgSketch里面已经做了,但是由于要将3个波段组织正确,还需要字节对齐,也就是这里的bytePerLine,这个是byte型表示的这个图像的宽度,当然是包含了RGB三个波段。然后就是将每个波段的值一一对齐放入allBandUC这个数组中,最后根据这个组合后的数组构QImage,QPixmap以及QGraphicsPixmapItem,最后把这个Item加入到scene里面用MapView显示就行了。需要注意的是,这个过程看似简单,但里面有很多细节的地方需要认真对待,仔细思考,不然很容易得不到正确的结果。
4.实现图像的漫游功能
这一部分我在网上看了很多代码,大部分人都是从底层原理开始又来写一遍,但其实对于漫游的操作其实Qt在后台已经支持的非常好了,我们的MapView继承自QGraphicsView,这个父类已经把很多操作封装到位了,我们只要合理组织就好了,没有必要再去写一遍。那就很简单了,来分别看看实现代码吧。
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
///<summary>
///鼠标滚轮事件,实现图像缩放
///</summary>
///<param name="event">滚轮事件</param>
voidMapCanvas::wheelEvent( QWheelEvent *event )
{
// 滚轮向上滑动,放大图像
if ( event->delta() > 0 )
{
ZoomIn();
}
// 滚轮向下滑动,缩小图像
if ( event->delta() < 0 )
{
ZoomOut();
}
}
///<summary>
///鼠标按键按下事件
///</summary>
///<param name="event">鼠标事件.</param>
voidMapCanvas::mousePressEvent( QMouseEvent *event )
{
// 滚轮键按下,平移图像
if ( event->button() == Qt::MidButton )
{
this->setDragMode(QGraphicsView::ScrollHandDrag );
this->setInteractive( false );
lastEventCursorPos = event->pos();
}
}
///<summary>
/// 鼠标移动事件
///</summary>
///<param name="event">鼠标事件</param>
voidMapCanvas::mouseMoveEvent( QMouseEvent *event )
{
if ( this->dragMode() ==QGraphicsView::ScrollHandDrag )
{
QPoint delta = ( event->pos() -lastEventCursorPos ) / 10;
this->horizontalScrollBar()->setValue(this->horizontalScrollBar()->value() + ( isRightToLeft() ? delta.x() :-delta.x() ) );
this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() - delta.y() );
this->viewport()->setCursor(Qt::ClosedHandCursor );
}
}
///<summary>
///鼠标按键释放事件
///</summary>
///<param name="event">鼠标事件</param>
voidMapCanvas::mouseReleaseEvent( QMouseEvent *event )
{
if ( event->button() == Qt::MidButton )
{
this->setDragMode(QGraphicsView::NoDrag );
}
}
重写鼠标事件,这里需要说明的是,漫游的操作Qt采用鼠标左键按下并移动来实现视图平移,但是我这里强制定义为了鼠标中键按下,所以还比较麻烦相对来说,如果你只想用默认的漫游操作,直接把this->setDragMode(QGraphicsView::NoDrag
);这句加入到你的构造函数中就行了,非常简单。事实上我这个平移函数有问题,我后面再解释。这里我们先关注放大缩小功能,这部分代码我是通过inline的方式添加到头文件中的。
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
///<summary>
/// 放大图像
/// </summary>
void ZoomIn()
{
ScaleImg( 1.2 );
};
/// <summary>
/// 缩小图像
/// </summary>
void ZoomOut()
{
ScaleImg( 0.8 );
};
///<summary>
/// 图像缩放
/// </summary>
/// <paramname="factor">缩放因子</param>
void ScaleImg( double factor )
{
m_scaleFactor *= factor;
QMatrix matrix;
matrix.scale( m_scaleFactor,m_scaleFactor );
this->setMatrix( matrix );
};
图像缩放也非常简单,让你的QGraphicsView去处理细节,不要自己又去从底层计算然后写一大堆代码,还不一定就比Qt默认的方式效率高。
做到这里基本就搞定了,我刚刚说的平移函数那里,实际上我在mouseMoveEvent里采用计算当前坐标点和事件发生的坐标点差值的方式,然后去设置水平和竖直滑动条的值来实现平移
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
QPointdelta = ( event->pos() - lastEventCursorPos ) / 10;
this->horizontalScrollBar()->setValue(this->horizontalScrollBar()->value() + ( isRightToLeft() ? delta.x() :-delta.x() ) );
this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() - delta.y() );
我这样写后,我的平移实际上并不是非常流畅,而是有点像浏览网页时按住鼠标中键往下浏览的效果,但实际上很多GIS和遥感软件的平移方式都不是这样的。这里应该是我对Qt还有些不熟悉,也希望大家知道的话告诉我一下,我认为Qt应该是支持的。
好了,差不多了,其他的功能我也还在完善,先写到这里吧,希望对大家有所帮助。最后还是把MapView的实现文件整体粘上来。VS2010的工程文件我放到CSDN的资源里面大家可以下载。
[cpp] view
plaincopy
![](https://code.csdn.net/assets/CODE_ico.png)
#include"MapCanvas.h"
#include<QtGui/QMessageBox>
#include<QtCore/QFileInfo>
#include<QtGui/QImage>
#include<QtGui/QPixmap>
#include<QtGui/QGraphicsPixmapItem>
#include<QtGui/QMatrix>
#include<QtGui/QWheelEvent>
#include<QtGui/QScrollBar>
MapCanvas::MapCanvas(QWidget *parent /*= 0 */ )
: QGraphicsView( parent )
{
poDataset = NULL;
m_scaleFactor = 1.0;
m_showColor = true;
imgMetaModel = new QStandardItemModel;
imgMetaModel->setColumnCount( 2 );
fileListModel = new QStandardItemModel;
QSizePolicy policy( QSizePolicy::Preferred,QSizePolicy::Preferred );
this->setSizePolicy( policy );
}
///<summary>
///Finalizes an instance of the <see cref="MapCanvas" /> class.
///</summary>
MapCanvas::~MapCanvas()
{
}
///<summary>
/// 读取图像文件
///</summary>
///<param name="imgPath">图像文件</param>
voidMapCanvas::ReadImg( const QString imgPath )
{
GDALAllRegister();
CPLSetConfigOption("GDAL_FILENAME_IS_UTF8", "NO" );
poDataset = ( GDALDataset* )GDALOpen(imgPath.toStdString().c_str(), GA_ReadOnly );
if ( poDataset == NULL )
{
QMessageBox::critical( this, tr("Error!" ), tr( "Can not open file %1" ).arg( imgPath ) );
return;
}
ShowFileList( imgPath );
ShowImgInfor( imgPath );
// 如果图像文件并非三个波段,则默认只显示第一波段灰度图像
if ( poDataset->GetRasterCount() != 3 )
{
m_showColor = false;
ShowBand( poDataset->GetRasterBand(1 ) );
}
// 如果图像正好三个波段,则默认以RGB的顺序显示彩色图
else
{
m_showColor = true;
QList<GDALRasterBand*> bandList;
bandList.append(poDataset->GetRasterBand( 1 ) );
bandList.append(poDataset->GetRasterBand( 2 ) );
bandList.append(poDataset->GetRasterBand( 3 ) );
ShowImg( &bandList );
}
GDALClose( poDataset );
}
///<summary>
///关闭当前图像文件
///</summary>
voidMapCanvas::CloseCurrentImg()
{
poDataset = NULL;
imgMetaModel->clear();
fileListModel->clear();
}
///<summary>
///显示单波段图像
///</summary>
///<param name="band">图像波段</param>
voidMapCanvas::ShowBand( GDALRasterBand* band )
{
if ( band == NULL )
{
return;
}
QList<GDALRasterBand*> myBand;
myBand.append( band );
myBand.append( band );
myBand.append( band );
ShowImg( &myBand );
}
///<summary>
/// 显示图像
///</summary>
///<param name="imgBand">图像波段</param>
voidMapCanvas::ShowImg( QList<GDALRasterBand*> *imgBand )
{
if ( imgBand->size() != 3 )
{
return;
}
int imgWidth = imgBand->at( 0)->GetXSize();
int imgHeight = imgBand->at( 0)->GetYSize();
m_scaleFactor = this->height() * 1.0 /imgHeight;
int iScaleWidth = ( int )( imgWidth *m_scaleFactor - 1 );
int iScaleHeight = ( int )( imgHeight *m_scaleFactor - 1 );
GDALDataType dataType = imgBand->at( 0)->GetRasterDataType();
// 首先分别读取RGB三个波段
float* rBand = new float[iScaleWidth *iScaleHeight];
float* gBand = new float[iScaleWidth *iScaleHeight];
float* bBand = new float[iScaleWidth *iScaleHeight];
unsigned char *rBandUC, *gBandUC, *bBandUC;
// 根据是否显示彩色图像,判断RGB三个波段的组成方式,并分别读取
if ( m_showColor == true )
{
imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
imgBand->at( 1 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, gBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
imgBand->at( 2 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, bBand, iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
// 分别拉伸每个波段并将Float转换为unsigned char
rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );
gBandUC = ImgSketch( gBand,imgBand->at( 1 ), iScaleWidth * iScaleHeight, imgBand->at( 1)->GetNoDataValue() );
bBandUC = ImgSketch( bBand,imgBand->at( 2 ), iScaleWidth * iScaleHeight, imgBand->at( 2)->GetNoDataValue() );
}
else
{
imgBand->at( 0 )->RasterIO(GF_Read, 0, 0, imgWidth, imgHeight, rBand , iScaleWidth, iScaleHeight,GDT_Float32, 0, 0 );
rBandUC = ImgSketch( rBand,imgBand->at( 0 ), iScaleWidth * iScaleHeight, imgBand->at( 0)->GetNoDataValue() );
gBandUC = rBandUC;
bBandUC = rBandUC;
}
// 将三个波段组合起来
int bytePerLine = ( iScaleWidth * 24 + 31 )/ 8;
unsigned char* allBandUC = new unsignedchar[bytePerLine * iScaleHeight * 3];
for( int h = 0; h < iScaleHeight; h++ )
{
for( int w = 0; w < iScaleWidth; w++)
{
allBandUC[h * bytePerLine + w * 3 +0] = rBandUC[h * iScaleWidth + w];
allBandUC[h * bytePerLine + w * 3 +1] = gBandUC[h * iScaleWidth + w];
allBandUC[h * bytePerLine + w * 3 +2] = bBandUC[h * iScaleWidth + w];
}
}
// 构造图像并显示
QGraphicsPixmapItem *imgItem = newQGraphicsPixmapItem( QPixmap::fromImage(QImage( allBandUC, iScaleWidth, iScaleHeight, bytePerLine,QImage::Format_RGB888 ) ) );
QGraphicsScene *myScene = newQGraphicsScene();
myScene->addItem( imgItem );
this->setScene( myScene );
}
///<summary>
///显示图像基本信息
///</summary>
///<param name="filename">文件名</param>
voidMapCanvas::ShowImgInfor( const QString filename )
{
if ( filename == "" || poDataset== NULL )
{
return;
}
int row = 0; // 用来记录数据模型的行号
// 图像的格式信息
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Description" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetDescription() ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Meta Infor" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetDriver()->GetMetadataItem( GDAL_DMD_LONGNAME) ) ) ;
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Data Type" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( GDALGetDataTypeName( ( poDataset->GetRasterBand( 1)->GetRasterDataType() ) ) ) );
// 图像的大小和波段个数
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "X Size" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterXSize() ) ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Y Size" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterYSize() ) ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Band Count" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( QString::number( poDataset->GetRasterCount() ) ) );
// 图像的投影信息
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Projection" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( poDataset->GetProjectionRef() ) );
// 图像的坐标和分辨率信息
double adfGeoTransform[6];
QString origin = "";
QString pixelSize = "";
if( poDataset->GetGeoTransform(adfGeoTransform ) == CE_None )
{
origin = QString::number(adfGeoTransform[0] ) + ", " + QString::number( adfGeoTransform[3] );
pixelSize = QString::number(adfGeoTransform[1] ) + ", " + QString::number( adfGeoTransform[5] );
}
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Origin" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( origin ) );
imgMetaModel->setItem( row, 0, newQStandardItem( tr( "Pixel Size" ) ) );
imgMetaModel->setItem( row++, 1, newQStandardItem( pixelSize ) );
}
///<summary>
///显示文件结构树
///</summary>
///<param name="filename">文件名</param>
voidMapCanvas::ShowFileList( const QString filename )
{
if ( filename == "" || poDataset== NULL )
{
return;
}
QFileInfo fileInfo( filename );
QStandardItem *rootItem = newQStandardItem( fileInfo.fileName() );
for ( int i = 0; i <poDataset->GetRasterCount(); i++ )
{
QStandardItem *childItem = newQStandardItem( tr( "Band %1" ).arg( i + 1 ) );
rootItem->setChild( i, childItem );
}
fileListModel->setItem( 0, rootItem );
}
///<summary>
/// 图像线性拉伸
///</summary>
///<param name="buffer">图像缓存</param>
///<param name="currentBand">当前波段</param>
///<param name="size">The size.</param>
///<param name="noValue">图像中的异常值</param>
///<returns>经过拉伸的8位图像缓存</returns>
unsignedchar* MapCanvas::ImgSketch( float* buffer , GDALRasterBand* currentBand, intbandSize, double noValue )
{
unsigned char* resBuffer = new unsignedchar[bandSize];
double max, min;
double minmax[2];
currentBand->ComputeRasterMinMax( 1,minmax );
min = minmax[0];
max = minmax[1];
if( min <= noValue && noValue<= max )
{
min = 0;
}
for ( int i = 0; i < bandSize; i++ )
{
if ( buffer[i] > max )
{
resBuffer[i] = 255;
}
else if ( buffer[i] <= max&& buffer[i] >= min )
{
resBuffer[i] =static_cast<uchar>( 255 - 255 * ( max - buffer[i] ) / ( max - min ) );
}
else
{
resBuffer[i] = 0;
}
}
return resBuffer;
}
///<summary>
/// 控件大小
///</summary>
///<returns>QSize.</returns>
QSizeMapCanvas::sizeHint() const
{
return QSize( 1024, 768 );
}
///<summary>
///鼠标滚轮事件,实现图像缩放
///</summary>
///<param name="event">滚轮事件</param>
voidMapCanvas::wheelEvent( QWheelEvent *event )
{
// 滚轮向上滑动,放大图像
if ( event->delta() > 0 )
{
ZoomIn();
}
// 滚轮向下滑动,缩小图像
if ( event->delta() < 0 )
{
ZoomOut();
}
}
///<summary>
///鼠标按键按下事件
///</summary>
///<param name="event">鼠标事件.</param>
voidMapCanvas::mousePressEvent( QMouseEvent *event )
{
// 滚轮键按下,平移图像
if ( event->button() == Qt::MidButton )
{
this->setDragMode(QGraphicsView::ScrollHandDrag );
this->setInteractive( false );
lastEventCursorPos = event->pos();
}
}
///<summary>
/// 鼠标移动事件
///</summary>
///<param name="event">鼠标事件</param>
voidMapCanvas::mouseMoveEvent( QMouseEvent *event )
{
if ( this->dragMode() ==QGraphicsView::ScrollHandDrag )
{
QPoint delta = ( event->pos() -lastEventCursorPos ) / 10;
this->horizontalScrollBar()->setValue(this->horizontalScrollBar()->value() + ( isRightToLeft() ? delta.x() :-delta.x() ) );
this->verticalScrollBar()->setValue(this->verticalScrollBar()->value() - delta.y() );
this->viewport()->setCursor(Qt::ClosedHandCursor );
}
}
///<summary>
///鼠标按键释放事件
///</summary>
///<param name="event">鼠标事件</param>
voidMapCanvas::mouseReleaseEvent( QMouseEvent *event )
{
if ( event->button() == Qt::MidButton )
{
this->setDragMode(QGraphicsView::NoDrag );
}
}
相关文章推荐
- Qt 知识的总结
- Qt学习(16)
- Qt5.5.0 vs2013 64位编译
- 最近在 OS-10.9下配置opencv, cgal, latex, qt, pillow
- Qt父-子对象机制
- QT控件提升之QPushButton提升为QMenu
- MQTT协议及拓展
- <二> Qt更新sqlite3插件
- Qt学习之parent参数
- Qt下OpenCv中cvGetWindowHandle的使用
- QT窗体滑动(QPropertyAnimation)
- 自学QT之创建json
- Qt 程序运行过程中提示toolChains.xml磁盘已满
- ubuntu下QT出现cannot find -lGL错误的解决办法
- 关于Qt的图形旋转问题
- Retrieve OpenGL Context from Qt 5.5 on OSX
- QT 编写Rtf(富文本格式) 文件实例
- VS201x+Qt5开发,中文乱码解决
- PyQt5系列教程(六)如何让界面和逻辑分离
- QT 编写xml文件实例