QGIS二次开发入门:从官方PyQGIS示例到工程实战

发布时间:2026/8/31 2:59:57
QGIS二次开发入门:从官方PyQGIS示例到工程实战 简介本资源是QGIS官方提供的完整示例代码集合面向地理信息系统初学者、二次开发人员及开源GIS教学实践者有效缓解当前中文社区QGIS编程案例稀缺、入门门槛高的痛点。压缩包共220个文件涵盖C源码20个.cpp、11个.h、构建配置15个CMakeLists.txt、7个.pro、界面资源6个.ui、6个.qrc、矢量与栅格测试数据3个.shp/.dbf/.prj、3个.tif及文档说明readme、html、pdf等辅以21张png和5张jpg示意图整体仅1.1MB轻量易解压。已有355人学习下载目录结构清晰划分为8大主题模块如Hello World风格初始化、基础主窗口搭建、矢量属性访问、自定义地图工具开发等每个示例均含可运行工程与配套说明便于逐层理解QGIS API调用逻辑与插件开发范式。 我在最开始接触QGIS二次开发时绕了很多弯路当时“官方例子”这四个字差点把我劝退——github上qgis/qgis这个主仓库动辄几十万个文件我压根不知道该从哪里下手。很多人都问过“design compile 官方lab_workshop的例子在哪”其实这个问题背后真正的痛点是QGIS的官方示例代码并不集中在一个地方它散落在多个仓库、文档和测试用例里没有一张“藏宝图”的话你连入口都找不到。这篇文章就是我自己学习过程中的一张地图。我会从官方例子的仓库结构讲起教你怎样读懂PyQGIS示例的代码骨架再结合几个真实的高频需求按拐点坐标创建多边形、建立缓冲区进行匹配、把线要素分成两段、加载CSV点位图层、构建金字塔把官方例子改成自己的工具最后分享我踩过的一堆坑和排查思路。适合刚接触QGIS脚本开发、想通过官方例子快速上手的读者也适合那些已经会操作QGIS界面、但面对代码还一头雾水的人。1. 官方示例代码到底藏在哪里先搞清QGIS学习资源的仓库地图很多人默认“官方例子”都在qgis/qgis主仓库里于是进去就迷路。实际上QGIS生态下的官方仓库有好几个各有分工搞混了自然找不到东西。1.1 四个主要仓库的分工与内容定位我把它们梳理成一个表格方便你对照查找仓库名主要内容适合谁看qgis/QGISQGIS主程序C源码、Python控制台内置脚本、tests/src/python下的Python测试代码想做深度开发、阅读底层实现的人qgis/QGIS-Documentation官方文档、训练手册Training Manual、PyQGIS开发者食谱PyQGIS Cookbooklab_workshop材料就在这里的docs/training_manual下几乎所有学习者都该从这里开始qgis/QGIS-Training-Data训练手册配套的练习数据矢量、栅格、样式、工程文件做训练手册练习时必须下载qgis/QGIS-ProcessingQGIS 3.x处理框架下的算法代码、相关脚本示例想写Processing算法、自定义处理工具的人qgis/pyqgis-cookbookPyQGIS开发食谱的源码仓库和QGIS-Documentation中的cookbook部分对应Python开发者学习PyQGIS API我见过不少人问“官方lab_workshop的例子在哪”其实答案就是QGIS-Documentation仓库下的docs/training_manual/目录里面的每个章节都是“lab workshop”的形式带步骤、带说明而配套的练习数据在QGIS-Training-Data仓库里。你光看文档不下载练习数据很多实验根本没法做所以这两个仓库要一起配合。1.2 我最推荐的入手顺序我自己的经验是不要一上来就看C源码也不要直接啃tests/src/python里那些动辄几百行的测试用例那样会被信息量淹没。按下面顺序来最顺先把Training Manual通读一遍了解QGIS的界面操作和基本概念至少知道图层、要素、几何、属性表是怎么回事。再打开PyQGIS Cookbook这是官方最系统的Python示例集合从加载图层、遍历要素、几何操作到符号化都有可直接复制的代码。想写插件或者工具脚本时再去qgis/QGIS仓库的tests/src/python目录看测试代码以及浏览qgis/QGIS-Documentation仓库里的插件开发相关章节。最后有需要才去读C源码比如你搞不清某个API底层行为时。这个顺序的好处是先建立“看得见”的地图思维再进入“能运行”的代码世界最后由浅入深地去啃底层每一步都有前面知识的支撑。1.3 浏览官方仓库时的筛选技巧在GitHub上浏览qgis/QGIS这个仓库时代码搜索功能特别重要。当你想知道某个API在官方代码里是怎么用的直接在仓库内搜索这个方法名通常能看到它在测试用例中的用法。比如我想知道QgsGeometry.buffer()的完整参数用法搜一下就能在tests目录里找到调用示例。还有一个技巧官方文档网站右上角有搜索框但很多人不知道文档的「cookbook」部分和「training_manual」部分是可以直接在GitHub上under Docs目录独立浏览的。遇到文档站打开慢直接在GitHub仓库看Markdown源码也可以代码复制起来更顺手。2. 读懂PyQGIS例子的关键核心对象模型与代码骨架官方例子你拿到手不是复制下来跑一遍就完了得能看懂它为什么这么写。PyQGIS本质上是一套对QGIS C核心库的Python绑定它的编码逻辑和普通Python脚本不太一样。我拆解一段最典型的官方示例讲清楚骨架。2.1 典型例子的四段式结构几乎所有PyQGIS示例都可以归成四步创建或加载图层、获取要素、操作几何、把结果保存或显示出来。PyQGIS Cookbook里最经典的一段加载一个矢量图层并遍历要素代码大概是这样的from qgis.core import QgsVectorLayer, QgsProject layer QgsVectorLayer(/path/to/shapefile.shp, 我的图层, ogr) if not layer.isValid(): print(图层加载失败) else: QgsProject.instance().addMapLayer(layer) for feature in layer.getFeatures(): geometry feature.geometry() print(feature.id(), geometry)这里有一个很多新手会忽略的关键点QgsVectorLayer的第二个参数是显示名称第三个参数是数据提供器ogr表示走OGR矢量驱动。如果路径不对或格式不支持isValid()会返回False所以官方例子几乎都会先做这个判断。QgsProject.instance().addMapLayer(layer)是把图层注册到当前工程里的唯一标准方式在QGIS 2.x时代这行代码是QgsMapLayerRegistry.instance().addMapLayer(layer)API变了这是版本差异里最常见的坑。2.2 几何对象是核心中的核心QGIS里的几何万物皆QgsGeometry。官方例子里你会频繁看到.geometry()方法返回要素的几何然后对这个几何做变换或分析。QgsGeometry这个类封装了各种几何类型包括点、线、面、多点、多线、多面等它内部还有个constGet()方法返回底层几何对象但普通开发不需要碰这个。几何操作里最常见的三个# 缓冲区分析distance是缓冲距离segments是圆滑程度 buffered geometry.buffer(distance, segments) # 求面积返回的是平方单位取决于图层CRS area geometry.area() # 求质心 centroid geometry.centroid()buffer()第二个参数segments经常被人忽略。默认值是5会出现明显的多边形边数不足现象如果你需要圆滑一点的缓冲区建议设置成36或者更高代价是计算变慢。这个细节官方文档有写但很多抄代码的人没注意。2.3 迭代器与要素不要用列表把内存撑爆layer.getFeatures()返回的是一个迭代器不是列表。这意味着QGIS不需要一次性把所有要素读进内存而是流式读取。但很多人会习惯性地做list(layer.getFeatures())小数据没问题大数据会把内存撑爆。我见过有人拿官方例子跑几百万条的线数据直接内存溢出最后把代码改回迭代器才解决。迭代器配合过滤条件也很好用from qgis.core import QgsFeatureRequest request QgsFeatureRequest().setFilterExpression(区域 \华东\) for feature in layer.getFeatures(request): ...这种写法比先取出全部要素再在Python里做条件判断要高效得多它就是SQL的WHERE只不过以表达式形式传给底层。2.4 图层CRS与坐标单位官方例子不会告诉你的隐形杀手很多官方例子跑出来的坐标值和你预期不一样十有八九是CRS问题。QgsGeometry的坐标单位取决于图层本身如果一个图层是WGS84EPSG:4326它的几何操作单位就是度buffer距离写1就代表1度不是你想象的一公里。所以做缓冲区之前最好先用QgsCoordinateReferenceSystem确认单位必要时用QgsCoordinateTransform把几何投影到合适的投影坐标系。from qgis.core import QgsCoordinateReferenceSystem, QgsCoordinateTransform, QgsProject crs_dest QgsCoordinateReferenceSystem(EPSG:3857) transform QgsCoordinateTransform(layer.crs(), crs_dest, QgsProject.instance()) geometry.transform(transform)这个坑在官方例子里不显眼因为官方数据通常都是同坐标系但你自己一换数据就露馅。3. 从官方例子到实际需求五个高频场景的迁移实战看热词就能知道大家真正想问的是那些具体需求“根据拐点坐标创建多边形”“建立缓冲区进行匹配”“将线要素分成两段”“导入CSV点位图层”“怎么构建金字塔”。这些需求官方例子都能找到影子但需要你会改造。下面我逐个演示。3.1 根据拐点坐标创建多边形这是热词里很典型的问题。官方示例里创建多边形用的是QgsGeometry.fromPolygonXY()它的参数是QgsPointXY二维列表的列表。外层列表是环的集合内层列表是一个环的点序列。注意一个环必须是闭合的也就是第一个点和最后一个点要相同。from qgis.core import QgsVectorLayer, QgsFeature, QgsGeometry, QgsPointXY, QgsProject points [ QgsPointXY(120.1, 30.1), QgsPointXY(120.5, 30.1), QgsPointXY(120.5, 30.8), QgsPointXY(120.1, 30.8), QgsPointXY(120.1, 30.1) # 闭合 ] polygon_geometry QgsGeometry.fromPolygonXY([points]) layer QgsVectorLayer(Polygon?crsEPSG:4326, 手动创建多边形, memory) layer.dataProvider().addFeatures([QgsFeature(1, polygon_geometry)]) QgsProject.instance().addMapLayer(layer)这里我用的是内存图层memory数据提供器直接指定Polygon?crsEPSG:4326方便快速验证。如果拐点坐标是经纬度CRS务必确认是4326如果是投影坐标就把crs换成对应的EPSG编码。官方例子里通常用临时图层做几何验证这个思路很值得学因为你直接往文件里写之前先能在界面上看到结果能省很多Debug时间。如果你有多个互相独立的多边形外层列表里的每个环都要单独闭合且不能相互重叠除非你用多部分几何。生成多部分几何可以用QgsGeometry.fromMultiPolygonXY()参数层级多一层结构是多边形列表 → 每个多边形的环列表 → 每个环的点列表。3.2 建立缓冲区进行匹配热词里的“建立缓冲区进行匹配”典型场景是一批点要匹配到附近的道路、建筑物你需要先按给定距离给点做缓冲区然后用缓冲区和目标图层做空间关系判断。PyQGIS Cookbook里的缓冲区示例简单但真正的工程问题在于“匹配”的效率。我的做法是先用QgsSpatialIndex建立空间索引避免全表笛卡尔积from qgis.core import QgsSpatialIndex, QgsFeatureRequest target_layer iface.activeLayer() # 目标图层 index QgsSpatialIndex(target_layer.getFeatures()) for point_feature in point_layer.getFeatures(): buffer_geom point_feature.geometry().buffer(500, 36) # 500米缓冲 candidate_ids index.intersects(buffer_geom.boundingBox()) request QgsFeatureRequest().setFilterFids(candidate_ids) for target_feature in target_layer.getFeatures(request): if buffer_geom.intersects(target_feature.geometry()): print(匹配到目标要素, target_feature.id())这种写法先从空间索引拿到候选要素ID再用真实几何做精确判断比拿两层要素两两嵌套快了不止一个数量级。注意buffer单位的问题如果你的图层是WGS84500会被当成500度结果完全不可用所以做距离相关分析前先用QgsCoordinateTransform转到投影坐标系或者直接用QgsDistanceArea来测量并转换。另外intersects()返回的是候选要素的FID列表传入setFilterFids()时QGIS只返回这些要素不会把整个表扫一遍。这个模式在官方测试代码里很常见但许多新手把它忽略掉一味用两层for循环嵌套数据一多就卡死。3.3 将线要素分成两段线要素分段的官方工具在Processing里有Split with lines和v.split但你如果只想在脚本或插件里完成splitGeometry()才是正牌API。line_geom feature.geometry() split_points [QgsPointXY(120.3, 30.5)] result, new_geometries, topo_test line_geom.splitGeometry(split_points, True) if result 0: for g in new_geometries: print(g.asWkt())splitGeometry()第一个参数是分割点列表第二个参数是拓扑容差(是否允许极小的差异归为同一点)。返回值第一个是状态码0表示成功返回的new_geometries列表里是分割后的多个几何对象。需要特别说明的是这个操作修改的是原始几何对象本身而不是返回一个新的几何所以在调用之前最好先QgsGeometry(line_geom)做一个拷贝避免把原图层里的几何搞坏了。如果你希望按线上已有节点分段而不是按外部点打断可以简单地把line_geom.asPolyline()取出来自己在Python切片构造两个新几何。官方例子里也有类似思路但直接用splitGeometry更省事。还有一个更工程化的做法把线图层和目标点图层丢到Processing的Split lines at points算法里跑一遍能得到一个新的分段图层方便后续做统计分析。3.4 用CSV点位图层生成点要素并编辑热词“我导入的csv点位图层qgis怎么编辑”是新手重灾区。QGIS本身支持直接把带经纬度字段的CSV拖进界面当图层但拖进去之后它是只读的很多人就以为CSV不能编辑。官方例子和文档里其实有便捷办法在脚本中通过“delimitedtext”数据提供器创建点位图层编辑后再另存为GeoPackage或Shapefile。uri file:///D:/points.csv?delimiter,xField经度yField纬度crsEPSG:4326 csv_layer QgsVectorLayer(uri, CSV点位, delimitedtext) print(csv_layer.isValid())这里有几个容易踩坑的地方xField和yField指定经纬度字段名如果字段名是中文需要确认CSV文件的编码是UTF-8且字段名和CSV表头完全一致。delimiter参数用分隔多个参数不能写成中文全角逗号。路径如果是Windows盘符要用file:///D:/xxx.csv这种三斜杠的写法反斜杠在URI里要转成正斜杠。如果CSV里的坐标字符串带千分位或空格先清洗数据否则会被QGIS当作无效字段值。想进一步编辑把CSV图层另存为本地文件再编辑即可from qgis.core import QgsVectorFileWriter QgsVectorFileWriter.writeAsVectorFormatV3( csv_layer, /path/to/output.gpkg, layer.crs(), UTF-8, driverNameGPKG )这个API在QGIS 3.20以上的版本是writeAsVectorFormatV3旧一点的版本是writeAsVectorFormatV2两者参数顺序有差异查官方文档时留意版本号。3.5 给栅格图层构建金字塔热词“qgis怎么构建金字塔”看起来像界面操作问题但脚本里怎么处理官方例子也有涉及。在QGIS里金字塔也叫概览overviews是栅格数据的一种降采样副本用于加速大影像的缩放显示。构建金字塔在界面上是右键图层→构建金字塔本质上调用的是GDAL的gdaladdo工具。在PyQGIS里你可以直接调用系统命令也可以避免依赖外部命令用QgsRasterLayer的buildPyramidList和buildPyramids方法from qgis.core import QgsRasterLayer raster QgsRasterLayer(/path/to/big.tif, 大影像) if raster.isValid(): overviews raster.buildPyramidList([2, 4, 8, 16, 32]) raster.buildPyramids(overviews, NEAREST)buildPyramidList接收的列表是金字塔缩放级别比如[2,4,8,16,32]表示分别生成2倍、4倍、8倍概览。buildPyramids第二个参数是重采样方法常见的有NEAREST最近邻、AVERAGE、GAUSS。对于分类影像用最近邻不容易损坏类别值对于连续影像用平均更平滑。如果你觉得PyQGIS的封装有时不够透明也可以直接用Python调用gdaladdoimport subprocess cmd [gdaladdo, -r, average, /path/to/big.tif, 2, 4, 8, 16, 32] subprocess.run(cmd, checkTrue)这两种方式殊途同归。我更推荐前者因为它不依赖额外的命令行环境和PATH配置。4. 跑不通官方例子的常见原因与排查链路官方例子给你跑不通很多时候不是你代码写错而是环境或版本问题。这一节我把压箱底的排查清单翻出来。4.1 版本差异是最大杀手QGIS 2.x与3.x的API对照PyQGIS Cookbook里的不少例子是老版本遗留的QGIS 3.x开始API变化非常大。两个最常见的差异QgsMapLayerRegistry改成了QgsProjectQgsPoint改成QgsPointXY。下面的表是按我的踩坑记录整理的功能QGIS 2.x写法QGIS 3.x写法注册图层到工程QgsMapLayerRegistry.instance().addMapLayer(layer)QgsProject.instance().addMapLayer(layer)获取工程中所有图层QgsMapLayerRegistry.instance().mapLayers()QgsProject.instance().mapLayers()创建二维点QgsPoint(x, y)QgsPointXY(x, y)获取要素几何feature.geometry()feature.geometry()返回值类型一致数据提供器layer.dataProvider()layer.dataProvider()文件写入QgsVectorFileWriter.writeAsVectorFormat(...)writeAsVectorFormatV2/V3(...)遇到老示例代码直接报错先看墙上QGIS界面右下角的版本号再确认你参考的教程是针对哪个版本的。官方文档有版本切换按钮GitHub仓库也有对应分支这是解决问题最快速的起点。一个简单粗暴的排查方法在Python控制台里输入qgis.core.Qgis.QGIS_VERSION_INT查看版本再去文档中搜索对应版本的API说明。官方例子的代码块下方有时会标注“New in 3.10”或“Deprecated since 3.16”看到这些标记就有了判断依据。4.2 路径与编码问题中文路径、空格和UTF-8QGIS对中文路径的支持在Windows上一直是老大难。官方例子里的路径多半是英文但国内用户数据文件往往放在“D:\数据\项目文件\”这种目录下于是各种加载失败、乱码、保存异常就来了。排查步骤路径尽量全英文命名如果必须用中文先把路径打印出来检查转义是否正确。用print(layer.source())查看数据源字符串是否含有URL编码%20代表空格。CSV和DBF文件注意编码。QGIS默认使用UTF-8识别如果你的CSV是GBK编码最好先用文本编辑器另存为UTF-8或者在创建图层时指定encoding参数uri file:///D:/points.csv?delimiter,xField经度yField纬度encodingUTF-8如果图层属性表出现乱码尝试右键图层→属性→数据源→编码改成对应编码。这个操作在脚本里可以写成layer.setProviderEncoding(GBK) layer.dataProvider().reloadData()4.3 独立Python环境无法import qgis很多人喜欢在系统Python或者PyCharm里写QGIS脚本想直接import qgis结果报错ModuleNotFoundError: No module named qgis。这是因为QGIS的Python包没有安装到系统Python环境里你需要手动设置环境变量PYTHONPATH指向QGIS安装目录下的python目录以及QGIS_PREFIX_PATH指向QGIS安装根目录。以Windows为例set PYTHONPATHC:\Program Files\QGIS 3.28\apps\qgis\python set QGIS_PREFIX_PATHC:\Program Files\QGIS 3.28\apps\qgis python myscript.py每次都要手动设置很麻烦其实最稳妥的方式就是直接用QGIS自带的Python控制台或者用QGIS安装包自带的Python解释器一般叫python-qgis.bat。很多人问我为什么他们的代码在PyCharm里报错、放到QGIS内置控制台却能跑答案就在这里。4.4 图层CRS不匹配导致的“看似错误”如果你的两个图层坐标都显示正常但匹配时结果完全不对先检查它们的CRS。QGIS工程可以实时投影但底层的原始坐标是固定的。官方例子里的数据看似CRS一致实际上换数据后经常出现这种问题。用代码检测两个图层的CRSprint(layer1.crs().authid()) print(layer2.crs().authid())如果不一致可以用QgsCoordinateTransform做转换或者用QgsProject.instance().setCrs(crs)强制统一工程CRS。需要注意的是工程CRS和图层CRS不是一回事setCrs只是改变了项目的显示坐标系实际数据坐标并没有变这在做空间分析时尤其容易造成错觉。4.5 依赖插件或处理算法不存在的坑有些官方示例来自Processing框架它会调用某个算法ID比如qgis:buffer或native:buffer。如果你在独立的Python环境里跑可能报错找不到算法因为Processing框架没有初始化或者对应插件没装。处理这个问题的标准做法是from qgis.core import QgsApplication from qgis.analysis import QgsNativeAlgorithms QgsApplication.setPrefixPath(/path/to/qgis, True) QgsApplication.initQgis() QgsApplication.processingRegistry().addProvider(QgsNativeAlgorithms())没有这段初始化你在独立脚本里跑Processing算法就会一脸懵。还有QGIS 3.x里很多原生算法ID变了比如2.x的qgis:fixeddistancebuffer在3.x里变成了native:buffer查阅官方算法文档时要注意。5. 从官方例子到独立插件我更建议你的训练路线跑通官方脚本只是第一步。我见过太多人学了三个月PyQGIS还是只会一行行敲控制台命令遇到实际问题依然不会组合。我的建议是尽早走一遍“从脚本到插件”的完整流程这会逼你把API用熟。5.1 用Plugin Builder生成插件骨架官方推荐用Plugin Builder 3这个插件生成骨架。生成后你会得到一个标准插件目录里面至少有__init__.py、main.py、tool.py、metadata.txt等文件。然后把你写好的脚本逻辑塞进run()方法或自定义的算法类里。这个过程最重要的不是代码本身而是理解QGIS插件系统的结构元数据如何声明、图标资源如何加载、菜单如何注册。5.2 把脚本封装成QgsProcessingAlgorithm如果你只想做一个小工具直接写成Processing算法反而比写完整插件更省事。定义一个算法类继承QgsProcessingAlgorithm重写name()、displayName()、initAlgorithm()和processAlgorithm()然后通过QgsApplication.processingRegistry().addProvider()注册到工具箱。官方例子里的Processing算法很多但一开始看会有点晕。我的建议是先抄一个最简单的比如输入一个图层、输出一个缓冲图层。代码框架一旦跑通后面的输入输出参数都是往initAlgorithm()里堆。5.3 在官方源码里搜索API用法的技巧当你需要某个API的完整用法时GitHub主仓库的代码搜索几乎是最好的“参考答案”。举个例子我想知道QgsGeometry.difference()到底怎么处理自相交多边形直接在GitHub上搜difference(会看到很多单元测试用例。这些测试代码通常带着前置数据和期望结果比官方文档写得还清楚。我还会专门去看tests/src/python/目录下的测试文件文件名和功能对应比如test_qgsgeometry.py里面是几何算法最全面的测试每个测试方法都是一个小例子。读测试代码有一种“对着答案做习题”的感觉是我学QGIS最有效的方式之一。我个人在后期的项目里已经形成了这样的习惯先想清楚要做什么再去官方测试用例里搜对应的API确认用法后先在Python控制台做最小验证最后才封装进插件。这个流程帮我避开了大量“看起来能用、一跑就崩”的尴尬。最后再分享一个小技巧在QGIS的Python控制台里输入dir(QgsGeometry)可以快速列出该类的所有方法输入help(QgsGeometry.buffer)可以查看这个方法的官方签名和说明。这个“临时查手册”的方式比反复去网页搜索又快又准值得你养成习惯。本文还有配套的精品资源点击获取