17.2. 开发第一个插件

当你想实现交互式工具或者非常特殊的图形用户界面时,是时候研究插件开发了。在前面的练习中,我们介绍了QGIS Python API。因此, 我们现在可以专注于启动第一个QGIS插件的必要步骤。为QGIS创建插件的好处是有一个插件!它叫插件生成器。当你在那里的时候, 也要安装 插件重载器 ,这对插件开发人员非常有用。因为它可以让你快速地重新加载你的插件,而不必每次你对代码进行修改时都重新启动QGIS。 当您安装了两个插件后插件工具栏将如下所示 图 17.3

_images/image310_x3v.jpg

图 17.3 插件工具栏

在开始之前,我们还需要安装设计器 ,这是我们用来设计用户界面的应用程序。如果你使用Windows,我建议 WinPython (http://winpython.github.io/) 版本2.7.10.3(编写本书时Python 2.7的最新版本),它提供Qt设计器和斯派德(Python的集成开发环境)。在Ubuntu上,您可以使用 sudo apt-get install qt4-designer。在Mac上,你可以得到Qt创建者从http://Qt-project.org/downloads下载安装程序(包括Qt设计器)。

17.2.1. 使用plugin Builder创建插件模板

Plugin Builder将创建我们的插件所需的所有文件。要创建插件模板,请执行以下步骤:

1.起点插件生成器输入基本插件信息,包括:

  • 类名 (一个单词用驼色大写,即每个单词以大写字母开头)

  • 插件名称 (简短的描述)

  • 模块名称 (插件的Python模块名)

将鼠标悬停在“插件生成器”对话框中的输入字段上时,将显示帮助信息,如以下屏幕截图所示 图 17.4

_images/image311_xw3.jpg

图 17.4 插件生成器设置

2.点击"Next"到达“关于”对话框,您可以在其中输入插件功能的更详细描述。因为我们计划创建第一个插件只用于学习目的, 所以我们可以在这里放置一些随机文本并单击"Next"。

3.现在我们可以选择一个插件模板并指定“菜单项的文本”以及"Menu"插件应该在中列出,如下面的屏幕截图所示。 可用的模板包括带对话框的工具按钮,带停靠小部件的工具按钮和处理提供程序。在本练习中,我们将创建带对话框的工具按钮然后点击"Next" , 如 图 17.5

_images/image312_xk6.jpg

图 17.5 插件生成器

4.下面的对话框显示复选框,我们可以在其中选择应创建哪些非必需的插件文件。您可以选择所提供选项的任何子集,然后单击"Next" , 如 图 17.6

_images/image313_xnu.jpg

图 17.6 插件生成器

5.在下一个对话框中,我们需要指定插件“窃听器” 还有“密码知识库”。同样,由于我们创建这个插件只是为了学习, 我只是在下一个截图中创建一些url,但是如果您打算公开您的插件,您应该使用适当的追踪器和代码库。

6.一旦你点击"Next",将要求您选择一个文件夹来存储插件。你可以直接保存在QGIS插件文件夹中, ~.qgis2pythonplugins 在窗户上,或 ~/.qgis2/python/plugins 在Linux和Mac上。

7.选择插件文件夹后,它将显示“插件生成器结果”确认对话框,它确认您的插件文件夹的位置以及您的QGIS 插件文件夹的位置。如前所述,我直接保存在QGIS插件文件夹中,如下截图所示。如果您已保存在其他位置, 现在可以将插件文件夹移动到QGIS插件文件夹中,以确保QGIS可以找到并加载它,如 图 17.7

_images/image314_xs3.jpg

图 17.7 插件生成器结果

我们仍然要做的一件事是为插件工具栏准备图标。这需要我们编译"resources.qrc"文件,其中“插件生成器”自动创建, 将图标转换为可用的Python代码。这是在命令行上完成的。在Windows上,我建议使用 OSGeo 4W外壳 , 因为它确保环境变量的设置方式能够找到必要的工具。导航到插件文件夹并运行以下命令:

pyrcc4 -o resources.py resources.qrc

您可以替换默认图标( icon.png )添加你自己的插件图标。之后,你只需要重新编译"resources_rc.qrc"如前所示。

重新启动QGIS,您现在应该会看到插件管理器中列出了您的插件,如下所示 图 17.8

_images/image315_x2v.jpg

图 17.8 插件管理器

在插件管理器中激活你的插件,你会看到它列在“插件”菜单。当你启动你的插件时,它会显示一个空白对话框,等待你自定义它。

17.2.2. 自定义插件GUI

要自定义空白默认插件对话框,我们使用“设计器”。您可以在plugin文件夹中找到对话框文件。在我的情况下, 它被称为"my_first_plugin_dialog_base.ui"(派生自我在插件生成器中指定的模块名)。当你打开你的插件时 .ui.用户界面在Qt设计器中, 您将看到空白对话框。现在您可以通过从小部件框"on the left-hand side of the Qt Designer window"。 In the following screenshot,you can see that I added a 标签 and a drop-down list widget(listed as 组合框 在 Widgetbox公司 ). 您可以将标签文本更改为层 by double-clicking on the default label text. Additionally,it is good practice to assign descriptive names to the widget objects; for example,I renamed the combobox to layerCombo,如右下角所示 图 17.9

_images/image316_x74.jpg

图 17.9 设计器

完成对插件对话框的更改后,可以保存它们。然后你可以回到QGIS。在QGIS中,现在可以配置“插件重载器”点击“选择要重新加载的插件” 按钮在“插件”工具栏并选择插件。如果你现在点击“重新加载插件”按钮和按下你的插件按钮,你的新插件对话框将显示。

17.2.3. 实现插件功能

正如您肯定注意到的,层组合框仍然是空的。要用加载的层列表填充组合框,我们需要添加几行代码到"my_first_plugin.py"(位于“插件”文件夹)。 更具体地说,我们扩展了运行() 方法:

定义运行(自):

“运行执行所有实际工作的方法”

# show the dialog self.dlg.show()

# clear the combo box to list only current layers
self.dlg.layerCombo.clear()

# get the layers and add them to the combo box layers =
QgsMapLayerRegistry.instance().mapLayers().values() for layer in
layers:

if layer.type() == QgsMapLayer.VectorLayer:

self.dlg.layerCombo.addItem(layer.name(),层)

# Run the dialog event loop result = self.dlg.exec_()

# See if OK was pressed if result:

# Check which layer was selected index =
self.dlg.layerCombo.currentIndex() layer =
self.dlg.layerCombo.itemData(index)

# Display information about the layer

QMessageBox.information(self.iface.mainWindow(),“学习

QGIS“,”%s有%d个功能。“%(layer.name(),layer.featureCount()))

您还必须在脚本顶部添加以下导入行以避免 NameErrors 关于 QgsMapLayerRegistry 和 QMessageBox :

from qgis.core import * from PyQt4.QtGui import QMessageBox

一旦你完成了对"my_first_plugin.py",您可以保存文件并使用“重新加载插件”按钮重新加载你的插件。 如果现在启动插件,则组合框将填充当前QGIS项目中所有层的列表,当您单击"OK",您将看到一个消息框, 其中显示选定图层中的要素数。

17.2.4. 创建自定义地图工具

虽然上一个练习演示了如何创建一个自定义GUI,使用户能够与QGIS交互,但是在这个练习中,我们将更进一步,实现我们自己的自定义 地图工具类似于违约识别工具。这意味着用户可以点击地图,工具报告地图上的哪个功能被点击了。

为此,我们创建了另一个带对话框的工具按钮插件模板调用"MyFirstMapTool"。对于这个工具,我们不需要创建对话框。相反,我们必须比前 面的例子写更多的代码。首先,我们创建自定义映射工具类,我们称之为"IdentifyFeatureTool"。 除了__init__() 构造函数, 此工具有一个名为 画布发布事件()定义释放鼠标按钮时(即,按下鼠标按钮后松开鼠标按钮时)工具的操作:

class IdentifyFeatureTool(QgsMapToolIdentify): def __init__(self,
canvas): QgsMapToolIdentify.__init__(self, canvas) def
canvasReleaseEvent(self, mouseEvent):

打印“canvasReleaseEvent”

# get features at the current mouse position results =
self.identify(mouseEvent.x(),mouseEvent.y(),
self.TopDownStopAtFirst, self.VectorLayer) if len(results) > 0:

[# signal that a feature was identified self.emit( SIGNAL( "geomIdentified" ),
results[0].mLayer, results[0].mFeature)]

