☰
QGIS插件开发环境配置全指南:从Python环境到热重载调试
2026/10/2 15:42:25 网站建设 项目流程

第一次动 QGIS 插件的念头,多半不是想搞什么大工程,而是被某个具体需求逼的——图层字段命名规则太乱想一键规范化,或者重复的裁剪合并流程想做成按钮点一下。我也是这么入坑的,当时以为装个 QGIS 就能开工,结果在 QGIS 插件开发环境配置这一步卡了整整两天:Python 版本对不上、IDE 补全全是红线、改一行代码要重启三次 QGIS。所以这篇就把 QGIS 插件开发环境配置这件事从头到尾捋一遍,从 QGIS 安装、自带 Python 的验证、编辑器选型,到插件骨架生成、界面资源编译、断点调试,全部按小白视角拆开讲。不管你是刚学会图层操作的新手,还是写过脚本但没做过插件的 GIS 从业者,跟着走一遍就能把环境跑通,后面写代码会顺很多。

1. 先搞清楚要配的到底是什么环境

1.1 插件的本质:一个被 QGIS 进程加载的 Python 包

很多人一上来就装一堆东西,装完也不知道各自干嘛用,出问题就无从下手。所以在动手之前,先把这件事的本质想明白:QGIS 插件不是独立程序,它是一段 Python 代码,被 QGIS 主程序启动时动态扫描并加载进同一个进程里。这意味着插件的运行环境不是你自己电脑上那个 Python,而是 QGIS 内置的那个 Python。

这一点决定了后面所有配置的方向。你在系统命令行里敲python或者用 Anaconda 的 Python 装了一堆包,QGIS 完全看不见,因为它是用自己目录下的解释器启动的。反过来,你给 QGIS 的 Python 装的包,系统 Python 也用不上,两边是隔离的。我第一次踩的坑就在这儿:用系统 Python 装了个requests,插件里 import 死活报 ModuleNotFoundError,折腾一小时才发现装错地方了。

理解了这个前提,后面遇到"为什么我的编辑器能跑但 QGIS 里不行"、"为什么补全提示没有 QgsVectorLayer"这类问题,思路就很清晰了——先问一句,当前说话的是哪个 Python。

1.2 三件事:能写、能跑、能热改

环境配置听起来很虚,其实拆开只有三件事,每一件对应一个具体的体验指标。

第一件是"能写",也就是编辑器里要有正确的解释器和类型提示,敲Qg能自动弹出QgsVectorLayer、QgsFeature这些类,函数签名能看见,写错了有波浪线。没有补全的插件开发基本靠背 API,效率低到没法忍。

第二件是"能跑",插件被 QGIS 加载后不报错、菜单能点开、功能正常执行。这一步依赖的是 QGIS 自带的 Python 环境是否完整,以及插件的目录结构和metadata.txt是否合规。

第三件是"能热改",改完代码不用关掉 QGIS 重开。QGIS 启动一次动辄十几秒,加上重新加载工程数据,一次重启半分钟起步。如果每改一行都要重启,一天下来光等启动就浪费掉两小时。热重载靠的是 Plugin Reloader 这类插件,这是效率分水岭。

把这三件事分开看,配置过程就从"玄学"变成了三份独立的检查清单,哪一步不对劲就单独排查那一步,不用全盘推翻重来。

1.3 版本对齐这件事,比装多少软件都重要

QGIS 每个大版本系列绑定的 Python 版本是固定的,而且 Windows 独立安装包里带着的那套 Python 是专门裁剪过的,跟你官网下的 CPython 不完全是一回事。近几年的 QGIS 3.x 桌面版,自带的 Python 大致落在 3.9 到 3.12 这个区间,具体是哪个,不要靠记忆,直接用下面的命令看,看你机器上实际输出了什么。

"C:\Program Files\QGIS 3.34.1\bin\python-qgis.bat" -c "import sys; print(sys.version)"

这里的路径要换成你自己安装 QGIS 时的实际目录。python-qgis.bat这个批处理的精髓在于它会先设置好PYTHONHOME、PATH、QGIS_PREFIX_PATH等一串环境变量,然后再启动 Python。你直接双击python.exe是拿不到这套环境的,import qgis.core一定会失败。这个区别值得刻在脑子里。

