1. 先说清楚:QGIS 插件开发为什么不能当成普通 Python 项目来做
1.1 插件不是独立程序,它是寄生在 QGIS 进程里的一段代码
这是所有新手最容易走偏的地方。你写一个普通的 Python 脚本,python main.py就能跑,进程是你自己启动的,依赖是你自己 pip 装的。QGIS 插件完全不是这个逻辑:插件是一段被 QGIS 主程序在运行时动态加载的 Python 代码,它运行在 QGIS 已经启动好的那个 Python 解释器进程里,用的也是 QGIS 自带的 PyQt5 版本、自带的 GDAL 和 PROJ 库、自带的那套 Python site-packages。
举个生活化的类比:普通 Python 项目像是你自己开一家店,租店面、进货、装修全由你决定;QGIS 插件像是你在别人的商场里租一个柜台,水电、消防、客流全是商场给的,你只能按商场的规矩来。所以你在编辑器里能不能import qgis.core成功,并不是看你 pip 装了多少东西,而是看编辑器有没有用上 QGIS 那套 Python 和那套环境变量。
理解了这一层,后面所有的配置动作就有了统一的判断标准:凡是能让编辑器"借用" QGIS 自带 Python 环境的做法都是对的,凡是试图用系统 Python 硬装 PyQGIS 的做法基本都是弯路。PyQGIS 在 pip 上没有官方可分发的独立包,强行安装会遇到 SIP 绑定、Qt 版本、GDAL 编译链一堆问题,投入产出比极低。
所以整套环境配置的核心目标只有三个:让编辑器能读到 QGIS 的 Python 解释器、让import qgis.core能成功、让改完的代码能快速在 QGIS 里看到效果。剩下的都是围绕这三件事的细节。
1.2 三种环境配置路线,各自适合什么样的人
实际工作中我见过并试过三条路线,各有取舍,先把结论摆出来,你对号入座。
| 路线 | 做法 | 优点 | 缺点 | 适合谁 |
|---|---|---|---|---|
| 路线 A:QGIS 自带控制台 | 直接在 QGIS 里打开 Python 控制台写代码 | 零配置,立刻能跑 | 没有补全、没有断点、代码管理困难 | 只做实验、验证 API |
| 路线 B:OSGeo4W Shell 启动编辑器 | 从 QGIS 附带的命令行环境里启动 VS Code / PyCharm | 环境变量完整继承,import qgis.core一次通过 | 每次要从 Shell 里启动 | 绝大多数开发场景 |
| 路线 C:手工配置解释器与 env | 在编辑器里手动指定解释器、PYTHONPATH、PATH | 启动方式自由,能做调试配置 | 路径多、版本升级易失效 | 想深度整合调试的人 |
路线 B 是我最推荐的起点。原因很直接:QGIS 附带的o4w_env.bat会把PATH、PYTHONPATH、GDAL_DATA、PROJ_LIB、QT_PLUGIN_PATH这些变量一次性设好,你在那个环境里启动的编辑器天然就继承了全部配置,省掉大量试错时间。等你对这套东西熟了,再去做路线 C 的精细配置。
路线 C 不是不必要,而是"进阶选项"。它的价值在于调试:只有环境变量在编辑器里被正确声明,断点调试才可能跑通。而环境变量的具体内容,你完全可以从路线 B 的环境里抄出来。
1.3 配错了会长什么样:三种典型报错先认清
提前认识报错,比事后瞎猜快得多。以下三种几乎每个新手都会遇到:
ModuleNotFoundError: No module named 'qgis'。这说明解释器根本不知道 PyQGIS 在哪。要么解释器指错了(用了系统 Python),要么PYTHONPATH里没加 QGIS 的 python 目录。ImportError: DLL load failed while importing core: 找不到指定的模块。这个是 Windows 上最经典的坑,说明PYTHONPATH对了但PATH里缺 QGIS 的bin目录,导致 Qt 的 DLL 加载不到。加路径没用,得加对顺序。- 插件出现在列表里但打勾后无反应,或者勾完立刻掉回未勾选状态。这通常是插件本身的代码问题:
__init__.py里的classFactory写错了、metadata.txt字段缺失、或者运行时报了异常被 QGIS 静默吞掉。
第三种最坑,因为 QGIS 默认不会把插件的加载异常直接弹出来。排查手段后面第 5 章会详细讲。现在你只需要记住:环境问题报错在 import,代码问题报错在加载。分清这两类,排查方向就不会跑偏。
2. 装 QGIS 与装编辑器:路径这一步决定了后面顺不顺
2.1 选 LTR 还是最新版,安装目录要不要改
QGIS 的 Windows 安装包分两类:LTR(长期支持版)和 Latest(最新版)。做插件开发请优先选LTR。理由不是保守,而是插件兼容性:LTR 版本的 API 更稳定,你写的一次代码能覆盖的人群更广;最新版可能引入了还没被广泛测试的 API 变化,早期的插件会在这些版本上直接报错。
安装目录这一项,很多人图省事直接点默认的C:\Program Files\QGIS 3.xx.x\,这本身没问题,但你要接受一个后果:这个路径带空格,还带版本号。带空格会导致后面在命令行、脚本、配置里引用时必须加引号,稍不留神就是一堆诡异错误。带版本号意味着你以后升级 QGIS 时所有配置里的路径都要跟着改。
我的做法是:不改默认路径,但把版本号单独记下来,并且所有配置里引用路径都老老实实加引号。改到C:\QGIS\这种短路径虽然看着干净,但和官方文档、社区教程里的路径对不上,反而增加沟通成本。
安装组件这一步也要留意。自定义安装界面里有一堆可选组件,做插件开发有两个别漏:Python 相关的组件(PyQt5、Python 解释器本体)以及 Qt 的工具链。如果你用的是网络安装器(OSGeo4W 那套),确保勾选qgis-ltr-full这类完整包,别只装了残缺的精简版,否则后面pyrcc5找不到。
装完之后,第一件事是打开 QGIS,从菜单里找到 Python 控制台(一般在"插件"菜单下,或者用工具栏那个 Python 图标)。能打开、能敲一行print("ok")并且窗口里出现ok,说明基础环境没问题。
2.2 编辑器选型:VS Code、PyCharm,还是先用自带控制台
编辑器这件事不用纠结太久,三个选项的实际差别在于"你要花多少时间在配置上"。
QGIS 自带的 Python 控制台是零配置的,打开就能跑,iface、QgsProject这些对象都是现成的。它的定位是"探路工具":你写插件时不确定某个 API 怎么调用,先在控制台里敲两行验证,验证通过再搬进代码。但别指望用它写正式项目——没有代码补全、没有版本管理、没有断点,写超过两百行的插件就是自虐。
VS Code 是大多数人的选择,免费、轻量、插件生态好。要注意的是 Python 扩展和 Pylance 这两块是必须装的(Python 扩展提供解释器管理和调试,Pylance 提供补全和类型提示)。装完这两个,PyQGIS 的补全才能通过extraPaths生效。
PyCharm 社区版也能用,它对大型项目的重构和跳转更顺手,但解释器和路径要在设置里手工配,配置条目比 VS Code 多。PyCharm 专业版有远程调试和更强的调试器,不过是付费的。
我的建议:先用 QGIS 自带控制台把 API 摸熟,然后直接上 VS Code。别在编辑器选型上反复横跳,时间和精力都应该花在写插件逻辑上。三大平台的插件目录差异也顺便记一下,后面生成插件时会反复用到:
- Windows:
C:\Users\<用户名>\AppData\Roaming\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
最省事的确认方式是在 QGIS 的 Python 控制台里跑这两行:
from qgis.core import QgsApplication print(QgsApplication.qgisSettingsDirPath()) import qgis.utils print(qgis.utils.plugin_paths())第一行输出你的活动 profile 目录,第二行输出插件搜索路径列表。不要凭记忆写路径,永远以这两行的输出为准。版本不同、用户目录带中文、装了多份 QGIS,这些情况都会让"我记得是那个路径"变成错误。
2.3 profile 目录、插件目录、缓存目录,别混为一谈
这三个目录很容易被当成一回事,实际职责完全不同。
profile 目录是整个用户配置的根,里面装着界面布局、最近打开的项目、坐标系设置、已启用插件的清单,以及python/plugins这个子目录。你在 QGIS 里点"设置 → 用户配置 → 打开活动配置文件夹",打开的就是它。
插件目录是 profile 目录下的python/plugins,所有第三方插件(包括你自己开发的)都放在这儿。QGIS 启动时会扫描这个目录,逐个读metadata.txt判断能不能加载。
缓存目录则是python/plugins同级的cache之类的位置,一些插件会把临时数据写在这里。清缓存不等于删插件,这一点要分清楚。
还有一个隐藏的坑:QGIS 会记住"哪些插件被启用过"。这个状态存在 profile 目录下的一个配置文件里。你手动删了插件文件夹但没重启 QGIS,插件列表里可能还残留着条目;反过来,你新拷进去一个插件但没重启,它可能不出现。改插件文件后要么用 Plugin Reloader(第 4 章讲),要么老老实实重启 QGIS。
顺带提一下:如果你不想污染默认 profile,可以在 QGIS 启动时用--profile参数指定一个新 profile,这样插件、设置都是独立的。做实验性开发时很有用。
3. 让编辑器真正"看见" PyQGIS:解释器与路径配置全流程
3.1 找到 QGIS 自带的 Python 与那几个 bat 脚本
这一步是整个配置的基石,先把你机器上的文件找齐。打开 QGIS 的安装目录,进入apps子目录,里面应该能看到一个类似Python39、Python312这样的文件夹(版本号取决于你装的 QGIS 版本,3.28 和 3.34 常见的是 Python 3.9,3.40 之后常见的是 3.12,以你机器上实际存在的为准,别照抄)。
里面那个python.exe就是 QGIS 自带的解释器。注意:它和系统的 Python 是两套东西,互不干扰,你也别尝试用它去装系统的包。
再看安装目录下的bin文件夹,里面有几个批处理脚本很关键:
python-qgis.bat和python-qgis-ltr.bat:启动一个已经加载好 QGIS 环境的 Python 交互式环境o4w_env.bat:只设置环境变量,不启动 Pythonqgis-ltr-bin.exe:QGIS 主程序本体
o4w_env.bat是重中之重。在普通命令提示符里执行它,当前会话就会获得 QGIS 的全部环境变量。你可以这样验证:
call "C:\Program Files\QGIS 3.34.0\bin\o4w_env.bat" python -c "import qgis.core; print(qgis.core.Qgis.QGIS_VERSION)"如果这一行能打印出版本号,说明 QGIS 自带的环境是完整可用的,问题就只剩"怎么让编辑器也用上这个环境"。
注意:
o4w_env.bat只在当前命令行会话生效,关掉窗口就没了。不要指望把它加到系统环境变量里一劳永逸,那样会污染系统 Python 的正常使用。
3.2 VS Code:settings.json、终端 profile 与解释器三件套
VS Code 的配置分三层,层与层之间容易互相打断,按顺序来。
第一层是终端 profile。在settings.json里加一段,让 VS Code 的集成终端可以选择性地以 QGIS 环境启动:
{ "terminal.integrated.profiles.windows": { "QGIS Env": { "path": "C:\\Windows\\System32\\cmd.exe", "args": ["/k", "\"C:\\Program Files\\QGIS 3.34.0\\bin\\o4w_env.bat\""] } }, "terminal.integrated.defaultProfile.windows": "QGIS Env" }配完之后,VS Code 里新开的终端会自动带上 QGIS 的环境变量。这是最省心的做法,因为接下来不管你是敲python还是敲pip,用的都是 QGIS 那套。
第二层是解释器。按下Ctrl+Shift+P,输入Python: Select Interpreter,然后手动输入路径,指向:
C:\Program Files\QGIS 3.34.0\apps\Python39\python.exe选完之后 VS Code 状态栏会显示这个解释器。注意别选成系统里的 Anaconda 或微软商店版 Python,这是新手最常见的失误。
第三层是补全路径。光选解释器还不够,因为 QGIS 的 Python 库不在标准 site-packages 里,Pylance 找不到它。需要手工加:
{ "python.analysis.extraPaths": [ "C:\\Program Files\\QGIS 3.34.0\\apps\\qgis\\python", "C:\\Program Files\\QGIS 3.34.0\\apps\\qgis\\python\\plugins" ], "python.autoComplete.extraPaths": [ "C:\\Program Files\\QGIS 3.34.0\\apps\\qgis\\python", "C:\\Program Files\\QGIS 3.34.0\\apps\\qgis\\python\\plugins" ] }加完之后重启一下 VS Code(或者执行Python: Restart Language Server),再打开一个.py文件敲from qgis.core import QgsProject,如果QgsProject有补全提示、能跳转到定义,说明补全这块通了。
如果还要在 VS Code 里直接运行脚本,就得配launch.json了,这属于路线 C 的范畴:
{ "version": "0.2.0", "configurations": [ { "name": "QGIS 环境跑当前文件", "type": "debugpy", "request": "launch", "program": "${file}", "console": "integratedTerminal", "env": { "PYTHONPATH": "C:\\Program Files\\QGIS 3.34.0\\apps\\qgis\\python;C:\\Program Files\\QGIS 3.34.0\\apps\\qgis\\python\\plugins", "PATH": "C:\\Program Files\\QGIS 3.34.0\\bin;C:\\Program Files\\QGIS 3.34.0\\apps\\qgis\\bin;${env:PATH}" } } ] }这套 env 写法是"够用版本"。完整版还要加GDAL_DATA、PROJ_LIB、QT_PLUGIN_PATH,具体值请从o4w_env.bat里抄,或者在一个已经执行过它的终端里敲set查看。不要凭印象写这些变量,路径错一个字母就是 DLL 加载失败。
3.3 PyCharm:解释器、路径变量与外部工具
PyCharm 的配置思路和 VS Code 一致,但入口分散在几个地方。
解释器在File → Settings → Project → Python Interpreter。点齿轮图标选Add,然后选System Interpreter,路径填C:\Program Files\QGIS 3.34.0\apps\Python39\python.exe。添加完之后 PyCharm 会去扫这个解释器的 site-packages,QGIS 自带的那些库会被识别到。
但 PyQGIS 不在 site-packages 里,所以要再手工加路径,位置在Settings → Project → Python Interpreter → 解释器右侧的路径按钮,把apps\qgis\python和apps\qgis\python\plugins加进去。加完之后 PyCharm 的索引会重建一次,耐心等。
PyCharm 还有一个更好用的东西是外部工具配置。在Settings → Tools → External Tools里加一个工具,命令指向o4w_env.bat,每次点一下就能在一个带 QGIS 环境的终端里干活。对不想改launch.json的人来说,这比手工配 env 靠谱。
PyCharm 运行/调试配置里的环境变量编辑框,直接粘贴从o4w_env.bat抄来的那几个变量即可。要注意 PyCharm 的环境变量编辑框里,多条路径之间用的是分号分隔,且不要用引号把整条PATH值包起来。
3.4 验证环节:怎样才算真的配好了
配置完别急着写插件,先做三层验证,一层通不了就别往下走。
第一层,解释器认对了。在编辑器的文件里写:
import sys print(sys.executable) print(sys.version)运行并看输出,路径必须指向 QGIS 安装目录里的python.exe,而不是C:\Python3xx或者 Anaconda。
第二层,PyQGIS 能导入。同一个文件里加:
from qgis.core import QgsApplication, Qgis print(Qgis.QGIS_VERSION)能打印出版本号说明导入链路和 DLL 加载都没问题。如果这里报DLL load failed,回去检查PATH里有没有bin目录,并且确认bin排在系统 Python 相关路径前面。
第三层,补全和跳转可用。随便敲一个QgsVectorLayer(,看有没有参数提示;按住Ctrl点类名,看能不能跳到qgis/core/__init__.pyi或者对应的源码。这两项都通过,才算是一个真正好用的开发环境。
提示:如果第二层过了、第三层没过,通常是 Pylance 的缓存问题,执行一次
Python: Restart Language Server就好。如果第三层过了、第二层没过,那是运行时环境变量的问题,不是编辑器的问题,要往o4w_env.bat的方向查。
4. 生成第一个插件骨架:Plugin Builder 与它的产物解剖
4.1 安装 Plugin Builder 并生成骨架
有了环境,下一步就是造一个能加载进 QGIS 的插件骨架。手写骨架不是不行,但metadata.txt字段多、__init__.py的classFactory有固定写法、资源文件要编译,手写容易漏东西。所以推荐用Plugin Builder这个官方社区插件生成模板,再按需修改。
安装路径是 QGIS 里的插件 → 管理并安装插件,搜索框输入 "Plugin Builder",找到后点安装。装完后菜单里会多出一项。
使用流程大致是:点开 Plugin Builder,填一组表单,然后选择输出目录。表单里几项要特别注意:
- Class name:插件主类的名字,建议用英文驼峰,别用中文,也别和 QGIS 内置类名撞车。
- Plugin name:显示在菜单和插件管理器里的名字,可以是中文,但建议保留英文原名做目录名,方便社区发布。
- Module name:这个决定了文件夹名和 Python 模块名,必须是纯小写英文加下划线。
- Description / About:这两项会写进
metadata.txt,是发布时给别人看的门面,先大致填,发布前再打磨。 - Minimum QGIS version:这个值直接决定插件能在哪些版本上被加载,填太高会让老用户装不上,填太低可能在不支持的版本上出问题。保守做法是填你实际测试过的最低版本,比如
3.22。 - Template:选
Tool button with dialog或者Tool button with dock widget,前者适合弹窗式工具,后者适合常驻面板。
输出目录建议直接填到插件目录(第 2.2 节那个路径)下。Plugin Builder 会自动建子目录,你确认一下生成的位置对不对。
4.2 逐个文件拆解:哪些要改、哪些别碰
生成出来的目录大概长这样:
my_first_plugin/ ├── __init__.py ├── metadata.txt ├── my_first_plugin.py ├── my_first_plugin_dialog.py ├── my_first_plugin_dialog_base.ui ├── resources.qrc ├── resources.py ├── icon.png ├── Makefile └── i18n/ └── af.ts挨个说清楚每个文件干什么、能不能动。
metadata.txt是插件的身份证。QGIS 启动扫描时只读这个文件来判断插件是否可用。关键字段包括name、qgisMinimumVersion、description、version、author、email、about、tracker、repository、tags、experimental。version字段每次发布必须递增,否则插件仓库会拒绝。experimental=True表示标记为实验版,只有勾选了"显示实验性插件"的用户才能看到。这个字段在你自测阶段可以开着,正式发布时改成False。
__init__.py是插件入口,里面的classFactory函数负责把插件类交给 QGIS。这个文件很短,但千万别改结构,只改返回的类名:
def classFactory(iface): from .my_first_plugin import MyFirstPlugin return MyFirstPlugin(iface)my_first_plugin.py是核心逻辑,主类里固定有这么几个方法:
__init__(self, iface):构造,iface是 QGIS 给你的接口对象,通过它能访问图层树、菜单、工具栏、状态栏。initGui(self):QGIS 加载插件时调用,通常在这里把动作(QAction)加到菜单和工具栏。unload(self):插件被卸载时调用,要在这里把你加进去的动作从界面移除,不然会出现重复菜单项。run(self):动作被点击时触发,业务逻辑从这儿开始。
*_dialog.py和*_dialog_base.ui是界面部分。.ui文件是 Qt Designer 的格式,可以用Qt Designer打开拖控件,也可以用文本编辑器直接改 XML。生成的*_dialog.py会把.ui加载进来,同时给你一个run()方法,这是你写业务逻辑的地方。注意.ui改完不需要重新生成.py,因为它是在运行时加载的,这跟resources.qrc的情况不同。
resources.qrc和resources.py是资源打包。.qrc里记录图标等资源文件的路径,.py是编译后的结果,代码里通过:/plugins/...这种形式引用。改完.qrc必须重新编译。
Makefile和i18n/是给pb_tool和翻译用的,初始阶段可以不管。
4.3 资源文件与 pyrcc5,以及 pb_tool 能省多少事
资源编译是很多新手第一次改图标时踩的坑:把新图标拷进目录、在.qrc里改了路径,但界面上还是旧图标。原因就是没重新编译.qrc。
编译命令是:
call "C:\Program Files\QGIS 3.34.0\bin\o4w_env.bat" pyrcc5 -o resources.py resources.qrcpyrcc5是 PyQt5 附带的资源编译器,在o4w_env.bat设好环境后直接在命令行可用。如果你的 QGIS 版本用的是 Qt6 路线,对应的命令可能换成pyrcc6或者已经不再需要这一步(Qt6 更推荐用文件系统直接加载图标),以你机器上有哪个命令为准,敲pyrcc5 --help试一下就知道。
手工敲pyrcc5当然可以,但插件项目通常还要做别的重复劳动:把插件"部署"到 profile 目录、打包成 zip、清理临时文件、更新翻译。这些琐事可以用pb_tool一次性解决。
它在 QGIS 的 Python 环境里安装:
call "C:\Program Files\QGIS 3.34.0\bin\o4w_env.bat" python -m pip install pb_tool然后在插件目录下运行:
pb_tool compile REM 编译 .qrc 和 .ui pb_tool deploy REM 部署到 profile 目录 pb_tool zip REM 打包成可发布的 zip pb_tool clean REM 清理中间产物pb_tool读取插件目录里的pb_tool.cfg判断各项参数。Plugin Builder 生成的目录一般已经带了这个配置文件,如果没带,运行pb_tool create生成一个。
提示:
pb_tool deploy会覆盖 profile 目录里同名插件。如果你同时手工改过 profile 里的文件,部署前先备份,免得辛苦改的代码被覆盖。这个坑我踩过不止一次。
4.4 Plugin Reloader:把"改一行重启一次"这件事干掉
QGIS 启动一次要好几秒,改一行代码就重启一次,一天下来时间全耗在等待上。Plugin Reloader就是解决这个的。
安装方式同样是在插件管理器里搜 "Plugin Reloader"。装完后菜单里会出现Plugin Reloader → Choose plugin,选你的插件,然后点Reload plugin。它的工作原理是调用qgis.utils.reloadPlugin(),把插件的模块从内存里卸载再重新加载。
实测下来它的边界要清楚:
- 能热重载的:插件的 Python 逻辑代码、
.ui文件(因为运行时加载)、metadata.txt的部分字段。 - 不能热重载的:
__init__.py的结构性改动、新增的.py文件(有可能不被识别)、resources.py的改动(有时能,有时不能,稳妥起见还是重启)。
另外主类的unload()方法必须写对。如果卸载时没把动作从界面移除,重载后你会看到菜单里出现两份同样的项,点哪个都可能出问题。生成的模板里unload()一般是写好的,你自己加动作时记得同步在unload()里清理。
还有一个更原生的方式,在 QGIS 的 Python 控制台里直接执行:
import qgis.utils qgis.utils.reloadPlugin('my_first_plugin')效果和 Plugin Reloader 一样,但胜在快,可以把它绑定到一段你自己的小脚本里。
5. 调试、排错与打包:从能跑到能发
5.1 断点调试的可行做法
插件调试比普通脚本麻烦,因为代码运行在 QGIS 进程里,你不能直接在编辑器里按 F5 启动调试。目前可行的做法有两条。
第一条是远程附加调试。原理是让 QGIS 进程里跑起一个调试服务端,编辑器作为客户端附加过去。以debugpy为例,先把它装到 QGIS 的 Python 环境:
call "C:\Program Files\QGIS 3.34.0\bin\o4w_env.bat" python -m pip install debugpy然后在 QGIS 的 Python 控制台里执行:
import debugpy debugpy.listen(("127.0.0.1", 5678)) print("waiting for debugger...")接着在 VS Code 里配一个 attach 配置:
{ "name": "Attach to QGIS", "type": "debugpy", "request": "attach", "connect": { "host": "127.0.0.1", "port": 5678 }, "pathMappings": [ { "localRoot": "${workspaceFolder}/my_first_plugin", "remoteRoot": "C:\\Users\\<用户名>\\AppData\\Roaming\\QGIS\\QGIS3\\profiles\\default\\python\\plugins\\my_first_plugin" } ] }路径映射这一项是关键,两边的目录必须能对上,否则断点会显示为"未绑定"。启动 attach 之后,你在插件里加的断点就能命中了。
第二条是日志法,也就是最土但最有效的办法。在代码里插打印,或者把异常信息写进 QGIS 的日志面板:
from qgis.core import QgsMessageLog, Qgis def run(self): try: # 你的业务逻辑 pass except Exception as e: import traceback QgsMessageLog.logMessage(traceback.format_exc(), "MyPlugin", Qgis.Critical)QgsMessageLog会把消息写进 QGIS 的日志消息面板(菜单里可以打开),出错时带上完整堆栈,比单行 print 有用得多。开发期把print和QgsMessageLog混着用,效率最高。
5.2 报错定位的顺序:先看这里再看那里
排查插件问题有一套固定顺序,照着走能省很多时间。
第一步,看插件到底有没有被加载。在 QGIS 的 Python 控制台里执行:
import qgis.utils print(qgis.utils.plugins.keys())如果你的插件模块名不在里面,说明加载就失败了,问题在metadata.txt或__init__.py。如果在里面但功能不对,说明加载成功,问题在业务逻辑。
第二步,看 QGIS 启动时的日志。菜单里的日志消息面板会记录插件加载异常。很多时候你以为插件没加载,其实加载了但initGui里抛了异常,QGIS 捕获后没弹窗。
第三步,检查路径。qgis.utils.plugin_paths()的输出里必须包含你的插件所在目录。如果插件放在自定义目录,要去设置 → 选项 → 系统 → 插件路径里手工加进去。
第四步,检查版本门槛。metadata.txt里的qgisMinimumVersion高于当前 QGIS 版本时,插件会直接不显示,且几乎不给提示。这是最阴的一个坑。
第五步,看是否有命名冲突。插件模块名如果和已安装的其他 Python 包重名(比如叫test、utils),导入时可能被解析到别的模块上,表现为"代码明明改了却没变化"。
5.3 跨版本兼容与 Python 版本差异
QGIS 3.x 系列内部是 PyQt5,但 Python 版本在系列内部有过变化。早期 3.x 版本多用 Python 3.6/3.7,LTR 阶段常见 3.9,较新的版本开始用 3.12。这个差异会直接影响到你的代码能不能跑:
- 语法层面:3.12 里一些被弃用的写法会警告甚至报错,比如某些
datetime用法、imp模块。你如果在 3.12 上开发却想兼容 3.9,就避免使用 3.10+ 才有的语法特性(比如match语句)。 - API 层面:QGIS 的 API 在不同小版本间会做弃用调整。以前能用的
QgsGeometry.fromPolygon()这类方法,新版本会推荐换成QgsGeometry.fromPolygonXY(),旧方法可能还能用但会告警。 - 打包层面:如果你想同时在多个 QGIS 版本上测试,最省事的方法是装多个版本的 QGIS 到不同的目录,用不同 profile 隔离配置。
实际做法上,我建议以 LTR 版本为开发基准,然后在metadata.txt里把qgisMinimumVersion设成你真正测过的最低版本。别为了兼容很老的版本去牺牲代码可读性,社区主流用户都在 LTR 及以上。
5.4 上架前的自检清单
插件写完了,要发布或交付之前,这套清单过一遍,能挡掉大部分低级问题。
| 检查项 | 具体要求 | 常见失误 |
|---|---|---|
metadata.txt完整性 | name、version、description、author、email、qgisMinimumVersion 必填 | 缺 email 或 version 格式不规范 |
version递增 | 每次发布必须比上一次大 | 直接覆盖发布导致仓库拒绝 |
experimental状态 | 正式发布设为 False | 忘了改,用户看不到插件 |
| 图标引用正确 | 图标路径与.qrc编译结果一致 | 改了图但没重新 pyrcc5 |
| 依赖声明 | 如果用了额外 pip 包,要在about或文档里说明 | 用户装上后 ImportError |
| 卸载干净 | unload()里移除所有添加的动作和界面元素 | 重载后出现重复菜单项 |
| 中英文资源 | 有翻译需求时补齐.ts并 lrelease 编译 | 翻译文件没编译,界面还是英文 |
| 异常兜底 | 关键路径加 try/except 并写 QgsMessageLog | 出错时用户完全不知道为什么 |
打包成 zip 之后,自己先在一台干净的机器上(或者一个新建的 profile 里)装一遍。这一步能发现"在我机器上没问题"这类隐蔽问题,比如某个路径写死成了你的用户名。
6. 几个我反复踩的坑和顺手的小习惯
6.1 中文路径、空格与权限
这三个问题在 Windows 上出现频率最高。
中文路径的问题出在编码环节。有些工具链在读取.qrc、.ts、.ui这些 XML 文件时,对非 ASCII 路径的处理不一致,表现为编译报错或者资源加载不出来。最省事的规避办法是把整个开发链路放在纯英文路径下,包括 QGIS 安装目录、编辑器工作区、插件目录里的用户名的部分。
用户目录带中文这一点比较麻烦,因为%APPDATA%是 Windows 给定的。如果你的用户名是中文,C:\Users\张三\AppData\...就会带中文。规避方式是新建一个英文名的用户,或者把插件开发目录放在D:\qgis-dev\这种纯英文路径下,只把编译产物部署到 profile 目录。命令行引用带空格路径时,所有路径都要加双引号,包括o4w_env.bat的路径。
权限问题上,C:\Program Files\下的文件默认不可写。装插件不需要写安装目录,所以一般不冲突;但如果你想改 QGIS 自带的 Python 库文件(不建议),会碰到权限拒绝。pip install装到 QGIS Python 环境时,如果报权限错误,说明装到了系统级的Program Files目录下,这时候要么用管理员权限,要么加--user装到用户目录。
6.2 环境变量顺序与"两个 Python 打架"
PATH的顺序决定命令解析的结果。如果你系统里同时有 Anaconda、微软商店版 Python、QGIS 自带 Python,敲python和pip时到底用哪个,完全取决于PATH里谁在前面。
插件开发时这个问题的表现是:你在终端里敲python,启动的却是 Anaconda,然后import qgis.core报错,你以为是 QGIS 环境坏了,其实是走错解释器了。
判断方法很简单,敲:
where python where pip输出的第一行就是实际生效的那个。确认是 QGIS 目录下的再往下做。
根治方法是不要把这个混乱留给环境变量,而是记住一条规矩:涉及 QGIS 的操作,永远先在终端里执行o4w_env.bat,这个脚本会把 QGIS 相关的路径插到PATH前面,压制掉其他 Python。养成这个习惯,能避免大量"明明配好了却报错"的困惑。
6.3 改了代码没生效的三层原因
这个现象我遇到过三种完全不同的成因,排查顺序是从外到内。
第一层,代码没到 QGIS 会读取的位置。你在编辑器工作区改的是源目录里的文件,但 QGIS 加载的是 profile 目录下的插件。这两个目录如果没做链接或同步,改了源目录等于没改。解决办法是用pb_tool deploy同步,或者在 profile 目录里做符号链接(Windows 下用mklink /D)。
第二层,代码到了但模块没重新加载。Python 的模块一旦被导入就缓存在sys.modules里,改文件不会自动重新执行。所以要么用 Plugin Reloader,要么在 Python 控制台里调qgis.utils.reloadPlugin()。注意新加的文件有时热重载识别不到,这种情况还是得重启。
第三层,代码加载了但走的是缓存。这种最少见但最迷惑:.pyc缓存文件、或者插件自己在某个地方存了配置/状态,导致看起来"行为没变"。清掉插件目录下的__pycache__文件夹,重启 QGIS,基本能解决。
一个顺手的小习惯:在插件主类的initGui里加一行版本日志:
QgsMessageLog.logMessage("MyPlugin v0.1.3 loaded", "MyPlugin")每次重载后看一眼日志里的版本号,就知道到底加载的是不是最新代码。这一行花费几秒钟,省下的排查时间是以小时计的。
6.4 多版本 QGIS 共存时的切换办法
如果你的工作需要同时维护面向不同 QGIS 版本的插件,多版本共存是必须的。安装时把不同版本装到不同目录,比如C:\Program Files\QGIS 3.28.0\和C:\Program Files\QGIS 3.34.0\,互不干扰。
切换的关键在 profile。不同版本的 QGIS 使用的 profile 目录其实是可以共用的(都在%APPDATA%\QGIS\QGIS3\下),但共享 profile 会导致插件列表和设置混在一起。更干净的做法是给每个版本建独立 profile:
"C:\Program Files\QGIS 3.28.0\bin\qgis-ltr-bin.exe" --profile qgis328 "C:\Program Files\QGIS 3.34.0\bin\qgis-ltr-bin.exe" --profile qgis334这样两个版本的配置完全隔离,插件各自装各自的。开发插件时,同一个源目录可以通过pb_tool分别部署到两个 profile 的插件目录下,测试一遍就能确认跨版本兼容性。
编辑器这边的处理是:为每个 QGIS 版本建一个独立的 VS Code 工作区,settings.json里的解释器路径、extraPaths分别指向对应版本的目录。切工作区就等于切环境,避免手工改路径。
最后再提一个容易被忽略的点:profile 名字一旦确定就别随便改。改了之后 QGIS 会当成一个全新的 profile,之前的插件启用状态、界面布局全丢,你会以为出问题了。
我个人在这个话题上的体会是,QGIS 插件开发的环境配置难点不在于步骤多,而在于"环境是外部给的"这件事。普通 Python 项目里你几乎不需要关心解释器怎么来的,而这里必须把解释器、环境变量、加载路径这三件事想明白。想明白之后,配置本身十分钟就能做完;想不明白的话,可能折腾一整天还在报同一个错。所以我通常建议新手先花半小时把o4w_env.bat里到底设了哪些变量打印出来看一遍,那几十行输出,比任何教程都直观。