您可以将前面的代码粘贴到"my_first_map_tool.py"代码。当然,我们现在必须好好利用我们的新地图工具。 在初始化用户界面()函数,我们替换运行() 新方法"map_tool_init()"功能。此外,我们定义我们的地图工具是可检查的; 这意味着用户可以单击工具图标来激活它,然后再次单击它来停用它:

def initGui(自身):

# create the toolbar icon and menu entry icon_path = ':/plugins
/MyFirstMapTool/icon.png' self.map_tool_action=self.add_action(
icon_path,

text=self.tr(u'My 1st Map Tool'), callback=self.map_tool_init,
parent=self.iface.mainWindow())
self.map_tool_action.setCheckable(True)

新的"map_tool_init()"功能负责在单击按钮时激活或停用我们的地图工具。在激活过程中, 它创建了一个自定义的实例"IdentifyFeatureTool",以下行连接映射工具的“地质鉴定”向发送信号do_something()函数, 稍后我们将讨论。类似地,当"map"工具被停用时,我们会断开信号并恢复先前的"map"工具:

def map_tool_init(self):

# this function is called when the map tool icon is clicked print
"maptoolinit" canvas = self.iface.mapCanvas() if
self.map_tool_action.isChecked(): # when the user activates the tool
self.prev_tool = canvas.mapTool() self.map_tool_action.setChecked(
True ) self.map_tool = IdentifyFeatureTool(canvas)

