Skip to content

Commit

Permalink
[FEATURE] Export processing models as PDF/SVG
Browse files Browse the repository at this point in the history
  • Loading branch information
nirvn committed Jan 2, 2017
1 parent 4603eb0 commit f54476c
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 19 deletions.
75 changes: 68 additions & 7 deletions python/plugins/processing/modeler/ModelerDialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,11 @@
import os

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.QtGui import QIcon, QImage, QPainter
from qgis.PyQt.QtSvg import QSvgGenerator
from qgis.PyQt.QtPrintSupport import QPrinter
from qgis.core import QgsApplication
from qgis.gui import QgsMessageBar
from processing.core.ProcessingConfig import ProcessingConfig
Expand Down Expand Up @@ -213,6 +215,8 @@ def _mimeDataAlgorithm(items):
self.mActionSave.triggered.connect(self.save)
self.mActionSaveAs.triggered.connect(self.saveAs)
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.mActionEditHelp.triggered.connect(self.editHelp)
self.mActionRun.triggered.connect(self.runModel)
Expand Down Expand Up @@ -287,6 +291,7 @@ def saveAs(self):
self.saveModel(True)

def exportAsImage(self):
self.repaintModel(controls=False)
filename, fileFilter = QFileDialog.getSaveFileName(self,
self.tr('Save Model As Image'), '',
self.tr('PNG files (*.png *.PNG)'))
Expand All @@ -296,23 +301,79 @@ def exportAsImage(self):
if not filename.lower().endswith('.png'):
filename += '.png'

totalRect = QRectF(0, 0, 1, 1)
for item in list(self.scene.items()):
totalRect = totalRect.united(item.sceneBoundingRect())
totalRect = self.scene.itemsBoundingRect()
totalRect.adjust(-10, -10, 10, 10)
imgRect = QRectF(0, 0, totalRect.width(), totalRect.height())

img = QImage(totalRect.width(), totalRect.height(),
QImage.Format_ARGB32_Premultiplied)
img.fill(Qt.white)
painter = QPainter()
painter.setRenderHint(QPainter.Antialiasing)
painter.begin(img)
self.scene.render(painter, totalRect, totalRect)
self.scene.render(painter, imgRect, totalRect)
painter.end()

img.save(filename)

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):
filename, filter = QFileDialog.getSaveFileName(self,
Expand Down Expand Up @@ -399,11 +460,11 @@ def openModel(self):
self.tr('The selected model could not be loaded.\n'
'See the log for more information.'))

def repaintModel(self):
def repaintModel(self, controls=True):
self.scene = ModelerScene()
self.scene.setSceneRect(QRectF(0, 0, ModelerAlgorithm.CANVAS_SIZE,
ModelerAlgorithm.CANVAS_SIZE))
self.scene.paintModel(self.alg)
self.scene.paintModel(self.alg, controls)
self.view.setScene(self.scene)

def addInput(self):
Expand Down
22 changes: 14 additions & 8 deletions python/plugins/processing/modeler/ModelerGraphicItem.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,9 @@ class ModelerGraphicItem(QGraphicsItem):
BOX_HEIGHT = 30
BOX_WIDTH = 200

def __init__(self, element, model):
def __init__(self, element, model, controls):
super(ModelerGraphicItem, self).__init__(None)
self.controls = controls
self.model = model
self.element = element
if isinstance(element, ModelerParameter):
Expand All @@ -73,7 +74,7 @@ def __init__(self, element, model):
self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
self.setZValue(1000)

if not isinstance(element, ModelerOutput):
if not isinstance(element, ModelerOutput) and controls:
svg = QSvgRenderer(os.path.join(pluginPath, 'images', 'edit.svg'))
picture = QPicture()
painter = QPainter(picture)
Expand Down Expand Up @@ -101,13 +102,15 @@ def __init__(self, element, model):
if alg.parameters:
pt = self.getLinkPointForParameter(-1)
pt = QPointF(0, pt.y())
self.inButton = FoldButtonGraphicItem(pt, self.foldInput, self.element.paramsFolded)
self.inButton.setParentItem(self)
if controls:
self.inButton = FoldButtonGraphicItem(pt, self.foldInput, self.element.paramsFolded)
self.inButton.setParentItem(self)
if alg.outputs:
pt = self.getLinkPointForOutput(-1)
pt = QPointF(0, pt.y())
self.outButton = FoldButtonGraphicItem(pt, self.foldOutput, self.element.outputsFolded)
self.outButton.setParentItem(self)
if controls:
self.outButton = FoldButtonGraphicItem(pt, self.foldOutput, self.element.outputsFolded)
self.outButton.setParentItem(self)