为什么版本对齐这么要命?因为插件里用到的 PyQt 版本、sip绑定版本、qgis模块的 C++ 扩展 ABI,都和 Python 版本强绑定。你在 IDE 里挂了一个 3.11 的解释器去做静态分析,实际运行的是 3.9,语法糖和类型注解行为不一致,补全出来的签名也可能是错的。轻则误报红线,重则写出在 QGIS 里跑不起来的代码。

我个人的建议很直接:把 QGIS 自带的那个解释器路径,同时用作编辑器的解释器和运行时的解释器,不要试图另起炉灶装个干净的 Python 再往上拼qgis包。Windows 上想单独 pip 装qgis是件很折磨的事,官方也没打算让你这么干。

2. 从零安装:QGIS 和它的 Python 一起到位

2.1 下载安装 QGIS:安装包怎么选

Windows 上装 QGIS 有两条路线,我按使用场景说清楚区别。

第一条是官方独立安装包(MSI),双击一路下一步就完事,装完在开始菜单里能看到 QGIS Desktop。它的好处是干净、版本固定、Python 和 Qt 都是打包好的,不会污染系统里已有的 Python 环境。缺点是它跟系统里其他 GIS 工具(比如 GDAL 命令行)不共享,插件目录也固定在用户 profile 下面。绝大多数插件开发场景,走这条路就够了,我推荐新手从这儿开始。

第二条是 OSGeo4W 安装器,分 Express 和 Advanced 两种模式。Express 装的是和 MSI 差不多的组合;Advanced 可以自己勾选组件,比如同时装 QGIS LTR 和最新版、单独装 GDAL、装 Python 开发头文件等。它自带一个 OSGeo4W Shell,这个终端非常好用,后面编译资源文件、给 QGIS 的 Python 装包,我基本都在这个 Shell 里做,因为环境变量已经配好了。缺点是组件依赖关系复杂,乱勾容易把环境搞坏。

安装路径上有个细节:尽量别用带空格和中文的目录。默认的C:\Program Files\QGIS 3.34.1\里有空格,虽然大多数情况没事,但某些第三方工具在处理路径时会把空格当分隔符,出现莫名其妙的失败。如果你愿意,装到D:\QGIS\3.34.1\这种路径下会省心不少。我早期装在C:\Program Files下,用 pyrcc5 时就遇到过路径没加引号导致参数被截断的问题,改成短路径后再没出现过。

还有一点,LTR 版本和最新版本选哪个。LTR 是长期支持版,插件 API 更稳定,社区插件基本都支持;最新版功能新但接口偶尔变动。做插件开发,建议先对齐你日常用的那个版本,因为你调试的插件最终是给自己或身边同事用的,版本一致才不用来回切换。

2.2 验证自带的 Python 能不能 import qgis

装完之后别急着装编辑器,先花两分钟验证 QGIS 的 Python 环境是通的。打开 QGIS 桌面,菜单里找到"插件",点"Python 控制台",在弹出的窗口里敲两行:

from qgis.core import QgsProject, Qgis print(Qgis.QGIS_VERSION) print(QgsProject.instance().fileName())

如果版本号打印出来了,说明 QGIS 内部的 Python 环境完好。这一步的价值在于建立一个"基准线"——后面所有外部环境的配置,都是为了对齐这个基准。如果这里就报错,那问题出在安装本身,先把安装修好再说,别往下走。

接着在外部终端里验证一次。打开开始菜单里的 OSGeo4W Shell(或者直接用完整路径调python-qgis.bat),执行同样的代码。这一步是给后续的 pip 装包、脚本执行做准备。

"C:\Program Files\QGIS 3.34.1\bin\python-qgis.bat" -c "from qgis.core import Qgis; print(Qgis.QGIS_VERSION)"

两条路都能打印版本号,说明内外一致,环境地基就打好了。如果只有 QGIS 内部能跑、外部不行,通常是环境变量的问题,检查python-qgis.bat是不是被你改动过,或者路径里有没有写错版本号。

2.3 编辑器的取舍:VSCode、PyCharm 还是别的

编辑器这块没有标准答案,但我可以按场景给出推荐,省得你在选型上纠结太久。

VSCode 的优势是轻、启动快、远程开发体验好,插件生态丰富,配置靠settings.json和launch.json两个文件搞定,配合 QGIS 做附加调试非常顺。缺点是默认对大型 Python 项目的类型推断一般,需要手动指定额外的分析路径。如果你平时已经用 VSCode 写 Python、配过 Python 环境,那继续用它做 QGIS 插件开发是最省事的。

