[FEATURE] Export processing models as PDF/SVG

This commit is contained in:
nirvn 2017-01-02 10:27:41 +07:00
parent 4603eb0f3d
commit f54476cdda
4 changed files with 112 additions and 19 deletions

View File

@ -31,9 +31,11 @@ import sys
import os import os
from qgis.PyQt import uic from qgis.PyQt import uic
from qgis.PyQt.QtCore import Qt, QRectF, QMimeData, QPoint, QPointF, QSettings, QByteArray, QSize, pyqtSignal from qgis.PyQt.QtCore import Qt, QRectF, QMimeData, QPoint, QPointF, QSettings, QByteArray, QSize, QSizeF, pyqtSignal
from qgis.PyQt.QtWidgets import QGraphicsView, QTreeWidget, QMessageBox, QFileDialog, QTreeWidgetItem, QSizePolicy, QMainWindow from qgis.PyQt.QtWidgets import QGraphicsView, QTreeWidget, QMessageBox, QFileDialog, QTreeWidgetItem, QSizePolicy, QMainWindow
from qgis.PyQt.QtGui import QIcon, QImage, QPainter from qgis.PyQt.QtGui import QIcon, QImage, QPainter
from qgis.PyQt.QtSvg import QSvgGenerator
from qgis.PyQt.QtPrintSupport import QPrinter
from qgis.core import QgsApplication from qgis.core import QgsApplication
from qgis.gui import QgsMessageBar from qgis.gui import QgsMessageBar
from processing.core.ProcessingConfig import ProcessingConfig from processing.core.ProcessingConfig import ProcessingConfig
@ -213,6 +215,8 @@ class ModelerDialog(BASE, WIDGET):
self.mActionSave.triggered.connect(self.save) self.mActionSave.triggered.connect(self.save)
self.mActionSaveAs.triggered.connect(self.saveAs) self.mActionSaveAs.triggered.connect(self.saveAs)
self.mActionExportImage.triggered.connect(self.exportAsImage) self.mActionExportImage.triggered.connect(self.exportAsImage)
self.mActionExportPdf.triggered.connect(self.exportAsPdf)
self.mActionExportSvg.triggered.connect(self.exportAsSvg)
self.mActionExportPython.triggered.connect(self.exportAsPython) self.mActionExportPython.triggered.connect(self.exportAsPython)
self.mActionEditHelp.triggered.connect(self.editHelp) self.mActionEditHelp.triggered.connect(self.editHelp)
self.mActionRun.triggered.connect(self.runModel) self.mActionRun.triggered.connect(self.runModel)
@ -287,6 +291,7 @@ class ModelerDialog(BASE, WIDGET):
self.saveModel(True) self.saveModel(True)
def exportAsImage(self): def exportAsImage(self):
self.repaintModel(controls=False)
filename, fileFilter = QFileDialog.getSaveFileName(self, filename, fileFilter = QFileDialog.getSaveFileName(self,
self.tr('Save Model As Image'), '', self.tr('Save Model As Image'), '',
self.tr('PNG files (*.png *.PNG)')) self.tr('PNG files (*.png *.PNG)'))
@ -296,10 +301,9 @@ class ModelerDialog(BASE, WIDGET):
if not filename.lower().endswith('.png'): if not filename.lower().endswith('.png'):
filename += '.png' filename += '.png'
totalRect = QRectF(0, 0, 1, 1) totalRect = self.scene.itemsBoundingRect()
for item in list(self.scene.items()):
totalRect = totalRect.united(item.sceneBoundingRect())
totalRect.adjust(-10, -10, 10, 10) totalRect.adjust(-10, -10, 10, 10)
imgRect = QRectF(0, 0, totalRect.width(), totalRect.height())
img = QImage(totalRect.width(), totalRect.height(), img = QImage(totalRect.width(), totalRect.height(),
QImage.Format_ARGB32_Premultiplied) QImage.Format_ARGB32_Premultiplied)
@ -307,12 +311,69 @@ class ModelerDialog(BASE, WIDGET):
painter = QPainter() painter = QPainter()
painter.setRenderHint(QPainter.Antialiasing) painter.setRenderHint(QPainter.Antialiasing)
painter.begin(img) painter.begin(img)
self.scene.render(painter, totalRect, totalRect) self.scene.render(painter, imgRect, totalRect)
painter.end() painter.end()
img.save(filename) img.save(filename)
self.bar.pushMessage("", "Model was correctly exported as image", level=QgsMessageBar.SUCCESS, duration=5) self.bar.pushMessage("", "Model was correctly exported as image", level=QgsMessageBar.SUCCESS, duration=5)
self.repaintModel(controls=True)
def exportAsPdf(self):
self.repaintModel(controls=False)
filename, fileFilter = QFileDialog.getSaveFileName(self,
self.tr('Save Model As PDF'), '',
self.tr('SVG files (*.pdf *.PDF)'))
if not filename:
return
if not filename.lower().endswith('.pdf'):
filename += '.pdf'
totalRect = self.scene.itemsBoundingRect()
totalRect.adjust(-10, -10, 10, 10)
printerRect = QRectF(0, 0, totalRect.width(), totalRect.height())
printer = QPrinter()
printer.setOutputFormat(QPrinter.PdfFormat)
printer.setOutputFileName(filename)
printer.setPaperSize(QSizeF(printerRect.width(), printerRect.height()), QPrinter.DevicePixel)
printer.setFullPage(True)
painter = QPainter(printer)
self.scene.render(painter, printerRect, totalRect)
painter.end()
self.bar.pushMessage("", "Model was correctly exported as PDF", level=QgsMessageBar.SUCCESS, duration=5)
self.repaintModel(controls=True)
def exportAsSvg(self):
self.repaintModel(controls=False)
filename, fileFilter = QFileDialog.getSaveFileName(self,
self.tr('Save Model As SVG'), '',
self.tr('SVG files (*.svg *.SVG)'))
if not filename:
return
if not filename.lower().endswith('.svg'):
filename += '.svg'
totalRect = self.scene.itemsBoundingRect()
totalRect.adjust(-10, -10, 10, 10)
svgRect = QRectF(0, 0, totalRect.width(), totalRect.height())
svg = QSvgGenerator()
svg.setFileName(filename)
svg.setSize(QSize(totalRect.width(), totalRect.height()))
svg.setViewBox(svgRect)
svg.setTitle(self.alg.name)
painter = QPainter(svg)
self.scene.render(painter, svgRect, totalRect)
painter.end()
self.bar.pushMessage("", "Model was correctly exported as SVG", level=QgsMessageBar.SUCCESS, duration=5)
self.repaintModel(controls=True)
def exportAsPython(self): def exportAsPython(self):
filename, filter = QFileDialog.getSaveFileName(self, filename, filter = QFileDialog.getSaveFileName(self,
@ -399,11 +460,11 @@ class ModelerDialog(BASE, WIDGET):
self.tr('The selected model could not be loaded.\n' self.tr('The selected model could not be loaded.\n'
'See the log for more information.')) 'See the log for more information.'))
def repaintModel(self): def repaintModel(self, controls=True):
self.scene = ModelerScene() self.scene = ModelerScene()
self.scene.setSceneRect(QRectF(0, 0, ModelerAlgorithm.CANVAS_SIZE, self.scene.setSceneRect(QRectF(0, 0, ModelerAlgorithm.CANVAS_SIZE,
ModelerAlgorithm.CANVAS_SIZE)) ModelerAlgorithm.CANVAS_SIZE))
self.scene.paintModel(self.alg) self.scene.paintModel(self.alg, controls)
self.view.setScene(self.scene) self.view.setScene(self.scene)
def addInput(self): def addInput(self):

