ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

QGIS C++二次开发:VS2019+Qt5.15.2构建独立GIS桌面应用

QGIS C++二次开发:VS2019+Qt5.15.2构建独立GIS桌面应用 简介本资源是一份面向GIS开发初学者与C桌面应用开发者的技术实践项目聚焦QGIS二次开发核心能力训练解决轻量级GIS桌面工具从零构建的工程落地问题适用于自然资源管理、城市规划等领域的定制化空间数据处理需求。压缩包共10个文件含3个cpp源文件实现主窗口、图层树菜单及核心逻辑、2个h头文件定义类接口、1个README.md与1个说明.txt提供项目结构与编译指引辅以界面截图png、附赠文档docx及gitignore配置整体仅59KB精简实用。已有102人学习下载资源结构清晰、模块职责分明读者可直接复用图层树右键菜单扩展机制、矢量数据Shapefile/GeoJSON等加载流程及QTQGIS混合框架集成方案并掌握VisualStudio环境下跨平台GIS应用的调试要点与性能优化思路。1. 基于QGIS与QT框架在VisualStudio环境下进行二次开发实现地理信息系统桌面应用不是插件、不是脚本而是真正可独立分发的.exe桌面GIS工具你有没有试过——在QGIS里写完一个Python插件功能很全但一换电脑就报错“ModuleNotFoundError: No module named qgis”或者用PyQt写了个带地图的窗口结果底图加载失败、矢量渲染卡顿、坐标系乱套最后发现是PROJ库版本冲突这不是玄学是QGIS原生C API Qt Widgets VS编译链没对齐的真实翻车现场。这个资源包就是一套绕过Python插件沙箱、不依赖QGIS安装环境、直接用Visual Studio 2019/2022编译生成独立.exe文件的轻量级GIS桌面应用开发骨架。它把QGIS核心qgis_core、qgis_gui作为静态/动态链接库嵌入Qt项目用Qt Designer设计界面用VS做构建和调试最终产出一个双击即用、带矢量加载、图层控制、简单空间查询能力的原生Windows GIS工具。适合需要交付给非技术人员、要求离线运行、或需深度集成业务逻辑如测绘内业质检、国土数据预处理、应急指挥前端的中小团队。新手能照着跑通第一个shp加载窗口熟手能快速切入自定义图层渲染器或坐标转换模块——前提是你得先让Qt和QGIS的ABI对上。2. QGIS C SDK与Qt版本选型为什么必须用Qt 5.15.2 QGIS 3.28 LTS VS2019组合2.1 QGIS二开不是“装个插件就行”本质是C ABI兼容性工程QGIS官方不提供“一键SDK包”其C API暴露在qgis_core.dll、qgis_gui.dll中这些DLL由QGIS源码编译生成严格绑定其构建时的Qt版本、编译器、运行时库MSVCRT。比如QGIS 3.28官方Windows安装包由Qt 5.15.2 MSVC 2019 x64编译若你在VS2022中用Qt 6.5创建项目链接QGIS 3.28的DLL必然触发fatal: cannot mix incompatible qt library (version ex50601)——这个错误代码ex50601就是Qt 5.15.2的ABI签名。网上很多教程让你“下载Qt官网任意版本”结果卡在第一步。真实路径只有一条QGIS版本 → 其构建日志 → 锁定Qt版本 → 锁定VS版本 → 锁定CMake Generator。2.2 为什么是Qt 5.15.2而不是更新的5.15.3或6.xQt 5.15.2是Qt 5系列最后一个LTS长期支持版本也是QGIS 3.28及之前所有稳定版的官方构建基线。Qt 5.15.3虽小版本更新但内部QMetaObject布局微调导致QGIS DLL中导出的QgsMapCanvas类虚函数表偏移错位VS链接时看似成功运行时new QgsMapCanvas()直接崩溃。Qt 6.x则彻底废弃QPainter绘图栈改用QRhi而QGIS 3.x全部基于Qt 5的QPainter重绘机制强行桥接等于重写整个渲染管线——这已超出“二次开发”范畴属于重构。因此本项目明确限定Qt 5.15.2 MinGW 7.3.0 或 MSVC 2019 16.11 x64二者不可混用对应VS2019 16.11.22及以上。2.3 Visual Studio版本选择不是越新越好而是要匹配QGIS的CRTQGIS 3.28 Windows安装包使用/MDdDebug和/MDRelease链接MSVCRT即动态链接Microsoft C Runtime。VS2019默认CRT版本为v142对应MSVC 14.29与QGIS构建时的v142完全一致。若用VS2022默认v143即使强制指定-DMSVC_VERSION142其生成的.lib导入库仍含v143符号链接时提示LNK2001: unresolved external symbol __imp_??0QgsApplicationQGIS_COREQEAAXZ——这是典型的CRT不匹配。解决方案只有两个降级VS到2019或在VS2022中安装**“Desktop development with C”工作负载下的“CMake tools for Visual Studio”组件并在CMakeLists.txt中显式指定set(CMAKE_MSVC_RUNTIME_LIBRARY MultiThreadedDLL)**。本资源包采用前者确保零配置开箱即用。2.4 QGIS开发包获取不要从官网下载安装包要拿Build CacheQGIS官网下载的是运行时安装包含GUI、插件、Python不含开发头文件.h和导入库.lib。你需要的是QGIS Build Cache——即CI系统编译QGIS时生成的中间产物。访问https://github.com/qgis/QGIS/releases/tag/release-3_28_0下拉至“Assets”找到qgis-3.28.0-src.zip源码和关键的qgis-3.28.0-win64-dev.7z开发包。后者解压后包含include/所有qgis_core.h、qgis_gui.h等头文件lib/qgis_core.lib、qgis_gui.lib用于VS链接bin/qgis_core.dll、qgis_gui.dll运行时必需必须与exe同目录plugins/libqgis_app.dll等插件本项目暂不用但保留结构提示qgis-3.28.0-win64-dev.7z体积约180MB解压后include目录有327个头文件lib目录含21个.lib。不要试图用dumpbin /exports qgis_core.dll exports.txt自己生成.lib——QGIS大量使用Q_DECL_EXPORT宏控制符号导出手动处理极易遗漏QgsVectorLayer::addFeature等关键函数。3. Visual Studio项目结构搭建从空解决方案到可加载SHP的主窗口3.1 创建Qt VS Tools项目拒绝手工配置Include路径安装Qt VS Tools插件v2.10支持Qt 5.15.2在VS2019中File → New → Project → Qt Widget Application命名QgisDesktopApp路径设为D:\gisdev\QgisDesktopApp。关键设置Qt Version选择已安装的Qt 5.15.2 MSVC2019 64-bit必须与QGIS Dev包一致Target Platformx64QGIS 3.28仅提供64位DLLProject Settings → General → Configuration TypeApplication (.exe)Project Settings → C/C → General → Additional Include Directories添加D:\qgis-dev\includeQGIS头文件路径Project Settings → Linker → General → Additional Library Directories添加D:\qgis-dev\libQGIS .lib路径Project Settings → Linker → Input → Additional Dependencies填入qgis_core.lib;qgis_gui.lib;qtmain.lib注意qtmain.lib是Qt WinMain入口封装库若缺失会导致LNK2019: unresolved external symbol WinMain。此库位于Qt\5.15.2\msvc2019_64\lib\qtmain.libVS Qt Tools会自动添加但手动配置时务必确认。3.2 初始化QGIS应用比Qt QApplication多三行关键代码main.cpp不能直接QApplication app(argc, argv)必须先初始化QGIS环境#include QApplication #include QMainWindow #include qgsapplication.h // QGIS核心头文件 #include qgsmapcanvas.h int main(int argc, char *argv[]) { // 1. 设置QGIS数据路径必须否则找不到投影定义、符号库 QString qgisDataPath D:/qgis-dev/share/qgis; // 指向QGIS安装目录的share子目录 QgsApplication::setPrefixPath(qgisDataPath, true); // 2. 创建QGIS应用实例传入argc/argv但不接管GUI事件循环 QgsApplication qgisApp(argc, argv, true); // 第三个参数true表示GUI模式 // 3. 初始化QGIS加载PROJ、GDAL、SIP等依赖 qgisApp.initQgis(); // 4. 启动Qt应用此时QGIS已就绪 QApplication app(argc, argv); QMainWindow window; window.setWindowTitle(QGIS Desktop Lite); // 5. 创建地图画布并设为中心部件 QgsMapCanvas *canvas new QgsMapCanvas(window); canvas-setCanvasColor(Qt::white); canvas-enableAntiAliasing(true); window.setCentralWidget(canvas); // 6. 加载一个SHP示例绝对路径相对路径在VS调试中易失效 QgsVectorLayer *layer new QgsVectorLayer(D:/data/test.shp, test_layer, ogr); if (layer-isValid()) { QgsProject::instance()-addMapLayer(layer); canvas-setLayers({layer}); canvas-zoomToFullExtent(); } window.show(); int result app.exec(); // Qt事件循环 qgisApp.exitQgis(); // 退出前释放QGIS资源 return result; }关键点说明setPrefixPath必须指向QGIS安装目录的share/qgis含resources、projection等子目录不是Dev包路径。若无QGIS安装需从qgis-3.28.0-win64-dev.7z中提取share目录并复制到指定位置。QgsApplication构造函数第三个参数为true表示启用GUI模式否则无法创建QgsMapCanvas。initQgis()必须在QApplication构造之后、app.exec()之前调用顺序错则PROJ初始化失败坐标系识别为Unknown CRS。QgsProject::instance()-addMapLayer()是QGIS 3.x标准图层管理方式替代旧版QgsMapLayerRegistry::instance()-addMapLayer()。3.3 Qt Designer界面集成用.ui文件驱动主窗口而非硬编码右键项目 →Add → New Item → Qt → Qt Designer Form Class命名为MainWindow.ui。拖入QgsMapCanvas控件需先注册自定义控件在MainWindow.ui空白处右键 →Promote to...Promoted class name填QgsMapCanvasHeader file填qgsmapcanvas.hGlobal include勾选点击Add→Promote生成的ui_mainwindow.h会自动包含#include qgsmapcanvas.h并在setupUi()中创建QgsMapCanvas* canvas成员。此时MainWindow.cpp中只需#include mainwindow.h #include ui_mainwindow.h #include qgsvectorlayer.h #include qgsproject.h MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui-setupUi(this); // ui-canvas 是QgsMapCanvas指针已由UI文件创建 ui-canvas-setCanvasColor(Qt::white); // 加载SHP此处用相对路径因VS调试工作目录可设 QgsVectorLayer *layer new QgsVectorLayer(../data/test.shp, test, ogr); if (layer-isValid()) { QgsProject::instance()-addMapLayer(layer); ui-canvas-setLayers({layer}); ui-canvas-zoomToFullExtent(); } }提示VS调试时Working Directory需设为$(ProjectDir)项目根目录否则../data/test.shp无法定位。可在Project Properties → Configuration Properties → Debugging → Working Directory中设置。4. 矢量数据加载与基础交互从SHP读取到属性表弹窗的完整链路4.1 OGR驱动加载SHP为什么不用QgsVectorLayer::setDataProviderQgsVectorLayer构造函数第三个参数是providerKeyogr是GDAL/OGR驱动标识符。QGIS内部通过QgsOgrProvider封装GDAL支持SHP、GeoJSON、GDB等多种格式。关键在于SHP必须包含.shx和.dbf文件否则isValid()返回false。常见错误只复制.shp文件漏掉索引和属性表。验证方法用ogrinfo -so test.shp检查是否输出Geometry: Polygon和Feature Count: 123。4.2 坐标系自动识别与强制设置避免“地图歪斜”的血泪经验SHP若无.prj文件QGIS默认用WGS84EPSG:4326但实际数据可能是CGCS2000EPSG:4490或地方坐标系。正确做法QgsVectorLayer *layer new QgsVectorLayer(D:/data/test.shp, test, ogr); if (layer-isValid()) { // 方案1从.prj文件自动识别推荐 layer-setCrs(QgsCoordinateReferenceSystem::fromOgcWmsCrs(EPSG:4490)); // 方案2若.prj缺失手动指定必须否则渲染错位 layer-setCrs(QgsCoordinateReferenceSystem::fromEpsgId(4490)); QgsProject::instance()-addMapLayer(layer); ui-canvas-setLayers({layer}); }fromEpsgId(4490)比fromOgcWmsCrs(EPSG:4490)更可靠后者依赖proj.db数据库查询而fromEpsgId直接查内置CRS表。4.3 属性表弹窗实现复用QGIS原生对话框非自制TableViewQGIS提供QgsAttributeTableDialog可直接显示图层属性// 在MainWindow中添加槽函数 void MainWindow::showAttributeTable() { QgsVectorLayer *layer qobject_castQgsVectorLayer*(QgsProject::instance()-mapLayers().begin().value()); if (layer layer-type() QgsMapLayer::VectorLayer) { QgsAttributeTableDialog *dialog new QgsAttributeTableDialog(layer, this); dialog-setAttribute(Qt::WA_DeleteOnClose); dialog-show(); } } // 连接到菜单栏Action connect(ui-actionAttribute_Table, QAction::triggered, this, MainWindow::showAttributeTable);注意QgsAttributeTableDialog依赖QgsGui::enableAutoGeometryRestore()需在main()中调用QgsGui::enableAutoGeometryRestore()否则窗口位置记忆失效。4.4 空间查询基础点选要素并高亮显示QgsMapToolIdentify是QGIS标准识别工具但需继承自QgsMapTool以定制行为class IdentifyTool : public QgsMapTool { Q_OBJECT public: IdentifyTool(QgsMapCanvas *canvas) : QgsMapTool(canvas) {} protected: void canvasReleaseEvent(QMouseEvent *e) override { QgsPointXY point mCanvas-getCoordinateTransform()-toMapCoordinates(e-x(), e-y()); QListQgsMapToolIdentify::IdentifyResult results; QgsMapToolIdentify *identifyTool new QgsMapToolIdentify(mCanvas); results identifyTool-identify(point.x(), point.y(), QgsMapToolIdentify::TopDownStopAtFirst, {QgsMapLayer::VectorLayer}); if (!results.isEmpty()) { QgsVectorLayer *layer qobject_castQgsVectorLayer*(results[0].mLayer); QgsFeature feature results[0].mFeature; // 高亮选中要素临时图层 QgsHighlight *highlight new QgsHighlight(mCanvas, feature.geometry(), layer); highlight-setColor(Qt::red); highlight-setWidth(3); highlight-show(); // 弹窗显示属性 QMessageBox::information(this, Feature Info, QString(ID: %1\nName: %2).arg(feature.id()).arg(feature.attribute(name).toString())); } } };提示QgsHighlight需手动delete否则内存泄漏。生产环境应存入QListQgsHighlight*并在图层变更时清理。5. 避坑指南五个让开发者凌晨三点还在查dumpbin的致命问题5.1 现象程序启动闪退事件查看器显示“应用程序无法正常启动(0xc000007b)”原因32位/64位混用。QGIS 3.28 Dev包是纯64位但VS项目配置为Win32即x86。0xc000007b是Windows加载器检测到PE头架构不匹配的错误码。解决Project Properties → Configuration Manager → Active solution platform → New → x64确保所有项目包括Qt库、QGIS lib均为x64。5.2 现象QgsMapCanvas显示空白控制台无报错但canvas-extent()返回0,0,0,0原因QgsProject::instance()未初始化或setLayers()前未调用canvas-setExtent()。QGIS 3.x要求图层添加到Project后再通过setLayers()触发画布重绘。解决严格按顺序执行QgsProject::instance()-addMapLayer(layer)canvas-setLayers({layer})canvas-zoomToFullExtent()或canvas-setExtent(layer-extent())5.3 现象加载SHP后中文字段名显示为“????”属性值乱码原因GDAL默认用ISO-8859-1解码.dbf而国内SHP多用GBK。QGIS 3.16支持OGR_ENABLE_PARTIAL_REPROJECTION但需显式设置编码。解决在main()中initQgis()后添加QgsSettings settings; settings.setValue(/gdal/encoding, GBK); // 强制GDAL用GBK读.dbf或在SHP路径后加编码参数D:/data/test.shp|encodingGBK5.4 现象Qt Designer中Promote的QgsMapCanvas编译报错“undefined reference tovtable for QgsMapCanvas”原因QgsMapCanvas是QObject派生类需在头文件中声明Q_OBJECT宏且该头文件必须被moc工具处理。但qgsmapcanvas.h是QGIS头文件未被moc扫描。解决不直接PromoteQgsMapCanvas改用QWidget占位然后在MainWindow.cpp中new QgsMapCanvas并setParent()// MainWindow.h 中声明 private: QgsMapCanvas *mCanvas; // MainWindow.cpp 构造函数中 mCanvas new QgsMapCanvas(this); mCanvas-setParent(ui-widgetCanvas); // widgetCanvas是UI中的QWidget容器 ui-widgetCanvas-layout()-addWidget(mCanvas);5.5 现象VS调试时断点无效提示“当前不会命中断点尚未为文档加载任何符号”原因QGIS DLL未加载调试符号.pdb。QGIS Dev包不含PDB需自行编译或下载Debug版本。解决下载qgis-3.28.0-win64-dev-debug.7z若存在或在VS中Debug → Options → Debugging → Symbols添加QGIS PDB服务器需QGIS官方提供实用替代方案在关键位置加qDebug() here;配合Output窗口查看输出比断点更可靠。6. 进阶技巧打包成单文件.exe并嵌入天地图底图URL国内可用方案6.1 使用windeployqt 自定义脚本打包摆脱QGIS安装依赖windeployqt只能处理Qt依赖无法复制QGIS DLL。需编写批处理package.batecho off set APP_DIRD:\gisdev\QgisDesktopApp\x64\Release set QGIS_DEVD:\qgis-dev set OUT_DIRD:\gisdev\QgisDesktopApp\dist mkdir %OUT_DIR% copy %APP_DIR%\QgisDesktopApp.exe %OUT_DIR% copy %QGIS_DEV%\bin\qgis_core.dll %OUT_DIR% copy %QGIS_DEV%\bin\qgis_gui.dll %OUT_DIR% copy %QGIS_DEV%\bin\Qt5Core.dll %OUT_DIR% copy %QGIS_DEV%\bin\Qt5Gui.dll %OUT_DIR% copy %QGIS_DEV%\bin\Qt5Widgets.dll %OUT_DIR% copy %QGIS_DEV%\bin\Qt5Network.dll %OUT_DIR% copy %QGIS_DEV%\bin\Qt5Svg.dll %OUT_DIR% copy %QGIS_DEV%\bin\libwinpthread-1.dll %OUT_DIR% :: 复制QGIS资源必需否则报错“Could not find style ‘default’” xcopy /E /I %QGIS_DEV%\share\qgis %OUT_DIR%\share\qgis :: 复制GDAL数据必需否则坐标系识别失败 xcopy /E /I %QGIS_DEV%\share\gdal %OUT_DIR%\share\gdal echo Packaging done. Run %OUT_DIR%\QgisDesktopApp.exe pause6.2 天地图底图集成用QgsXyzConnection而非WMS规避跨域与证书问题天地图Web服务http://t0.tianditu.gov.cn/vec_w/wmts?...是WMTS协议但QGIS C API对WMTS支持有限。更稳方案是XYZ瓦片// 创建天地图矢量底图 QgsRasterLayer *tdtVec new QgsRasterLayer( typexyzurlhttps://t0.tianditu.gov.cn/vec_w/wmts?serviceWMTSrequestGetTileversion1.0.0layervecstyledefaultformattilestileMatrixSetwTileMatrix{zoom}TileRow{y}TileCol{x}tkyour_token, Tianditu Vector, wms ); // 但WMS在C中需QgsWmsProvider复杂度高。改用XYZQGIS 3.16支持 QgsRasterLayer *tdtVecXYZ new QgsRasterLayer( typexyzurlhttps://t0.tianditu.gov.cn/vec_w/tile/{z}/{y}/{x}.pngzmax18zmin0crsEPSG:3857, Tianditu Vector XYZ, wms );关键参数说明typexyz强制使用XYZ瓦片提供器url天地图官方XYZ模板{z}/{y}/{x}为瓦片坐标占位符zmax/zmin缩放级别范围天地图公开服务为0-18crsEPSG:3857必须指定否则QGIS用默认WGS84导致偏移注意天地图需申请tktoken免费配额足够测试。URL中tkyour_token替换为实际token否则返回403。6.3 离线PROJ数据库防止“Unknown CRS”错误的后悔药QGIS依赖proj.db查找坐标系该文件位于share/proj/。若打包时漏掉所有坐标系显示为Unknown CRS。验证方法运行QgsCoordinateReferenceSystem::fromEpsgId(4326).isValid()返回false。补救措施在main()中initQgis()前强制设置PROJ数据路径qputenv(PROJ_LIB, D:/qgis-dev/share/proj); // 必须是绝对路径 QgsApplication::setPrefixPath(D:/qgis-dev/share/qgis, true);从那以后我每次打包新版本都强制走一遍dumpbin /dependents QgisDesktopApp.exe确认qgis_core.dll、qgis_gui.dll、Qt5Core.dll等关键DLL都在dist目录且share子目录结构完整。少一个proj.db用户打开就报错而你收到的反馈只会是“软件打不开”没人告诉你缺了哪个文件。希望帮到你。本文还有配套的精品资源点击获取
返回列表