PyCharm 的优势是代码导航和重构能力强,对 Python 包的索引更彻底,跳到 QGIS 源码定义体验更好。社区版免费,功能足够。缺点是索引慢、内存占用高,附加到进程调试在社区版里支持有限(专业版才比较完整)。如果你的机器配置好、习惯 JetBrains 那一套,PyCharm 也很合适。

至于记事本、Sublime、Vim 这些,写插件不是不行,但没有跳转和补全,效率会掉一大截,尤其是你还不熟悉 QGIS API 的时候。我建议前期先用带补全的编辑器把 API 摸熟,后面再谈什么轻量。

VSCode 里有两个配置项值得提前记下来:一个是解释器路径指向 QGIS 自带的python.exe,另一个是python.analysis.extraPaths指向 QGIS 的 Python 包目录。具体怎么写,4.1 节会给出完整配置。

2.4 插件目录在哪里:三套路径和一个开发专用目录

QGIS 找插件是扫目录的,路径不对插件永远不出现。这个目录分两种情况:一种是你在插件管理器里在线安装的插件,会放到用户 profile 下;另一种是你自己开发、想被加载的插件,同样要放到能被扫描到的地方。

Windows 下的用户插件目录:

%APPDATA%\QGIS\QGIS3\profiles\default\python\plugins

把%APPDATA%展开大概是C:\Users\你的用户名\AppData\Roaming。如果你建了自定义 profile,default会换成对应的 profile 名。

QGIS 安装目录下的系统插件目录也存在,但不建议把开发中的插件放那儿,因为升级 QGIS 时整个目录可能被覆盖,你的代码就丢了。用用户目录,稳妥。

主目录下的路径有:

系统插件目录
Windows%APPDATA%\QGIS\QGIS3\profiles\default\python\plugins
Linux~/.local/share/QGIS/QGIS3/profiles/default/python/plugins
macOS~/Library/Application Support/QGIS/QGIS3/profiles/default/python/plugins

Linux 和 macOS 用户照上表找即可。我个人在 Linux 上开发时习惯把这个目录软链接到代码仓库里,这样 Git 管理的是同一个目录,改完直接热重载,不需要拷贝来拷贝去。

还有一个更省事的技巧:QGIS 支持在设置里额外添加插件搜索路径,但入口比较隐蔽,而且要改配置文件,不如直接用软链接。Windows 上创建目录软链接需要管理员权限,命令是mklink /D,Linux 和 macOS 用ln -s。这个做法我用了很久,好处是代码仓库和运行目录物理隔离又逻辑统一,重装 QGIS 都不影响代码。

3. 生成第一个插件骨架

3.1 装上两个必备小插件:Plugin Builder 3 与 Plugin Reloader

不要从空文件开始手写插件结构,QGIS 社区早就把模板工具做好了。打开 QGIS 的插件管理器,搜索并安装两个插件。

一个是Plugin Builder 3,它是插件生成向导,负责按你的回答生成一套标准目录结构和样板代码,包括__init__.py、主逻辑文件、对话框文件、metadata.txt、resources.qrc、图标和帮助文档目录。这套结构是跟着 QGIS 官方约定走的,比你自己拍脑袋设计要靠谱得多。

另一个是Plugin Reloader,它负责热重载。装上之后在工具栏会多一个小图标,点开选插件名,回车,插件就重新加载了,不需要重启 QGIS。

这里有个新手常踩的坑:Plugin Reloader 在很多版本里被标记为实验性插件,插件管理器默认不显示。你要先在插件管理器的"设置"页里勾上"显示实验性插件",再回到"全部"页搜索,才能看到它。我第一次找的时候翻了半天没找着,就是这个原因。

装完之后建议重启一次 QGIS,确保两个插件都正常注册。重启后 Plugin Reloader 的图标如果没出现在工具栏,去"插件"菜单里找,或者检查"视图 -> 工具栏"里有没有把它勾上。

3.2 生成器会问你什么,怎么答

Plugin Builder 3 的向导是一连串问答,我把关键项和填写思路列一下,避免你随手一填后面返工。

Class name(类名):用大驼峰,比如FieldCleaner。这个类是整个插件的入口,QGIS 通过它实例化插件。

