构建处理插件(QGIS3)

在上一个教程中 以 Python 制作附加元件 (QGIS3) ,您学习了如何创建一个python插件-包括用户界面和处理数据的自定义逻辑。虽然这些类型的插件很有用,但它将设计用户界面的负担推给了插件作者。这导致每个插件都有不同的方式与之交互——这让用户感到困惑。此外,常规插件不会与QGIS的其他部分交互。例如,不能使用其他算法的插件功能。如果您想要编写的插件主要用于分析,而您想要的用户交互仅限于让用户选择输入和输出,那么使用处理框架编写插件有一种更简单、更受欢迎的方法。它消除了设计用户界面的需要—简化了流程。内置的处理库根据您的输入创建一个标准的处理接口,它的外观和行为与QGIS中的任何其他处理算法一样。它还与处理框架的其他部分无缝集成,因此您的插件算法可以用于批处理、图形建模、从python控制台调用等。

任务概述

我们将从教程中重新实现一个简单的插件 以 Python 制作附加元件 (QGIS3) 作为处理插件。它将产生一个新的处理提供程序 Save Attributes 还有一个算法 Save Attributes as CSV 这将允许用户选择一个向量层并将其属性写入CSV文件。

把工具拿来

文本编辑器或Python IDE

任何一种软件开发都需要一个好的文本编辑器。如果您已经有了一个喜爱的文本编辑器或IDE(集成开发环境),则可以在本教程中使用它。否则,每个平台都为文本编辑器提供多种免费或付费选项。选择一个适合你需要的。

本教程在Windows上使用Notepad++编辑器。

Windows

Notepad++ 是一个很好的windows免费编辑器。下载并安装 Notepad++ editor .

注解

如果您使用的是记事本++,请确保转到 Settings ‣ Preferences ‣ Tab Settings 使能 Replace by space . Python对空白非常敏感,此设置将确保正确处理制表符和空格。

插件生成器插件

有一个很有用的QGIS插件名为 Plugin Builder 它为插件创建所有必需的文件和样板代码。查找并安装 Plugin Builder 插件。见 使用附加元件 有关如何安装插件的详细信息。

插件重新加载插件

这是另一个帮助插件,它允许插件的迭代开发。使用此插件,您可以更改插件代码并将其反映在QGIS中,而无需每次重新启动QGIS。查找并安装 Plugin Reloader 插件。见 使用附加元件 有关如何安装插件的详细信息。

注解

插件重载器是一个实验性插件。确保你检查过 Show also experimental plugins 在里面 Plugin Manager 如果找不到设置。

程序

  1. 打开QGIS。去 Plugins ‣ Plugin Builder ‣ Plugin Builder .

../../_images/1107.png
  1. 你会看到 QGIS Plugin Builder 与窗体对话。您可以填写与我们插件相关的详细信息。这个 Class name 将是包含插件逻辑的Python类的名称。这也是包含所有插件文件的文件夹的名称。进入 SaveAttributes 作为类名。这个 Plugin name 是插件将出现在 Plugin Manager . 输入名称为 Save Attributes (Processing) . 在中添加说明 Description 字段。这个 Module name 将是插件的主python文件的名称。输入为 save_attributes_processing . 保持版本号不变,并在相应的字段中输入您的姓名和电子邮件地址。点击 Next .

../../_images/260.png
  1. 输入插件的简要说明 About 对话并单击 Next .

../../_images/338.png
  1. 选择 Processing ProviderTemplate 选择器。这个 Algorithm name 值将是用户如何在处理工具箱中找到处理算法。输入为 Save Attributes as CSV . 离开 Algorithm group 空白。输入 Provider name 作为 Save Attributes . 在中输入说明 Provider description 字段。点击 Next .

../../_images/423.png
  1. Plugin builder将提示您生成文件的类型。保留默认选择并单击 Next .

