1

I have a PyQGIS code made partially with graphical modeler and some personal completion.

Basically, based on this code I want:

  1. Type any city from my point shapefile, select it by attribute.
  2. Zoom to the point shapefile at a specific scale.

Issue: On the second part of the script, while trying to zoom at a specific scale, the map canvas freezes, trying to zoom in or out does not rescale the map canvas.

Example:

a) 4 cities as example enter image description here b) Selecting a specific city by attribute enter image description here c) Zooming to specific scale (from here starts the issue). enter image description here d) Trying to zoom out it does not render anything else except what it was already rendered. enter image description here This is the first part exported from graphical modeler which works perfecly:

def initAlgorithm(self, config=None):
    self.addParameter(QgsProcessingParameterVectorLayer('cities', 'Cities', types=[QgsProcessing.TypeVectorPoint], defaultValue=None))
    self.addParameter(QgsProcessingParameterExpression('city_names', 'City Names', parentLayerParameterName='', defaultValue=''))

def processAlgorithm(self, parameters, context, model_feedback):
    feedback = QgsProcessingMultiStepFeedback(1, model_feedback)
    results = {}
    outputs = {}

    # Select by attribute
    alg_params = {
        'FIELD': 'Name',
        'INPUT': parameters['cities'],
        'METHOD': 0,  # creating new selection
        'OPERATOR': 0,  # =
        'VALUE': parameters['city_names']
    }
    outputs['SelectByAttribute'] = processing.run('qgis:selectbyattribute', alg_params, context=context, feedback=feedback, is_child_algorithm=True)

This is the second part completed by me (where the issue starts at zoomscale):

    iface.actionZoomToSelected().trigger()
    layer = iface.activeLayer()
    canvas_city = iface.mapCanvas()
    area = layer.selectedFeatures()[0]['Area_km2']

    if area > 400:
        canvas_city.zoomScale(100000)
    else:
        canvas_city.zoomScale(50000)

    canvas_city.refresh()
    
    return results

def name(self):
    return 'model'

def displayName(self):
    return 'model'

def group(self):
    return ''

def groupId(self):
    return ''

def createInstance(self):
    return Model()

I have also tried inserting the zoom scale values into a variable like this (with no success):

    iface.actionZoomToSelected().trigger()
    layer = iface.activeLayer()
    area = layer.selectedFeatures()[0]['Area_km2']
    canvas_city = iface.mapCanvas()
    
    if area > 400:
        scale = 10000
    else:
        scale = 50000
    
    canvas_city.zoomScale(scale)

    # refresh the canvas
    canvas_city.refresh()
    
    return results

def name(self):
    return 'model'

def displayName(self):
    return 'model'

def group(self):
    return ''

def groupId(self):
    return ''

def createInstance(self):
    return Model()

Does anyone know if I did something wrong regarding the second part of the code lines, or is a QGIS issue?

1
  • 1
    Please read the Tip given here (the paragraph with green background). Interacting with the map canvas from inside the processAlgorithm() method is definitely not thread safe. Try re-implementing the flags() method and return QgsProcessingAlgorithm.FlagNoThreading. Not really what it's intended for, but you could also try putting the zooming logic in the postProcessAlgorithm() method.
    – Ben W
    Jul 16 at 11:44

1 Answer 1

0

I have updated the script by implementing flags() and return QgsProcessingAlgorithm.FlagNoThreading and it works perfectly.

This part of code where flags() was added:

    if area > 400:
        scale = 150000
    else:
        scale = 50000
    
    canvas.zoomScale(scale)

    # refresh the canvas
    canvas.refresh()
    
    return results

def flags(self):
    return QgsProcessingAlgorithm.FlagNoThreading

def name(self):
    return 'model'

def displayName(self):
    return 'model'

def group(self):
    return ''

def groupId(self):
    return ''

def createInstance(self):
    return Model()

def groupId(self):
    return ''

def createInstance(self):
    return Model()

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.