十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

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

QGIS官方示例代码深度拆解:PyQGIS二次开发从入门到实践

QGIS官方示例代码深度拆解:PyQGIS二次开发从入门到实践 简介本资源是QGIS官方示例代码的完整整理包面向地理信息系统初学者、开源GIS开发者及桌面端插件编写人员有效缓解当前中文社区QGIS二次开发示例稀缺、API实践资料零散的痛点。压缩包共220个文件涵盖C源码20个.cpp、11个.h、构建配置15个CMakeLists.txt、7个.pro、界面资源6个.ui、6个.qrc、矢量与栅格测试数据3个.shp/.dbf/.prj/.shx、3个.tif及文档说明3个README、2个PDF、4个HTML总大小仅1.1MB轻量易部署。已有355人下载学习内容结构清晰按功能模块组织为8个典型场景从Hello World风格入门、基础主窗口搭建、矢量标注、栅格加载到橡皮筋交互、属性访问、自定义地图工具开发再到插件编写工作坊覆盖QGIS C API核心使用链路配套图像资源与构建脚本可直接编译运行是系统掌握QGIS桌面端开发的优质实践入口。 写这篇文章的念头来自我一次真实的“翻车”经历。早几年刚开始接触QGIS二次开发时我沉迷于在网上抄各种零散的PyQGIS代码今天复制一段加载图层明天粘贴一段缓冲分析看起来能跑但一旦换数据、换版本、换需求立刻罢工。后来我花了一整个周末把QGIS官方示例代码老老实实从PyQGIS Cookbook到源码tests目录过了一遍才发现之前踩的坑官方文档里早就有答案。对于想用Python玩转QGIS、想写插件、想把ArcGIS里的工作流迁移到开源方案里的人来说官方示例代码就是最好的老师它解决的不只是“这行代码怎么写”而是“QGIS这套API到底是怎么设计的”。这篇文章不打算给你罗列一堆网上随便能搜到的代码片段而是把我自己学习QGIS官方示例代码的方法、拆解过程、踩坑实录完整复盘一遍。内容会涉及环境准备、官方示例结构、几个典型代码的逐行解读以及从示例到真实项目落地时要注意的问题。适合刚接触PyQGIS的初学者也适合已经写了几个脚本但总觉得不踏实的开发者。1. 为什么要死磕QGIS官方示例代码1.1 官方示例代码背后是API设计者的思路很多人觉得官方示例代码“太简单”“太基础”不如网上那些花里胡哨的实现有参考价值。这个看法我早年也有但后来吃了亏才明白官方示例的价值从来不在代码量而在代码里体现的设计思路。举个例子。网上搜“QGIS加载点图层”你会搜到十几种写法有的直接构造QgsVectorLayer有的用QgsVectorLayerCache有的甚至直接操作底层数据源。官方示例里通常只给一种最干净、最标准的写法比如从文件加载Shapefilelayer QgsVectorLayer(/path/to/shapefile.shp, 图层名, ogr) if not layer.isValid(): print(图层加载失败) QgsProject.instance().addMapLayer(layer)这段代码看着平平无奇但注意两个细节第一加载完必须检查isValid()这就是官方在教你健壮性意识第二addMapLayer之前layer还只存在于内存里不加入Project就不会显示在图层面板中这体现了QGIS“数据源、图层、项目”三层分离的核心架构。网上很多代码忽略这些细节初学者抄了也不知道为什么要这么写出了问题更无从排查。我自己的体会是官方示例代码至少有三个层面可以学第一层是API调用方法解决“这个功能用哪个类哪个函数”的问题第二层是代码组织方式解决“一个完整流程应该按什么顺序串联”的问题第三层是设计与取舍解决“为什么用这个方案而不是另一个”的问题。第三层才是最值钱的这也是本篇文章想带大家深入的东西。1.2 官方示例到底散落在哪里学习官方示例前得先搞清楚资源在哪。我踩过几次坑之后把QGIS官方示例的主要来源整理成了几类建议按顺序查阅PyQGIS Developer Cookbook也就是PyQGIS开发手册官方文档里面每个章节都有可复制的代码片段是入门的第一手资料。QGIS源码仓库tests目录GitHub上qgis/QGIS下的python目录里有大量自动化测试用例覆盖了几乎所有核心类的读写、空间操作、CRS变换等场景。官方插件与Processing算法脚本QGIS内置的算法和官方插件代码就是活生生的最佳实践。QGIS官方GitHub仓库中的examples目录部分版本会附带示例脚本。我给新手一个实用建议不要把官方文档从第一页读到最后一页那样效率极低。正确做法是先搞定第一章和第二章的代码加载图层、创建要素然后把文档当作字典遇到具体功能需求时按关键词检索。比如我要做缓冲区分析就去搜“buffer”相关章节我要处理属性表就搜“QgsVectorLayer edit attribute”。1.3 为什么搜索热词里总有“官方示例”的痕迹从大家搜索的关键词看比如“qgis官方例子学习代码”“示例代码讲解”这类词长期热度很高说明市场上有大量刚接触QGIS二次开发的人在寻找可靠的学习路径。同时像“qgis怎么下载osm道路”“qgis怎么构建金字塔”“qgis怎么打开mxd”这类问题本质上也都是在和官方API打交道。所以这篇博文定位就很明确了不追求覆盖所有功能而是带着你把官方示例的代码逻辑和调试方法学透让你以后面对任何新的API都能自己查文档、自己试错、自己搞定。2. 准备环境先把代码跑起来再说2.1 QGIS自带的Python控制台就是最好的练习场学习官方示例代码不需要一开始就配置复杂的IDE或创建插件工程。QGIS自带的Python控制台是最快捷的练功场。打开QGIS菜单栏找到“插件”或“扩展”里的Python控制台也可以按CtrlAltP快捷键里面可以逐行执行PyQGIS代码还能看到图层列表、打印输出非常适合做最小复现。我在学习加载图层示例时就是先在Python控制台里逐行粘贴代码观察每一行的输出。Python控制台还有一个很有用的功能输入对象后加“.”会自动弹出该对象的所有方法和属性配合官方文档一起用能极大提升摸索速度。比如你输入layer.就能看到layer.name()、layer.extent()、layer.featureCount()这些方法当场就能试。2.2 Python控制台环境细节Python控制台本质上运行的是QGIS内置Python解释器已默认导入qgis core、qgis gui这些包多数情况下不需要手动import。但如果你写独立脚本就需要自己导入模块后面第三节的示例代码里我会分别标注控制台模式和独立脚本模式的差异。有个很常见的环境坑如果你在控制台里定义了一个变量比如layer然后重新运行加载代码旧对象可能还占用旧数据源修改代码后不生效。我建议每次做新实验前要么重启QGIS要么用清理命令把图层面板里的旧图层移除QgsProject.instance().removeAllMapLayers()这个命令在调试时非常实用能保证每次测试环境干净。2.3 版本差异和中文环境注意QGIS 3.x和QGIS 2.x的API差异巨大。官方示例代码主要以当前稳定版3.x为准网上很多老的教程还停留在2.x抄下来经常报错。最典型的就是2.x里用QgsMapLayerRegistry.instance().addMapLayer()而3.x改成QgsProject.instance().addMapLayer()。如果你在3.x环境里运行2.x代码第一步就报AttributeError。所以学官方示例前一定要确认版本。中文环境还有几个容易踩的坑一是中文路径QGIS在Windows下对中文路径支持算是比较好了但依然建议项目文件和脚本路径都用英文二是属性表字段编码常见的CSV文件如果是GBK编码QGIS默认按UTF-8读取中文会乱码后面加载CSV时我会讲解决方案三是图层名称PyQGIS里图层名用中文字符没问题但如果你把图层写入GeoPackage或Shapefile字段名最好还是用英文否则部分下游工具会不认。3. 五个最值得模仿的官方示例逐行拆解3.1 加载CSV点位图层解码官方第一课CSV转点图层是高频需求也是理解QGIS数据源机制最典型的一个示例。官方示例在PyQGIS Cookbook的“加载数据”章节里给出过清晰的方案。先看最基本写法# 在Python控制台里运行 uri file:///D:/data/points.csv?delimiter{}xField{}yField{}crs{}.format( ,, lon, lat, EPSG:4326 ) layer QgsVectorLayer(uri, csv_points, delimitedtext) print(layer.isValid()) QgsProject.instance().addMapLayer(layer)这段代码看起来只有几行但潜台词很多。首先要理解delimitedtext这个provider它是QGIS内置的文本数据源驱动专门用来读CSV、TXT等分隔符文本。uri字符串则是它的配置参数delimiter指定分隔符xField和yField指定经纬度字段crs指定坐标系。为什么官方要用uri字符串而不是像读Shapefile那样直接给文件路径因为CSV本身没有几何信息必须通过参数告诉QGIS“哪个字段是X哪个字段是Y”这层抽象是所有文本数据源的通用做法。理解了这个你再看到file:///开头就不会懵了。实际操作中CSV很容易遇到中文乱码问题。QGIS在处理CSV时默认按UTF-8读取如果你的文件是GBK编码字段值会变成乱码。解决办法是先在文件头加一个字符集声明参数uri file:///D:/data/points.csv?delimiter{}xField{}yField{}encoding{}.format( ,, lon, lat, GBK )这行encodingGBK是个很实用的细节官方文档里提过但很多人没注意我经常在群里看到有人问CSV中文乱码怎么解决其实加个参数就行。3.2 根据拐点坐标创建多边形官方示例教你几何对象的正确姿势有人搜“qgis如何根据拐点坐标创建多边形”这正好对应官方创建矢量要素的示例。核心思路是先用坐标点构造QgsGeometry再把它塞进QgsFeature最后写入图层。下面是我把官方示例改得更实用一点的版本在Python控制台里可以直接跑# 先创建一个内存点图层 layer QgsVectorLayer(Polygon?crsEPSG:4326, my_polygon, memory) pr layer.dataProvider() # 拐点坐标经纬度 pt1 QgsPointXY(116.3, 39.9) pt2 QgsPointXY(116.5, 39.9) pt3 QgsPointXY(116.5, 40.1) pt4 QgsPointXY(116.3, 40.1) # 注意多边形必须闭合即首尾点相同 ring [pt1, pt2, pt3, pt4, pt1] polygon_geom QgsGeometry.fromPolygonXY([ring]) feat QgsFeature() feat.setGeometry(polygon_geom) pr.addFeatures([feat]) # 更新图层范围并添加到项目 layer.updateExtents() QgsProject.instance().addMapLayer(layer)这里有两个坑第一个是QgsGeometry.fromPolygonXY的参数结构它接收的是一个“环列表”每个环是QgsPointXY的列表。外环必须闭合即第一个点和最后一个点一致否则部分算法不认。第二个坑是如果用QgsVectorLayer(Polygon?crs...)方式创建图层需要手动layer.updateExtents()否则画布缩放范围可能不对。也许你会问为什么要用[ring]包一层因为多边形可以有内环比如带洞的多边形就是[外环, 内环1, 内环2...]这个结构。官方示例里这种设计是为复杂几何做准备的。理解了这个结构后续写带洞多边形、带岛多边形都不在话下。如果已经有了拐点坐标表格想批量生成多边形方法也好扩展。遍历每一行的拐点字符串解析成QgsPointXY列表循环创建QgsFeature批量addFeatures即可。性能方面几千个多边形没问题几万个建议分批提交每批500个左右内存更可控。3.3 缓冲区分析处理算法和底层几何API哪个更靠谱“qgis建立缓冲区进行匹配”这个需求本质上是空间分析里的缓冲区查询。官方示例里做缓冲区有两条路一条是直接用Processing算法适合独立脚本和流程化处理另一条是直接用QgsGeometry自带的buffer方法适合在图层内部快速做几何计算。先看Processing算法的写法import processing from qgis.core import QgsProcessingFeedback result processing.run(native:buffer, { INPUT: layer, DISTANCE: 0.01, SEGMENTS: 5, OUTPUT: memory: }) buf_layer result[OUTPUT] QgsProject.instance().addMapLayer(buf_layer)这里要特别注意DISTANCE参数的单位。如果输入层是EPSG:4326经纬度缓冲区距离单位就是“度”0.01度大约对应1公里左右但这个换算在纬度不同位置差别很大。如果要做精确的米制缓冲区建议先把图层重投影到投影坐标系比如EPSG:3857或当地高斯投影再做缓冲。这个坑我踩过好几次做出来的缓冲区歪七扭八后来学乖了所有空间分析前先检查坐标系。再看不依赖Processing的低层写法geom feat.geometry() buf_geom geom.buffer(100, 8) # 100为距离单位与图层坐标一致这种写法适合在循环里对单个要素逐个处理不经过算法框架效率高但要注意手动管理坐标系。buffer的第二个参数是分段数分段数越大圆弧越平滑但计算量也越大。官方示例默认用5或8我在实际项目里一般取8到12对大多数场景足够。“官方示例为什么提供了两种方式”其实这正是QGIS API分层的体现。Processing是高层封装适合流程化、可复现的空间分析几何API是底层基础适合嵌入式、定制化计算。官方同时提供这两种就是告诉你要根据场景选工具而不是一味追求“高级写法”。3.4 线要素分割官方示例里最容易被忽略的宝藏“qgis将线要素分成两段”这个话题乍一看很简单其实涉及一个诡异的API——splitGeometry。官方示例在PyQGIS Cookbook的几何操作章节里有说明但不够详细网上也少有人讲透。这里我帮你踩平。splitGeometry的签名在QGIS 3.x中大致是result, new_geometries, topo_points line_geom.splitGeometry([split_point], True)其中split_point是QgsPoint或QgsPointXY组成的列表True表示使用拓扑运算。函数的返回值有三部分result为错误码0表示分割成功new_geometries是分割后的新geometry列表topo_points是拓扑信息点一般用不到。实际使用中要注意两个问题。第一分割点必须精确落在线上如果因为浮点误差偏了一点点分割可能会失败。解决办法是用line_geom.closestSegmentWithContext()或QgsGeometry.nearestPoint()先把点吸附到线上再做分割。第二内存图层的要素更新和提交要小心通常流程是with edit(layer): for feat in layer.getFeatures(): geom feat.geometry() result, new_geoms, _ geom.splitGeometry([split_pt], True) if result 0: layer.changeGeometry(feat.id(), new_geoms[0])这里用with edit(layer)是官方推荐的编辑事务写法保证编辑过程异常时能回滚避免数据损坏。新手经常直接startEditing()然后忘了commitChanges()结果数据没保存还找不到原因。这个示例是官方代码里“看着简单实际坑多”的典型代表。多花点时间弄懂它你后续做道路分割、河网拆分、地块边界修正都会顺畅很多。3.5 把官方示例塞进Qt界面才算真正会写QGIS插件“qt qgis”这个搜索词热度不低很多人其实想问怎么把PyQGIS代码和Qt界面绑在一起官方示例里对QGIS插件有详细的模板但我建议你先理解最小闭环点击按钮执行一段官方示例代码把结果展示到地图上。下面是一个最小版的QAction触发逻辑可以在QGIS插件骨架里使用from qgis.gui import QgsMapTool from PyQt5.QtWidgets import QAction def run_my_analysis(): layer iface.activeLayer() if layer is None: return result processing.run(native:buffer, { INPUT: layer, DISTANCE: 100, SEGMENTS: 8, OUTPUT: memory: }) QgsProject.instance().addMapLayer(result[OUTPUT]) action QAction(缓冲区分析, iface.mainWindow()) action.triggered.connect(run_my_analysis) iface.addToolBarIcon(action)这段代码的核心是把“数据处理逻辑”和“界面触发”分开。函数run_my_analysis里的内容就是官方示例代码的翻版QAction只是给它套了一层外壳。很多插件入门者容易把界面逻辑和处理逻辑混在一起写出来的代码又乱又难维护。官方插件示例在这方面很规范值得模仿。如果追求更正式的处理流程可以写QgsProcessingAlgorithm子类并在Processing工具箱里注册。这样既能在界面按钮里调用也能在批处理模型里复用。官方Processing算法示例在QGIS源码里的python/plugins/processing/algs目录下特别多读几个就能领会到框架的精髓。4. 从官方示例到项目落地还需要过这几关4.1 坐标系统一否则一切分析都是空中楼阁官方示例代码里经常默认坐标系已经统一但实际项目里不同数据源的坐标系可能五花八门GPS采集的WGS84经纬度、国家标准的CGCS2000高斯投影、互联网底图的Web墨卡托。如果你不统一坐标系缓冲区距离会错、要素对齐会错、面积计算会差出天文数字。我的经验是项目启动的第一件事就是定好基准坐标系然后所有数据加载后立即检查CRS。如果是独立脚本可以通过layer.crs().authid()输出当前坐标系用QgsCoordinateTransform做转换from qgis.core import QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsProject target_crs QgsCoordinateReferenceSystem(EPSG:3857) transform QgsCoordinateTransform(layer.crs(), target_crs, QgsProject.instance()) feat_geom.transform(transform)这段代码在官方文档里有现成示例关键是要意识到“每次操作数据前先想坐标系而不是等出错了再回头找”。我在帮朋友排查问题时超过一半的分析结果异常都跟坐标系有关大家一定要把这个当成第一优先级。4.2 大批量数据处理官方示例代码不一定够用官方示例代码为了可读性通常以单个或少量要素为例。现实项目动辄几十万条记录这时代码要做调整。先说加载用layer.getFeatures()遍历时官方示例是for feat in layer.getFeatures():这在数量大时会很慢可以考虑用layer.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]))只取几何或按空间范围分批读取。再说写入大量创建要素时不要每建一个feature就提交一次应该批量收集到list一次性dataProvider().addFeatures(all_feats)。我测试过批量提交比逐条提交快10倍以上。还有一个容易忽略的点Processing算法对内存图层的处理方式。OUTPUT参数为memory:时结果图层只在内存中不会落到磁盘。当数据量很大内存可能吃紧。这时候建议把OUTPUT写成本地临时文件路径比如/tmp/result.gpkg再加载到项目里。官方示例代码里看似只是参数区别实际对你的项目稳定性影响很大。4.3 读官方自动化测试用例学会自己验收代码官方示例代码里最被低估的资源其实是QGIS源码里的tests目录。这些自动化测试用例为了保证代码正确性会覆盖异常场景、边界条件、坐标系转换等。我学习某个API时如果光看Cookbook不够透彻就会去GitHub搜对应的test文件看官方怎么构造输入、断言输出。举个例子QgsGeometry.buffer的测试用例里会验证负距离缓冲区、空几何、极大距离等边界条件这些信息能帮你理解API的容错能力。自己写项目代码时我也会仿照官方测试的风格给核心函数写断言比如“缓冲区结果面积必须大于原要素面积”“分割后的线总长度必须接近原长度”。这套方法论让我的代码质量提升了一个档次。给一个简单的验收示例def test_buffer_area(): geom QgsGeometry.fromWkt(POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))) buf geom.buffer(2, 8) assert buf.area() geom.area()代码不长但能为后续重构兜底。官方示例的学习价值很大一部分就在这种测试思维里值得专门抽出时间研究。5. 常见问题与排查技巧实录下面我把学习QGIS官方示例代码和日常开发里最常遇到的问题整理成了速查表基本覆盖了初学者到中级开发者可能遇到的大部分情况。现象可能原因解决方向layer.isValid()返回False路径错误、uri参数写错、编码不支持先检查文件或uri是否正确再用print输出uri字符串排查图层能加载但不显示坐标系错误、图层范围超出画布范围、被添加但未刷新调用layer.triggerRepaint()检查CRS和extentCSV中文乱码文件编码不是UTF-8uri加encodingGBK或GB2312参数缓冲区结果位置偏移坐标系不是投影坐标系距离单位混乱先reproject到投影坐标系再做缓冲区splitGeometry返回非0分割点没精确落在线段上先用nearestPoint吸附再分割代码在Python控制台能跑独立脚本报错没有导入qgis包或没有初始化QgsApplication独立脚本需要QgsApplication init参考官方脚本配置Processing算法提示无法找到native:buffer算法名写错或QGIS版本过旧在Processing Toolbox里查看准确的算法名用processing.algorithmHelp()验证字段中文名称导出后乱码Shapefile的dbf编码限制改用GeoPackage或预先转英文字段名QGIS插件加载后无反应界面与处理逻辑分离不规范无日志输出用QgsMessageLog或print输出调试查看插件日志打开mxd文件没有正常转换QGIS原生不支持ArcGIS mxd需要手动转换用Project Import/Export导入mxd或先用ArcGIS端导出为通用格式再处理几个特别实用的排查技巧我再单独强调一下。第一善用print和type()。PyQGIS代码很多类型不直观比如QgsGeometry.fromPolygonXY([ring])的参数类型稍微写错就会静默失败。我在调试时习惯在每个关键步骤后加一行print(type(geom), geom.isNull())这样可以快速定位是哪一步产生了空几何或错误类型。官方示例代码很少出问题但你改写成自己的业务逻辑后就特别容易出类型错误这一步能省下大量时间。第二把官方示例当“最小复现用例”。如果你怀疑自己的业务代码出了问题先删减到官方示例的最小形态确认官方形态跑得通再一步步把业务逻辑加回去定位出是哪一个新增环节导致了崩溃。这个“二分法”排错思路是通用的对PyQGIS尤其好使因为QGIS的Python栈报错信息有时候很长很绕但去掉干扰信息后问题往往一下就暴露了。第三不要忽视QGIS的日志面板。菜单栏“查看”里的“日志消息”面板会记录算法运行情况、报错堆栈、Processing输出。很多同学在Python控制台里看到红色报错就慌了其实日志面板里的信息往往更加准确、完整。遇到问题先翻日志再上网搜效率要高很多。第四独立脚本和平台兼容性。前面热词里有人搜“银河麒麟离线安装qgis”说明不少人在信创环境下做GIS开发。在国产操作系统上跑PyQGIS和官方示例代码核心差异在于安装方式代码本身跨平台兼容性很好。如果你是非联网环境提前把QGIS安装包和Python依赖包下载好离线安装后配置好QGIS_PREFIX_PATH环境变量独立脚本一样能跑。这部分官方文档里没有很详细的说明但属于工程实践里的常见坑我特别提示一下。最后分享一点我的个人体会官方示例代码这东西很多人觉得只是一个“hello world”级别的练习材料但如果你真的沉下心去读会发现它是一套完整的设计指南。我自己的学习路径是第一遍先把所有示例跑通让它变成肌肉记忆第二遍尝试改需求比如把单要素改成批量处理把控制台输出改成文件输出第三遍去源码tests目录里看官方怎么测试对照理解API的边界和容错逻辑。三遍之后你会发现自己写PyQGIS代码的时候能条件反射般想到“这个场景官方会怎么处理”这就是真正的进步。最后送大家一句话QGIS的API设计远称不上完美但官方示例代码为它提供了一个足够稳定的坐标系照着学少走很多弯路。本文还有配套的精品资源点击获取
返回列表