../../_images/523.png
  1. 由于我们不打算发布插件,您可以将 Bug trackerRepositoryHome page 默认值。检查 Flag the plugin as experimental 在底部的框中单击 Next .

../../_images/623.png
  1. 系统将提示您为插件选择目录。现在,将其保存到计算机上可以轻松找到的目录中,然后单击 Generate .

../../_images/722.png
  1. 下一步,按 generate 按钮。创建插件模板后,您将看到一个确认对话框。

../../_images/822.png

注解

你可能会得到一个提示说路径中没有找到pyrcc5。您可以忽略此消息。

  1. QGIS中的插件存储在一个特殊的文件夹中。我们必须先将插件目录复制到该文件夹,然后才能使用它。在QGIS中,通过转到 Settings ‣ User Profiles ‣ Open Active Profile Folder .

../../_images/922.png
  1. 在profile文件夹中,将plugin文件夹复制到 python ‣ plugins 子文件夹。

../../_images/1022.png
  1. 现在我们准备好第一次看看我们创建的全新插件。关闭QGIS并再次启动它。去 Plugins ‣ Manage and Install plugins 并启用 Save Attributes (Processing) 中的插件 Installed 标签。

../../_images/1125.png
  1. Processing ‣ Toolbox . 您将注意到底部有一个新的提供者 Save Attributes . 展开它以找到一个名为 Save Attributes as CSV . 双击启动它。

../../_images/1222.png
  1. 您将注意到一个熟悉的处理算法对话框,其中一个下拉列表用于输入层,一个选择器用于输出层。我们现在将自定义此对话框以满足我们的需要。关闭此对话框。

../../_images/1321.png
  1. 转到插件目录并加载文件 save_attributes_processing_algorithm.py 在文本编辑器中。对于我们的插件,我们将向量层作为输入,并将CSV文件作为输出。所以不是进口 QgsProcessingParameterFeatureSink 作为输出-用于矢量层-添加 QgsProcessingParameterFileDestination 这是一个文件。

    from qgis.core import (QgsProcessing,
                     QgsFeatureSink,
                     QgsProcessingAlgorithm,
                     QgsProcessingParameterFeatureSource,
                     QgsProcessingParameterFileDestination)
    

    接下来,向下滚动并在下面定义输出参数 initAlgorithm() 方法,代码如下。

    self.addParameter(
          QgsProcessingParameterFileDestination(
              self.OUTPUT,
              self.tr('Output File'),
              'CSV files (*.csv)',
          )
    )
    
../../_images/14a.png ../../_images/14b.png
  1. 让我们重新加载插件,以便在对话框窗口中看到更改。去 Plugin ‣ Plugin Reloader ‣ Choose a plugin to be reloaded . 选择 save_attributes_processingConfigure Plugin reloader 对话框。

../../_images/1520.png
  1. 单击 Reload plugin 按钮加载插件的最新版本。为了测试这个新功能,我们必须在QGIS中加载一些层。加载一些层后,启动 Save Attributes ‣ Save Attributes as CSV 算法。您将看到输出更改为文件而不是图层。

../../_images/1618.png
  1. 让我们为算法添加一些逻辑,该算法接受选定的向量层并将属性写入CSV文件。有关此代码的说明,请参见 Python 程式设计入门 (QGIS3) . 这里的显著区别是帮助显示处理进度的计数器。将以下代码添加到 processAlgorithm 方法并保存文件。

    def processAlgorithm(self, parameters, context, feedback):
      """
      Here is where the processing itself takes place.
      """
      source = self.parameterAsSource(parameters, self.INPUT, context)
      csv = self.parameterAsFileOutput(parameters, self.OUTPUT, context)
    
      fieldnames = [field.name() for field in source.fields()]
    
      # Compute the number of steps to display within the progress bar and
      # get features from source
      total = 100.0 / source.featureCount() if source.featureCount() else 0
      features = source.getFeatures()
    
      with open(csv, 'w') as output_file:
        # write header
        line = ','.join(name for name in fieldnames) + '\n'
        output_file.write(line)
        for current, f in enumerate(features):
            # Stop the algorithm if cancel button has been clicked
            if feedback.isCanceled():
                break
    
            # Add a feature in the sink
            line = ','.join(str(f[name]) for name in fieldnames) + '\n'
            output_file.write(line)
    
            # Update the progress bar
            feedback.setProgress(int(current * total))
    
      return {self.OUTPUT: csv}
    