def foldInput(self, folded):
self.element.paramsFolded = folded
Expand Down Expand Up @@ -237,12 +240,15 @@ def paint(self, painter, option, widget=None):
ModelerGraphicItem.BOX_HEIGHT + 2)
color = QColor(172, 196, 114)
outline = QColor(90, 140, 90)
selected = QColor(90, 140, 90)
if isinstance(self.element, ModelerParameter):
color = QColor(238, 242, 131)
outline = QColor(234, 226, 118)
selected = QColor(151, 153, 83)
elif isinstance(self.element, Algorithm):
color = Qt.white
outline = Qt.gray
selected = Qt.gray
painter.setPen(QPen(outline, 1))
painter.setBrush(QBrush(color, Qt.SolidPattern))
painter.drawRect(rect)
Expand All @@ -255,7 +261,7 @@ def paint(self, painter, option, widget=None):
painter.setPen(QPen(Qt.gray))
text = text + "\n(deactivated)"
elif self.isSelected():
painter.setPen(QPen(outline))
painter.setPen(QPen(selected))
fm = QFontMetricsF(font)
text = self.getAdjustedText(self.text)
h = fm.ascent()
Expand Down Expand Up @@ -379,7 +385,7 @@ def paint(self, painter, option, widget=None):
rect = QRectF(pt.x(), pt.y(), self.WIDTH, self.HEIGHT)
if self.isIn:
painter.setPen(QPen(Qt.transparent, 1))
painter.setBrush(QBrush(Qt.lightGray,
painter.setBrush(QBrush(QColor(55, 55, 55, 33),
Qt.SolidPattern))
else:
painter.setPen(QPen(Qt.transparent, 1))
Expand Down
8 changes: 4 additions & 4 deletions python/plugins/processing/modeler/ModelerScene.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,11 +78,11 @@ def getItemsFromParamValue(self, value):
items.append((self.algItems[value.alg], i))
return items

def paintModel(self, model):
def paintModel(self, model, controls=True):
self.model = model
# Inputs
for inp in list(model.inputs.values()):
item = ModelerGraphicItem(inp, model)
item = ModelerGraphicItem(inp, model, controls)
item.setFlag(QGraphicsItem.ItemIsMovable, True)
item.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.addItem(item)
Expand All @@ -91,7 +91,7 @@ def paintModel(self, model):

# We add the algs
for alg in list(model.algs.values()):
item = ModelerGraphicItem(alg, model)
item = ModelerGraphicItem(alg, model, controls)
item.setFlag(QGraphicsItem.ItemIsMovable, True)
item.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.addItem(item)
Expand Down Expand Up @@ -131,7 +131,7 @@ def paintModel(self, model):
for key in outputs:
out = outputs[key]
if out is not None:
item = ModelerGraphicItem(out, model)
item = ModelerGraphicItem(out, model, controls)
item.setFlag(QGraphicsItem.ItemIsMovable, True)
item.setFlag(QGraphicsItem.ItemIsSelectable, True)
self.addItem(item)
Expand Down
26 changes: 26 additions & 0 deletions python/plugins/processing/ui/DlgModeler.ui
Original file line number Diff line number Diff line change
Expand Up @@ -310,6 +310,8 @@
<addaction name="mActionSaveAs"/>
<addaction name="separator"/>
<addaction name="mActionExportImage"/>
<addaction name="mActionExportPdf"/>
<addaction name="mActionExportSvg"/>
<addaction name="mActionExportPython"/>
<addaction name="separator"/>
<addaction name="mActionEditHelp"/>
Expand Down Expand Up @@ -373,6 +375,30 @@
<string>Export as image</string>
</property>
</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">
<property name="icon">
<iconset resource="../../images/images.qrc">
Expand Down

0 comments on commit f54476c

Please sign in to comment.