插件开发指南
第十五章 插件开发指南
15.1 插件开发概述
15.1.1 QGIS插件类型
| 类型 | 语言 | 特点 |
|---|---|---|
| Python插件 | Python | 开发简单,大多数插件 |
| C++插件 | C++ | 高性能,核心扩展 |
15.1.2 插件功能类别
- 数据处理工具
- 可视化增强
- Web服务集成
- 分析算法
- 界面定制
- 数据导入导出
15.2 开发环境准备
15.2.1 必要工具
- QGIS(最新版本)
- Python 3.x
- Qt Designer
- 文本编辑器/IDE
- Plugin Builder插件
15.2.2 安装Plugin Builder
插件 > 管理和安装插件 > 搜索"Plugin Builder"
15.2.3 推荐IDE配置
VS Code配置:
{
"python.pythonPath": "/path/to/qgis/python",
"python.autoComplete.extraPaths": [
"/path/to/qgis/python",
"/path/to/qgis/python/plugins"
]
}
15.3 插件结构
15.3.1 基本目录结构
my_plugin/
├── __init__.py # 插件入口
├── metadata.txt # 元数据
├── my_plugin.py # 主模块
├── my_plugin_dialog.py # 对话框类
├── my_plugin_dialog_base.ui # UI文件
├── resources.qrc # Qt资源
├── resources_rc.py # 编译的资源
├── icon.png # 插件图标
├── help/ # 帮助文档
│ └── index.html
├── i18n/ # 翻译文件
│ └── my_plugin_zh.ts
└── test/ # 测试代码
└── test_my_plugin.py
15.3.2 metadata.txt
[general]
name=My Plugin
qgisMinimumVersion=3.0
qgisMaximumVersion=3.99
description=This is my awesome plugin
version=1.0.0
author=Your Name
email=your.email@example.com
about=Detailed description of what this plugin does.
Can span multiple lines.
tracker=https://github.com/user/repo/issues
repository=https://github.com/user/repo
tags=analysis,vector,tool
homepage=https://example.com/my-plugin
category=Plugins
icon=icon.png
experimental=False
deprecated=False
changelog=
1.0.0 - Initial release
0.9.0 - Beta version
15.3.3 init.py
def classFactory(iface):
"""加载插件类
:param iface: QgisInterface实例
:return: 插件实例
"""
from .my_plugin import MyPlugin
return MyPlugin(iface)
15.3.4 主插件类
# my_plugin.py
from qgis.PyQt.QtCore import QSettings, QTranslator, QCoreApplication
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction
from .resources import *
from .my_plugin_dialog import MyPluginDialog
import os.path
class MyPlugin:
"""QGIS插件实现"""
def __init__(self, iface):
"""构造函数
:param iface: QgisInterface实例
"""
self.iface = iface
self.plugin_dir = os.path.dirname(__file__)
# 初始化翻译
locale = QSettings().value('locale/userLocale')[0:2]
locale_path = os.path.join(
self.plugin_dir, 'i18n',
f'MyPlugin_{locale}.qm'
)
if os.path.exists(locale_path):
self.translator = QTranslator()
self.translator.load(locale_path)
QCoreApplication.installTranslator(self.translator)
self.actions = []
self.menu = self.tr('&My Plugin')
self.toolbar = self.iface.addToolBar('MyPlugin')
self.toolbar.setObjectName('MyPlugin')
def tr(self, message):
"""获取翻译后的字符串"""
return QCoreApplication.translate('MyPlugin', message)
def add_action(
self,
icon_path,
text,
callback,
enabled_flag=True,
add_to_menu=True,
add_to_toolbar=True,
status_tip=None,
whats_this=None,
parent=None):
"""添加工具栏图标和菜单项"""
icon = QIcon(icon_path)
action = QAction(icon, text, parent)
action.triggered.connect(callback)
action.setEnabled(enabled_flag)
if status_tip:
action.setStatusTip(status_tip)
if whats_this:
action.setWhatsThis(whats_this)
if add_to_toolbar:
self.toolbar.addAction(action)
if add_to_menu:
self.iface.addPluginToMenu(self.menu, action)
self.actions.append(action)
return action
def initGui(self):
"""初始化GUI"""
icon_path = ':/plugins/my_plugin/icon.png'
self.add_action(
icon_path,
text=self.tr('My Plugin'),
callback=self.run,
parent=self.iface.mainWindow()
)
def unload(self):
"""卸载插件"""
for action in self.actions:
self.iface.removePluginMenu(self.tr('&My Plugin'), action)
self.iface.removeToolBarIcon(action)
del self.toolbar
def run(self):
"""运行插件"""
dlg = MyPluginDialog()
result = dlg.exec_()
if result:
# 执行操作
pass
15.4 UI设计
15.4.1 使用Qt Designer
打开Qt Designer设计界面:
# Linux
designer
# Windows (OSGeo4W)
C:\OSGeo4W\bin\designer.exe
15.4.2 常用控件
| 控件 | 类 | 用途 |
|---|---|---|
| 按钮 | QPushButton | 触发操作 |
| 标签 | QLabel | 显示文本 |
| 文本框 | QLineEdit | 单行输入 |
| 文本区 | QTextEdit | 多行输入 |
| 下拉框 | QComboBox | 选择选项 |
| 复选框 | QCheckBox | 布尔选择 |
| 单选框 | QRadioButton | 互斥选择 |
| 数值框 | QSpinBox | 整数输入 |
| 滑块 | QSlider | 数值选择 |
| 列表 | QListWidget | 列表显示 |
| 表格 | QTableWidget | 表格显示 |
15.4.3 QGIS专用控件
from qgis.gui import (
QgsMapLayerComboBox, # 图层选择
QgsFieldComboBox, # 字段选择
QgsFileWidget, # 文件选择
QgsProjectionSelectionWidget, # CRS选择
QgsColorButton, # 颜色选择
QgsSpinBox, # 数值输入
QgsDoubleSpinBox # 浮点输入
)
15.4.4 对话框类
# my_plugin_dialog.py
from qgis.PyQt import uic
from qgis.PyQt.QtWidgets import QDialog
import os
FORM_CLASS, _ = uic.loadUiType(os.path.join(
os.path.dirname(__file__), 'my_plugin_dialog_base.ui'))
class MyPluginDialog(QDialog, FORM_CLASS):
def __init__(self, parent=None):
"""构造函数"""
super(MyPluginDialog, self).__init__(parent)
self.setupUi(self)
# 连接信号
self.layerComboBox.layerChanged.connect(self.on_layer_changed)
self.runButton.clicked.connect(self.on_run)
def on_layer_changed(self, layer):
"""图层改变时更新字段列表"""
self.fieldComboBox.setLayer(layer)
def on_run(self):
"""执行操作"""
layer = self.layerComboBox.currentLayer()
field = self.fieldComboBox.currentField()
# 处理逻辑
self.accept()
15.5 资源管理
15.5.1 资源文件
<!-- resources.qrc -->
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="/plugins/my_plugin">
<file>icon.png</file>
<file>images/tool.png</file>
</qresource>
</RCC>
15.5.2 编译资源
pyrcc5 -o resources_rc.py resources.qrc
15.5.3 使用资源
# 在代码中使用
from . import resources_rc # 导入资源
icon_path = ':/plugins/my_plugin/icon.png'
icon = QIcon(icon_path)
15.6 国际化
15.6.1 标记可翻译字符串
# 使用self.tr()
message = self.tr("Hello World")
# 或直接使用QCoreApplication.translate
from qgis.PyQt.QtCore import QCoreApplication
message = QCoreApplication.translate("MyPlugin", "Hello World")
15.6.2 提取翻译
# 创建.ts文件
pylupdate5 -verbose my_plugin.py my_plugin_dialog.py -ts i18n/my_plugin_zh.ts
15.6.3 翻译
使用Qt Linguist编辑.ts文件:
linguist i18n/my_plugin_zh.ts
15.6.4 编译翻译
lrelease i18n/my_plugin_zh.ts
15.7 设置存储
15.7.1 使用QSettings
from qgis.PyQt.QtCore import QSettings
settings = QSettings()
# 保存设置
settings.setValue("my_plugin/last_path", "/path/to/file")
settings.setValue("my_plugin/buffer_distance", 100)
# 读取设置
last_path = settings.value("my_plugin/last_path", "")
distance = settings.value("my_plugin/buffer_distance", 50, type=int)
15.7.2 项目级设置
from qgis.core import QgsProject
project = QgsProject.instance()
# 保存到项目
project.writeEntry("my_plugin", "setting1", "value1")
# 从项目读取
value, ok = project.readEntry("my_plugin", "setting1", "default")
15.8 发布插件
15.8.1 准备发布
- 完善metadata.txt
- 添加帮助文档
- 添加图标
- 测试功能
- 检查代码质量
15.8.2 创建发布包
# 创建ZIP包
cd /path/to/plugins
zip -r my_plugin.zip my_plugin -x "*.pyc" -x "*__pycache__*"
15.8.3 上传到官方仓库
- 访问 https://plugins.qgis.org
- 注册/登录账户
- 上传插件包
- 等待审核
15.8.4 自托管
# 创建plugins.xml
<?xml version='1.0' encoding='UTF-8'?>
<plugins>
<pyqgis_plugin name="My Plugin" version="1.0.0">
<description>Plugin description</description>
<version>1.0.0</version>
<qgis_minimum_version>3.0</qgis_minimum_version>
<download_url>https://example.com/my_plugin.zip</download_url>
</pyqgis_plugin>
</plugins>
15.9 测试
15.9.1 单元测试
# test/test_my_plugin.py
import unittest
from my_plugin.my_plugin import MyPlugin
class TestMyPlugin(unittest.TestCase):
def setUp(self):
"""测试前准备"""
pass
def test_calculation(self):
"""测试计算功能"""
result = some_function(10, 20)
self.assertEqual(result, 30)
def tearDown(self):
"""测试后清理"""
pass
if __name__ == '__main__':
unittest.main()
15.9.2 运行测试
python -m pytest test/
15.10 最佳实践
15.10.1 代码风格
- 遵循PEP 8
- 使用类型提示
- 添加文档字符串
15.10.2 错误处理
try:
result = some_operation()
except Exception as e:
QgsMessageLog.logMessage(str(e), "MyPlugin", Qgis.Warning)
iface.messageBar().pushMessage("错误", str(e), level=Qgis.Critical)
15.10.3 性能考虑
- 使用进度反馈
- 避免阻塞UI
- 考虑使用QgsTask
15.11 小结
本章详细介绍了QGIS插件开发:
关键要点:
- 理解插件结构和组件
- 掌握UI设计方法
- 了解资源管理和国际化
- 掌握设置存储方式
- 了解发布和测试流程
插件开发是扩展QGIS功能的重要途径。
下一章:第16章 数据库集成

浙公网安备 33010602011771号