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
The main problems are:
You are missing the
is_child_algorithm=True
argument in theprocessing.run()
calls. This is required when an algorithm is being run as a processing step inside theprocessAlgorithm()
method of a parent algorithm script.You should not programmatically load your result layer. Instead, declare a proper output parameter and let the processing framework handle loading the output.
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()
Explore related questions
See similar questions with these tags.