3

I have a console script where I'm trying to iterate through saved bookmarks from the open project to have the map pan/zoom to the bookmark extent. I would like to eventually save each map canvas as pdf file. I can manually set the right parameters to cycle through the bookmarks and the canvas refreshes each time I set the extent.

However, if I cycle through the saved bookmarks and set the canvas extent in a for loop (as below), the canvas does not refresh with each extent change, it only refreshes once the script has run it's course. I've added sleep commands in the for loop to allow for redraw time, but that didn't work. Am I missing something simple?

bmarks = QgsApplication.bookmarkManager()

bmarks.bookmarks() 

for b in bmarks.bookmarks():
    canv = iface.mapCanvas()
    canv.setExtent(b.extent())
    iface.mapCanvas().refresh()
1

2 Answers 2

6

I see that your request is for the canvas, but it seems the ultimate goal is a file based output, so I'll answer that one instead.

You don't need to update the canvas itself. You can also very well do this in jobs that render to images.

bmarks = QgsApplication.bookmarkManager()
bmarks.bookmarks() 

settings = iface.mapCanvas().mapSettings()

for i, b in enumerate(bmarks.bookmarks()):
    settings.setExtent(b.extent())
    renderer = QgsMapRendererParallelJob(settings)

    event_loop = QEventLoop()
    renderer.finished.connect(event_loop.quit)
    renderer.start()
    event_loop.exec_()

    img = renderer.renderedImage()
    img.save('/tmp/img{}.png'.format(i))

The advantage of this approach is also that you are able to fine tune parameters in the settings, e.g. width and height of the rendered image (by default it depends on the window size) or that you can have a transparent background. See https://qgis.org/api/classQgsMapSettings.html for details.

You could even launch several background tasks in parallel for this, this is left as an exercise for the reader.

4
  • Excellent. Thanks for the help. Is there any trick to rendering to geopdf? I'll try it out and see if there is success on my end. Cheers...I do however like to see the render taking place while I'm working in the console because I can "see" it working rather than having to check directory outputs. Commented Nov 26, 2020 at 6:48
  • 1. I would possibly exclude python and explore layout atlas functionality / processing functionality 2. Add some print statements Commented Nov 26, 2020 at 11:04
  • How difficult would it be to automate a process to auto-open a project and have an atlas output pdf map products? Would I use a standalone script entirely, or would it be a combination of a standalone script and a python script embedded within the project that runs when the project opens? Ideally I'd like cron to open the project on a scheduled basis to produce map outputs, but also be able to manually open the project and get the same results on demand. Anyhow, thanks for your direction, I'll dive into atlas and see what happens! Commented Nov 26, 2020 at 21:16
  • That deserves a completely new question with the complete scope included. In the answer you will be redirected towards qgis_process, processing and layouts. Commented Nov 26, 2020 at 21:37
2

First of all, it looks like using time.sleep() might not give desired effect, see: https://stackoverflow.com/questions/55301747/how-to-make-python-wait-while-qgis-renders-a-shapefile

So in order to prove that solution works, I've introduced saving images (please don't forget to adjust the path) The key difference is using waitWhileRendering(), which apparently, well, waits until rendering is finished, so that canvas' refresh does make its job.

bmarks = QgsApplication.bookmarkManager()
canv = iface.mapCanvas()
i=0
for b in bmarks:
    i+=1
    canv.setExtent(b.extent(),False)
    canv.refresh()
    canv.waitWhileRendering() #probably that makes the difference
    canv.saveAsImage('/home/username/Documents/j{0}'.format(i)) ##adjust the path
2
  • Thanks for including the canv.waitWhileRendering() gift. It's so hard to find the right commands sometimes...guessing and reading gets old! It good to be able to borrow from other experience and brains. Great community here. Commented Nov 26, 2020 at 6:49
  • For the record, I was using sleep to try and cause the script to wait for the render, but that was a failure. I wish I could choose both answers as they both were very helpful in different ways....I do like the fact that I can now "see" what is happening with the 'waitWhileRendering()' command. Cheers. Commented Nov 26, 2020 at 7:10

Your Answer

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

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