Plugin name(插件显示名):出现在插件管理器里的名字,可以用中文,也可以中英混排。但要注意编码问题,早期版本中文名在某些系统上会显示成方块,稳妥起见建议用英文,或英文加简短中文后缀。

Module name(模块名):也就是生成的文件夹名,用小写加下划线,比如field_cleaner。这个名字一旦定了就别改,因为它在多个文件里被引用,改起来牵一发动全身。

Description(描述):简短说明插件干什么,会显示在插件列表的说明栏里。

Version(版本):从0.1开始,正式发布再往上加。

Minimum QGIS version(最低 QGIS 版本):填你当前开发的版本号,比如3.28。填太高会导致旧版本用户装不上,填太低可能用到不存在的 API。

Author、Email:会写进metadata.txt,发布插件时会被公开,注意别填私人邮箱如果介意的话。

后面几步会问你要不要生成工具栏按钮、要不要生成菜单项、要不要带对话框、要不要带帮助文件。建议第一次全部勾上,因为生成器给的样板代码是最佳实践,回头你不需要的再删,比从零加要容易。

生成位置选择"用户插件目录"。点确定后,去%APPDATA%\QGIS\QGIS3\profiles\default\python\plugins下面看,应该能看到你刚生成的文件夹。

3.3 metadata.txt 逐字段拆解

生成完打开metadata.txt,这是 QGIS 识别插件的身份证。字段看着多,真正影响加载的就那么几个,我按重要程度分组说。

[general] name=Field Cleaner qgisMinimumVersion=3.28 qgisMaximumVersion= description=批量规范化图层字段名称 version=0.1 author=Your Name email=you@example.com about=按照规则批量重命名字段,支持前缀移除、大小写转换、非法字符替换。 tracker= repository= tags=字段,重命名,批量 experimental=True deprecated=False

第一组是加载必需:name、qgisMinimumVersion、description、version、author。缺任何一个,插件管理器都可能直接忽略你的插件,而且不给明确报错,这是最坑的地方——你以为是代码问题,其实是元数据不完整。

第二组是兼容控制:qgisMaximumVersion留空表示不限最高版本,experimental=True表示这是实验性插件,插件管理器里默认不显示,需要用户勾选"显示实验性插件"。开发阶段设 True 没问题,正式发布前记得改掉,否则用户搜不到。

第三组是附加信息:about会显示在插件详情页,支持多行;tracker和repository指向你托管代码的地方;tags用于关键词搜索。这几个不影响加载,但影响别人能不能找到和用起来。

提示:metadata.txt的编码必须是 UTF-8,且不要带 BOM。Windows 上用记事本保存很容易带上 BOM,导致 QGIS 解析第一行的[general]失败,插件直接消失。用 VSCode 或 Notepad++ 保存,确认右下角显示的是 UTF-8 而不是"UTF-8 with BOM"。

我吃过一次这个亏:改完描述保存,插件突然从列表里没了。查了半天代码,最后用十六进制编辑器看文件头才发现多了三个字节的 BOM。这个坑现在每次改metadata.txt我都会下意识看一眼编码。

3.4init.py 与主类的加载链路

插件目录下最容易被忽视的是__init__.py,它短得让人以为没用,实际上它是 QGIS 找到你插件的入口。

生成器给出的典型内容是:

def classFactory(iface): from .field_cleaner import FieldCleaner return FieldCleaner(iface)

QGIS 加载插件时的流程是这样的:先按目录名扫描到你的插件文件夹,然后 import 这个包,也就是执行__init__.py,接着调用包里的classFactory函数,把iface对象传进去,拿到你返回的插件实例,再依次调用实例的initGui()和unload()。

这里有几个实用的推论。第一,classFactory必须存在且名字完全一致,写成class_factory或createPlugin都不行。第二,真正的业务代码不要写在__init__.py里,因为这个文件在插件被扫描时就会被执行,如果里面 import 了重量级库或者抛异常,会导致整个插件加载失败,而错误信息往往被吞掉,你会看到插件直接不出现。第三,from .field_cleaner import FieldCleaner这句里的模块名必须和实际文件名一致,改了文件名忘了改这里,就是经典的"插件不出现"。

