ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

QGIS编译好的CMake工程:VS2019+Qt5.15.2直接跑,含PyQGIS开发指南

QGIS编译好的CMake工程:VS2019+Qt5.15.2直接跑,含PyQGIS开发指南 简介面向QGIS二次开发者和源码编译学习者的完整工程包基于CMake、Visual Studio 2019与Qt 5.15.2构建集成常用GIS功能模块。下载后可直接用VS2019打开工程文件运行免去自行下载依赖、配置环境和长时间编译等环节适合在已有源码基础上快速进入研究或二次开发状态。包内共8183个文件整体约473.47MB压缩包为rar格式核心代码以h头文件、cpp源文件与dll动态链接库为主界面与资源包含svg图标、png图片、qgm工程配置、qss样式等同时附有大量py脚本与api文件类型覆盖编译产物、配置、素材和Python接口目录结构清晰便于按模块查看与修改。该工程已经实际编译验证功能内容完整既可直接运行也可在现有GIS模块上增添或删改附带PyQGIS、PyQt5等api文件可帮助编写插件、扩展工具或调用地理处理接口提高开发效率。借助完整的目录与编译配置还可快速定位地图渲染、数据访问、插件管理等模块入口对研究QGIS底层架构也有帮助。目前已有1210人学习下载适合需要快速搭建QGIS开发环境、理解CMake编译流程或对官方模块进行定制维护的开发者。1. QGIS编译好的工程文件直接能跑的CMake工程省掉的是一整条编译弯路编译好的QGIS工程文件意味着什么意味着你不用再走“下载源码→装依赖→配CMake→调Qt路径→编译三小时→看报错”这条老路。这份工程是用CMAKEVS2019QT5.15.2组合编译通过的里面带有PyQGIS、PyQt5、QScintilla、GDAL/OGR等常用GIS功能模块的接口文件和编译产物在Visual Studio 2019里打开工程文件就能直接编译运行。适合三类人准备拿QGIS做GIS二次开发但不想耗在编译墙上的要在VS工程里集成QGIS核心功能的以及想读QGIS源码结构却被构建环境反复劝退的。先花几分钟把工程结构读明白比上来就点“生成”管用得多。2. 工程文件里都有什么.api接口文件、依赖关系与模块边界2.1 先把.api文件读明白这不是库是IDE的类型说明书打开工程文件目录第一眼看到的就是一串.api文件PyQGIS.api、PyQt5.api、PyQGIS-2.2.api、PyQGIS-2.0.api、PyQGIS-1.8.api、Python-3.6.api、PyQGIS-1.7.api、QScintilla2-2.7.2.api、QScintilla2-2.4.5.api、OSGeo_GDAL-OGR-2.2.3.api。这套命名格式看起来像库文件实际它们是Qt Creator和QScintilla用来做代码补全、语法高亮与类型识别的描述文件。每个.api文件记录了一组类的成员函数、参数类型和枚举定义IDE拿到它才能在你输入“QgsVectorLayer”的时候弹出方法和参数。文件名提供什么类型信息典型用途PyQGIS.apiQGIS 3.x的Python绑定类型PyQGIS开发时自动补全PyQt5.apiPyQt5的绑定类型PyQt5界面开发PyQGIS-2.2.api、2.0.api、1.8.api、1.7.apiQGIS 2.x历史API定义加载旧版插件Python-3.6.apiPython 3.6运行时类型Python控制台绑定QScintilla2-2.7.2.api、2.4.5.apiQScintilla编辑器组件类型代码编辑器与Python控制台OSGeo_GDAL-OGR-2.2.3.apiGDAL/OGR的Python绑定类型栅格和矢量数据操作注意这里有一个信息量很大的细节PyQGIS-1.7、1.8、2.0、2.2这几个旧版本API同时存在说明工程在类型定义层面对QGIS 2.x时代做了兼容。写新代码用的是PyQGIS.api旧版本类型定义会在加载历史插件时起实际作用——QGIS 2.x时代的插件调用的就是旧版绑定接口如果IDE里只保留新API旧插件的代码会大片标红。这种新旧共存是QGIS插件生态能保持稳定的原因之一但也会带来一个实际问题编译新代码时如果IDE选错了api文件自动补全按旧接口给提示写出来的代码在3.x运行时直接报AttributeError。所以用这套工程时Qt Creator的“Tools→Options→Text Editor→Completion→Files”里要确认PyQGIS.api排在前面旧版本API只是兜底。2.2 模块边界哪些功能开箱即用哪些要自己加这份工程是通过源码编译的编译出来的内容取决于CMake配置。从工程里能看到的标准模块包括qgis_core核心库负责图层、几何、要素、坐标参照系、数据提供器qgis_gui界面库提供地图画布、渲染、图层控制、工具条qgis_analysis分析库提供栅格计算、网络分析、插值算法qgis_python提供PyQGIS绑定与Python控制台还有QScintilla组件它是内置Python控制台和代码编辑器的底座。模块功能这个工程里是否带qgis_core矢量/栅格数据模型、要素、坐标、数据提供器带qgis_gui画布、渲染、图层控件、工具条带qgis_analysis栅格分析、网络分析工具带qgis_pythonPyQGIS绑定与Python控制台带qgis_serverOGC服务发布不一定取决于编译时开关判断一个模块有没有被编进去最快的办法是看编译输出目录下有没有对应的dll。比如output里存在qgis_core.dll说明核心模块没问题要确认Python绑定是否生效启动QGIS后打开Python控制台能正常执行import qgis.core就说明绑定成功。CMake配置里这些模块通过WITH_BINDINGS、WITH_GDAL、WITH_QSCI等开关控制这份工程能直接跑说明编译时这些开关按“包含常用GIS模块”的要求打开了。2.3 为什么CMAKEVS2019QT5.15.2这个组合能一次过QGIS编译历来玄学集中区在版本匹配。这套组合能一次编译过核心原因是Qt官方5.15.2的msvc2019_64二进制包恰好就是VS2019v142工具集编译的而QGIS这个时期对Qt 5.15的支持已经比较完整两个库的ABI一致没有跨编译器版本的兼容性问题。GDAL方面接口文件里写的是OSGeo_GDAL-OGR-2.2.3说明编译时连接的是GDAL 2.2.3版本的绑定层GDAL的老版本API在QGIS核心库里兼容性很好这也是工程能稳定落地的原因之一。Python版本这有一个暗坑接口文件里放的是Python-3.6.api说明编译时找的是Python 3.6的开发库。PyQGIS绑定层在编译期就把Python 3.6的ABI锁死了运行时它会加载对应的解释器。系统里装了Python 3.9不影响QGIS主程序运行但外部Python脚本调PyQGIS时必须用3.6的解释器才能对上绑定。这个边界在后续避坑章节还会再展开。理解了工程结构和依赖关系下一步就可以动手把它跑起来。3. 打开工程文件直接运行三步把编译环境验明白3.1 检查CMakeCache编译者的路径在你机器上还可能有效吗拿到工程文件第一件事不是双击.sln就点生成而是检查CMakeCache.txt。CMake生成工程时会把所有关键路径——Qt目录、Python目录、GDAL目录、第三方依赖目录——写进这个缓存文件。换到另一台机器后缓存里的绝对路径大概率失效VS打开工程后会重新探查依赖找不到库就报一堆红叉。# 进入build目录用grep快速检查三个关键路径 grep -E CMAKE_PREFIX_PATH|QT_QMAKE_EXECUTABLE|PYTHON_LIBRARY CMakeCache.txt # 查GDAL路径是否还指向有效位置 grep -E GDAL_DIR|GDAL_INCLUDE_DIR CMakeCache.txt如果QT_QMAKE_EXECUTABLE指向的路径根本不存在说明Qt安装位置和编译者不一致。常见做法是把该变量重新指到你本机的Qt路径或者干脆删掉CMakeCache.txt重新跑一遍CMake让它重新探测环境。反过来如果路径存在但指向的是另一个版本比如编译者用的是msvc2019_64而你机器上是mingw73_64那也得到这个文件里把路径改掉。不要手动去改其他不相干的变量一个依赖路径的错误会在后面引爆几十个链接错误到时候无从查起。依赖检查这一步花五分钟能省下的排错时间是按小时算的。3.2 用CMake重新生成VS2019工程如果CMakeCache里的路径跟着编译者机器走我的习惯是重新执行一次CMake用命令行生成VS2019解决方案。这一步不会重新编译源码只是生成新的工程文件耗时几分钟。cmake .. \ -G Visual Studio 16 2019 \ -A x64 \ -DCMAKE_PREFIX_PATHD:/Qt/Qt5.15.2/5.15.2/msvc2019_64 \ -DPYTHON_EXECUTABLED:/Python36/python.exe \ -DPYTHON_LIBRARYD:/Python36/libs/python36.lib \ -DPYTHON_INCLUDE_DIRD:/Python36/include参数含义拆开说-G Visual Studio 16 2019告诉CMake生成VS2019格式的工程文件-A x64显式指定64位架构QGIS本身是64位程序混入32位架构后面必然出问题CMAKE_PREFIX_PATH是Qt5的查找根路径CMake的find_package(Qt5)机制会优先在这个路径下找Qt5Config.cmakePYTHON_EXECUTABLE、PYTHON_LIBRARY、PYTHON_INCLUDE_DIR这三项要和接口文件里的Python 3.6保持同版本否则编译PyQGIS模块时会报找不到Python.h或者链接失败。这里有个常见误用要提醒不要图省事把Qt路径写进系统环境变量PATH而不写CMAKE_PREFIX_PATH。虽然两种方式Qt都能被找到但系统里装过多个Qt版本时PATH顺序会决定find_package找到哪个版本顺序一乱就会链接到别的Qt版本上。把CMAKE_PREFIX_PATH写在命令行里指向哪个版本就锁定哪个版本干净且可复现。这套参数本身就是编译者机器上的自洽组合换机器只需要改路径不需要改开关选项。3.3 编译、运行再用最小脚本验证核心模块配置完成后回到VS2019打开新生成的ALL_BUILD项目右键选择生成。QGIS完整编译时间看机器性能一般二十分钟到一小时但这份工程文件本身是编译好的大部分中间产物已经在目录里增量编译会快很多。验证动作分三部分。第一步看启动双击qgis.exe正常弹出主界面且右下角没有红色错误提示。第二步打开Python控制台能输入并执行Python命令说明PyQGIS绑定装对了。第三步是最有价值的写一段最小脚本验证矢量图层核心功能。from qgis.core import QgsApplication, QgsVectorLayer QgsApplication.setPrefixPath(C:/qgis_build/output, True) QgsApplication.initQgis() layer QgsVectorLayer(Point?fieldid:integer, test_layer, memory) print(图层有效, layer.isValid()) print(字段数量, layer.fields().count()) QgsApplication.exitQgis()setPrefixPath指向编译输出的目录第二个参数True表示允许QgisCore插件目录等路径自动加载initQgis完成核心模块初始化。这里用“Point?fieldid:integer”这种URI方式动态创建内存矢量图层isValid返回True说明数据模型和字段系统正常字段数量返回1说明字段定义被正确解析。如果isValid为False按先后顺序排查GDAL依赖是否完整、QGIS核心库路径是否正确、数据提供器插件目录是否存在。这三个环节在Windows上出错率最高而且往往是静默失败脚本里多打印一行isValid能省掉很多猜疑。4. 自己加模块从CMake模板到PyQGIS插件的完整路径4.1 不重新编译整个QGIS用QgisConfig.cmake单独编插件二次开发最怕动主工程全套编译一次太伤时间。好在QGIS构建过程会生成QgisConfig.cmake这个文件记录了编译好的库路径、头文件路径和依赖情况。外部工程通过它来查找QGIS库就能做成独立插件工程只编自己写的模块不需要动整个QGIS源码树。cmake_minimum_required(VERSION 3.16) project(my_plugin) set(CMAKE_PREFIX_PATH D:/Qt/Qt5.15.2/5.15.2/msvc2019_64) set(QGIS_BUILD_DIR C:/qgis_build) # 编译好的QGIS的build目录 find_package(Qgis REQUIRED) add_library(my_plugin MODULE plugin.cpp) target_link_libraries(my_plugin PRIVATE Qgis::Core Qgis::Gui) target_include_directories(my_plugin PRIVATE ${Qgis_INCLUDE_DIRS}) install(TARGETS my_plugin LIBRARY DESTINATION bin RUNTIME DESTINATION bin)两个关键点。一是find_package(Qgis REQUIRED)执行时CMake会在QGIS_BUILD_DIR里寻找QgisConfig.cmake二是target_link_libraries链接的是Qgis::Core和Qgis::Gui这两个规范化目标这是QGIS的CMake导出机制给出的标准使用方式。如果插件只处理数据不做界面把Qgis::Gui去掉依赖面更小、编译更快。QGIS对第三方工程的CMake支持做得比较完整导出的是配置好的target而不是一长串手工拼的-ld.lib参数链上之后VS里能看到完整的头文件和依赖路径不用手动指定include目录。4.2 让PyQGIS加载并验证新模块C插件编译完只是第一步真正要验证的是加载到QGIS里能跑起来。QGIS的插件目录规则是把编译出来的dll放到用户profile下的python/plugins目录然后在Python控制台里加载from qgis.utils import plugins plugins.load(my_plugin) plugin plugins.get(my_plugin) if plugin is not None and plugin.isValid(): print(插件加载成功, plugin.name()) else: print(插件加载失败检查dll依赖与日志)plugins.load会按名称找到插件目录里的dll并初始化。加载失败时最常见的错误是缺依赖dll——QGIS插件不会自动帮你设置核心库的依赖搜索路径需要把编译时使用的Qt5Core.dll、Qt5Gui.dll所在目录加进系统PATH或者把依赖dll拷到插件目录旁边。Windows事件查看器里的Application Error日志会明确指出缺哪个模块比盲目试路径高效得多。如果不想编译C可以直接用PyQGIS写Python插件适合处理矢量数据、批量赋值、表达式公式这类不需要高性能计算的场景改完即生效。C插件则适合高频调用的算法模块启动速度和运行效率有明显优势。4.3 什么时候改源码模块什么时候写独立插件判断标准很直接新功能只需要调用QGIS已有类就能实现独立插件足够完全不必重新编译主工程只有当新功能要修改QGIS源码内部的数据结构或算法时才需要动源码树。这份工程已经包含qgis_core、qgis_gui等模块的源码和编译产物真要改源码在VS解决方案里定位到对应类所在项目修改后只重新编译那一个子项目不要全量编译整个解决方案。增量编译速度远快于全量能省下大把时间。我一般会在项目属性里确认“C/C→预编译头”开着没开的话编译速度能慢一到两倍尤其是涉及QGIS这种体量的头文件时差距非常明显。5. 避坑记录复现这份工程最容易翻车的五个位置5.1 打开解决方案后上千个红叉现象在VS2019里打开.sln几乎所有项目都提示“无法解析的外部符号”或找不到头文件错误列表刷屏。原因QT5.15.2有多个工具集变体msvc2019_64是给VS2019v142工具集用的。但有些机器上VS2019默认安装的是v143工具集或者Qt VS Tools扩展里没有正确绑定Qt版本导致编译器找不到Qt头文件。解决先确认Qt安装目录是msvc2019_64后缀然后在VS项目属性里把平台工具集改成Visual Studio 2019 (v142)。接着打开Qt VS Tools扩展确认Qt Version指向5.15.2 msvc2019_64。这两处对齐后红叉能消掉大半。5.2 链接阶段一直找不到gdal_i.lib现象编译qgis_core时报LNK1181提示无法打开输入文件“gdal_i.lib”。原因CMake把编译者的GDAL绝对路径写进了CMakeCache换机器后该路径不存在链接器自然拿不到GDAL库。GDAL是QGIS矢量数据访问的地基缺了它图层读写全都起不来。解决重新执行CMake配置并显式指定本机GDAL路径cmake -DGDAL_DIRD:/gdal/lib/cmake/gdal ..GDAL_DIR指向包含gdal-config.cmake的目录。配置完成后去CMakeCache.txt里确认GDAL_INCLUDE_DIR存在且里面有gdal.h。如果本机没有现成GDAL开发库直接装OSGeo4W完整包里面自带gdal的开发库把它的路径填进去即可。5.3 编译成功但一运行就弹0xc000007b现象编译一切正常双击qgis.exe窗口还没出来就报“无法正常启动0xc000007b”。原因这是Windows下典型的DLL位数不匹配错误。QGIS是64位程序Qt、GDAL、Python必须全是64位只要有一个依赖库被解析成了32位版本就会触发这个错误。解决用Dependencies工具打开qgis.exe查看导入表里哪些模块加载失败或加载了错误的位数。重点检查PATH里是否混入了x86子目录比如先装了32位Qt又装了64位QtPATH把32位目录排在前面。修正PATH顺序后重新运行能解决大部分情况。这个错误在换机器后出现的频率很高典型场景就是用了在线安装器默认装的32位Qt。5.4 外部Python环境import qgis.core直接失败现象在PyCharm或命令行里执行import qgis.core报ModuleNotFoundError或者提示无法导入动态库。原因工程编译时的Python绑定是基于Python 3.6生成的接口文件里的Python-3.6.api已经暗示了这一点。PyQGIS的绑定库在导入时会去找python36.dll外部Python环境如果是3.8或3.9解释器根本不会加载匹配的动态库绑定就失败。解决最省事的办法是在QGIS自带的Python控制台里执行它已经把QGIS库路径和Python运行时环境配好了。如果一定要在外部IDE里调试装一个Python 3.6的64位解释器并在代码开头把QGIS编译输出的bin目录插到PATH最前面再import qgis.core。版本差一个小版本都会出问题没有玄学只有匹配。5.5 QScintilla的API版本冲突现象编译QScintilla相关模块时出现重复定义或者编辑器代码补全行为混乱一会儿按2.7.2的类型提示一会儿按2.4.5的类型提示。原因工程里同时保留了QScintilla2-2.7.2.api和QScintilla2-2.4.5.api两个接口文件源码树里也存在对应版本定义的旧代码。编译时如果两个版本的宏定义或者类型字符串同时参与就会产生符号冲突。解决在Qt Creator配置里显式只加载QScintilla2-2.7.2.api把2.4.5那个从加载列表里去掉同时在CMakeLists里注释掉对应旧插件项目的add_subdirectory语句。如果只是做数据处理的二次开发编辑器补全功能受影响不太要紧把冲突清理干净后继续用即可。6. 进阶用法批量赋值之前先用表达式公式做单要素验证拿到编译好的工程之后我用到最频繁的功能是矢量属性表的表达式公式批量赋值。表面看很简单打开属性表→字段计算器→写表达式→更新全部要素。但翻车概率不低——公式写错一次几万条数据就被误改了。我的习惯是在批量更新之前新建一个临时字段先用同一个表达式对一两个要素做验证确认结果符合预期后再扩大范围。QGIS表达式公式里最常见的是条件赋值if(price 50, high, low)这个公式在字段计算器里会把price字段值大于等于50的要素赋值为high否则赋值为low。注意字段名要用双引号括起来字符串字面量用单引号。如果字段本身是文本型表达式引擎不会帮你做隐式类型转换直接返回NULL这是批量赋值后数据变成空值的最常见原因。更复杂的嵌套条件也常用比如if(left(code, 1) A and amount 100, A-高, if(amount 100, B-高, 低))left函数取code字段的前一个字符and组合两个条件嵌套if实现多级分类。要验证这类公式是否写对我习惯在Python控制台里用QgsExpression做单要素测试from qgis.core import QgsExpression, QgsFeature, QgsFields, QgsField from qgis.PyQt.QtCore import QVariant fields QgsFields() fields.append(QgsField(price, QVariant.Double)) feature QgsFeature(fields) feature[price] 50 expr QgsExpression(if(\price\ 50, high, low)) print(表达式合法, expr.isValid()) print(单要素结果, expr.evaluate(feature))QgsExpression.isValid()检查语法evaluate返回该要素上的实际求值结果。相比字段计算器弹窗里的错误提示这里的返回信息更直接能看出是函数名拼错还是类型不匹配。注意Python字符串里双引号前要加反斜杠转义这是小白最容易困惑的一行写法。批量赋值前跑一遍单要素验证成本只有几秒钟但能避免把几万条数据改错再找后悔药的麻烦。从那以后我每次拿到一个新的QGIS编译工程都会先走一遍这个习惯动作跑最小脚本确认核心模块正常把常用表达式在临时字段上试算一次字段类型与函数匹配确认无误后才开始正式处理数据。这样即使工程文件后续被别人改动过也不会影响自己手里的数据正确性。希望帮到你。本文还有配套的精品资源点击获取
返回列表