0

After building a plugin using Plugin Builder I've encountered an issue where selections made in a result-loaded layer are not visible in canvas. I have to zoom in or out to refresh this layer. This problem persists despite attempts to force the canvas to refresh using the following lines of code:

    iface.mapCanvas().refreshAllLayers()
    iface.mapCanvas().refresh()

My Algorithm:

from qgis.PyQt.QtCore import QCoreApplication
from qgis.utils import iface 
from qgis.core import (
    QgsProcessing,
    QgsProcessingAlgorithm,
    QgsProcessingParameterNumber,
    QgsProcessingParameterVectorLayer
)


class PluginTeste(QgsProcessingAlgorithm):
        
    INPUT1 = 'INPUT1'
    BUFFER1 = 'BUFFER1'


    def initAlgorithm(self, config):

        self.addParameter(
            QgsProcessingParameterVectorLayer(
                name = self.INPUT1,
                description = 'Vetor INPUT',
                defaultValue = 'TH_Geral',
                types = [QgsProcessing.TypeVectorPolygon]
            )
        )

        self.addParameter(
            QgsProcessingParameterNumber(
                name = self.BUFFER1,
                description = 'Buffer (m)',
                defaultValue = 100,
                type = QgsProcessingParameterNumber.Integer
            )
        )

    def processAlgorithm(self, parameters, context, feedback):

        input_th_geral = self.parameterAsVectorLayer(parameters, self.INPUT1, context)
        buffer_lim_th = self.parameterAsInt(parameters, self.BUFFER1, context)

        buffer_result = processing.run("native:buffer", {
            'INPUT': input_th_geral,
            'DISTANCE': f'-{buffer_lim_th}',
            'SEGMENTS': 5,
            'END_CAP_STYLE': 0,
            'JOIN_STYLE': 0,
            'MITER_LIMIT': 2,
            'DISSOLVE': False,
            'SEPARATE_DISJOINT': False,
            'OUTPUT': 'memory:TEMP Buffer'
        }, context=context, feedback=feedback)['OUTPUT']

        reprojection = processing.run('qgis:reprojectlayer', {
            'INPUT': buffer_result,
            'TARGET_CRS': QgsProject.instance().crs(),
            'OUTPUT': 'memory:Reprojected'
        }, context=context, feedback=feedback)
        
        reprojection_result = reprojection['OUTPUT']
        print(reprojection_result)

        QgsProject.instance().addMapLayer(reprojection_result, True)

        iface.mapCanvas().refreshAllLayers()
        iface.mapCanvas().refresh()

        return{}

        
    def name(self):
        return 'TESTE'

    def displayName(self):
        return self.tr(self.name())

    def group(self):
        return self.tr(self.groupId())

    def groupId(self):
        return 'Teste'

    def shortHelpString(self):
        return self.tr("""
                       Plugin para teste
                       """)

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

    def createInstance(self):
        return PluginTeste()

1 Answer 1

3

The main problems are:

  1. You are missing the is_child_algorithm=True argument in the processing.run() calls. This is required when an algorithm is being run as a processing step inside the processAlgorithm() method of a parent algorithm script.

  2. You should not programmatically load your result layer. Instead, declare a proper output parameter and let the processing framework handle loading the output.

  3. Processing algorithms are run in a background thread by default. Interacting with the main thread (canvas, interface, print statements etc.) in the processAlgorithm() method is a sure way to get unpredictable behavior or crashes.

By the way, inside a processing algorithm, it is better to access the project object via context.project() instead of the usual QgsProject.instance().

I recommend reading the following sections of the docs:

Writing new Processing algorithms as Python scripts- Best practices for writing script algorithms

Writing new Processing algorithms as Python scripts- Flags

Below is an example of how you could modify your script to work properly:

from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (
    QgsProcessing,
    QgsProcessingAlgorithm,
    QgsProcessingParameterDistance,
    QgsProcessingParameterVectorLayer,
    QgsProcessingParameterVectorDestination
)
import processing

