Refreshing MapCanvas while PyQGIS-script is running

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)

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()

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.