mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-17 00:04:02 -04:00
[FEATURE] Export processing models as PDF/SVG
This commit is contained in:
parent
4603eb0f3d
commit
f54476cdda
@ -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):
|
||||||
|
@ -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))
|
||||||
|
@ -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)
|
||||||
|
@ -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">
|
||||||
|
Loading…
x
Reference in New Issue
Block a user