Próbuję napisać skrypt, który zapisze renderowanie kilku warstw za pomocą kompozytora map. Problem, z którym się spotykam, polega na tym, że skrypt zapisuje się, zanim qgis zakończy renderowanie wszystkich warstw.
Opierając się na kilku innych odpowiedziach ( 1 , 2 , 3 ), próbowałem użyć iface.mapCanvas.mapCanvasRefreshed.connect()
i umieścić zapisywanie obrazu w funkcji, ale wciąż mam ten sam problem - obrazy nie zawierają wszystkich warstw.
Kod, którego używam, a także obrazy tego, jak wygląda okno główne i rendering, są wymienione poniżej.
Zauważyłem, że jeśli mam otwarte okno konsoli i odkomentuję trzy print layerList
linie, program będzie czekał na zakończenie renderowania przed zapisaniem obrazów. Nie jestem pewien, czy wynika to ze zwiększonego czasu przetwarzania, czy też zmienia to sposób działania programu.
Jak poprawnie to zaimplementować, aby wszystkie warstwy były zawarte w obrazie?
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")
Jak to wygląda w głównym oknie QGIS (jest wyświetlana losowa mapa rastrowa):
Jako dalsze informacje używam QGIS 2.18.7 na Windows 7
mapCanv.mapCanvasRefreshed.connect(custFunc)
zmapCanv.renderComplete.connect(custFunc)
?layerP .commitChanges()
.). Chociaż nie rozumiem, dlaczego to powinno pomóc, skoro zapisujesz obraz, ale warto spróbować. W przeciwnym razie, mam nadzieję, że inni mogą doradzić :)commitChanges()
, ale niestety niestety. Dzieki za sugestie.Odpowiedzi:
Pojawiają się tutaj różne problemy
Renderowanie na ekranie a renderowanie do obrazu
Sygnał
mapCanvasRefreshed
jest emitowany wielokrotnie, gdy płótno jest renderowane na ekran. W przypadku wyświetlania na ekranie daje to szybszą informację zwrotną, która może być przyjemna dla użytkownika, aby zobaczyć, co się dzieje lub pomóc w nawigacji.W przypadku renderowania poza ekranem, takiego jak zapisywanie do pliku, nie jest to niezawodne (ponieważ pełny obraz będzie dostępny tylko wtedy, gdy rendering był wystarczająco szybki).
Co można zrobić: nie potrzebujemy płótna mapy do renderowania twojego obrazu. Możemy po prostu skopiować
QgsMapSettings
z kanwy mapy. Ustawienia te są parametrami wysyłanymi do renderera i określają, co dokładnie i jak dokładnie rzeczy powinny zostać przekonwertowane od wszystkich dostawców danych do obrazu rastrowego.Rejestr warstw a płótno mapy
Warstwy dodane do rejestru nie kończą się natychmiast na kanwie, ale tylko w następnym uruchomieniu pętli zdarzeń. Dlatego lepiej jest zrobić jedną z następujących dwóch rzeczy
Rozpocznij renderowanie obrazu za pomocą timera.
QTimer.singleShot(10, render_image)
UruchomTo działa, ale jest to niebezpieczne wezwanie do użycia (czasami prowadzi do dziwnych awarii) i dlatego należy go unikać.QApplication.processEvents()
po dodaniu warstwy.Pokaż mi kod
Poniższy kod to robi (nieco dostosowany z QFieldSync , zajrzyj tam, jeśli chcesz bardziej dostosować)
źródło
renderComplete
sygnał nie działa?painter
emitowany parametr , na którym nadal możesz narysować dodatkowe rzeczy, które skończą na ostatecznym obrazie (i na podstawie których prawdopodobnie możesz również wziąć ostateczny obraz, aby to podejście działało).QTimer.singleShot(10, render_image)