View File

@ -45,8 +45,9 @@ class ModelerGraphicItem(QGraphicsItem):
BOX_HEIGHT = 30 BOX_HEIGHT = 30
BOX_WIDTH = 200 BOX_WIDTH = 200
def __init__(self, element, model): def __init__(self, element, model, controls):
super(ModelerGraphicItem, self).__init__(None) super(ModelerGraphicItem, self).__init__(None)
self.controls = controls
self.model = model self.model = model
self.element = element self.element = element
if isinstance(element, ModelerParameter): if isinstance(element, ModelerParameter):
@ -73,7 +74,7 @@ class ModelerGraphicItem(QGraphicsItem):
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True) self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
self.setZValue(1000) self.setZValue(1000)
if not isinstance(element, ModelerOutput): if not isinstance(element, ModelerOutput) and controls:
svg = QSvgRenderer(os.path.join(pluginPath, 'images', 'edit.svg')) svg = QSvgRenderer(os.path.join(pluginPath, 'images', 'edit.svg'))
picture = QPicture() picture = QPicture()
painter = QPainter(picture) painter = QPainter(picture)
@ -101,13 +102,15 @@ class ModelerGraphicItem(QGraphicsItem):
if alg.parameters: if alg.parameters:
pt = self.getLinkPointForParameter(-1) pt = self.getLinkPointForParameter(-1)
pt = QPointF(0, pt.y()) pt = QPointF(0, pt.y())
self.inButton = FoldButtonGraphicItem(pt, self.foldInput, self.element.paramsFolded) if controls:
self.inButton.setParentItem(self) self.inButton = FoldButtonGraphicItem(pt, self.foldInput, self.element.paramsFolded)
self.inButton.setParentItem(self)
if alg.outputs: if alg.outputs:
pt = self.getLinkPointForOutput(-1) pt = self.getLinkPointForOutput(-1)
pt = QPointF(0, pt.y()) pt = QPointF(0, pt.y())
self.outButton = FoldButtonGraphicItem(pt, self.foldOutput, self.element.outputsFolded) if controls:
self.outButton.setParentItem(self) self.outButton = FoldButtonGraphicItem(pt, self.foldOutput, self.element.outputsFolded)
self.outButton.setParentItem(self)
def foldInput(self, folded): def foldInput(self, folded):
self.element.paramsFolded = folded self.element.paramsFolded = folded
@ -237,12 +240,15 @@ class ModelerGraphicItem(QGraphicsItem):
ModelerGraphicItem.BOX_HEIGHT + 2) ModelerGraphicItem.BOX_HEIGHT + 2)
color = QColor(172, 196, 114) color = QColor(172, 196, 114)
outline = QColor(90, 140, 90) outline = QColor(90, 140, 90)
selected = QColor(90, 140, 90)
if isinstance(self.element, ModelerParameter): if isinstance(self.element, ModelerParameter):
color = QColor(238, 242, 131) color = QColor(238, 242, 131)
outline = QColor(234, 226, 118) outline = QColor(234, 226, 118)
selected = QColor(151, 153, 83)
elif isinstance(self.element, Algorithm): elif isinstance(self.element, Algorithm):
color = Qt.white color = Qt.white
outline = Qt.gray outline = Qt.gray
selected = Qt.gray
painter.setPen(QPen(outline, 1)) painter.setPen(QPen(outline, 1))
painter.setBrush(QBrush(color, Qt.SolidPattern)) painter.setBrush(QBrush(color, Qt.SolidPattern))
painter.drawRect(rect) painter.drawRect(rect)
@ -255,7 +261,7 @@ class ModelerGraphicItem(QGraphicsItem):
painter.setPen(QPen(Qt.gray)) painter.setPen(QPen(Qt.gray))
text = text + "\n(deactivated)" text = text + "\n(deactivated)"
elif self.isSelected(): elif self.isSelected():
painter.setPen(QPen(outline)) painter.setPen(QPen(selected))
fm = QFontMetricsF(font) fm = QFontMetricsF(font)
text = self.getAdjustedText(self.text) text = self.getAdjustedText(self.text)
h = fm.ascent() h = fm.ascent()
@ -379,7 +385,7 @@ class FlatButtonGraphicItem(QGraphicsItem):
rect = QRectF(pt.x(), pt.y(), self.WIDTH, self.HEIGHT) rect = QRectF(pt.x(), pt.y(), self.WIDTH, self.HEIGHT)
if self.isIn: if self.isIn:
painter.setPen(QPen(Qt.transparent, 1)) painter.setPen(QPen(Qt.transparent, 1))
painter.setBrush(QBrush(Qt.lightGray, painter.setBrush(QBrush(QColor(55, 55, 55, 33),
Qt.SolidPattern)) Qt.SolidPattern))
else: else:
painter.setPen(QPen(Qt.transparent, 1)) painter.setPen(QPen(Qt.transparent, 1))

