Đợi canvas hoàn thành kết xuất trước khi lưu ảnh


11

Tôi đang cố gắng viết một kịch bản sẽ lưu kết xuất một vài lớp bằng trình soạn thảo bản đồ. Vấn đề tôi gặp phải là tập lệnh lưu trước khi qgis hoàn thành kết xuất tất cả các lớp.

Dựa trên một số câu trả lời khác ( 1 , 2 , 3 ), tôi đã cố gắng sử dụng iface.mapCanvas.mapCanvasRefreshed.connect()và đặt lưu ảnh bên trong một chức năng, nhưng tôi vẫn gặp phải cùng một vấn đề - hình ảnh không bao gồm tất cả các lớp.

Mã mà tôi đang sử dụng, cũng như hình ảnh của cửa sổ chính và kết xuất đồ họa trông như thế nào được liệt kê bên dưới.

Tôi đã nhận thấy rằng nếu tôi mở cửa sổ giao diện điều khiển và bỏ ghi chú ba print layerListdòng, chương trình sẽ đợi kết xuất hoàn tất trước khi lưu hình ảnh. Tôi không chắc chắn nếu điều này là do thời gian xử lý tăng lên, hoặc nếu nó đang thay đổi cách chương trình thực thi.

Làm thế nào để tôi thực hiện đúng điều này để tất cả các lớp được bao gồm trong hình ảnh?

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

Nó trông như thế nào trong cửa sổ chính của QGIS (có một bản đồ raster ngẫu nhiên mà nó đang được hiển thị trên): nhập mô tả hình ảnh ở đây

Những gì được lưu: nhập mô tả hình ảnh ở đây

Để biết thêm thông tin, tôi đang sử dụng QGIS 2.18.7 trên Windows 7


Tôi cũng đã tham khảo một số trang web, 12 . Tôi đã cố thêm những thứ này để đăng, nhưng đại diện của tôi không đủ cao
EastWest

Trong dòng cuối cùng thứ hai của bạn, hãy thử thay thế mapCanv.mapCanvasRefreshed.connect(custFunc)bằng mapCanv.renderComplete.connect(custFunc)?
Joseph

@Joseph Thật không may, nó dường như không tạo ra sự khác biệt. Tôi vẫn nhận được kết quả tương tự như trên
EastWest

Có lẽ hãy thử cam kết các tính năng bạn đã thêm vào lớp? (tức là layerP .commitChanges()). Mặc dù tôi không hiểu tại sao nó lại hữu ích vì bạn chỉ lưu hình ảnh nhưng đáng để thử. Nếu không thì hy vọng người khác có thể khuyên :)
Joseph

@Joseph Mình đã thử commitChanges(), nhưng không may, tiếc. Cám ơn vì sự gợi ý.
EastWest

Câu trả lời:


5

Có nhiều vấn đề khác nhau xuất hiện ở đây

Kết xuất trên màn hình so với kết xuất thành hình ảnh

Tín hiệu mapCanvasRefreshedđược phát đi phát lại trong khi khung vẽ được hiển thị trên màn hình. Đối với màn hình hiển thị trên màn hình, điều này mang lại phản hồi nhanh hơn, có thể tốt cho người dùng thấy điều gì đó đang diễn ra hoặc trợ giúp trong điều hướng.

Đối với kết xuất màn hình tắt như lưu vào một tệp, điều này không đáng tin cậy (vì bạn sẽ chỉ có một hình ảnh hoàn chỉnh nếu kết xuất đủ nhanh).

Điều gì có thể được thực hiện: chúng tôi không cần khung vẽ bản đồ để hiển thị hình ảnh của bạn. Chúng tôi chỉ có thể sao chép QgsMapSettingstừ vải bản đồ. Các cài đặt này là các tham số được gửi đến trình kết xuất và xác định chính xác và cách chính xác mọi thứ sẽ được chuyển đổi từ tất cả các nhà cung cấp dữ liệu sang hình ảnh raster.

Đăng ký lớp so với bản đồ

Các lớp được thêm vào sổ đăng ký không kết thúc ngay trên khung vẽ mà chỉ trong lần chạy tiếp theo của vòng lặp sự kiện. Vì vậy, tốt hơn hết bạn nên làm một trong hai điều sau đây

  • Bắt đầu kết xuất hình ảnh trong một bộ đếm thời gian. QTimer.singleShot(10, render_image)

  • Chạy QApplication.processEvents()sau khi thêm lớp. Điều này hoạt động nhưng đó là một cuộc gọi nguy hiểm để sử dụng (đôi khi dẫn đến sự cố kỳ lạ) và do đó nên tránh.

Cho tôi xem mã

Đoạn mã sau thực hiện điều này (được điều chỉnh một chút từ QFieldSync , hãy xem trong đó nếu bạn quan tâm đến nhiều tùy chỉnh hơn)

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
Bất cứ ý tưởng về renderCompletetín hiệu không hoạt động?
Giuse

Cảm ơn tất cả các thông tin! Thật không may, tôi đã thử chèn mã được đề xuất vào tập lệnh của mình, thay thế hoàn toàn phần soạn thảo bản đồ và vẫn gặp phải vấn đề tương tự. Hình ảnh đã lưu không bao gồm các lớp đường hoặc điểm, chỉ có raster được tải sẵn.
EastWest

Tôi nghĩ rằng tín hiệu được phát ra giữa công việc đã hoàn tất và hình ảnh được vẽ lên màn hình. Có một tham số được painterphát ra trên đó mà bạn vẫn có thể vẽ thêm những thứ sẽ xuất hiện trên hình ảnh cuối cùng (và từ đó bạn có thể cũng có thể lấy hình ảnh cuối cùng để làm cho phương pháp này hoạt động).
Matthias Kuhn

1
Điều này trông giống như giải pháp. Cảm ơn tất cả sự giúp đỡ của bạn. Một lưu ý nhanh nếu bất kỳ ai khác kết thúc việc tìm kiếm này - để làm cho QTimer hoạt động chính xác, hãy bỏ dấu ngoặc đơn sau khi render_image hoặc con trăn khác đưa ra cảnh báo. Nên đọcQTimer.singleShot(10, render_image)
EastWest

Rất tiếc, tất nhiên. Đã sửa lỗi trong mã trên
Matthias Kuhn
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.