이미지를 저장하기 전에 캔버스가 렌더링을 완료 할 때까지 기다리십시오.


11

지도 작성기를 사용하여 여러 레이어의 렌더링을 저장하는 스크립트를 작성하려고합니다. 내가 겪고있는 문제는 qgis가 모든 레이어 렌더링을 완료하기 전에 스크립트가 저장된다는 것입니다.

몇 가지 다른 답변 ( 1 , 2 , 3 )을 기반으로 iface.mapCanvas.mapCanvasRefreshed.connect()이미지 저장 기능 을 사용 하고 함수 내부에 넣으 려고 했지만 여전히 동일한 문제가 발생합니다. 이미지에 모든 레이어가 포함되어 있지 않습니다.

내가 사용하는 코드와 기본 창 및 렌더링 이미지는 다음과 같습니다.

콘솔 창을 열고 세 print layerList줄의 주석 처리를 제거 하면 이미지를 저장하기 전에 프로그램이 렌더링이 완료 될 때까지 기다립니다. 처리 시간이 길어 졌는지 또는 프로그램 실행 방법이 변경되는지 확실하지 않습니다.

모든 레이어가 이미지에 포함되도록 어떻게 올바르게 구현합니까?

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

QGIS 메인 창에 보이는 모습 (임의의 래스터 맵이 표시됩니다) : 여기에 이미지 설명을 입력하십시오

저장되는 내용 : 여기에 이미지 설명을 입력하십시오

추가 정보로 Windows 7에서 QGIS 2.18.7을 사용하고 있습니다.


또한 여러 웹 페이지 12를 참조했습니다 . 나는 이것을 우편으로 추가하려고 노력했지만 내 담당자는 충분히 높지 않습니다.
EastWest

두 번째 마지막 줄에서 교체 시도 mapCanv.mapCanvasRefreshed.connect(custFunc)mapCanv.renderComplete.connect(custFunc)?
Joseph

@Joseph 불행히도, 그것은 차이를 보이지 않는 것 같습니다. 나는 여전히 위와 같은 결과를 얻습니다
EastWest

레이어에 추가 한 기능을 커밋 하시겠습니까? (즉 layerP .commitChanges()). 이미지 만 저장하고 시도해 볼만한 가치가 있기 때문에 왜 도움이되는지 모르겠습니다. 그렇지 않으면 희망이 다른 사람 :) 조언 해 줄 수 있습니다
조셉

@Joseph 나는 시도 commitChanges()했지만 불행히도 운이 없다. 제안 해 주셔서 감사합니다.
EastWest

답변:


5

여기에 떠오르는 다른 문제가 있습니다

화면에서의 렌더링 및 이미지로의 렌더링

mapCanvasRefreshed캔버스가 화면에 렌더링되는 동안 신호 가 반복적으로 방출됩니다. 화면 표시의 경우 사용자가 진행중인 작업을 보거나 탐색에 도움을 줄 수있는 빠른 피드백을 제공합니다.

파일로 저장하는 것과 같은 오프 스크린 렌더링의 경우 신뢰할 수 없습니다 (렌더링이 충분히 빠르면 완전한 이미지 만 제공되므로).

수행 할 수있는 작업 : 이미지를 렌더링하기 위해 맵 캔버스가 필요하지 않습니다. QgsMapSettings맵 캔버스에서를 복사하면 됩니다. 이 설정은 렌더러로 전송되는 매개 변수이며 모든 데이터 제공 업체에서 래스터 이미지로 정확히 무엇이 정확하게 변환되어야 하는지를 정의합니다.

레이어 레지스트리와 맵 캔버스

레지스트리에 추가 된 레이어는 캔버스에서 즉시 끝나지 않고 다음 이벤트 루프 실행시에만 나타납니다. 따라서 다음 두 가지 중 하나를 수행하는 것이 좋습니다.

  • 타이머에서 이미지 렌더링을 시작하십시오. QTimer.singleShot(10, render_image)

  • QApplication.processEvents()레이어를 추가 한 후 실행하십시오 . 이것은 작동하지만 사용하기에 위험한 전화 (때로는 이상한 충돌로 이어짐)를 피해야합니다.

코드를 보여줘

다음 코드는 이것을 수행합니다 ( QFieldSync 에서 약간 조정 , 더 많은 사용자 정의에 관심이 있다면 거기를 살펴보십시오)

from PyQt4.QtGui import QImage, QPainter

def render_image():
    size = iface.mapCanvas().size()
    image = QImage(size, QImage.Format_RGB32)

    painter = QPainter(image)
    settings = iface.mapCanvas().mapSettings()

    # You can fine tune the settings here for different
    # dpi, extent, antialiasing...
    # Just make sure the size of the target image matches

    # You can also add additional layers. In the case here,
    # this helps to add layers that haven't been added to the
    # canvas yet
    layers = settings.layers()
    settings.setLayers([layerP.id(), layerL.id()] + layers)

    job = QgsMapRendererCustomPainterJob(settings, painter)
    job.renderSynchronously()
    painter.end()
    image.save('/tmp/image.png')

# If you don't want to add additional layers manually to the
# mapSettings() you can also do this:
# Give QGIS give a tiny bit of time to bring the layers from 
# the registry to the canvas (the 10 ms do not matter, the important
# part is that it's posted to the event loop)

# QTimer.singleShot(10, render_image)

1
renderComplete신호가 작동하지 않는 것에 대한 아이디어가 있습니까?
Joseph

모든 정보에 감사드립니다! 불행히도, 제안 된 코드를 스크립트에 삽입하여 맵 작성기 섹션을 완전히 대체하려고 시도했지만 여전히 동일한 문제가 발생합니다. 저장된 이미지에는 선 또는 점 레이어가 포함되어 있지 않으며 사전로드 된 래스터 만 있습니다.
EastWest

작업 사이에 신호가 방출되고 이미지가 화면에 그려지는 것 같습니다. painter최종 이미지에서 끝나는 추가 항목을 그릴 수 있는 매개 변수가 있습니다 (이 접근 방식을 작동시키기 위해 최종 이미지를 가져올 수도 있습니다).
Matthias Kuhn

1
이것은 솔루션처럼 보입니다. 모든 도움을 주셔서 감사합니다. QTimer가 제대로 작동하게하려면 render_image 뒤에 괄호를 남기지 마십시오. 그렇지 않으면 python에서 경고가 표시됩니다. 읽어야합니다QTimer.singleShot(10, render_image)
EastWest

물론 이죠 위 코드에서 수정
Matthias Kuhn
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.