View File

@ -78,11 +78,11 @@ class ModelerScene(QGraphicsScene):
items.append((self.algItems[value.alg], i)) items.append((self.algItems[value.alg], i))
return items return items
def paintModel(self, model): def paintModel(self, model, controls=True):
self.model = model self.model = model
# Inputs # Inputs
for inp in list(model.inputs.values()): for inp in list(model.inputs.values()):
item = ModelerGraphicItem(inp, model) item = ModelerGraphicItem(inp, model, controls)
item.setFlag(QGraphicsItem.ItemIsMovable, True) item.setFlag(QGraphicsItem.ItemIsMovable, True)
item.setFlag(QGraphicsItem.ItemIsSelectable, True) item.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.addItem(item) self.addItem(item)
@ -91,7 +91,7 @@ class ModelerScene(QGraphicsScene):
# We add the algs # We add the algs
for alg in list(model.algs.values()): for alg in list(model.algs.values()):
item = ModelerGraphicItem(alg, model) item = ModelerGraphicItem(alg, model, controls)
item.setFlag(QGraphicsItem.ItemIsMovable, True) item.setFlag(QGraphicsItem.ItemIsMovable, True)
item.setFlag(QGraphicsItem.ItemIsSelectable, True) item.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.addItem(item) self.addItem(item)
@ -131,7 +131,7 @@ class ModelerScene(QGraphicsScene):
for key in outputs: for key in outputs:
out = outputs[key] out = outputs[key]
if out is not None: if out is not None:
item = ModelerGraphicItem(out, model) item = ModelerGraphicItem(out, model, controls)
item.setFlag(QGraphicsItem.ItemIsMovable, True) item.setFlag(QGraphicsItem.ItemIsMovable, True)
item.setFlag(QGraphicsItem.ItemIsSelectable, True) item.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.addItem(item) self.addItem(item)

