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()
PolyGeo
65.5k29 gold badges115 silver badges350 bronze badges
asked Mar 6, 2024 at 22:41

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()
answered Mar 7, 2024 at 11:13
0

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

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.