Ich versuche, ein Skript zu schreiben, das das Rendern mehrerer Ebenen mit dem Map Composer speichert. Das Problem, auf das ich stoße, ist, dass das Skript gespeichert wird, bevor qgis alle Ebenen gerendert hat.
Basierend auf mehreren anderen Antworten ( 1 , 2 , 3 ) habe ich versucht, iface.mapCanvas.mapCanvasRefreshed.connect()
die Bildspeicherung zu verwenden und in eine Funktion zu integrieren, aber ich habe immer noch das gleiche Problem: Die Bilder enthalten nicht alle Ebenen.
Der von mir verwendete Code sowie Bilder des Hauptfensters und der Renderings sind unten aufgeführt.
Ich habe festgestellt print layerList
, dass das Programm wartet, bis das Rendern abgeschlossen ist , wenn das Konsolenfenster geöffnet und die drei Zeilen auskommentiert werden, bevor die Bilder gespeichert werden. Ich bin nicht sicher, ob dies auf die erhöhte Verarbeitungszeit zurückzuführen ist oder ob sich die Ausführung des Programms ändert.
Wie implementiere ich das richtig, damit alle Ebenen im Bild enthalten sind?
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")
Wie es im QGIS-Hauptfenster aussieht (es gibt eine zufällige Rasterkarte, auf der es angezeigt wird):
Als weitere Information verwende ich QGIS 2.18.7 unter Windows 7
mapCanv.mapCanvasRefreshed.connect(custFunc)
mitmapCanv.renderComplete.connect(custFunc)
?layerP .commitChanges()
). Ich verstehe zwar nicht, warum es helfen sollte, da Sie nur das Bild speichern, aber einen Versuch wert sind, denke ich. Ansonsten können hoffentlich andere raten :)commitChanges()
, aber leider kein Glück. Danke für den Vorschlag.Antworten:
Hier tauchen verschiedene Probleme auf
Rendern auf dem Bildschirm vs. Rendern in ein Bild
Das Signal
mapCanvasRefreshed
wird wiederholt ausgegeben, während die Leinwand auf dem Bildschirm gerendert wird. Für die Bildschirmanzeige gibt dies ein schnelleres Feedback, das für einen Benutzer hilfreich sein kann, um etwas zu sehen oder bei der Navigation zu helfen.Für das Rendern außerhalb des Bildschirms wie das Speichern in einer Datei ist dies nicht zuverlässig (da Sie nur dann ein vollständiges Bild erhalten, wenn das Rendern schnell genug war).
Was kann getan werden: Wir benötigen keine Kartenleinwand, um Ihr Bild zu rendern. Wir können das einfach
QgsMapSettings
von der Kartenleinwand kopieren . Diese Einstellungen sind die Parameter, die an den Renderer gesendet werden und definieren, was genau und wie genau Dinge von allen Datenanbietern in ein Rasterbild konvertiert werden sollen.Ebenenregistrierung gegen Kartenleinwand
Zur Registrierung hinzugefügte Ebenen landen nicht sofort auf der Zeichenfläche, sondern erst beim nächsten Durchlauf der Ereignisschleife. Daher ist es besser, eines der folgenden beiden Dinge zu tun
Starten Sie die Bildwiedergabe in einem Timer.
QTimer.singleShot(10, render_image)
Führen SieDies funktioniert, ist jedoch ein gefährlicher Aufruf (führt manchmal zu seltsamen Abstürzen) und sollte daher vermieden werden.QApplication.processEvents()
nach der Schicht hinzugefügt wird .Zeig mir den Code
Der folgende Code tut dies (leicht angepasst von QFieldSync , schauen Sie dort nach, wenn Sie an weiteren Anpassungen interessiert sind).
quelle
renderComplete
Signal nicht funktioniert?painter
ausgegeben, mit dem Sie noch zusätzliche Dinge zeichnen können, die auf dem endgültigen Bild landen (und von denen Sie wahrscheinlich auch das endgültige Bild nehmen könnten, damit dieser Ansatz funktioniert).QTimer.singleShot(10, render_image)