QGIS二次开发技术文档
目录
- 工程概述
- 开发环境搭建与项目配置
- QGIS初始化与核心架构
- 地图画布(QgsMapCanvas)(#4-地图画布qgsmapcanvas)
- 打开矢量图层
- 打开栅格图层
- 坐标测量与显示
- 测量面积与距离
- 跳转到指定坐标
- [波段组合(Band Combination)](#波段组合(Band Combination))
- 图层树与项目管理
- 状态栏定制
- Ribbon风格界面集成
- 常见问题与注意事项
- 总结与最佳实践
1. 工程概述
1.1 工程背景
本工程QtGisDemo是一个基于 QGIS(Quantum GIS)C++ API 进行二次开发的桌面GIS应用程序。工程包含两个主要部分:
1.一个轻量级的QGIS二次开发演示项目,演示了包括打开栅格/矢量数据、坐标显示、比例尺控制、放大镜、图层树等核心功能,采用了Office风格的Ribbon界面(QtitanRibbon库)。
2.一个更复杂的卫星遥感数据处理平台,包含辐射定标(Radiometric Calibration)、几何校正(Geometric Correction)、MTF(Modulation Transfer Function)计算、影像配准(Registration)、信噪比计算(SNR)等专业的遥感数据处理功能。
1.2 技术栈
| 技术 | 用途 |
|---|---|
| C++ 17 | 主开发语言 |
| Qt 5.12+ | GUI框架 |
| QGIS 3.x C++ API(qgis_core.dll, qgis_gui.dll) | GIS核心功能库 |
| GDAL 3.0.4 | 栅格/矢量数据读写 |
| QtitanRibbon 5.x | Office风格Ribbon界面 |
| Qwt | 数据可视化组件 |
| Visual Studio 2017/2019 | 开发与编译环境 |
| qmake | 构建系统 |
1.3 核心依赖库
运行时所需的DLL文件清单(位于x64/Release目录下):
-
QGIS核心库 :
qgis_core.dll、qgis_gui.dll、qgis_native.dll -
GDAL/OGR库 :
gdal304.dll -
PROJ库 :
proj_7_2.dll、proj_8_2.dll -
GEOS库 :
geos.dll、geos_c.dll -
其他 :
spatialite.dll、sqlite3.dll、libxml2.dll、zlib.dll等
2. 开发环境搭建与项目配置
2.1 QGIS SDK编译与引用
QGIS二次开发的第一步是获取QGIS SDK。开发者需要自行编译QGIS源码或使用OSGeo4W安装的SDK。本工程采用了独立编译的QGIS核心库 ,直接链接到 qgis_core.lib 和 qgis_gui.lib。
关键配置要点:
- 包含目录(Include Directories) :指向QGIS SDK头文件目录,如
qgis_core/include、qgis_gui/include - 库目录(Library Directories) :指向QGIS编译后的
.lib文件目录 - 预处理器定义 :需定义
GUI_EXPORT=、CORE_EXPORT=用于导出类的引用 - QGIS插件目录:运行时需设置QGIS插件搜索路径
2.3 运行时环境设置
运行编译后的程序需要保证:
- 所有依赖DLL位于可执行文件目录或系统PATH中
- GDAL的数据文件路径(
GDAL_DATA)需要正确设置 - PROJ的proj.db路径需要正确
- QGIS的插件、样式、SVG数据文件路径
3. QGIS初始化与核心架构
3.1 QGIS Application初始化
在 main.cpp 中,QGIS二次开发的初始化流程是固定的:
cpp
#include "qgsapplication.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString appDir = QCoreApplication::applicationDirPath();
QCoreApplication::setOrganizationName("QtQgis");
QCoreApplication::setApplicationName("QtQgis Application");
// ★★ 核心初始化步骤 ★★
QgsApplication::init(appDir); // 初始化QGIS应用路径
QgsApplication::initQgis(); // 初始化QGIS核心服务(数据提供者、坐标参考系统等)
QgsApplication::setMaxThreads(8); // 设置最大线程数(并行渲染)
QtGis w;
w.showMaximized();
return a.exec();
}
注意事项:
QgsApplication::initQgis()必须在创建任何QgsMapCanvas之前调用- 它初始化了GDAL、OGR、PROJ等底层库,并注册了所有数据提供者
setMaxThreads控制并行渲染的线程数量,需根据CPU核心数设置- QGIS的初始化必须在
QApplication对象创建之后进行
3.2 项目对象(QgsProject)
QgsProject 是QGIS的项目级单例,管理所有图层、坐标参考系统(CRS)、项目设置等:
cpp
// 设置项目CRS
QgsProject::instance()->setCrs(QgsCoordinateReferenceSystem("EPSG:4326"));
// 标记项目为已修改
QgsProject::instance()->setDirty(true);
// 获取图层树根节点
QgsProject::instance()->layerTreeRoot();
3.3 数据提供者机制
QGIS采用插件式数据提供者 架构,通过 QgsProviderRegistry 单例管理所有数据驱动:
- GDAL :栅格数据提供者(
"gdal"),支持GeoTIFF、IMG、NetCDF等 - OGR :矢量数据提供者(
"ogr"),支持Shapefile、GeoJSON、KML等 - 内存提供者 :临时数据层(
"memory")
4. 地图画布(QgsMapCanvas)
QgsMapCanvas 是QGIS应用的核心可视组件,负责地图的渲染、交互、缩放、平移等操作。
4.1 创建与配置
cpp
// 创建地图画布
mMapCanvas = new QgsMapCanvas();
// 基本配置
mMapCanvas->setPreviewJobsEnabled(true); // 启用预览任务(渲染优化)
mMapCanvas->setCanvasColor(QColor(255, 255, 255)); // 画布背景色(白色)
mMapCanvas->setSelectionColor(QColor(0, 255, 255)); // 选择要素高亮色(青色)
mMapCanvas->setProject(QgsProject::instance()); // 关联项目对象
mMapCanvas->setParallelRenderingEnabled(true); // 启用并行渲染
mMapCanvas->setCachingEnabled(true); // 启用缓存(提升平移缩放性能)
mMapCanvas->setMapUpdateInterval(250); // 更新间隔(毫秒)
4.2 与TabWidget集成
工程将画布放置于 QTabWidget 中,支持多视图管理:
cpp
mCenterTabWidget = new QTabWidget(this);
mCenterTabWidget->addTab(mMapCanvas, "Map View");
// 移除Tab右侧的图标按钮
mCenterTabWidget->tabBar()->setTabButton(
mCenterTabWidget->indexOf(mMapCanvas),
QTabBar::RightSide, nullptr);
setCentralWidget(mCenterTabWidget);
4.3 画布冻结与刷新机制
为了提高批量操作的性能,工程实现了画布冻结(freeze)机制:
cpp
// 冻结所有画布,防止频繁重绘
void QtGis::freezeCanvases(bool frozen) {
const auto canvases = mapCanvases();
for (QgsMapCanvas* canvas : canvases) {
canvas->freeze(frozen);
}
}
// 刷新所有画布
void QtGis::refreshMapCanvas(bool redrawAllLayers) {
const auto canvases = mapCanvases();
for (QgsMapCanvas* canvas : canvases) {
canvas->stopRendering();
if (redrawAllLayers)
canvas->refreshAllLayers();
else
canvas->refresh();
}
}
RAII冻结机制 (QgsCanvasRefreshBlocker):
cpp
class QgsCanvasRefreshBlocker {
// 构造时冻结,析构时解冻
// 支持嵌套使用,只有最外层的析构才触发解冻刷新
};
这种设计模式在大批量数据加载时非常有用------避免每添加一个图层就触发一次重绘。
5. 打开矢量图层
5.1 功能入口与UI
栅格和矢量图层的打开分别通过Ribbon工具栏的"Add Raster Layer"和"Add Vector Layer"按钮触发。用户点击后,弹出文件选择对话框。
5.2 打开矢量图层的核心代码
矢量图层的打开通过OGR数据提供者实现,代码位于 QLayerCommand.cpp:
cpp
bool QtGis::addVectorLayer(const QStringList& layerFiles) {
// 冻结画布,防止逐个添加图层时频繁重绘
QgsCanvasRefreshBlocker refreshBlocker;
QList<QgsMapLayer*> layersToAdd;
for (QString src : layerFiles) {
src = src.trimmed();
QFileInfo fi(src);
QString baseName = fi.completeBaseName();
// 创建矢量图层
QgsVectorLayer::LayerOptions options{
QgsProject::instance()->transformContext()
};
options.loadDefaultStyle = false; // 不加载默认样式
QgsVectorLayer* layer = new QgsVectorLayer(
src, // 文件路径
baseName, // 图层名称(使用文件名)
QStringLiteral("ogr"), // 数据提供者:OGR
options // 图层选项
);
if (layer != nullptr && layer->isValid()) {
layersToAdd << layer;
} else {
delete layer;
// 弹出错误提示
QMessageBox::warning(this, tr("Invalid Data Source"),
tr("%1 is not a valid or recognized data source.").arg(src));
}
}
if (layersToAdd.size() > 0) {
// 批量添加到项目
QgsProject::instance()->addMapLayers(layersToAdd);
markDirty(); // 标记项目已修改
}
return true;
}
5.3 文件过滤设置
cpp
void QtGis::slot_addVectorLayer() {
QStringList layerFiles;
QString e;
QString title = tr("Open Vector Layer");
// 支持多种矢量格式
QString vectorFileFilter = "OGR Layer(*.shp *.geojson *.gmt);; KML(*.kml)";
QgsGuiUtils::openFilesRememberingFilter(
QStringLiteral("lastRasterFileFilter"),
vectorFileFilter, layerFiles, e, title);
if (layerFiles.isEmpty())
return;
addVectorLayer(layerFiles);
}
5.4 技术要点
-
QgsVectorLayer构造参数:- 第一个参数(source):可以是文件路径、数据库连接字符串、WMS/WFS URL等
- 第二个参数(name):图层的显示名称
- 第三个参数(providerType):数据提供者类型,矢量通常为
"ogr" - 第四参数(options):
LayerOptions包含变换上下文、样式加载选项等
-
LayerOptions说明:transformContext:坐标变换上下文,来自项目对象loadDefaultStyle:是否加载QML样式文件,设为false可避免加载不存在的样式文件导致的警告
-
支持的矢量数据源:
- Shapefile (.shp)
- GeoJSON (.geojson)
- GMT格式 (.gmt)
- KML (.kml)
- 通过GDAL/OGR还可支持GeoPackage、DWG/DXF、PostGIS、SpatiaLite等
-
注意事项:
- 必须通过
isValid()验证图层是否成功创建 - 返回
nullptr或无效图层时需delete释放内存 - 使用
QgsCanvasRefreshBlocker防止多次重绘,提升性能
- 必须通过

6. 打开栅格图层
6.1 实现原理
栅格图层的打开通过GDAL数据提供者实现,代码同样位于 QLayerCommand.cpp:
cpp
bool QtGis::addRaterLayer(const QStringList& layerFiles) {
if (layerFiles.empty()) return false;
QgsCanvasRefreshBlocker refreshBlocker;
bool returnValue = true;
QList<QgsMapLayer*> myList;
for (auto myIterator = layerFiles.begin();
myIterator != layerFiles.end(); ++myIterator) {
QString errMsg;
bool ok = false;
// 检查是否为有效的栅格文件名
if (QgsRasterLayer::isValidRasterFileName(*myIterator, errMsg)) {
QFileInfo myFileInfo(*myIterator);
QString layerName = myFileInfo.completeBaseName();
// 从URI中解码更精确的图层名
const QVariantMap uriDetails = QgsProviderRegistry::instance()
->decodeUri(QStringLiteral("gdal"), *myIterator);
if (!uriDetails[QStringLiteral("layerName")].toString().isEmpty()) {
layerName = uriDetails[QStringLiteral("layerName")].toString();
}
// 创建栅格图层
QgsRasterLayer* layer = new QgsRasterLayer(
*myIterator, layerName, QStringLiteral("gdal"));
if (layer && layer->isValid()) {
myList << layer;
ok = true;
}
} else {
ok = false;
QString msg = tr("%1 is not a supported raster data source").arg(*myIterator);
if (!errMsg.isEmpty())
msg += '\n' + errMsg;
QMessageBox::warning(this, "Unsupported Data Source", msg);
}
if (!ok) returnValue = false;
}
QgsProject::instance()->addMapLayers(myList);
markDirty();
return returnValue;
}
6.2 文件过滤设置
cpp
void QtGis::slot_addRaterLayer() {
QStringList layerFiles;
QString e;
QString title = tr("Open Raster Layer");
// 支持多种栅格格式
QString rasterFileFilter =
"All supported(*.tif *.img *.hgt *.grd);;"
"GeoTIFF(*.tif *.tiff *.TIF *.TIFF);;"
"Erdas Imagine Images(*.img *.IMG);;"
"NetCDF(*.grd *.GRD);;"
"STRMHGT File(*.hgt *.HGT)";
QgsGuiUtils::openFilesRememberingFilter(
QStringLiteral("lastRasterFileFilter"),
rasterFileFilter, layerFiles, e, title);
if (layerFiles.isEmpty())
return;
addRaterLayer(layerFiles);
}
6.3 技术要点
-
QgsRasterLayer::isValidRasterFileName():这是QGIS提供的静态验证方法,在执行文件打开前先检查文件是否为QGIS可识别的栅格格式,避免无效操作。 -
QgsProviderRegistry::decodeUri():通过GDAL提供者解码文件URI,可以从某些复合格式(如NetCDF的多波段文件)中提取更精确的图层名称。 -
栅格图层名称的处理:
- 默认使用文件的
completeBaseName()(不带扩展名) - 对于HDF4、NetCDF等复合格式,
decodeUri可以提取内部子数据集名称
- 默认使用文件的
-
支持的栅格数据源:
- GeoTIFF (.tif, .tiff)
- Erdas Imagine (.img)
- NetCDF (.grd)
- SRTM HGT (.hgt)
- 通过GDAL还可支持JPEG2000、ENVI、HDF4/5、GRIB等几乎所有的遥感栅格格式
-
与矢量图层的区别:
- 栅格使用
QgsRasterLayer类,矢量使用QgsVectorLayer类 - 栅格的数据提供者为
"gdal",矢量为"ogr" - 栅格需要先通过
isValidRasterFileName检查,矢量则直接构建并检查有效性
- 栅格使用

7. 坐标测量与显示
7.1 功能概述
坐标显示是GIS应用中最重要的交互功能之一。工程通过 QgsStatusBarCoordinatesWidget 实现了以下功能:
- 鼠标追踪坐标显示:实时显示鼠标在地图画布上的地理坐标
- 视图范围显示:显示当前地图视图的经纬度范围
- 坐标有效性验证:用户可手动输入坐标并验证格式
- 跳转到坐标:输入坐标后按回车,地图中心跳转到指定位置
7.2 核心实现
QgsStatusBarCoordinatesWidget 通过信号-槽机制与画布交互:
cpp
void QgsStatusBarCoordinatesWidget::setMapCanvas(QgsMapCanvas *mapCanvas) {
// 断开旧的连接
if (mMapCanvas) {
disconnect(mMapCanvas, &QgsMapCanvas::xyCoordinates, this,
&QgsStatusBarCoordinatesWidget::showMouseCoordinates);
disconnect(mMapCanvas, &QgsMapCanvas::extentsChanged, this,
&QgsStatusBarCoordinatesWidget::showExtent);
}
mMapCanvas = mapCanvas;
// 连接信号:鼠标移动时更新坐标
connect(mMapCanvas, &QgsMapCanvas::xyCoordinates, this,
&QgsStatusBarCoordinatesWidget::showMouseCoordinates);
// 连接信号:视图范围变化时更新
connect(mMapCanvas, &QgsMapCanvas::extentsChanged, this,
&QgsStatusBarCoordinatesWidget::showExtent);
}
7.3 鼠标坐标实时显示
cpp
void QgsStatusBarCoordinatesWidget::showMouseCoordinates(const QgsPointXY &p) {
if (!mMapCanvas || mToggleExtentsViewButton->isChecked())
return;
// 使用项目设置格式化坐标(可配置经纬度显示格式)
mLineEdit->setText(QgsCoordinateUtils::formatCoordinateForProject(
QgsProject::instance(), p,
mMapCanvas->mapSettings().destinationCrs(),
mMousePrecisionDecimalPlaces));
ensureCoordinatesVisible(); // 自动调整文本框宽度
}
关键API说明:
QgsMapCanvas::xyCoordinates信号 :鼠标在画布上移动时连续发射,参数为鼠标位置的地理坐标QgsPointXYQgsCoordinateUtils::formatCoordinateForProject():根据项目设置格式化坐标字符串,支持多种显示格式(DD、DMS、DMM等)mMapCanvas->mapSettings().destinationCrs():获取画布当前的坐标系
7.4 视图范围显示
cpp
void QgsStatusBarCoordinatesWidget::extentsViewToggled(bool flag) {
if (flag) {
// 切换到视图范围显示模式
mToggleExtentsViewButton->setIcon(
QgsApplication::getThemeIcon(QStringLiteral("extents.svg")));
mLineEdit->setReadOnly(true);
showExtent();
} else {
// 切换到鼠标坐标模式
mToggleExtentsViewButton->setIcon(
QgsApplication::getThemeIcon(QStringLiteral("tracking.svg")));
mLineEdit->setReadOnly(false);
mLabel->setText(tr("Coordinate"));
}
}
void QgsStatusBarCoordinatesWidget::showExtent() {
if (!mToggleExtentsViewButton->isChecked()) return;
QgsRectangle myExtents = mMapCanvas->extent();
mLabel->setText(tr("Extents"));
mLineEdit->setText(myExtents.toString(true)); // 格式化显示
}
7.5 坐标输入验证
坐标输入框使用正则表达式进行验证,允许带符号的小数坐标:
cpp
QRegExp coordValidator("[+-]?\\d+\\.?\\d*\\s*,\\s*[+-]?\\d+\\.?\\d*");
mCoordsEditValidator = new QRegExpValidator(coordValidator, this);
mLineEdit->setToolTip(tr("Current map coordinate (longitude,latitude or east,north)"));
支持的输入格式:
119.5, 32.8(经度在前,纬度在后,逗号分隔)119.5 32.8(空格分隔)
7.6 注意事项
- 坐标精度控制 :通过
setMouseCoordinatesPrecision()可设置小数位数,默认为自动 - 文本自适应宽度 :
ensureCoordinatesVisible()方法根据文本长度动态调整输入框宽度 - 坐标系感知:坐标值始终以画布当前的目标坐标系显示,默认为 WGS84(EPSG:4326)
8. 测量面积与距离
8.1 使用QGIS原生的测量工具
QGIS提供了 QgsMeasureTool 及其子类,可直接用于距离和面积的测量:
cpp
// 距离测量工具
#include "qgsmeasuretool.h"
// 创建距离测量工具
QgsMeasureTool* distanceTool = new QgsMeasureTool(
mMapCanvas,
QgsMeasureTool::MeasureLine // 距离测量模式
);
mMapCanvas->setMapTool(distanceTool);
// 面积测量工具
QgsMeasureTool* areaTool = new QgsMeasureTool(
mMapCanvas,
QgsMeasureTool::MeasureArea // 面积测量模式
);
mMapCanvas->setMapTool(areaTool);
8.2 自定义测量实现方案
如果需要自定义测量交互,可以使用 QgsMapTool 基类派生新的测量工具:
基本架构:
cpp
class CustomMeasureTool : public QgsMapTool {
Q_OBJECT
public:
CustomMeasureTool(QgsMapCanvas* canvas)
: QgsMapTool(canvas) {}
// 鼠标点击事件 - 添加测量点
virtual void canvasPressEvent(QgsMapMouseEvent* e) override;
// 鼠标移动事件 - 实时更新测量线
virtual void canvasMoveEvent(QgsMapMouseEvent* e) override;
// 右键取消/完成测量
private:
QList<QgsPointXY> mPoints; // 测量点列表
double calculateDistance(); // 计算距离
double calculateArea(); // 计算面积(多边形)
};
距离计算 使用 QgsDistanceArea 类:
cpp
QgsDistanceArea da;
da.setSourceCrs(mMapCanvas->mapSettings().destinationCrs(),
QgsProject::instance()->transformContext());
da.setEllipsoid(QgsProject::instance()->ellipsoid());
// 计算两点间的椭球距离
double distance = da.measureLine(p1, p2); // 返回米
// 计算多边形面积
double area = da.measurePolygon(points); // 返回平方米
8.4 关键技术参数
- QgsMeasureTool 配置:支持度量单位配置(米、千米、英尺、英里等)
- 椭球体设置:面积和距离的精确计算依赖于椭球体参数的设置
- 结果显示:可以在状态栏或浮动标签中实时显示测量结果
- 标注 :可以使用
QgsAnnotation在画布上标注测量数值
8.5 注意事项
- 测量工具是模态的,激活后接管鼠标事件,需要显式切换回默认的平移/缩放工具
- 高精度测量(特别是面积)需要正确设置椭球体和坐标系
- 对于大范围测量,应考虑地图投影变形的影响
- 距离测量支持测地线(大圆路径)和欧氏距离两种模式
9. 跳转到指定坐标
9.1 功能描述
跳转到指定坐标功能允许用户在坐标输入框中输入经纬度坐标,按回车后地图画布自动将视图中心移动到该坐标位置。
9.2 核心实现
cpp
void QgsStatusBarCoordinatesWidget::validateCoordinates() {
if (!mMapCanvas) return;
// (省略彩蛋处理代码...)
bool xOk = false, yOk = false;
double x = 0., y = 0.;
QString coordText = mLineEdit->text();
coordText.replace(QRegExp(" {2,}"), QStringLiteral(" "));
// 尝试逗号分隔解析
QStringList parts = coordText.split(',');
if (parts.size() == 2) {
x = parts.at(0).toDouble(&xOk);
y = parts.at(1).toDouble(&yOk);
}
// 尝试空格分隔解析
if (!xOk || !yOk) {
parts = coordText.split(' ');
if (parts.size() == 2) {
x = parts.at(0).toDouble(&xOk);
y = parts.at(1).toDouble(&yOk);
}
}
if (!xOk || !yOk) return;
// ★★ 核心功能:设置画布中心点并刷新 ★★
mMapCanvas->setCenter(QgsPointXY(x, y));
mMapCanvas->refresh();
}
9.3 技术要点
-
QgsMapCanvas::setCenter():将画布视图中心移动到指定的地理坐标点。坐标值使用画布当前的目标坐标系。 -
坐标输入解析:
- 支持
"经度,纬度"(逗号分隔,如:119.5,32.8) - 支持
"经度 纬度"(空格分隔,如:119.5 32.8) - 自动去除多余空格
- 支持带符号和带小数点的数值
- 支持
-
坐标系一致性:输入的坐标应与画布的目标坐标系一致。如果画布使用WGS84(EPSG:4326),则需要输入十进制度数。如果使用投影坐标系,则需要输入投影坐标值。
9.4 扩展应用:跳转到指定XY
如果需要支持从其他UI控件(如独立的输入对话框)跳转到坐标,可以封装为独立方法:
cpp
void QtGis::goToCoordinate(double x, double y) {
if (!mMapCanvas) return;
mMapCanvas->setCenter(QgsPointXY(x, y));
mMapCanvas->refresh();
// 可选:设置缩放级别
// mMapCanvas->zoomScale(10000); // 1:10000
}
9.5 注意事项
-
坐标顺序 :在GIS领域,坐标通常以
(X, Y)表示,在国际标准中对应(经度, 纬度)。但部分用户习惯(纬度, 经度)格式,需要在UI中明确提示。 -
有效性检查:对于WGS84坐标,经度范围为 -180° 到 180°,纬度范围为 -90° 到 90°,需要添加范围验证。
-
边界处理:当输入的坐标超出当前图层范围时,画布会显示空白区域。建议判断坐标是否在合理范围内。

10. 波段组合(Band Combination)
10.1 功能概述
波段组合是遥感图像处理中的核心功能,用于将多波段栅格数据的任意三个波段分别映射到RGB三个颜色通道。根据 TODO.txt 的功能规划,波段组合功能位于 "Home > Bands > Band Combinations" 和 "Red Green Blue"。
10.2 实现原理
在QGIS中,多波段栅格图层的显示是通过 QgsRasterRenderer 及其子类管理的。默认情况下,多波段数据的前三个波段分别映射到RGB通道(RGB合成)。要改变波段组合,需要修改渲染器设置。
核心流程:
cpp
#include "qgsrasterlayer.h"
#include "qgsmultibandcolorrenderer.h"
#include "qgsrasterrenderer.h"
void setBandCombination(QgsRasterLayer* layer,
int redBand, int greenBand, int blueBand) {
if (!layer || !layer->isValid()) return;
// 获取当前渲染器
QgsRasterRenderer* renderer = layer->renderer();
// 创建多波段彩色渲染器
QgsMultiBandColorRenderer* rgbRenderer =
new QgsMultiBandColorRenderer(
layer->dataProvider(), // 数据提供者
redBand, // 红色波段索引
greenBand, // 绿色波段索引
blueBand, // 蓝色波段索引
QgsContrastEnhancement::StretchToMinimumMaximum // 对比度增强
);
// 设置渲染器到图层
layer->setRenderer(rgbRenderer);
// 触发图层重绘
layer->triggerRepaint();
}
10.3 获取波段信息
在设置波段组合前,通常需要获取栅格数据的波段信息供用户选择:
cpp
void getBandInfo(QgsRasterLayer* layer) {
if (!layer || !layer->isValid()) return;
QgsRasterDataProvider* provider = layer->dataProvider();
int bandCount = provider->bandCount(); // 波段总数
for (int band = 1; band <= bandCount; band++) {
// 获取波段名称
QString bandName = provider->generateBandName(band);
// 获取波段数据类型
Qgis::DataType dataType = provider->dataType(band);
// 获取波段统计信息(最小值、最大值、均值、标准差)
QgsRasterBandStats stats = provider->bandStatistics(band);
double minVal = stats.minimumValue;
double maxVal = stats.maximumValue;
double meanVal = stats.mean;
// TODO: 显示到UI组件供用户选择
}
}
10.4 Raster Band Stats计算
cpp
// 获取波段统计信息(需要先设置感兴趣的统计项)
QgsRasterBandStats stats = provider->bandStatistics(
band, // 波段序号
QgsRasterBandStats::Min | QgsRasterBandStats::Max |
QgsRasterBandStats::Mean | QgsRasterBandStats::StdDev,
QgsRectangle(), // 感兴趣区域(空表示全图)
0 // 采样大小(0表示全分辨率)
);
10.5 对比度增强
QgsContrastEnhancement 提供了多种对比度增强方式,用于优化显示效果:
StretchToMinimumMaximum:拉伸到最小最大值StretchAndClipToMinimumMaximum:拉伸并裁剪ClipToMinimumMaximum:裁剪到最小最大值NoEnhancement:不做增强
cpp
// 设置每个波段的对比度增强
QgsContrastEnhancement* redEnhancement = new QgsContrastEnhancement(
Qgis::Float64 // 数据类型
);
redEnhancement->setMinimumValue(minRed);
redEnhancement->setMaximumValue(maxRed);
redEnhancement->setContrastEnhancementAlgorithm(
QgsContrastEnhancement::StretchToMinimumMaximum);
rgbRenderer->setRedContrastEnhancement(redEnhancement);
10.6 单波段伪彩色渲染
除了RGB组合,单波段数据还可以使用伪彩色渲染(如NDVI显示):
cpp
#include "qgssinglebandpseudocolorrenderer.h"
void setPseudoColorRenderer(QgsRasterLayer* layer, int band) {
// 创建伪彩色着色器
QgsColorRampShader* shader = new QgsColorRampShader();
shader->setColorRampType(QgsColorRampShader::Interpolated);
// 添加颜色节点
shader->setColorRampItemList({
QgsColorRampShader::ColorRampItem(0, QColor(255, 0, 0), "Low"),
QgsColorRampShader::ColorRampItem(128, QColor(255, 255, 0), "Medium"),
QgsColorRampShader::ColorRampItem(255, QColor(0, 255, 0), "High")
});
QgsRasterShader* rasterShader = new QgsRasterShader();
rasterShader->setRasterShaderFunction(shader);
QgsSingleBandPseudoColorRenderer* renderer =
new QgsSingleBandPseudoColorRenderer(
layer->dataProvider(), band, rasterShader);
layer->setRenderer(renderer);
layer->triggerRepaint();
}
10.7 注意事项
- 波段编号从1开始:不同于C++数组的索引从0开始,QGIS的波段编号从1开始
- 渲染器所有权 :将渲染器设置到图层后,图层会接管渲染器的所有权,不要再手动删除
- 触发重绘 :修改渲染器后必须调用
triggerRepaint()或refresh()才能看到效果 - 数据类型:不同卫星传感器的波段数量和数据格式不同,需要动态获取
11. 图层树与项目管理
11.1 功能概述
图层树(Layer Tree)是QGIS应用中最核心的UI组件之一,负责管理所有已加载的图层,包括图层的增删、排序、可见性切换、分组管理、右键菜单等操作。在本工程中,图层树通过 QgsLayerTreeView 结合 QgsLayerTreeModel 实现,并通过 QgsLayerTreeMapCanvasBridge 与地图画布联动。
11.2 核心实现
11.2.1 图层树视图创建
cpp
void QtGis::createLayerTreeView()
{
// 新建图层树控件
mLayerTreeView = new QgsLayerTreeView(this);
mLayerTreeView->setObjectName(QStringLiteral("theLayerTreeView"));
// 创建QgsLayerTreeModel实例,绑定项目图层根节点
QgsLayerTreeModel* model = new QgsLayerTreeModel(
QgsProject::instance()->layerTreeRoot(), this);
// 设置模型标志位
model->setFlag(QgsLayerTreeModel::AllowNodeChangeVisibility);
model->setFlag(QgsLayerTreeModel::AllowNodeReorder);
model->setFlag(QgsLayerTreeModel::AllowNodeRename);
model->setFlag(QgsLayerTreeModel::ShowLegendAsTree);
model->setFlag(QgsLayerTreeModel::UseEmbeddedWidgets);
model->setFlag(QgsLayerTreeModel::UseTextFormatting);
model->setAutoCollapseLegendNodes(10);
// 视图绑定模型
mLayerTreeView->setModel(model);
// 创建画布桥接------连接图层树与地图画布
mLayerTreeCanvasBridge = new QgsLayerTreeMapCanvasBridge(
QgsProject::instance()->layerTreeRoot(), mMapCanvas, this);
}
11.2.2 图层树工具栏
cpp
// 添加群组
QAction* addGroup = new QAction(tr("Add Group"), this);
addGroup->setIcon(createIcon(Add_Group));
connect(addGroup, &QAction::triggered,
mLayerTreeView->defaultActions(), &QgsLayerTreeViewDefaultActions::addGroup);
// 展开全部
QAction* actionExpandAll = new QAction(tr("Expand All"), this);
actionExpandAll->setIcon(createIcon(Expand_All));
connect(actionExpandAll, &QAction::triggered,
mLayerTreeView, &QgsLayerTreeView::expandAllNodes);
// 折叠全部
QAction* actionCollapseAll = new QAction(tr("Collapse All"), this);
actionCollapseAll->setIcon(createIcon(Collapse_All));
connect(actionCollapseAll, &QAction::triggered,
mLayerTreeView, &QgsLayerTreeView::collapseAllNodes);
11.2.3 右键菜单提供者
图层树的右键菜单通过 QgsLayerTreeViewMenuProvider 接口实现自定义上下文菜单:
cpp
QMenu* QtGisLayerTreeViewMenuProvider::createContextMenu()
{
QMenu* menu = new QMenu;
QModelIndex index = mView->currentIndex();
if (!index.isValid()) return nullptr;
QgsLayerTreeNode* node = mView->index2node(index);
if (QgsLayerTree::isGroup(node)) {
// 群组节点菜单
menu->addAction(mView->defaultActions()->actionRemoveGroupOrLayer(menu));
menu->addAction(mView->defaultActions()->actionRenameGroupOrLayer(menu));
}
else if (QgsLayerTree::isLayer(node)) {
QgsMapLayer* layer = QgsLayerTree::toLayer(node)->layer();
if (layer) {
menu->addAction(mView->defaultActions()->actionZoomToLayer(mMapCanvas, menu));
menu->addAction(mView->defaultActions()->actionRemoveGroupOrLayer(menu));
menu->addAction(mView->defaultActions()->actionRenameGroupOrLayer(menu));
}
// 矢量图层额外显示要素计数
QgsVectorLayer* vLayer = qobject_cast<QgsVectorLayer*>(layer);
if (vLayer) {
menu->addAction(mView->defaultActions()->actionShowFeatureCount(menu));
}
}
return menu;
}
11.3 图层自动选择与插入点管理
cpp
// 新增图层时自动选中该图层
void QtGis::autoSelectAddedLayer(QList<QgsMapLayer*> layers)
{
if (!layers.isEmpty()) {
QgsLayerTreeLayer* nodeLayer =
QgsProject::instance()->layerTreeRoot()->findLayer(layers[0]->id());
if (!nodeLayer) return;
auto index = mLayerTreeView->layerTreeModel()->node2index(nodeLayer);
mLayerTreeView->setCurrentIndex(index);
}
}
// 更新新图层的插入位置
void QtGis::updateNewLayerInsertionPoint()
{
QgsLayerTreeGroup* insertGroup = mLayerTreeView->layerTreeModel()->rootGroup();
QModelIndex current = mLayerTreeView->currentIndex();
int index = 0;
QgsLayerTreeRegistryBridge::InsertionPoint insertionPoint(insertGroup, index);
if (current.isValid()) {
index = current.row();
QgsLayerTreeNode* currentNode = mLayerTreeView->currentNode();
if (currentNode) {
if (QgsLayerTree::isGroup(currentNode)) {
// 插入到群组中
insertGroup = QgsLayerTreeUtils::firstGroupWithoutCustomProperty(
QgsLayerTree::toGroup(currentNode), QStringLiteral("embedded"));
insertionPoint = QgsLayerTreeRegistryBridge::InsertionPoint(insertGroup, 0);
} else {
// 插入到当前节点前
QgsLayerTreeNode* parentNode = currentNode->parent();
if (QgsLayerTree::isGroup(parentNode)) {
QgsLayerTreeGroup* parentGroup = QgsLayerTree::toGroup(parentNode);
insertGroup = QgsLayerTreeUtils::firstGroupWithoutCustomProperty(
parentGroup, QStringLiteral("embedded"));
if (parentGroup != insertGroup) index = 0;
}
insertionPoint = QgsLayerTreeRegistryBridge::InsertionPoint(insertGroup, index);
}
}
}
QgsProject::instance()->layerTreeRegistryBridge()->setLayerInsertionPoint(insertionPoint);
}
11.4 技术要点
-
QgsLayerTreeModel标志位 :通过setFlag()控制模型行为,AllowNodeChangeVisibility启用复选框切换可见性,AllowNodeReorder允许拖拽排序,AllowNodeRename允许重命名。 -
QgsLayerTreeMapCanvasBridge:是连接图层管理和地图显示的核心桥梁,自动将图层树中的添加/删除/顺序变化同步到画布。 -
QgsLayerTreeViewDefaultActions:提供常见操作的预置Action,包括缩放至图层(actionZoomToLayer)、移除图层(actionRemoveGroupOrLayer)、重命名(actionRenameGroupOrLayer)、显示要素计数(actionShowFeatureCount)等。 -
QgsDockWidget停靠布局 :图层树停靠在左侧(Qt::LeftDockWidgetArea),属性面板停靠在右侧(Qt::RightDockWidgetArea)。
12. QGIS初始化与运行时环境
12.1 工程入口与初始化流程
cpp
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QString appDir = QCoreApplication::applicationDirPath();
QCoreApplication::setOrganizationName("QtQgis");
QCoreApplication::setApplicationName("QtQgis Application");
// ★★ 核心:初始化QGIS环境 ★★
QgsApplication::init(appDir); // 设置QGIS路径
QgsApplication::initQgis(); // 初始化QGIS核心服务
QgsApplication::setMaxThreads(8); // 设置最大渲染线程数
QtGis w;
w.showMaximized();
return a.exec();
}
12.2 QgsApplication::initQgis() 的作用
initQgis() 是QGIS二次开发中必不可少的初始化调用,它完成以下工作:
| 初始化项 | 说明 |
|---|---|
| GDAL/OGR 驱动注册 | 注册所有支持的栅格/矢量格式驱动 |
| PROJ 库初始化 | 初始化坐标参考系统支持 |
| QGIS 插件路径设置 | 设置插件搜索路径 |
| SVG 渲染库初始化 | 初始化SVG符号渲染支持 |
| 数据库连接 | 初始化SpatiaLite等内置数据库 |
12.3 运行时依赖
从工程的 Release 目录可以观察到运行时需要的依赖库:
核心GIS库:
gdal304.dll--- GDAL栅格数据抽象库proj_7_2.dll/proj_8_2.dll--- PROJ坐标投影库geos.dll/geos_c.dll--- GEOS几何引擎spatialindex-64.dll--- 空间索引库spatialite.dll/sqlite3.dll--- SpatiaLite空间数据库libxml2.dll--- XML解析库
QGIS框架库:
qgis_core.dll--- QGIS核心库(数据访问、几何运算、工程管理等)qgis_gui.dll--- QGIS GUI库(地图控件、渲染器等)qgis_native.dll--- 原生对话框支持
Qt框架库:
Qt5Core.dll/Qt5Gui.dll/Qt5Widgets.dll--- Qt基础库Qt5Concurrent.dll--- 并发处理Qt5Svg.dll--- SVG渲染Qt5Xml.dll--- XML处理Qt5Network.dll--- 网络请求Qt5Sql.dll--- 数据库访问Qt5WebKit.dll/Qt5WebKitWidgets.dll--- Web引擎
12.4 注意事项
-
初始化顺序 :必须在
QApplication创建之后调用initQgis(),因为QGIS底层依赖Qt的事件循环和资源管理。 -
多线程渲染 :通过
QgsApplication::setMaxThreads(8)设置最大并行渲染线程数,需根据目标硬件配置调整。 -
路径设置 :
init()的参数应为QGIS安装目录(包含qgis_core.dll等文件的目录),SDK路径配置错误会导致初始化失败。 -
QSetting配置 :在初始化前设置
OrganizationName和ApplicationName,确保QGIS配置(QgsSettings)能正确读写注册表或配置文件。
13. 状态栏与辅助信息显示
13.1 功能概述
工程自定义了丰富的状态栏组件,包括坐标显示、比例尺、缩放倍数、旋转角度、坐标参考系信息等,为用户提供全面的地图状态反馈。
13.2 状态栏架构
cpp
void QtGis::createStatusBar()
{
mStatusBar = new QgsStatusBar();
mStatusBar->setFont(statusBarFont);
statusBar()->addPermanentWidget(mStatusBar, 10);
// 进度条
mProgressBar = new QProgressBar(mStatusBar);
mProgressBar->setMaximumWidth(100);
mProgressBar->setMaximumHeight(18);
mProgressBar->hide();
// 任务管理器
mTaskManagerWidget = new QgsTaskManagerStatusBarWidget(
QgsApplication::taskManager(), mStatusBar);
// 坐标显示
mCoordsEdit = new QgsStatusBarCoordinatesWidget(mStatusBar);
mCoordsEdit->setMapCanvas(mMapCanvas);
// 比例尺
mScaleWidget = new QgsStatusBarScaleWidget(mMapCanvas, mStatusBar);
// 放大镜(缩放倍数)
mMagnifierWidget = new QgsStatusBarMagnifierWidget(mStatusBar);
// 旋转角度
mRotationLabel = new QLabel(QString(), mStatusBar);
mRotationEdit = new QgsDoubleSpinBox(mStatusBar);
// 投影状态
mOnTheFlyProjectionStatusButton = new QToolButton(mStatusBar);
}
13.3 坐标显示组件
坐标显示组件是状态栏最重要的组件之一,实时响应鼠标移动并显示地理坐标:
信号连接:
cpp
// 鼠标移动时更新坐标显示
connect(mMapCanvas, &QgsMapCanvas::xyCoordinates,
this, &QgsStatusBarCoordinatesWidget::showMouseCoordinates);
// 视图范围变化时更新范围显示
connect(mMapCanvas, &QgsMapCanvas::extentsChanged,
this, &QgsStatusBarCoordinatesWidget::showExtent);
坐标格式化:
cpp
void QgsStatusBarCoordinatesWidget::showMouseCoordinates(const QgsPointXY &p)
{
if (!mMapCanvas || mToggleExtentsViewButton->isChecked()) return;
// 使用QGIS内置工具格式化坐标
mLineEdit->setText(
QgsCoordinateUtils::formatCoordinateForProject(
QgsProject::instance(),
p,
mMapCanvas->mapSettings().destinationCrs(),
mMousePrecisionDecimalPlaces
)
);
ensureCoordinatesVisible();
}
13.4 比例尺组件
cpp
// 用户修改比例尺后触发缩放
connect(mScale, &QgsScaleComboBox::scaleChanged,
this, &QgsStatusBarScaleWidget::userScale);
void QgsStatusBarScaleWidget::userScale() const
{
mMapCanvas->zoomScale(mScale->scale());
}
13.5 放大镜组件
放大镜组件允许用户在不改变比例尺的情况下放大显示内容(锁定比例尺后通过放大镜缩放):
cpp
// 画布放大倍数变化 -> 更新控件显示
connect(mMapCanvas, &QgsMapCanvas::magnificationChanged,
mMagnifierWidget, &QgsStatusBarMagnifierWidget::updateMagnification);
// 控件值变化 -> 设置画布放大倍数
connect(mMagnifierWidget, &QgsStatusBarMagnifierWidget::magnificationChanged,
mMapCanvas, [=](double factor) {
mMapCanvas->setMagnificationFactor(factor);
});
// 缩放锁定信号传递
connect(mMagnifierWidget, &QgsStatusBarMagnifierWidget::scaleLockChanged,
mMapCanvas, &QgsMapCanvas::setScaleLocked);
connect(mMagnifierWidget, &QgsStatusBarMagnifierWidget::scaleLockChanged,
mScaleWidget, &QgsStatusBarScaleWidget::setLocked);
13.6 旋转角度控制
cpp
mRotationEdit = new QgsDoubleSpinBox(mStatusBar);
mRotationEdit->setClearValue(0.0);
mRotationEdit->setKeyboardTracking(false);
mRotationEdit->setDecimals(1);
mRotationEdit->setRange(-360.0, 360.0);
mRotationEdit->setWrapping(true);
mRotationEdit->setSingleStep(5.0);
mRotationEdit->setSuffix(QStringLiteral("°"));
connect(mRotationEdit,
static_cast<void (QgsDoubleSpinBox::*)(double)>(&QgsDoubleSpinBox::valueChanged),
this, &QtGis::userRotation);
13.7 技术要点
-
QgsStatusBar:QGIS自定义的状态栏容器,支持弹性布局,通过addPermanentWidget()添加右侧固定位置的组件。 -
QgsCoordinateUtils::formatCoordinateForProject():根据项目设置自动格式化坐标显示(单位、精度、显示格式)。 -
QgsDoubleSpinBox:QGIS扩展的DoubleSpinBox,支持setClearValue()设置清空值模式、setKeyboardTracking(false)减少实时信号触发。 -
放大镜与比例尺锁定:当用户锁定比例尺后,缩放操作会通过放大镜实现(改变渲染DPI),而不是改变比例尺,这在制图输出时特别有用。
14. Ribbon界面与UI框架
14.1 框架选型
工程使用 QtitanRibbon 组件库(QtitanRibbon5.dll / QtitanBase1.dll)实现Office风格的Ribbon界面,继承自 RibbonMainWindow(封装在 DemoRibbonWindow 中)。
14.3 当前已实现的RibbonBar代码
cpp
void QtGis::createRibbonBar()
{
// 创建Layer页面
RibbonPage* pageFile = ribbonBar()->addPage(tr("&Layer"));
RibbonGroup* layerGroup = pageFile->addGroup(QStringLiteral("Layer"));
// 添加栅格/矢量图层按钮
layerGroup->addAction(mAddRasterLayer, Qt::ToolButtonTextUnderIcon);
layerGroup->addAction(mAddVectorLayer, Qt::ToolButtonTextUnderIcon);
ribbonBar()->setFrameThemeEnabled();
}
14.4 快速访问栏
cpp
void QtGis::createQuickAccessBar()
{
RibbonQuickAccessBar* quickAccessBar = ribbonBar()->quickAccessBar();
QAction* openBtn = quickAccessBar->addAction(
DemoRibbonWindow::createIcon(Image_Open), "Open Project");
connect(openBtn, SIGNAL(triggered()), this, SLOT(slot_fileOpen()));
QAction* saveBtn = quickAccessBar->addAction(
DemoRibbonWindow::createIcon(Image_Save), "Save Project");
connect(saveBtn, SIGNAL(triggered()), this, SLOT(slot_fileSave()));
}
14.5 System Menu(文件菜单)
cpp
void QtGis::createMenuFile()
{
QAction* actionFile = ribbonBar()->addSystemButton(iconLogo, tr("&Project"));
ribbonBar()->systemButton()->setToolButtonStyle(Qt::ToolButtonTextOnly);
RibbonSystemMenu* systemMenu = qobject_cast<RibbonSystemMenu*>(actionFile->menu());
systemMenu->addAction(mFileNew);
systemMenu->addAction(mFileOpen);
systemMenu->addAction(mFileSave);
systemMenu->addAction(mFileSaveAs);
systemMenu->addAction(mFileClose);
systemMenu->addAction(mFileExit);
// 最近文件列表
RibbonPageSystemRecentFileList* pageRecentFile =
systemMenu->addPageRecentFile("Recent Maps");
pageRecentFile->setSize(9);
connect(pageRecentFile, SIGNAL(openRecentFile(const QString&)),
this, SLOT(openRecentFile(const QString&)));
}
14.6 技术要点
-
QtitanRibbon:商业Ribbon组件库,提供与Office一致的UI体验。支持多种风格主题(Office 2007/2010/2013/2016、Adobe Photoshop等)。
-
图标管理 :通过
DemoRibbonWindow::createIcon(ImagesId)统一管理,支持16x16和32x32双尺寸图标。 -
多风格支持 :
DemoRibbonWindow实现了完整的风格切换机制,用户可通过 Options > Style 菜单切换界面风格。
15. 综合注意事项与最佳实践
15.1 内存管理
-
图层面生命周期 :通过
QgsProject::instance()->addMapLayers()添加的图层由QGIS项目对象管理,无需手动删除。直接new QgsRasterLayer()创建的图层若未添加到项目,需要手动delete。 -
渲染器所有权 :
layer->setRenderer(renderer)后,图层接管渲染器的所有权,不要再手动删除,否则会导致悬空指针崩溃。 -
Canvas Refresh Blocker :使用
QgsCanvasRefreshBlocker的RAII机制批量添加多个图层时冻结画布刷新,避免频繁重绘:
cpp
{
QgsCanvasRefreshBlocker refreshBlocker;
// 批量添加图层
for (auto& file : files) {
QgsRasterLayer* layer = new QgsRasterLayer(...);
layersToAdd << layer;
}
QgsProject::instance()->addMapLayers(layersToAdd);
} // refreshBlocker析构时自动解冻并刷新画布
15.2 线程安全
-
渲染线程:QGIS的图层渲染在独立线程中进行,不要在渲染线程中直接操作UI组件。
-
信号槽跨线程 :使用Qt的跨线程信号槽机制(
Qt::QueuedConnection)确保数据变更能安全通知UI更新。 -
canvasRefreshStarted/canvasRefreshFinished:通过连接这两个信号可以在状态栏显示渲染进度。
15.3 坐标参考系统
-
项目CRS设置 :
QgsProject::instance()->setCrs(QgsCoordinateReferenceSystem("EPSG:4326"))设置项目的默认CRS,所有图层的坐标会在显示时实时投影到此CRS。 -
动态投影:QGIS支持动态投影(On-The-Fly投影),即使图层的源CRS不同,也能在同一画布中正确叠加显示。
-
CRS一致性检查:添加图层时建议检查图层的CRS与项目CRS是否匹配,避免坐标偏差。
15.4 文件格式支持
栅格格式(通过GDAL驱动):
| 格式 | 扩展名 | 说明 |
|---|---|---|
| GeoTIFF | .tif, .tiff | 最常用的地理栅格格式 |
| Erdas Imagine | .img | 遥感常用格式 |
| NetCDF | .grd, .nc | 科学数据格式 |
| SRTM HGT | .hgt | DEM高程数据 |
| JPEG2000 | .jp2 | 压缩栅格格式 |
矢量格式(通过OGR驱动):
| 格式 | 扩展名 | 说明 |
|---|---|---|
| Shapefile | .shp | 最通用的矢量格式 |
| GeoJSON | .geojson | WebGIS标准格式 |
| GMT | .gmt | 通用制图工具格式 |
| KML | .kml | Google Earth格式 |
| GeoPackage | .gpkg | 新一代开放标准格式 |
15.5 异常处理最佳实践
-
图层有效性检查 :在操作任何图层前检查
layer->isValid(),无效图层可能是文件损坏、格式不支持或路径错误导致。 -
中文路径支持:GDAL/OGR在Windows下对中文路径的支持有限,建议在UI中提示用户使用英文路径。
-
大文件处理 :对于大型遥感影像(>1GB),使用
setPreviewJobsEnabled(true)和setParallelRenderingEnabled(true)启用预览作业和并行渲染以提升交互体验。 -
Ribbon界面注意事项 :QtitanRibbon与QGIS的样式存在潜在冲突,使用
ribbonBar()->setFrameThemeEnabled()控制框架主题的启用。
16. 总结
本工程是一个典型的基于QGIS SDK的C++二次开发项目,集成了QtitanRibbon商业UI组件,构建了具有Office风格的卫星遥感图像处理平台。通过对该工程的源码分析,我们可以系统性地掌握QGIS二次开发的核心技术要点。
核心技术栈
| 技术层 | 组件 | 用途 |
|---|---|---|
| 框架层 | Qt 5 + QGIS Core/GUI | GIS基础框架 |
| 数据层 | GDAL/OGR + PROJ + GEOS | 空间数据读写与投影 |
| 界面层 | QtitanRibbon | Office风格Ribbon界面 |
| 业务层 | 算法库 | 卫星遥感数据专用处理 |
结语
QGIS SDK为GIS二次开发提供了完善的基础设施,从数据访问(GDAL/OGR集成)、空间分析(GEOS/Proj)、地图渲染到UI组件(地图画布、图层树、状态栏),形成了一站式的开发框架。开发者可以在此基础上专注于业务逻辑的实现,大幅降低GIS软件开发的技术门槛与周期。