View File

@ -310,6 +310,8 @@
<addaction name="mActionSaveAs"/> <addaction name="mActionSaveAs"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="mActionExportImage"/> <addaction name="mActionExportImage"/>
<addaction name="mActionExportPdf"/>
<addaction name="mActionExportSvg"/>
<addaction name="mActionExportPython"/> <addaction name="mActionExportPython"/>
<addaction name="separator"/> <addaction name="separator"/>
<addaction name="mActionEditHelp"/> <addaction name="mActionEditHelp"/>
@ -373,6 +375,30 @@
<string>Export as image</string> <string>Export as image</string>
</property> </property>
</action> </action>
<action name="mActionExportPdf">
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionSaveAsPDF.svg</normaloff>:/images/themes/default/mActionSaveAsPDF.svg</iconset>
</property>
<property name="text">
<string>Export as PDF...</string>
</property>
<property name="toolTip">
<string>Export as PDF</string>
</property>
</action>
<action name="mActionExportSvg">
<property name="icon">
<iconset resource="../../images/images.qrc">
<normaloff>:/images/themes/default/mActionSaveAsSVG.svg</normaloff>:/images/themes/default/mActionSaveAsSVG.svg</iconset>
</property>
<property name="text">
<string>Export as SVG...</string>
</property>
<property name="toolTip">
<string>Export as SVG</string>
</property>
</action>
<action name="mActionExportPython"> <action name="mActionExportPython">
<property name="icon"> <property name="icon">
<iconset resource="../../images/images.qrc"> <iconset resource="../../images/images.qrc">