class PluginTeste(QgsProcessingAlgorithm):
        
    INPUT1 = 'INPUT1'
    BUFFER1 = 'BUFFER1'
    OUTPUT = 'OUTPUT'


    def initAlgorithm(self, config):

        self.addParameter(
            QgsProcessingParameterVectorLayer(
                self.INPUT1,
                'Input Layer',
                [QgsProcessing.TypeVectorPolygon],
                'TH_Geral'
            )
        )

        self.addParameter(
            QgsProcessingParameterDistance(
                self.BUFFER1,
                'Buffer distance',
                -100,
                self.INPUT1
            )
        )
        
        self.addParameter(
            QgsProcessingParameterVectorDestination(
            self.OUTPUT,
            'Output Layer',
            QgsProcessing.TypeVectorPolygon
            )
        )

    def processAlgorithm(self, parameters, context, feedback):

        input_th_geral = self.parameterAsVectorLayer(parameters, self.INPUT1, context)
        buffer_lim_th = self.parameterAsInt(parameters, self.BUFFER1, context)

        buffer_result = processing.run("native:buffer", {
            'INPUT': input_th_geral,
            'DISTANCE': buffer_lim_th,
            'SEGMENTS': 5,
            'END_CAP_STYLE': 0,
            'JOIN_STYLE': 0,
            'MITER_LIMIT': 2,
            'DISSOLVE': False,
            'SEPARATE_DISJOINT': False,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }, context=context, feedback=feedback, is_child_algorithm=True)['OUTPUT']

        reprojection = processing.run('native:reprojectlayer', {
            'INPUT': buffer_result,
            'TARGET_CRS': context.project().crs(),
            'OUTPUT': parameters[self.OUTPUT]
        }, context=context, feedback=feedback, is_child_algorithm=True)
        
        reprojection_result = reprojection['OUTPUT']

        return{'OUTPUT': reprojection_result}

        
    def name(self):
        return 'TESTE'

    def displayName(self):
        return self.tr(self.name())

    def group(self):
        return self.tr(self.groupId())

    def groupId(self):
        return 'Teste'

    def shortHelpString(self):
        return self.tr("Plugin para teste")

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

    def createInstance(self):
        return PluginTeste()

If you really want to just create an output memory layer and automatically add it to the project without giving the user the option to specify an output (temporary or save to file/load/don't load etc), you can use context.addLayerToLoadOnCompletion(). E.g.

details = context.LayerDetails('Reprojected', context.project(), 'Reprojected')

context.addLayerToLoadOnCompletion(reprojection_result, details)

Full example:

from qgis.PyQt.QtCore import QCoreApplication
from qgis.core import (
    QgsProcessing,
    QgsProcessingAlgorithm,
    QgsProcessingParameterDistance,
    QgsProcessingParameterVectorLayer
)
import processing

class PluginTeste(QgsProcessingAlgorithm):
        
    INPUT1 = 'INPUT1'
    BUFFER1 = 'BUFFER1'


    def initAlgorithm(self, config):

        self.addParameter(
            QgsProcessingParameterVectorLayer(
                self.INPUT1,
                'Input Layer',
                [QgsProcessing.TypeVectorPolygon],
                'TH_Geral'
            )
        )

        self.addParameter(
            QgsProcessingParameterDistance(
                self.BUFFER1,
                'Buffer distance',
                -100,
                self.INPUT1
            )
        )
        

    def processAlgorithm(self, parameters, context, feedback):

        input_th_geral = self.parameterAsVectorLayer(parameters, self.INPUT1, context)
        buffer_lim_th = self.parameterAsInt(parameters, self.BUFFER1, context)

        buffer_result = processing.run("native:buffer", {
            'INPUT': input_th_geral,
            'DISTANCE': buffer_lim_th,
            'SEGMENTS': 5,
            'END_CAP_STYLE': 0,
            'JOIN_STYLE': 0,
            'MITER_LIMIT': 2,
            'DISSOLVE': False,
            'SEPARATE_DISJOINT': False,
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }, context=context, feedback=feedback, is_child_algorithm=True)['OUTPUT']

        reprojection = processing.run('native:reprojectlayer', {
            'INPUT': buffer_result,
            'TARGET_CRS': context.project().crs(),
            'OUTPUT': QgsProcessing.TEMPORARY_OUTPUT
        }, context=context, feedback=feedback, is_child_algorithm=True)
        
        reprojection_result = reprojection['OUTPUT']
        
        details = context.LayerDetails('Reprojected', context.project(), 'Reprojected')
        
        context.addLayerToLoadOnCompletion(reprojection_result, details)

        return{'OUTPUT': reprojection_result}

        
    def name(self):
        return 'TESTE'

    def displayName(self):
        return self.tr(self.name())

    def group(self):
        return self.tr(self.groupId())

    def groupId(self):
        return 'Teste'

    def shortHelpString(self):
        return self.tr("Plugin para teste")

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

    def createInstance(self):
        return PluginTeste()
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.