PyQGIS – How to Zoom to Each Vector Layer Extent One at a Time

extentslayerspyqgiszoom

I'm trying to write a python script that zooms to each layer's extents and then exports the canvas as an image after every zoom.

I wrote a for loop that iterates through each layer, makes each one active, one at a time, then fetches the extents of layer while it is active, and then zooms to the extent.

While the script successfully iterates through each layer, the map canvas does not change meaning that it only zooms to the first layer extent in the list and stops there.

I even added a canvas.refresh() at the end of the loop, but nothing. Additionally, I added a sleep function using time.sleep(5), but no luck either.

I can see that each layer in the list being iterated through because each is selected and highlighted via setActiveLayer, but how can I zoom each active layer's extents one at a time, perhaps every 5 seconds? And how can I script the map canvas to reflect this selection, and zoom?

This is the code:

import math
import time
from qgis.core import QgsProject

for layer in QgsProject.instance().mapLayers().values():
    
    qgis.utils.iface.setActiveLayer(layer)
    extent = layer.extent()
    canvas.setExtent(extent)
    iface.mapCanvas().zoomScale(700)
    time.sleep(5)
    canvas.refresh()
    print(extent)

Best Answer

You can solve your issue using QTimer which controls when to execute functions and it's asynchronous, i.e., it allows other functions to continue to run (think of a map.refresh()) while waiting some milliseconds to run the next instruction.

You need to define two functions, one to prepare the map (in your case, set the new extent and refresh the map) and another one to export the map. After the map is prepared, a QTimer is set to wait 1 second before calling exportMap(). Such second gives enough time for the map to reflect changes and be ready to be exported: Reference

Use this script:

from os.path import join

canvas = iface.mapCanvas()
root = QgsProject.instance().layerTreeRoot()
layers = root.layerOrder()

i = 0
folder = "c:/path/to/folder"

def process():
    iface.actionHideAllLayers().trigger() # make all layers invisible
    root.findLayer(layers[i].id()).setItemVisibilityChecked(True)
    canvas.setExtent(layers[i].extent())
    canvas.refresh()
    QTimer.singleShot(250, save_as_image) # 250 milisecond

def save_as_image():
    global i
    file_name = join(folder, layers[i].name()+".png")
    canvas.saveAsImage(file_name)
    if i < len(layers) - 1:
        QTimer.singleShot(250, process)
    i += 1

process()
Related Question