Tunggu kanvas untuk menyelesaikan rendering sebelum menyimpan gambar


11

Saya mencoba menulis skrip yang akan menyimpan rendering beberapa layer menggunakan komposer peta. Masalah yang saya temui adalah script menyimpan sebelum qgis selesai merender semua layer.

Berdasarkan beberapa jawaban lain ( 1 , 2 , 3 ), saya telah berusaha untuk menggunakan iface.mapCanvas.mapCanvasRefreshed.connect()dan menempatkan penyimpanan gambar di dalam suatu fungsi, tetapi saya masih menghadapi masalah yang sama - gambar tidak termasuk semua lapisan.

Kode yang saya gunakan, serta gambar-gambar seperti apa tampilan jendela utama dan rendering tercantum di bawah ini.

Saya perhatikan bahwa jika saya memiliki jendela konsol terbuka dan menghapus tanda komentar tiga print layerListbaris, bahwa program akan menunggu render selesai sebelum menyimpan gambar. Saya tidak yakin apakah ini karena peningkatan waktu pemrosesan, atau apakah itu mengubah cara program dijalankan.

Bagaimana cara menerapkan ini dengan benar sehingga semua lapisan termasuk dalam gambar?

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

Seperti apa di jendela utama QGIS (ada peta raster acak yang sedang ditampilkan): masukkan deskripsi gambar di sini

Apa yang disimpan: masukkan deskripsi gambar di sini

Sebagai informasi lebih lanjut, saya menggunakan QGIS 2.18.7 pada Windows 7


Saya juga mereferensikan beberapa halaman web, 1 dan 2 . Saya mencoba menambahkan ini untuk dikirim, tetapi perwakilan saya tidak cukup tinggi
EastWest

Di baris terakhir kedua Anda, coba ganti mapCanv.mapCanvasRefreshed.connect(custFunc)dengan mapCanv.renderComplete.connect(custFunc)?
Joseph

@ Joseph Sayangnya, sepertinya tidak ada bedanya. Saya masih mendapatkan hasil yang sama seperti di atas
EastWest

Mungkin mencoba mengkomit fitur yang Anda tambahkan ke layer? (yaitu layerP .commitChanges()). Meskipun saya tidak mengerti mengapa itu bisa membantu karena Anda hanya menyimpan gambar tetapi patut dicoba kurasa. Kalau tidak mudah-mudahan orang lain dapat menyarankan :)
Joseph

@ Joseph aku mencoba commitChanges(), tetapi tidak berhasil, sayangnya. Terima kasih untuk sarannya.
EastWest

Jawaban:


5

Ada berbagai masalah yang muncul di sini

Rendering di layar vs rendering ke gambar

Sinyal mapCanvasRefresheddipancarkan berulang kali saat kanvas ditampilkan ke layar. Untuk tampilan di layar ini memberikan umpan balik yang lebih cepat yang dapat menyenangkan bagi pengguna untuk melihat sesuatu yang terjadi atau membantu dalam navigasi.

Untuk rendering di luar layar seperti menyimpan ke file, ini tidak dapat diandalkan (karena Anda hanya akan memiliki gambar lengkap jika renderingnya cukup cepat).

Apa yang bisa dilakukan: kita tidak perlu kanvas peta untuk membuat gambar Anda. Kami hanya dapat menyalin QgsMapSettingsdari kanvas peta. Pengaturan ini adalah parameter yang dikirim ke renderer dan menentukan apa yang sebenarnya dan bagaimana tepatnya hal-hal yang harus dikonversi dari semua penyedia data ke gambar raster.

Registri layer vs kanvas peta

Lapisan yang ditambahkan ke registri tidak berakhir di kanvas segera tetapi hanya dalam menjalankan berikutnya dari acara. Karena itu Anda lebih baik melakukan salah satu dari dua hal berikut ini

  • Mulai rendering gambar dalam timer. QTimer.singleShot(10, render_image)

  • Jalankan QApplication.processEvents()setelah menambahkan layer. Ini berfungsi tetapi itu adalah panggilan berbahaya untuk digunakan (kadang-kadang menyebabkan crash aneh) dan karenanya harus dihindari.

Tunjukkan kodenya

Kode berikut melakukan ini (sedikit disesuaikan dari QFieldSync , lihat di sana jika Anda tertarik pada lebih banyak penyesuaian)

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
Ada gagasan tentang renderCompletesinyal tidak bekerja?
Joseph

Terima kasih atas semua informasinya! Sayangnya, saya telah mencoba memasukkan kode yang disarankan ke dalam skrip saya, sepenuhnya menggantikan bagian komposer peta, dan saya masih mengalami masalah yang sama. Gambar yang disimpan tidak menyertakan layer garis atau titik, hanya raster yang dimuat sebelumnya.
EastWest

Saya pikir sinyal dipancarkan antara pekerjaan selesai dan gambar ditarik ke layar. Ada parameter yang painterdipancarkan di mana Anda masih bisa menggambar hal-hal tambahan yang akan berakhir pada gambar akhir (dan dari mana Anda mungkin juga dapat mengambil gambar akhir untuk membuat pendekatan ini berfungsi).
Matthias Kuhn

1
Ini sepertinya solusinya. Terima kasih atas seluruh bantuan Anda. Satu catatan cepat jika orang lain akhirnya menemukan ini - untuk membuat QTimer berfungsi dengan baik, tinggalkan tanda kurung setelah render_image, atau python melempar peringatan. Harus membacaQTimer.singleShot(10, render_image)
EastWest

Ups, tentu saja. Diperbaiki dalam kode di atas
Matthias Kuhn
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.