छवि बचाने से पहले रेंडर करने के लिए कैनवास के लिए प्रतीक्षा करें


11

मैं एक स्क्रिप्ट लिखने का प्रयास कर रहा हूं जो मानचित्र संगीतकार का उपयोग करके कई परतों के प्रतिपादन को बचाएगा। मैं जिस समस्या का सामना कर रहा हूं वह यह है कि क्यूगिस की सभी परतों को प्रस्तुत करने से पहले स्क्रिप्ट बच जाती है।

कई अन्य उत्तरों ( 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 मुख्य विंडो में कैसा दिखता है (इस पर प्रदर्शित किया जा रहा है एक यादृच्छिक रेखापुंज नक्शा है): यहाँ छवि विवरण दर्ज करें

क्या बचा है: यहाँ छवि विवरण दर्ज करें

अधिक जानकारी के रूप में, मैं विंडोज 7 पर QGIS 2.18.7 का उपयोग कर रहा हूं


मैंने कई वेब पेजों, 1 और 2 का भी उल्लेख किया है । मैंने इन्हें पोस्ट करने के लिए जोड़ने की कोशिश की, लेकिन मेरा प्रतिनिधि पर्याप्त नहीं है
ईस्टवेस्ट

अपनी दूसरी अंतिम पंक्ति में, के mapCanv.mapCanvasRefreshed.connect(custFunc)साथ बदलने का प्रयास करें mapCanv.renderComplete.connect(custFunc)?
जोसेफ

@ जोसेफ दुर्भाग्य से, इससे कोई फर्क नहीं पड़ा। मुझे अभी भी उपरोक्त परिणाम मिलता है
ईस्टवेस्ट

शायद परत में आपके द्वारा जोड़ी गई सुविधाओं को लाने का प्रयास करें? (यानी layerP .commitChanges())। हालाँकि मैं यह नहीं देखता कि आपको मदद क्यों करनी चाहिए क्योंकि आप केवल छवि को बचा रहे हैं लेकिन एक कोशिश के लायक है जो मुझे लगता है। अन्यथा उम्मीद है कि अन्य लोग सलाह दे सकते हैं :)
जोसेफ

@ जोसेफ मैंने कोशिश की commitChanges(), लेकिन दुर्भाग्य नहीं। सलाह के लिये धन्यवाद।
ईस्टवेस्ट

जवाबों:


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सिग्नल के बारे में कोई विचार काम नहीं कर रहा है?
जोसफ

सभी जानकारी के लिए शुक्रिया! दुर्भाग्य से, मैंने अपनी स्क्रिप्ट में सुझाए गए कोड को डालने की कोशिश की है, पूरी तरह से मानचित्र संगीतकार अनुभाग की जगह ले रहा है, और अभी भी उसी मुद्दे का सामना कर रहा हूं। सहेजी गई छवि में लाइन या बिंदु परतें शामिल नहीं हैं, केवल पूर्व-लोड किए गए रेखापुंज।
ईस्टवेस्ट

मुझे लगता है कि काम पूरा होने के बीच सिग्नल उत्सर्जित होता है और छवि स्क्रीन पर आ जाती है। painterइसके साथ उत्सर्जित एक पैरामीटर है जिस पर आप अभी भी अतिरिक्त चीजें खींच सकते हैं जो अंतिम छवि पर समाप्त हो जाएंगे (और जिससे आप संभवतः इस दृष्टिकोण को काम करने के लिए अंतिम छवि भी ले सकते हैं)।
मथायस कुह्न

1
यह समाधान जैसा दिखता है। आपकी सभी मदद का धन्यवाद। एक त्वरित नोट अगर किसी और को यह पता लगाना समाप्त हो जाता है - क्यूटीमर को ठीक से काम करने के लिए, रेंडर_मेज के बाद कोष्ठक को छोड़ दें, या फिर अजगर एक चेतावनी फेंकता है। पढ़ना चाहिएQTimer.singleShot(10, render_image)
ईस्टवेस्ट

उफ़, बिल्कुल। उपरोक्त कोड में निश्चित
माथियास कुह्न
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.