2

Using 3.10.1-A Coruña on Windows 10

I need to automate the production of thousands of images in QGIS3. I've tried the many sleep and QTimer solutions offered in many questions on this site but that is not working for me. It appears that QGIS pauses once with the sleep and QTimer and then executes the rest of the whole script in one go. Consequently the images produced with the Sleep and QTimer methods are all the same.

I can pause the script with a QMessageBox between each zoomToSelected as shown below, and I get the correct images, but that means I'll be clicking the message box thousands of times for 1 dataset.

This example is not optimized with a function but is laid out so I can see what is happening. I'm quite new to QGIS3 scripting.

import time
from qgis.PyQt.QtCore import QTimer

def doNothing(): # pausing kludge
print ("doing nothing")

# MAKE IMAGES
pcode = '0800'
layer = iface.activeLayer()
layer.removeSelection()
layer.selectByExpression('"POA_CODE16"='+pcode)
iface.mapCanvas().resize(QSize(1200,1000))
iface.mapCanvas().zoomToSelected()
iface.mapCanvas().refresh()

QMessageBox.information(None, "DEBUG:", pcode) 
#time.sleep(1) 
#QTimer.singleShot(3000, doNothing)
iface.mapCanvas().saveAsImage("D:/test/file_" + pcode + ".png")

pcode = '0810'
layer = iface.activeLayer()
layer.removeSelection()
layer.selectByExpression('"POA_CODE16"='+pcode)
iface.mapCanvas().zoomToSelected()
iface.mapCanvas().refresh()

QMessageBox.information(None, "DEBUG:", pcode) 
#time.sleep(1) 
#QTimer.singleShot(3000, doNothing)
iface.mapCanvas().saveAsImage("D:/test/file_" + pcode + ".png")

pcode = '0822'
layer = iface.activeLayer()
layer.removeSelection()
#selectid = [4]
#layer.select(selectid)
layer.selectByExpression('"POA_CODE16"='+pcode)
iface.mapCanvas().zoomToSelected()
iface.mapCanvas().refresh()

QMessageBox.information(None, "DEBUG:", pcode) 
#time.sleep(5) 
#QTimer.singleShot(3000, doNothing)
iface.mapCanvas().saveAsImage("D:/test/file_" + pcode + ".png")

Requested additional info for PolyGeo:

I have about 100 links in my Google Chrome History for visits to gis.stackexchange yesterday. I was initially trying to get zoomToSelected to work (in a loop) - that problem was solved with help from a forum user - now I'm looking at the saveAsImage in the loop as well. So the pages I visited and tested were about controlling loops - either for saveAsImage or zoomToSelected.

Some links from my research/ tests yesterday:

How to refresh MapCanvas while PyQGIS-script is running?

Select feature one by one not working in pyQGIS?

mapCanvas.saveAsImage() doesn't work properly within loop in standalone script (only .PGW)

  • 1
    What are links to the earlier Q&As that you mention? – PolyGeo Dec 28 '19 at 0:48
  • 1
    You seem to be re-inventing the wheel. Have you tried a print layout atlas? qgistutorials.com/en/docs/3/automating_map_creation.html – user2856 Dec 28 '19 at 5:08
  • Thank you for your suggestion about Atlas user2856. I am trying to keep it simple and just saving an image seems straightforward. – Corpuscular Dec 29 '19 at 0:19
1

As user2856 suggests, the atlas function may well suit your needs, however- if you wish to pursue using the saveAsImage() function, the script below is working for me in QGIS 3.4. I have based this script on an answer by Germán Carrillo in one of the previous questions you linked to.

Just copy and paste into an editor in the Python console, select your layer and run.

I have also added a QProgressDialog() to show progress and allow to cancel the operation.

*Don't forget to change the file path to suit your requirements.

from PyQt5.QtCore import QTimer

canvas = iface.mapCanvas()
layer = iface.activeLayer()
pcodes = []

for feat in layer.getFeatures():
    code = feat["POA_CODE16"]
    if code not in pcodes:
        pcodes.append(code)

prog = QProgressDialog()
indx = 0

def set_extent():
    pcode = pcodes[indx]
    ids = [f.id() for f in layer.getFeatures() if f["POA_CODE16"] == pcode]
    canvas.zoomToFeatureIds(layer, ids)
    canvas.refresh()
    if prog.wasCanceled():
        return
    QTimer.singleShot(100, save_image)

def save_image():
    global indx
    pcode = pcodes[indx]
    # Change file path below as required
    canvas.saveAsImage('C:\\Users\\Ben\\Desktop\\Test\\file_{}.png'.format(pcode))
    # print('Images for fids like: {} saved'.format(pcode))
    if indx < len(pcodes)-1:
        QTimer.singleShot(100, set_extent)
    indx = indx+1
    pcnt = (indx/len(pcodes))*100
    prog.setValue(pcnt)

set_extent()
prog.show()
| improve this answer | |
  • 1
    Thank you Ben W. This works very well. I had to make the Timer 1+ seconds. My datasets have many labels and I was getting images without labels. The ProgressDialog is also very helpful. I don't understand why calling a def function seems to be the only way to get the Timer function to work properly. – Corpuscular Dec 29 '19 at 0:16
  • No worries @Corpuscular, glad you got it working for you- my test data was fairly simple so I made the timer as short as possible to cut down on total processing time. TBH I don't really understand this either. I tried a few different approaches but couldn't get anything else working properly. – Ben W Dec 29 '19 at 0:40
  • 1
    Dude you are too kind - you got it working for me. I did the hard work of copying your code into my Editor. Thanks! – Corpuscular Dec 29 '19 at 0:50
  • No worries, mate- happy to help! – Ben W Dec 29 '19 at 6:33

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.