iface这个对象值得单独说一句。它是 QGIS 给插件的操作把手,通过它你能拿到主窗口、图层树、地图画布、消息栏、状态栏。常用的有iface.activeLayer()取当前图层、iface.mapCanvas()取画布、iface.messageBar()弹提示。插件初始化时把这个对象存下来,后面所有交互都靠它。我在早期版本里习惯用iface.legendInterface(),后来这个接口被弃用,换成图层树的 API,代码得跟着改。所以尽量用官方推荐的新接口,旧的能用但不保证长久。

4. 打通 IDE:补全、跳转与断点调试

4.1 把 QGIS 的 site-packages 挂进 IDE

这一步解决"能写"的问题。以 VSCode 为例,工程根目录下建.vscode/settings.json:

{ "python.defaultInterpreterPath": "C:/Program Files/QGIS 3.34.1/apps/Python39/python.exe", "python.analysis.extraPaths": [ "C:/Program Files/QGIS 3.34.1/apps/qgis-ltr/python", "C:/Program Files/QGIS 3.34.1/apps/qgis-ltr/python/plugins", "C:/Program Files/QGIS 3.34.1/apps/Python39/Lib/site-packages" ], "python.analysis.typeCheckingMode": "basic" }

这里的路径要按你的实际安装版本调整。apps\qgis-ltr\python下面就是qgis包本体,apps\Python39\Lib\site-packages下面有 PyQt5 相关的包,python/plugins里则是 QGIS 内置的 Python 插件,看看官方插件怎么写是很好的学习材料。

这么配之后,打开插件代码,敲Qgs应该能弹出补全列表,鼠标悬停能看到函数签名和文档。如果没生效,先确认 Pylance 扩展装了没,再确认extraPaths里的路径真实存在——这里最容易错的是版本号文件夹名,比如你装的是 3.28 却写了 3.34.1,路径不存在,Pylance 会静默忽略,没有任何报错提示。

PyCharm 的思路一样,在"项目结构"里把上述目录添加为"内容根"或"源根",或者用Settings -> Project -> Python Interpreter -> Show All -> 添加路径。PyCharm 的索引更彻底,配置正确后跳转体验更好,但首次索引大目录会花几分钟。

注意:IDE 里的解释器只负责静态分析,插件实际执行时用的仍然是 QGIS 启动时加载的那套环境。所以不要因为"IDE 里不报错了"就以为万事大吉,两边的对齐是为了减少误报,不是替代运行时验证。

4.2 VSCode 里用 debugpy 附加到 QGIS

"能跑"之后要想清楚怎么调试。QGIS 插件跑在 QGIS 进程里,你不能像普通脚本那样按 F5 直接跑,得用附加(attach)的方式。原理是在 QGIS 进程里起一个调试服务端,VSCode 作为客户端连上去。

第一步,给 QGIS 的 Python 装 debugpy:

python-qgis.bat -m pip install debugpy

注意必须用python-qgis.bat,这样包才会装进 QGIS 自己的环境里。用系统 pip 装完,QGIS 是看不到的。

第二步,在插件代码里加一段启动调试服务的代码,放在initGui或者你按钮的回调里:

def run(self): try: import debugpy debugpy.listen(("127.0.0.1", 5678)) debugpy.wait_for_client() debugpy.breakpoint() except ImportError: pass

debugpy.breakpoint()那行是给程序设一个初始断点,让你能在连上之后先停下来,再从断点处往下单步。调试完记得把这段删掉或者注释掉,否则每次运行都会卡住等连接。

第三步,VSCode 侧建.vscode/launch.json:

{ "version": "0.2.0", "configurations": [ { "name": "Attach to QGIS", "type": "debugpy", "request": "attach", "connect": { "host": "127.0.0.1", "port": 5678 }, "justMyCode": false, "pathMappings": [] } ] }

justMyCode设成 false 很关键,这样你才能单步进入 QGIS 自己的代码,有时候排查问题需要看框架层是怎么调用你的插件的。

操作顺序是:先在 VSCode 里点"运行和调试",选 Attach to QGIS,此时它会等待连接;然后回到 QGIS,触发你插件里那段debugpy.listen的代码路径,两边就接上了。这个先后顺序别搞反,先启动 QGIS 再点附加也行,但得确保listen还没执行过或者已经重新触发了。

端口占用是常见问题。5678被别的进程占了,listen会抛异常。换一个不常用的端口,比如5680,两边同步改。