QObject.connect(self.map_tool,SIGNAL("geomIdentified"),
self.do_something ) canvas.setMapTool(self.map_tool)

QObject.connect(canvas,SIGNAL("mapToolSet(QgsMapTool *)"),
self.map_tool_changed) else:

# when the user deactivates the tool
QObject.disconnect(canvas,SIGNAL("mapToolSet(QgsMapTool *)"

),self.map_tool_changed) canvas.unsetMapTool(self.map_tool) print
"restore prev tool %s" %(self.prev_tool)
canvas.setMapTool(self.prev_tool)

我们的新习惯do_something()当我们的地图工具被用来成功地识别一个特征时,函数被调用。 在本例中,我们只需在Python控制台。当然,您可以在这里获得创意并添加所需的自定义功能:

def do_something(self, layer, feature): print feature.attributes()

最后,我们还必须处理当用户切换到其他地图工具时的情况。这类似于用户在map_tool_init()函数:

def map_tool_changed(self): print "maptoolchanged" canvas = self.iface.mapCanvas()

QObject.disconnect(canvas,SIGNAL("mapToolSet(QgsMapTool *)"),
self.map_tool_changed) canvas.unsetMapTool(self.map_tool)
self.map_tool_action.setChecked(False)

您还必须在脚本顶部添加以下导入行,以避免与QObject,QgsMapTool ,以及其他:

**from qgis.core import * from qgis.gui import * from PyQt4.QtCore import ** *

当你准备好了,你可以重新加载插件并尝试它。你应该有Python控制台 打开以便能够跟踪插件的输出。当你激活工具栏中的插件时, 你首先会看到它打印出来 映射工具初始化 在控制台上。然后,如果你点击地图,它就会打印出来拉票活动 ,如果单击某个要素, 它也将显示该要素的属性。最后,如果您切换到另一个地图工具(例如,泛地图 工具)它将打印地图工具已更改在控制台上, 插件工具栏中的图标将被取消选中。