../../_images/1719.png
  1. 回到QGIS主窗口,点击 Reload plugin 按钮。启动 Save Attributes ‣ Save Attributes as CSV 算法。为 Input layer . 下一步,单击 按钮旁边 Output file .

../../_images/1818.png
  1. 命名输出文件 test.csv 然后点击 Run . 算法将在选定的位置运行并生成CSV文件。

../../_images/1915.png
  1. 如前所述,尽管该算法来自插件,但它与内置的处理工具集成得非常好。为了演示这一点,让我们使用内置的批处理接口运行此算法。右键单击算法并选择 Execute as Batch Process.. .

../../_images/2012.png
  1. 您可以选择多个输入并成批运行此算法,以便在一次运行中生成多个CSV文件。如果不熟悉批处理界面,请参见 使用处理框架进行批次处理 (QGIS3) 用于分步说明。

../../_images/2117.png
  1. 插件已经准备好了,您可以以当前形式发送它。但是,我们可以通过使处理插件的行为类似于常规插件来改善用户体验。使用下面概述的混合方法,可以添加菜单项和工具栏按钮。这样,用户就可以更容易地发现和启动作为插件一部分安装的工具。我们需要一个插件图标。下载 logo.png 并将其复制到插件目录。

../../_images/2215.png
  1. 打开文件 save_attributes_processing.py . 在文件顶部添加以下导入。

    from qgis.PyQt.QtWidgets import QAction
    from qgis.PyQt.QtGui import QIcon
    
    from qgis.core import QgsProcessingAlgorithm, QgsApplication
    import processing
    

    向下滚动并修改 __init__() 方法初始化iface。

    def __init__(self, iface):
     self.provider = None
     self.iface = iface
    

    进一步向下滚动并找到 initGui 方法。它只包含初始化处理提供程序的代码。我们将添加代码以添加工具栏按钮和菜单项。我们还需要将代码添加到 unload 方法,以便在移除插件时移除这些元素。

    def initGui(self):
      self.initProcessing()
    
      icon = os.path.join(os.path.join(cmd_folder, 'logo.png'))
      self.action = QAction(
          QIcon(icon),
          u"Save Attributes as CSV", self.iface.mainWindow())
      self.action.triggered.connect(self.run)
      self.iface.addPluginToMenu(u"&SaveAttributes", self.action)
      self.iface.addToolBarIcon(self.action)
    
    def unload(self):
      QgsApplication.processingRegistry().removeProvider(self.provider)
      self.iface.removePluginMenu(u"&SaveAttributes", self.action)
      self.iface.removeToolBarIcon(self.action)
    

    我们已经连接了按钮和菜单项来触发 run 方法。在底部添加使用helper方法的新方法 execAlgorithmDialog 启动处理算法。

    def run(self):
      processing.execAlgorithmDialog("Save Attributes:Save Attributes as CSV")
    
../../_images/23a1.png ../../_images/23b1.png ../../_images/23c.png
  1. 接下来,我们需要对 __init__.py 插件目录中的文件。打开文件并添加 iface 返回语句,因此对QGIS接口的引用将传递给插件。

../../_images/2411.png
  1. 回到QGIS主窗口,点击 Reload plugin 按钮。您将在下面看到一个新的工具栏图标和一个菜单项 Plugins ‣ SaveAttributes ‣ Save Attributes as CSV . 您可以单击这些启动 Save Attributes as CSV 算法。您将注意到处理提供程序和工具栏中的算法仍具有默认图标。我们来解决这个问题。