4.3 PyCharm 的 Attach to Process 思路

PyCharm 专业版对远程调试支持比较完善,社区版可以用pydevd的手动方式。整体思路和 debugpy 类似:在 QGIS 的 Python 里装上pydevd-pycharm,代码里引入并连接到 PyCharm 的调试服务端,然后在 PyCharm 里启动 Debug Server。

社区版用户如果觉着折腾,我觉得没必要死磕调试器。插件开发的调试场景 80% 靠日志就够了,尤其是涉及 QGIS 内部数据流转的问题,断点停下来看到的对象状态和日志打出来的信息差别不大,而日志不阻塞界面,对 QGIS 这种 GUI 程序更友好。真需要断点的时候,临时切换到 VSCode 那条链路就行,两个 IDE 完全可以混着用,不必强求统一。

4.4 日志打印比断点更好用的场景

QGIS 有内置的消息日志,在"视图 -> 面板 -> 日志消息"里可以看到。用好它比自己往控制台 print 强得多。

from qgis.core import QgsMessageLog, Qgis def log(msg): QgsMessageLog.logMessage(str(msg), "FieldCleaner", level=Qgis.Info)

QgsMessageLog的好处是:消息按标签分类,可以在日志面板里按插件名过滤;有级别区分(Info、Warning、Critical),排查时能快速定位严重问题;窗口关了消息还在,不像 print 会被控制台滚动冲掉。

我的一贯做法是在插件里封一个log函数,然后在关键分支都打点。比如读取图层后打一下要素数量,处理完打一下耗时,异常分支打完整堆栈。这样用户反馈"点了没反应"的时候,让对方打开日志面板截个图,基本一眼能定位到是哪一步出的问题。

还有个小技巧,QgsMessageLog.logMessage的第一个参数直接用repr(obj)而不是str(obj),能看出对象类型和内部结构,排查"传进来的到底是不是 QgsLayer"这类问题时特别好用。

5. 界面与资源文件:Qt Designer 加 pyrcc5

5.1 用 Qt Designer 画对话框

插件生成器已经帮你生成了一个基础对话框的.ui文件,但如果你要加控件,手写 XML 太痛苦,用 Qt Designer 拖拽更快。QGIS 安装目录的apps\Qt5\bin下通常有designer.exe,找不到的话,在开始菜单打开 OSGeo4W Shell,直接输入designer回车,一般也能启动。

Designer 的用法很直白:左边是控件面板,把按钮、下拉框、表格拖到中间画布上,右侧属性栏改对象名(objectName)。对象名这一步一定要认真取,因为代码里就是靠这个名字找控件的。比如按钮叫btnRun,输入框叫comboLayer,后面写绑定时直接self.dlg.btnRun,一眼能看出是干什么的。我见过有人全用默认的pushButton_1、pushButton_2,一周后自己都分不清哪个是哪个。

布局管理是新手最容易忽略的部分。拖控件的时候,Designer 会自动套用某种布局,但如果你只是随手一放,窗口一拉伸控件就乱跑。正确做法是先拖一个容器(比如QWidget或QVBoxLayout),再往里放控件,最后右键选"布局 -> 垂直布局"让容器自适应。这样缩放窗口时控件会跟着变,不至于出现按钮被挤没的情况。

保存时会得到一个.ui文件,本质是 XML,可以打开看看,但不建议手工改。

5.2 .ui 的两种用法,选哪个

第一种是运行时加载,也是 Plugin Builder 生成的默认写法:

import os from qgis.PyQt import uic FORM_CLASS, _ = uic.loadUiType( os.path.join(os.path.dirname(__file__), "field_cleaner_dialog_base.ui") ) class FieldCleanerDialog(QtWidgets.QDialog, FORM_CLASS): def __init__(self, parent=None): super().__init__(parent) self.setupUi(self)

好处是不用编译,改完.ui保存,热重载插件就能看到新界面,迭代快。坏处是每次加载都要解析 XML,理论上有一点点开销,实际可以忽略。

第二种是先编译成.py:

pyuic5 -o field_cleaner_dialog_base.py field_cleaner_dialog_base.ui

生成的是 Python 代码,能直接被静态分析工具索引,补全更准。缺点是每次改界面都得重新编译一次,容易忘。

