J'essaie d'écrire un script qui enregistrera un rendu de plusieurs couches à l'aide du compositeur de carte. Le problème que je rencontre est que le script enregistre avant que qgis ait fini de rendre toutes les couches.
Sur la base de plusieurs autres réponses ( 1 , 2 , 3 ), j'ai essayé d'utiliser iface.mapCanvas.mapCanvasRefreshed.connect()
et de mettre l'enregistrement d'image à l'intérieur d'une fonction, mais je rencontre toujours le même problème - les images n'incluent pas tous les calques.
Le code que j'utilise, ainsi que des images de l'apparence de la fenêtre principale et des rendus sont répertoriés ci-dessous.
J'ai remarqué que si la fenêtre de la console est ouverte et que les trois print layerList
lignes ne sont pas commentées , le programme attendra la fin du rendu avant d'enregistrer les images. Je ne sais pas si cela est dû à l'augmentation du temps de traitement ou si cela change la façon dont le programme s'exécute.
Comment puis-je implémenter cela correctement pour que tous les calques soient inclus dans l'image?
from qgis.core import *
from qgis.utils import *
from qgis.gui import *
from PyQt4.QtCore import *
from PyQt4.QtGui import *
import os.path
##StackExchange Version=name
##Map_Save_Folder=folder
##Map_Save_Name=string roadmap
# Create save file location
mapName = "%s.png" %Map_Save_Name
outfile = os.path.join(Map_Save_Folder,mapName)
pdfName = "%s.pdf" %Map_Save_Name
outPDF = os.path.join(Map_Save_Folder,pdfName)
# Create point and line layers for later
URIstrP = "Point?crs=EPSG:3035"
layerP = QgsVectorLayer(URIstrP,"pointsPath","memory")
provP = layerP.dataProvider()
URIstrL = "LineString?crs=EPSG:3035"
layerL = QgsVectorLayer(URIstrL,"linePath","memory")
provL = layerL.dataProvider()
# Add points to point layer
feat1 = QgsFeature()
feat2 = QgsFeature()
feat3 = QgsFeature()
feat1.setGeometry(QgsGeometry.fromPoint(QgsPoint(5200000,2600000)))
feat2.setGeometry(QgsGeometry.fromPoint(QgsPoint(5300000,2800000)))
provP.addFeatures([feat1, feat2])
# Add line to line layer
feat3.setGeometry(QgsGeometry.fromPolyline([feat1.geometry().asPoint(),feat2.geometry().asPoint()]))
provL.addFeatures([feat3])
# Set symbology for line layer
symReg = QgsSymbolLayerV2Registry.instance()
metaRegL = symReg.symbolLayerMetadata("SimpleLine")
symLayL = QgsSymbolV2.defaultSymbol(layerL.geometryType())
metaL = metaRegL.createSymbolLayer({'width':'1','color':'0,0,0'})
symLayL.deleteSymbolLayer(0)
symLayL.appendSymbolLayer(metaL)
symRendL = QgsSingleSymbolRendererV2(symLayL)
layerL.setRendererV2(symRendL)
# Set symbology for point layer
metaRegP = symReg.symbolLayerMetadata("SimpleMarker")
symLayP = QgsSymbolV2.defaultSymbol(layerP.geometryType())
metaP = metaRegP.createSymbolLayer({'size':'3','color':'0,0,0'})
symLayP.deleteSymbolLayer(0)
symLayP.appendSymbolLayer(metaP)
symRendP = QgsSingleSymbolRendererV2(symLayP)
layerP.setRendererV2(symRendP)
# Load the layers
QgsMapLayerRegistry.instance().addMapLayer(layerP)
QgsMapLayerRegistry.instance().addMapLayer(layerL)
iface.mapCanvas().refresh()
# --------------------- Using Map Composer -----------------
def custFunc():
mapComp.exportAsPDF(outPDF)
mapImage.save(outfile,"png")
mapCanv.mapCanvasRefreshed.disconnect(custFunc)
return
layerList = []
for layer in QgsMapLayerRegistry.instance().mapLayers().values():
layerList.append(layer.id())
#print layerList
#print layerList
#print layerList
mapCanv = iface.mapCanvas()
bound = layerP.extent()
bound.scale(1.25)
mapCanv.setExtent(bound)
mapRend = mapCanv.mapRenderer()
mapComp = QgsComposition(mapRend)
mapComp.setPaperSize(250,250)
mapComp.setPlotStyle(QgsComposition.Print)
x, y = 0, 0
w, h = mapComp.paperWidth(), mapComp.paperHeight()
composerMap = QgsComposerMap(mapComp, x, y, w, h)
composerMap.zoomToExtent(bound)
mapComp.addItem(composerMap)
#mapComp.exportAsPDF(outPDF)
mapRend.setLayerSet(layerList)
mapRend.setExtent(bound)
dpmm = dpmm = mapComp.printResolution() / 25.4
mapImage = QImage(QSize(int(dpmm*w),int(dpmm*h)), QImage.Format_ARGB32)
mapImage.setDotsPerMeterX(dpmm * 1000)
mapImage.setDotsPerMeterY(dpmm * 1000)
mapPaint = QPainter()
mapPaint.begin(mapImage)
mapRend.render(mapPaint)
mapComp.renderPage(mapPaint,0)
mapPaint.end()
mapCanv.mapCanvasRefreshed.connect(custFunc)
#mapImage.save(outfile,"png")
À quoi il ressemble dans la fenêtre principale de QGIS (il y a une carte raster aléatoire sur laquelle il est affiché):
Pour plus d'informations, j'utilise QGIS 2.18.7 sur Windows 7
mapCanv.mapCanvasRefreshed.connect(custFunc)
parmapCanv.renderComplete.connect(custFunc)
?layerP .commitChanges()
-à-d.). Bien que je ne vois pas pourquoi cela devrait aider, car vous enregistrez uniquement l'image, mais cela vaut la peine d'essayer, je suppose. Sinon, j'espère que d'autres pourront vous conseiller :)commitChanges()
, mais malheureusement pas de chance. Merci pour la suggestion.Réponses:
Il y a différents problèmes qui se posent ici
Rendu à l'écran vs rendu à une image
Le signal
mapCanvasRefreshed
est émis à plusieurs reprises pendant le rendu du canevas à l'écran. Pour l'affichage à l'écran, cela donne un retour plus rapide, ce qui peut être agréable pour un utilisateur de voir quelque chose en cours ou d'aider à la navigation.Pour le rendu hors écran comme l'enregistrement dans un fichier, ce n'est pas fiable (car vous n'aurez une image complète que si le rendu était assez rapide).
Ce qui peut être fait: nous n'avons pas besoin du canevas de carte pour rendre votre image. Nous pouvons simplement copier le
QgsMapSettings
depuis le canevas de la carte. Ces paramètres sont les paramètres qui sont envoyés au rendu et définissent exactement et comment exactement les choses doivent être converties de tous les fournisseurs de données en une image raster.Registre de couches vs canevas de carte
Les couches ajoutées au registre ne se retrouvent pas immédiatement sur le canevas, mais uniquement lors de la prochaine exécution de la boucle d'événements. Par conséquent, vous feriez mieux de faire l'une des deux choses suivantes
Démarrez le rendu de l'image dans une minuterie.
QTimer.singleShot(10, render_image)
ExécutezCela fonctionne mais c'est un appel dangereux à utiliser (conduit parfois à des plantages étranges) et doit donc être évité.QApplication.processEvents()
après avoir ajouté le calque.Montrez-moi le code
Le code suivant fait cela (légèrement ajusté de QFieldSync , jetez un œil là-dedans si vous êtes intéressé par plus de personnalisation)
la source
renderComplete
signal ne fonctionne pas?painter
émis avec lui sur lequel vous pouvez toujours dessiner des choses supplémentaires qui se retrouveront sur l'image finale (et à partir de laquelle vous pourriez probablement aussi prendre l'image finale pour faire fonctionner cette approche).QTimer.singleShot(10, render_image)