Skip to content

53_构建处理插件(QGIS3)

原文链接: https://www.qgistutorials.com/en/docs/3/processing_python_plugin.html

构建处理插件 (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

注意

如果您使用 Notepad++,请确保进入 设置 ‣ 首选项 ‣ 制表符设置 并启用 "用空格替换"。Python 对空格非常敏感,此设置将确保制表符和空格被正确处理。

Plugin Builder 插件

有一个名为 Plugin Builder 的实用 QGIS 插件,它能为插件创建所有必要的文件和样板代码。查找并安装 Plugin Builder 插件。有关如何安装插件的更多详细信息,请参阅 使用插件

Plugins Reloader 插件

这是另一个辅助插件,允许对插件进行迭代开发。使用此插件,您可以更改插件代码,并在 QGIS 中反映出来,而无需每次都重新启动 QGIS。查找并安装 Plugin Reloader 插件。有关如何安装插件的更多详细信息,请参阅 使用插件

注意

Plugin Reloader 是一个实验性插件。如果找不到它,请确保您已在插件管理器设置中勾选 "显示实验性插件"。

操作步骤

  1. 打开 QGIS。转到 插件 ‣ Plugin Builder ‣ Plugin Builder。

../../_images/1144.png

  1. 您将看到带有表单的 QGIS Plugin Builder 对话框。您可以在表单中填写与我们插件相关的详细信息。Class name 将是包含插件逻辑的 Python 类的名称。这也将是包含所有插件文件的文件夹的名称。输入 SaveAttributes 作为类名。Plugin name 是您的插件在插件管理器中显示的名称。输入名称为 Save Attributes (Processing)。在 Description 字段中添加描述。Module name 将是插件主 python 文件的名称。输入为 save_attributes_processing。将版本号保持原样,并在相应字段中输入您的姓名和电子邮件地址。点击 Next。

../../_images/2123.png

  1. 输入插件的简短描述以用于 About 对话框,然后点击 Next。

../../_images/359.png

  1. 从 Template 选择器中选择 Processing Provider。Algorithm name 值将是用户在 Processing Toolbox 中找到处理算法的方式。将其输入为 Save Attributes as CSV。将 Algorithm group 留空。输入 Provider name 为 Save Attributes。在 Provider description 字段中输入描述。点击 Next。

../../_images/433.png

  1. Plugin Builder 将提示您选择要生成的文件类型。保持默认选择并点击 Next。

../../_images/529.png

  1. 由于我们不打算发布此插件,您可以将 Bug tracker、Repository 和 Home page 的值保留为默认值。勾选底部的 Flag the plugin as experimental 框,然后点击 Next。

../../_images/629.png

  1. 系统将提示您为插件选择一个目录。现在,将其保存到计算机上容易找到的目录,然后单击 Generate。

../../_images/728.png

  1. 接下来,按下 generate 按钮。插件模板创建完成后,您将看到一个确认对话框。

../../_images/828.png

注意

您可能会收到提示,说在路径中找不到 pyrcc5。您可以忽略此消息。

  1. QGIS 中的插件存储在一个特殊文件夹中。我们必须先将插件目录复制到该文件夹,然后才能使用它。在 QGIS 中,通过转到 设置 ‣ 用户配置文件 ‣ 打开活动配置文件文件夹 来定位您当前的配置文件文件夹。

../../_images/928.png

  1. 在配置文件文件夹中,将插件文件夹复制到 python ‣ plugins 子文件夹中。

../../_images/1036.png

  1. 现在我们准备看看我们创建的全新插件。关闭 QGIS 并重新启动它。转到 插件 ‣ 管理和安装插件,然后在 "已安装" 选项卡中启用 Save Attributes (Processing) 插件。

../../_images/1145.png

  1. 转到 处理 ‣ 工具箱。您会注意到底部有一个名为 Save Attributes 的新提供者。展开它以找到一个名为 Save Attributes as CSV 的算法。双击以启动它。

../../_images/1237.png

  1. 您会看到一个熟悉的处理算法对话框,其中包含一个用于输入图层的下拉列表和一个用于输出图层的选择器。现在我们将自定义此对话框以满足我们的需求。关闭此对话框。

../../_images/1335.png

  1. 转到插件目录,在文本编辑器中加载文件 save_attributes_processing_algorithm.py。对于我们的插件,我们将矢量图层作为输入,并将 CSV 文件作为输出。因此,不要导入用于矢量图层的输出 QgsProcessingParameterFeatureSink,而是添加用于文件的 QgsProcessingParameterFileDestination

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

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

    bash self.addParameter( QgsProcessingParameterFileDestination( self.OUTPUT, self.tr('Output File'), 'CSV files (*.csv)', ) )

../../_images/14a.png ../../_images/14b.png

  1. 让我们重新加载我们的插件,以便我们可以在对话框中看到更改。转到 插件 ‣ Plugin Reloader ‣ Choose a plugin to be reloaded。在 Configure Plugin reloader 对话框中选择 save_attributes_processing

../../_images/1528.png

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

../../_images/1626.png

  1. 让我们为算法添加一些逻辑,该逻辑获取选定的矢量图层并将其属性写入 CSV 文件。此代码的说明可以在 Python 编程入门 (QGIS3) 中找到。这里值得注意的区别是有助于显示处理进度的计数器。将以下代码添加到 processAlgorithm 方法中并保存文件。

    ```bash 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/1728.png

  1. 回到主 QGIS 窗口,通过点击 Reload plugin 按钮重新加载插件。启动 Save Attributes ‣ Save Attributes as CSV 算法。为 Input layer 选择一个图层。接下来,点击 Output file 旁边的 ... 按钮。

../../_images/1824.png

  1. 将输出文件命名为 test.csv 并点击 Run。算法将运行并在选定位置生成一个 CSV 文件。

../../_images/1920.png

  1. 如前所述,即使此算法来自插件,它也能与内置的处理工具很好地集成。为了演示这一点,让我们使用内置的批处理界面运行此算法。右键单击该算法并选择 Execute as Batch Process...

../../_images/2019.png

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

../../_images/2124.png

  1. 插件已准备就绪,您可以按当前形式交付它。但我们可以通过使处理插件表现得像常规插件来改善用户体验。使用下面概述的混合方法,您可以添加菜单项和工具栏按钮。这样,您为用户提供了一种更简单的方式来发现和启动作为插件一部分安装的工具。我们需要一个插件的图标。下载 logo.png 并将其复制到插件目录。

../../_images/2220.png

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

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

    from qgis.core import QgsProcessingAlgorithm, QgsApplication import processing ```

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

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

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

    ```bash 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 方法。在底部添加一个新方法,该方法使用辅助方法 execAlgorithmDialog 来启动处理算法。

    bash def run(self): processing.execAlgorithmDialog("Save Attributes:Save Attributes as CSV")

../../_images/23a1.png ../../_images/23b1.png ../../_images/23c.png

  1. 接下来,我们需要对插件目录中的 __init__.py 文件进行小修改。打开文件并在 return 语句中添加 iface,以便将对 QGIS 接口的引用传递给插件。

../../_images/2417.png

  1. 回到主 QGIS 窗口,通过点击 Reload plugin 按钮重新加载插件。您将看到一个新的工具栏图标和一个位于 插件 ‣ SaveAttributes ‣ Save Attributes as CSV 下的菜单项。您可以点击这些来启动 Save Attributes as CSV 算法。您会注意到工具栏中的处理提供者和算法仍然使用默认图标。让我们解决这个问题。

../../_images/2516.png

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

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

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

    bash 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 文件。在顶部添加导入,如下所示。

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

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

    bash 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/2816.png

  1. 您可以将插件目录压缩并与用户分享。他们可以将内容解压缩到他们的插件目录中并试用您的插件。如果这是一个真正的插件,您可以将其上传到 QGIS 插件存储库,这样所有 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()

如果您想提供反馈或分享您对本教程的体验,请在下方评论。(需要 GitHub 帐户)