我的选择是开发阶段用运行时加载,发布前编译成 py。开发中改界面频繁,运行时加载省事;发布时不希望用户环境里因为路径问题找不到.ui,编译成 py 打包进去更稳。另外编译成 py 后 IDE 能识别控件属性,写self.dlg.btnRun时不会报红,这也是我后期偏好编译方式的原因。

5.3 resources.qrc 与 pyrcc5

插件用到图标、图片这类资源时,Qt 的做法是把它们登记在.qrc文件里,编译成 Python 模块后再导入,这样资源被打包进代码,不会因为路径变动丢图。

.qrc文件是 XML:

<RCC> <qresource prefix="/plugins/field_cleaner"> <file>icon.png</file> </qresource> </RCC>

编译命令:

pyrcc5 -o resources.py resources.qrc

Windows 上pyrcc5.exe通常在 QGIS 安装目录的bin下,或者apps\Python39\Scripts下。用python-qgis.bat调用也行:

python-qgis.bat -m PyQt5.pyrcc_main -o resources.py resources.qrc

编译完会生成resources.py,在代码里import resources就能通过:/plugins/field_cleaner/icon.png这样的路径引用图标了。

注意:每次改了.qrc或者换了图片文件,都必须重新跑一次 pyrcc5。很多人改了图标发现界面没变,就是忘了这一步。另外生成的resources.py建议一起提交到代码仓库,这样别人拉下来不用自己编译。

这里还有个路径问题:如果resources.qrc里引用的是相对路径,pyrcc5 执行时的当前工作目录会影响结果。稳妥做法是cd到.qrc所在目录再执行,或者写个批处理把路径固定下来。我习惯在插件目录里放一个build_resources.bat,内容就一行pyrcc5 -o resources.py resources.qrc,以后双击就行,不用每次敲命令。

5.4 一段可复用的事件绑定写法

界面和逻辑要连起来,靠的是信号槽。下面是插件主类里一段比较完整的写法:

def initGui(self): self.action = QAction(QIcon(":/plugins/field_cleaner/icon.png"), "字段清理", self.iface.mainWindow()) self.action.triggered.connect(self.run) self.iface.addToolBarIcon(self.action) self.iface.addPluginToMenu("字段清理", self.action) def run(self): if self.dlg is None: self.dlg = FieldCleanerDialog(self.iface.mainWindow()) self.dlg.btnRun.clicked.connect(self.on_run_clicked) self.dlg.comboLayer.currentIndexChanged.connect(self.on_layer_changed) self.refresh_layers() self.dlg.show() self.dlg.exec_() def unload(self): self.iface.removePluginMenu("字段清理", self.action) self.iface.removeToolBarIcon(self.action)

几个细节值得展开。self.dlg建议做成懒加载并复用,而不是每次点按钮都 new 一个对话框,因为反复创建销毁窗口在 QGIS 里容易留下悬挂引用,用久了会出诡异问题。exec_()是模态显示,show()是非模态,具体用哪个看需求,模态会锁住主窗口,非模态则可以和地图交互。

unload()里一定要把加进去的菜单项和工具栏图标删掉,否则插件被禁用后图标还挂在界面上,点了就报错。这是插件质量的一个明显分水岭,很多新手插件都有这个问题。

还有选图层的方式,QgsMapLayerComboBox是个现成的好控件,可以直接在 Designer 里用,或者代码里设置过滤器只显示矢量图层:

from qgis.gui import QgsMapLayerComboBox from qgis.core import QgsMapLayerProxyModel self.dlg.comboLayer.setFilters(QgsMapLayerProxyModel.VectorLayer)

这样下拉框里只会出现矢量图层,用户不会选错。这种小细节对插件易用性提升很大,实现成本又低。

6. 常见故障与排查速查

6.1 插件不出现在列表里

这是最高频的问题,原因按出现概率排序。

第一,metadata.txt有问题。字段缺失、编码带 BOM、[general]段头写错,都会导致 QGIS 静默跳过。排查办法是把metadata.txt和生成器刚生成时的版本逐行对比。

第二,目录层级错了。插件目录必须是plugins/你的插件名/,里面直接放__init__.py。如果你解压时多套了一层,变成plugins/你的插件名/你的插件名/__init__.py,QGIS 就找不到。

第三,classFactory名字拼错或者 import 路径不对。这种情况可以看日志面板,通常在启动时会有一条加载失败的记录。

