3

I try to write a PyQGIS script similar to this: Use of python console to prepare offline map from WMS (tile downloader).

Basically there are two loops to pan the mapCanvas, refresh the mapCanvas and export the mapCanvas to *.png.

iface.mapCanvas().setExtent(QgsRectangle(xmin, ymin, xmax, ymax))
time.sleep(5)
iface.mapCanvas().refresh()
time.sleep(5)
iface.mapCanvas().saveAsImage("D:/GE"+str(i)+"to"+str(j)+"img.png" )

If I execute this code line by line in the Python Console it is working fine. If I use it in a loop georeferenced png will be exported but they are either blank or contain all the same content. As you see above I tried to use time.sleep() to have a timeslot for the map to update. But was not successfull. As Joseph suggested I also tried:

for layer in qgis.utils.iface.mapCanvas().layers():
    layer.triggerRepaint()

Same problem here: works line by line in the Python Console but not in a loop.

How to refresh MapCanvas while PyQGIS-script is running?

(QGIS Version 2.14.3)

5

Sleep blocks the execution of your script for a while, synchronously.

One solution for your question is to use a 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.

Have a look at How to iterate over layers and export them as PNG images with PyQGIS in a standalone script for an implemented example that is similar to your case.

There are 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:

QTimer.singleShot(1000, exportMap)
  • 1
    The example was very helpful! – EikeMike May 30 '16 at 7:18
1

The problem is the for loop, too fast. You should made a workaround with a hand made loop.

I made a script which parsing every features of a preselected layer. The behavior is similar to the SaveAsImage of an Atlas base on a fix scale but with an export of the worldfile for georeferencing.

It's made from the exemple presented by German Carrillo (thanks).

import processing
from PyQt4.QtCore import QTimer

# set workdir export files
workdir = '/your/path/here'
#define the scale of screenshoot
scale = 640

count = 0
fileName = "name"
feat = []
#timer in millisecond
sleepTime=100

# Select the layer witch is used for the mapCanvas extent (do it by hand)
layer = iface.activeLayer()


#def refresh_layers(self):
#    for layer in qgis.utils.iface.mapCanvas().layers():
#        layer.triggerRepaint()


def prepareMap(): # Arrange layers
  #feature = feat[count]
  layer.select(count) 
  #set trigger to zoom automaticaly on select's layers
  iface.actionZoomToSelected().trigger()
  qgis.utils.iface.mapCanvas().zoomScale(scale)
  layer.deselect(count)
  QTimer.singleShot(sleepTime, exportMap) # Wait a second and export the map



def exportMap():
  global count # We need this because we'll modify its value
  #refresh_layers(layer)
  iface.mapCanvas().saveAsImage(workdir + fileName + "_" + str(count) + ".png")
  print "Map with layer",count,"exported!"
  if count < len(feat)-1:
    QTimer.singleShot(sleepTime, prepareMap) # Wait a second and prepare next map
  count += 1


features = processing.features(layer)

for feature in features:
  feat.append(feature.id)


prepareMap()
1

You can use qApp.processEvents().

For example:

from PyQt4.QtGui import qApp

layer = iface.activeLayer()
for f in layer.getFeatures():
    layer.setSelectedFeatures([f.id()])
    iface.mapCanvas().zoomToSelected(layer)
    qApp.processEvents()
    iface.mapCanvas().saveAsImage("feature_" + str(f.id()) + ".png")

Code above iterates on features in active layer. It selects and zooms to features, then save map canvas as image. If you use qApp.processEvents() in a loop, every feature will be saved as zoomed to it. If you don't, all images have same content as you mentioned and all will probably be last feature on iteration.

This is the shortest way (maybe it's not the best way) I have found so far for my own processes.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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