../../_images/2511.png
  1. 打开 save_attributes_processing_provider.py 插件目录中的文件。在顶部添加导入,如下所示。

    import os
    import inspect
    from qgis.PyQt.QtGui import QIcon
    

    修改 icon 方法添加自定义图标。

    def icon(self):
      cmd_folder = os.path.split(inspect.getfile(inspect.currentframe()))[0]
      icon = QIcon(os.path.join(os.path.join(cmd_folder, 'logo.png')))
      return icon
    
../../_images/26a.png ../../_images/26b.png
  1. 接下来,打开 save_attributes_processing_algorithm.py 文件。在顶部添加导入,如下所示。

    import os
    import inspect
    from qgis.PyQt.QtGui import QIcon
    

    添加新的 icon 方法,代码如下。

    def icon(self):
      cmd_folder = os.path.split(inspect.getfile(inspect.currentframe()))[0]
      icon = QIcon(os.path.join(os.path.join(cmd_folder, 'logo.png')))
      return icon
    
../../_images/27a.png ../../_images/27b.png
  1. 重新加载插件,你会看到提供者和算法都有我们的自定义图标。

../../_images/288.png
  1. 您可以压缩插件目录并与用户共享。他们可以解压到他们的插件目录的内容,并尝试您的插件。如果这是一个真正的插件,你可以把它上传到 QGIS Plugin Repository 这样所有的QGIS用户都能找到并下载你的插件。

注解

此插件仅用于演示。请勿发布此插件或将其上载到QGIS插件库。

下面是作为参考的完整源文件。

__init__.py

# -*- coding: utf-8 -*-
"""
/***************************************************************************
 SaveAttributes
                                 A QGIS plugin
 This plugin adds an algorithm to save attributes of selected layer as a CSV file
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2019-09-18
        copyright            : (C) 2019 by Ujaval Gandhi
        email                : ujaval@spatialthoughts.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
 This script initializes the plugin, making it known to QGIS.
"""

__author__ = 'Ujaval Gandhi'
__date__ = '2019-09-18'
__copyright__ = '(C) 2019 by Ujaval Gandhi'


# noinspection PyPep8Naming
def classFactory(iface):  # pylint: disable=invalid-name
    """Load SaveAttributes class from file SaveAttributes.

    :param iface: A QGIS interface instance.
    :type iface: QgsInterface
    """
    #
    from .save_attributes_processing import SaveAttributesPlugin
    return SaveAttributesPlugin(iface)

save_attributes_processing.py

# -*- coding: utf-8 -*-