第四,插件被标记为实验性且没开启显示。去插件管理器设置里勾一下。

排查顺序建议是:先看目录结构,再看metadata.txt,最后看日志。这个顺序覆盖了 90% 的情况。

6.2 中文乱码、路径空格与反斜杠

中文乱码在插件开发里有三个来源。一是文件编码不是 UTF-8,尤其metadata.txt和.py文件。二是运行时的默认编码,某些环境下open()不指定编码会按系统默认走。三是界面控件显示,老版本 PyQt 对中文支持有历史包袱。

我现在的习惯是:所有文本文件统一 UTF-8 无 BOM;所有open()都显式写encoding="utf-8";界面上涉及路径显示的地方用QDir.toNativeSeparators()做一次转换,避免 Windows 上出现C:/a\b/c这种混杂。

路径这块,Python 里建议一律用正斜杠或者os.path.join,Windows 也认正斜杠。反斜杠在字符串里是转义符,"C:\new"里的\n会被当成换行,这是经典事故。用原始字符串r"C:\new"也行,但我更推荐正斜杠,省得记。

路径里有空格时,命令行调用一定要加引号。pyrcc5 -o resources.py resources.qrc在插件目录里执行没问题,但如果写成完整路径又不加引号,C:\Program Files就会被拆开,报"找不到文件"。

6.3 改了代码没生效

三种可能,按顺序试。

一是没热重载。装了 Plugin Reloader 就点一下;没装的话,在插件管理器里取消勾选再勾选,也能触发重新加载。最保险当然是重启 QGIS,但没必要每次都用这招。

二是 Python 的.pyc缓存。正常情况 QGIS 会按修改时间判断是否需要重新编译,但偶尔会失效。删掉插件目录下的__pycache__文件夹再来一次。

三是模块级状态残留。如果插件在模块顶层缓存了数据,重载可能不会清掉。这种情况少见,但真遇到了就得重启。所以我一贯主张不要在模块顶层放可变状态,需要的状态都挂在插件实例上,重载时自然重建。

6.4 打包发布前的检查清单

写完了想分享给别人,或者提交到官方插件仓库,这几项必须过一遍。

检查项要求
metadata.txt字段完整,编码 UTF-8 无 BOM,experimental改 False
version已递增,不要和上一版重复
__init__.pyclassFactory正确,没有业务逻辑
图标资源已重新编译resources.py,图标路径正确
unload()菜单项、工具栏图标都被正确移除
异常处理关键操作有 try 包裹,异常写进日志
长任务耗时操作放后台线程或QgsTask,不阻塞界面
依赖声明第三方库有说明或降级为可选
测试在目标 QGIS 版本上全新环境验证过

关于第三方依赖这一项要单独强调。官方插件仓库审核时不接受未声明的第三方依赖,如果你的插件依赖某个 pip 包,得在文档里写清楚安装方式,或者把功能改成可选的、缺少时给出友好提示。我见过有人插件里直接import pandas,在自己机器上跑得好好的,用户装完一运行就崩,体验很差。

长任务那一项也值得说。QGIS 是单线程 GUI 程序,你在按钮回调里跑一个几万要素的循环,界面会直接卡死,用户以为崩溃了。正确做法是用QgsTask把活丢到后台:

from qgis.core import QgsTask, QgsApplication class CleanTask(QgsTask): def run(self): # 耗时处理,这里不要碰 GUI return True def finished(self, result): # 回到主线程,这里才能更新界面 pass task = CleanTask("字段清理", QgsTask.CanCancel) QgsApplication.taskManager().addTask(task)

run()里绝对不能操作界面控件,那是另一个线程,会直接让 QGIS 崩掉。所有界面更新都要放到finished()里做。这个坑我踩过一次,程序崩溃还没报错,查了很久才想到是线程问题。

我自己做插件的这些年,最深的体会是环境配置这事儿前期多花两小时,后期能省下几十小时。尤其热重载和日志这两样,看起来不起眼,实际决定了你一天能迭代多少个版本。还有一点,别一上来就追求 IDE 补全完美、调试链路齐全,先把插件骨架跑起来、能热重载、能看日志,这三样齐了就能开始写功能,剩下的边写边补。至于具体写什么功能,从你每天重复最多的那步操作入手,做出来的插件才真有人用。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询