"""
/***************************************************************************
 SaveAttributes
                                 A QGIS plugin
 This plugin adds an algorithm to save attributes of selected layer as a CSV file
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2019-09-18
        copyright            : (C) 2019 by Ujaval Gandhi
        email                : ujaval@spatialthoughts.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

__author__ = 'Ujaval Gandhi'
__date__ = '2019-09-18'
__copyright__ = '(C) 2019 by Ujaval Gandhi'

# This will get replaced with a git SHA1 when you do a git archive

__revision__ = '$Format:%H$'

import os
import sys
import inspect

from qgis.PyQt.QtWidgets import QAction
from qgis.PyQt.QtGui import QIcon

from qgis.core import QgsProcessingAlgorithm, QgsApplication
import processing
from .save_attributes_processing_provider import SaveAttributesProvider

cmd_folder = os.path.split(inspect.getfile(inspect.currentframe()))[0]

if cmd_folder not in sys.path:
    sys.path.insert(0, cmd_folder)


class SaveAttributesPlugin(object):

    def __init__(self, iface):
        self.provider = None
        self.iface = iface

    def initProcessing(self):
        """Init Processing provider for QGIS >= 3.8."""
        self.provider = SaveAttributesProvider()
        QgsApplication.processingRegistry().addProvider(self.provider)

    def initGui(self):
        self.initProcessing()

        icon = os.path.join(os.path.join(cmd_folder, 'logo.png'))
        self.action = QAction(
            QIcon(icon),
            u"Save Attributes as CSV", self.iface.mainWindow())
        self.action.triggered.connect(self.run)
        self.iface.addPluginToMenu(u"&SaveAttributes", self.action)
        self.iface.addToolBarIcon(self.action)

    def unload(self):
        QgsApplication.processingRegistry().removeProvider(self.provider)
        self.iface.removePluginMenu(u"&SaveAttributes", self.action)
        self.iface.removeToolBarIcon(self.action)
        
    def run(self):
      processing.execAlgorithmDialog("Save Attributes:Save Attributes as CSV")

save_attributes_processing_algorithm.py

# -*- coding: utf-8 -*-

"""
/***************************************************************************
 SaveAttributes
                                 A QGIS plugin
 This plugin adds an algorithm to save attributes of selected layer as a CSV file
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2019-09-18
        copyright            : (C) 2019 by Ujaval Gandhi
        email                : ujaval@spatialthoughts.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

__author__ = 'Ujaval Gandhi'
__date__ = '2019-09-18'
__copyright__ = '(C) 2019 by Ujaval Gandhi'

# This will get replaced with a git SHA1 when you do a git archive

__revision__ = '$Format:%H$'

import os
import inspect
from qgis.PyQt.QtGui import QIcon

from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (QgsProcessing,
                       QgsFeatureSink,
                       QgsProcessingAlgorithm,
                       QgsProcessingParameterFeatureSource,
                       QgsProcessingParameterFileDestination)


class SaveAttributesAlgorithm(QgsProcessingAlgorithm):
    """
    This is an example algorithm that takes a vector layer and
    creates a new identical one.

    It is meant to be used as an example of how to create your own
    algorithms and explain methods and variables used to do it. An
    algorithm like this will be available in all elements, and there
    is not need for additional work.

    All Processing algorithms should extend the QgsProcessingAlgorithm
    class.
    """

    # Constants used to refer to parameters and outputs. They will be
    # used when calling the algorithm from another algorithm, or when
    # calling from the QGIS console.

    OUTPUT = 'OUTPUT'
    INPUT = 'INPUT'

    def initAlgorithm(self, config):
        """
        Here we define the inputs and output of the algorithm, along
        with some other properties.
        """

        # We add the input vector features source. It can have any kind of
        # geometry.
        self.addParameter(
            QgsProcessingParameterFeatureSource(
                self.INPUT,
                self.tr('Input layer'),
                [QgsProcessing.TypeVectorAnyGeometry]
            )
        )

        # We add a file output of type CSV.
        self.addParameter(
            QgsProcessingParameterFileDestination(
                self.OUTPUT,
                self.tr('Output File'),
                'CSV files (*.csv)',
            )
        )

    def processAlgorithm(self, parameters, context, feedback):
        """
        Here is where the processing itself takes place.
        """
        source = self.parameterAsSource(parameters, self.INPUT, context)
        csv = self.parameterAsFileOutput(parameters, self.OUTPUT, context)

        fieldnames = [field.name() for field in source.fields()]

        # Compute the number of steps to display within the progress bar and
        # get features from source
        total = 100.0 / source.featureCount() if source.featureCount() else 0
        features = source.getFeatures()

        with open(csv, 'w') as output_file:
          # write header
          line = ','.join(name for name in fieldnames) + '\n'
          output_file.write(line)
          for current, f in enumerate(features):
              # Stop the algorithm if cancel button has been clicked
              if feedback.isCanceled():
                  break

              # Add a feature in the sink
              line = ','.join(str(f[name]) for name in fieldnames) + '\n'
              output_file.write(line)

              # Update the progress bar
              feedback.setProgress(int(current * total))

        return {self.OUTPUT: csv}

    def name(self):
        """
        Returns the algorithm name, used for identifying the algorithm. This
        string should be fixed for the algorithm, and must not be localised.
        The name should be unique within each provider. Names should contain
        lowercase alphanumeric characters only and no spaces or other
        formatting characters.
        """
        return 'Save Attributes as CSV'

    def displayName(self):
        """
        Returns the translated algorithm name, which should be used for any
        user-visible display of the algorithm name.
        """
        return self.tr(self.name())

    def group(self):
        """
        Returns the name of the group this algorithm belongs to. This string
        should be localised.
        """
        return self.tr(self.groupId())

    def groupId(self):
        """
        Returns the unique ID of the group this algorithm belongs to. This
        string should be fixed for the algorithm, and must not be localised.
        The group id should be unique within each provider. Group id should
        contain lowercase alphanumeric characters only and no spaces or other
        formatting characters.
        """
        return ''

    def tr(self, string):
        return QCoreApplication.translate('Processing', string)

    def icon(self):
        """
        Should return a QIcon which is used for your provider inside
        the Processing toolbox.
        """
        cmd_folder = os.path.split(inspect.getfile(inspect.currentframe()))[0]
        icon = QIcon(os.path.join(os.path.join(cmd_folder, 'logo.png')))
        return icon
        
    def createInstance(self):
        return SaveAttributesAlgorithm()

save_attributes_processing_provider.py

# -*- coding: utf-8 -*-

"""
/***************************************************************************
 SaveAttributes
                                 A QGIS plugin
 This plugin adds an algorithm to save attributes of selected layer as a CSV file
 Generated by Plugin Builder: http://g-sherman.github.io/Qgis-Plugin-Builder/
                              -------------------
        begin                : 2019-09-18
        copyright            : (C) 2019 by Ujaval Gandhi
        email                : ujaval@spatialthoughts.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""

__author__ = 'Ujaval Gandhi'
__date__ = '2019-09-18'
__copyright__ = '(C) 2019 by Ujaval Gandhi'

# This will get replaced with a git SHA1 when you do a git archive

__revision__ = '$Format:%H$'

import os
import inspect
from qgis.PyQt.QtGui import QIcon

from qgis.core import QgsProcessingProvider
from .save_attributes_processing_algorithm import SaveAttributesAlgorithm


class SaveAttributesProvider(QgsProcessingProvider):

    def __init__(self):
        """
        Default constructor.
        """
        QgsProcessingProvider.__init__(self)

    def unload(self):
        """
        Unloads the provider. Any tear-down steps required by the provider
        should be implemented here.
        """
        pass

    def loadAlgorithms(self):
        """
        Loads all algorithms belonging to this provider.
        """
        self.addAlgorithm(SaveAttributesAlgorithm())
        # add additional algorithms here
        # self.addAlgorithm(MyOtherAlgorithm())

    def id(self):
        """
        Returns the unique provider id, used for identifying the provider. This
        string should be a unique, short, character only string, eg "qgis" or
        "gdal". This string should not be localised.
        """
        return 'Save Attributes'

    def name(self):
        """
        Returns the provider name, which is used to describe the provider
        within the GUI.

        This string should be short (e.g. "Lastools") and localised.
        """
        return self.tr('Save Attributes')

    def icon(self):
        """
        Should return a QIcon which is used for your provider inside
        the Processing toolbox.
        """
        cmd_folder = os.path.split(inspect.getfile(inspect.currentframe()))[0]
        icon = QIcon(os.path.join(os.path.join(cmd_folder, 'logo.png')))
        return icon

    def longName(self):
        """
        Returns the a longer version of the provider name, which can include
        extra details such as version numbers. E.g. "Lastools LIDAR tools
        (version 2.2.1)". This string should be localised. The default
        implementation returns the same string as name().
        """
        return self.name()