2012-10-04 19:33:47 +02:00
# -*- coding: utf-8 -*-
"""
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
ModelerDialog . py
- - - - - - - - - - - - - - - - - - - - -
Date : August 2012
Copyright : ( C ) 2012 by Victor Olaya
Email : volayaf at gmail dot com
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* *
* This program is free software ; you can redistribute it and / or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation ; either version 2 of the License , or *
* ( at your option ) any later version . *
* *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
"""
__author__ = ' Victor Olaya '
__date__ = ' August 2012 '
__copyright__ = ' (C) 2012, Victor Olaya '
2013-10-01 20:52:22 +03:00
2013-09-13 16:25:41 +03:00
import codecs
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
import sys
2017-12-05 11:52:06 +07:00
import operator
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
import os
2018-05-21 14:23:26 +10:00
import warnings
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
2016-04-29 11:39:26 +02:00
from qgis . PyQt import uic
2018-07-10 10:07:33 +10:00
from qgis . PyQt . QtCore import (
Qt ,
QCoreApplication ,
2018-09-11 09:42:56 +07:00
QDir ,
2018-07-10 10:07:33 +10:00
QRectF ,
QMimeData ,
QPoint ,
QPointF ,
QByteArray ,
QSize ,
QSizeF ,
pyqtSignal ,
QDataStream ,
2018-09-11 09:42:56 +07:00
QIODevice ,
2019-03-04 20:12:29 +10:00
QUrl ,
QTimer )
2018-01-16 14:04:49 +03:00
from qgis . PyQt . QtWidgets import ( QGraphicsView ,
QTreeWidget ,
QMessageBox ,
QFileDialog ,
QTreeWidgetItem ,
QSizePolicy ,
QMainWindow ,
QShortcut ,
2018-05-31 12:45:47 +10:00
QLabel ,
QDockWidget ,
QWidget ,
QVBoxLayout ,
QGridLayout ,
QFrame ,
2019-02-01 12:22:59 +10:00
QLineEdit ,
QToolButton ,
QAction )
2018-05-31 12:45:47 +10:00
from qgis . PyQt . QtGui import ( QIcon ,
QImage ,
QPainter ,
QKeySequence )
2017-01-02 10:27:41 +07:00
from qgis . PyQt . QtSvg import QSvgGenerator
from qgis . PyQt . QtPrintSupport import QPrinter
2018-02-05 22:11:34 -04:00
from qgis . core import ( Qgis ,
QgsApplication ,
2019-02-01 12:22:59 +10:00
QgsProcessing ,
2018-07-18 07:18:08 +10:00
QgsProject ,
2017-04-24 14:35:50 +10:00
QgsSettings ,
QgsMessageLog ,
2017-06-13 16:05:59 +10:00
QgsProcessingUtils ,
2017-06-20 17:26:57 +10:00
QgsProcessingModelAlgorithm ,
2017-07-08 20:00:40 +10:00
QgsProcessingModelParameter ,
2019-02-19 16:27:50 +10:00
QgsProcessingParameterType ,
QgsExpressionContextScope ,
QgsExpressionContext
2018-01-16 14:04:49 +03:00
)
2018-05-31 12:45:47 +10:00
from qgis . gui import ( QgsMessageBar ,
QgsDockWidget ,
QgsScrollArea ,
2018-07-10 10:07:33 +10:00
QgsFilterLineEdit ,
QgsProcessingToolboxTreeView ,
2019-02-19 16:27:50 +10:00
QgsProcessingToolboxProxyModel ,
2019-06-30 20:08:11 +10:00
QgsProcessingParameterDefinitionDialog ,
QgsVariableEditorWidget ,
QgsProcessingParameterWidgetContext )
2013-08-12 20:44:27 +02:00
from processing . gui . HelpEditionDialog import HelpEditionDialog
2014-11-12 11:42:29 +02:00
from processing . gui . AlgorithmDialog import AlgorithmDialog
2014-05-21 16:33:20 +02:00
from processing . modeler . ModelerParameterDefinitionDialog import ModelerParameterDefinitionDialog
2013-08-12 20:44:27 +02:00
from processing . modeler . ModelerParametersDialog import ModelerParametersDialog
from processing . modeler . ModelerUtils import ModelerUtils
from processing . modeler . ModelerScene import ModelerScene
2018-07-18 07:18:08 +10:00
from processing . modeler . ProjectProvider import PROJECT_PROVIDER_ID
2019-02-01 12:22:59 +10:00
from processing . script . ScriptEditorDialog import ScriptEditorDialog
2019-03-05 17:04:00 +10:00
from processing . core . ProcessingConfig import ProcessingConfig
2019-06-30 20:08:11 +10:00
from processing . tools . dataobjects import createContext
2017-08-07 01:33:18 +10:00
from qgis . utils import iface
2013-10-01 20:52:22 +03:00
2015-05-18 19:51:26 +03:00
pluginPath = os . path . split ( os . path . dirname ( __file__ ) ) [ 0 ]
2018-05-21 14:23:26 +10:00
with warnings . catch_warnings ( ) :
warnings . filterwarnings ( " ignore " , category = DeprecationWarning )
WIDGET , BASE = uic . loadUiType (
os . path . join ( pluginPath , ' ui ' , ' DlgModeler.ui ' ) )
2015-05-18 19:51:26 +03:00
2013-10-01 20:52:22 +03:00
2018-07-10 10:07:33 +10:00
class ModelerToolboxModel ( QgsProcessingToolboxProxyModel ) :
def __init__ ( self , parent = None , registry = None , recentLog = None ) :
super ( ) . __init__ ( parent , registry , recentLog )
def flags ( self , index ) :
f = super ( ) . flags ( index )
source_index = self . mapToSource ( index )
if self . toolboxModel ( ) . isAlgorithm ( source_index ) :
f = f | Qt . ItemIsDragEnabled
return f
def supportedDragActions ( self ) :
return Qt . CopyAction
2015-05-18 21:04:20 +03:00
class ModelerDialog ( BASE , WIDGET ) :
2017-12-05 11:52:06 +07:00
ALG_ITEM = ' ALG_ITEM '
PROVIDER_ITEM = ' PROVIDER_ITEM '
GROUP_ITEM = ' GROUP_ITEM '
NAME_ROLE = Qt . UserRole
TAG_ROLE = Qt . UserRole + 1
TYPE_ROLE = Qt . UserRole + 2
2013-05-01 23:45:20 +02:00
2014-06-08 00:21:12 +02:00
CANVAS_SIZE = 4000
2013-05-01 23:45:20 +02:00
2016-10-31 14:37:12 +10:00
update_model = pyqtSignal ( )
2017-06-13 16:05:59 +10:00
def __init__ ( self , model = None ) :
2018-02-26 16:26:05 +10:00
super ( ) . __init__ ( None )
self . setAttribute ( Qt . WA_DeleteOnClose )
2012-12-01 20:21:42 +02:00
self . setupUi ( self )
2014-07-02 07:46:03 +02:00
2019-02-19 16:27:50 +10:00
self . _variables_scope = None
2018-05-31 12:45:47 +10:00
# LOTS of bug reports when we include the dock creation in the UI file
# see e.g. #16428, #19068
# So just roll it all by hand......!
self . propertiesDock = QgsDockWidget ( self )
self . propertiesDock . setFeatures (
QDockWidget . DockWidgetFloatable | QDockWidget . DockWidgetMovable )
self . propertiesDock . setObjectName ( " propertiesDock " )
propertiesDockContents = QWidget ( )
self . verticalDockLayout_1 = QVBoxLayout ( propertiesDockContents )
self . verticalDockLayout_1 . setContentsMargins ( 0 , 0 , 0 , 0 )
self . verticalDockLayout_1 . setSpacing ( 0 )
self . scrollArea_1 = QgsScrollArea ( propertiesDockContents )
sizePolicy = QSizePolicy ( QSizePolicy . MinimumExpanding ,
2018-05-31 12:51:53 +10:00
QSizePolicy . MinimumExpanding )
2018-05-31 12:45:47 +10:00
sizePolicy . setHorizontalStretch ( 0 )
sizePolicy . setVerticalStretch ( 0 )
sizePolicy . setHeightForWidth ( self . scrollArea_1 . sizePolicy ( ) . hasHeightForWidth ( ) )
self . scrollArea_1 . setSizePolicy ( sizePolicy )
self . scrollArea_1 . setFocusPolicy ( Qt . WheelFocus )
self . scrollArea_1 . setFrameShape ( QFrame . NoFrame )
self . scrollArea_1 . setFrameShadow ( QFrame . Plain )
self . scrollArea_1 . setWidgetResizable ( True )
self . scrollAreaWidgetContents_1 = QWidget ( )
self . gridLayout = QGridLayout ( self . scrollAreaWidgetContents_1 )
self . gridLayout . setContentsMargins ( 6 , 6 , 6 , 6 )
self . gridLayout . setSpacing ( 4 )
self . label_1 = QLabel ( self . scrollAreaWidgetContents_1 )
self . gridLayout . addWidget ( self . label_1 , 0 , 0 , 1 , 1 )
self . textName = QLineEdit ( self . scrollAreaWidgetContents_1 )
self . gridLayout . addWidget ( self . textName , 0 , 1 , 1 , 1 )
self . label_2 = QLabel ( self . scrollAreaWidgetContents_1 )
self . gridLayout . addWidget ( self . label_2 , 1 , 0 , 1 , 1 )
self . textGroup = QLineEdit ( self . scrollAreaWidgetContents_1 )
self . gridLayout . addWidget ( self . textGroup , 1 , 1 , 1 , 1 )
self . label_1 . setText ( self . tr ( " Name " ) )
self . textName . setToolTip ( self . tr ( " Enter model name here " ) )
self . label_2 . setText ( self . tr ( " Group " ) )
self . textGroup . setToolTip ( self . tr ( " Enter group name here " ) )
self . scrollArea_1 . setWidget ( self . scrollAreaWidgetContents_1 )
self . verticalDockLayout_1 . addWidget ( self . scrollArea_1 )
self . propertiesDock . setWidget ( propertiesDockContents )
2019-02-19 16:27:50 +10:00
self . propertiesDock . setWindowTitle ( self . tr ( " Model Properties " ) )
2018-05-31 12:45:47 +10:00
self . inputsDock = QgsDockWidget ( self )
self . inputsDock . setFeatures ( QDockWidget . DockWidgetFloatable | QDockWidget . DockWidgetMovable )
self . inputsDock . setObjectName ( " inputsDock " )
self . inputsDockContents = QWidget ( )
self . verticalLayout_3 = QVBoxLayout ( self . inputsDockContents )
self . verticalLayout_3 . setContentsMargins ( 0 , 0 , 0 , 0 )
self . scrollArea_2 = QgsScrollArea ( self . inputsDockContents )
sizePolicy . setHeightForWidth ( self . scrollArea_2 . sizePolicy ( ) . hasHeightForWidth ( ) )
self . scrollArea_2 . setSizePolicy ( sizePolicy )
self . scrollArea_2 . setFocusPolicy ( Qt . WheelFocus )
self . scrollArea_2 . setFrameShape ( QFrame . NoFrame )
self . scrollArea_2 . setFrameShadow ( QFrame . Plain )
self . scrollArea_2 . setWidgetResizable ( True )
self . scrollAreaWidgetContents_2 = QWidget ( )
self . verticalLayout = QVBoxLayout ( self . scrollAreaWidgetContents_2 )
self . verticalLayout . setContentsMargins ( 0 , 0 , 0 , 0 )
self . verticalLayout . setSpacing ( 0 )
self . inputsTree = QTreeWidget ( self . scrollAreaWidgetContents_2 )
self . inputsTree . setAlternatingRowColors ( True )
self . inputsTree . header ( ) . setVisible ( False )
self . verticalLayout . addWidget ( self . inputsTree )
self . scrollArea_2 . setWidget ( self . scrollAreaWidgetContents_2 )
self . verticalLayout_3 . addWidget ( self . scrollArea_2 )
self . inputsDock . setWidget ( self . inputsDockContents )
self . addDockWidget ( Qt . DockWidgetArea ( 1 ) , self . inputsDock )
self . inputsDock . setWindowTitle ( self . tr ( " Inputs " ) )
self . algorithmsDock = QgsDockWidget ( self )
2018-05-31 12:51:53 +10:00
self . algorithmsDock . setFeatures ( QDockWidget . DockWidgetFloatable | QDockWidget . DockWidgetMovable )
2018-05-31 12:45:47 +10:00
self . algorithmsDock . setObjectName ( " algorithmsDock " )
self . algorithmsDockContents = QWidget ( )
self . verticalLayout_4 = QVBoxLayout ( self . algorithmsDockContents )
self . verticalLayout_4 . setContentsMargins ( 0 , 0 , 0 , 0 )
self . scrollArea_3 = QgsScrollArea ( self . algorithmsDockContents )
sizePolicy . setHeightForWidth ( self . scrollArea_3 . sizePolicy ( ) . hasHeightForWidth ( ) )
self . scrollArea_3 . setSizePolicy ( sizePolicy )
self . scrollArea_3 . setFocusPolicy ( Qt . WheelFocus )
self . scrollArea_3 . setFrameShape ( QFrame . NoFrame )
self . scrollArea_3 . setFrameShadow ( QFrame . Plain )
self . scrollArea_3 . setWidgetResizable ( True )
self . scrollAreaWidgetContents_3 = QWidget ( )
self . verticalLayout_2 = QVBoxLayout ( self . scrollAreaWidgetContents_3 )
self . verticalLayout_2 . setContentsMargins ( 0 , 0 , 0 , 0 )
self . verticalLayout_2 . setSpacing ( 4 )
self . searchBox = QgsFilterLineEdit ( self . scrollAreaWidgetContents_3 )
self . verticalLayout_2 . addWidget ( self . searchBox )
2018-07-10 10:07:33 +10:00
self . algorithmTree = QgsProcessingToolboxTreeView ( None ,
QgsApplication . processingRegistry ( ) )
2018-05-31 12:45:47 +10:00
self . algorithmTree . setAlternatingRowColors ( True )
self . algorithmTree . header ( ) . setVisible ( False )
self . verticalLayout_2 . addWidget ( self . algorithmTree )
self . scrollArea_3 . setWidget ( self . scrollAreaWidgetContents_3 )
self . verticalLayout_4 . addWidget ( self . scrollArea_3 )
self . algorithmsDock . setWidget ( self . algorithmsDockContents )
self . addDockWidget ( Qt . DockWidgetArea ( 1 ) , self . algorithmsDock )
self . algorithmsDock . setWindowTitle ( self . tr ( " Algorithms " ) )
self . searchBox . setToolTip ( self . tr ( " Enter algorithm name to filter list " ) )
2018-05-31 12:51:53 +10:00
self . searchBox . setShowSearchIcon ( True )
2018-05-31 12:45:47 +10:00
2019-02-19 16:27:50 +10:00
self . variables_dock = QgsDockWidget ( self )
self . variables_dock . setFeatures ( QDockWidget . DockWidgetFloatable | QDockWidget . DockWidgetMovable )
self . variables_dock . setObjectName ( " variablesDock " )
self . variables_dock_contents = QWidget ( )
2019-03-04 21:26:30 +10:00
vl_v = QVBoxLayout ( )
2019-02-19 16:27:50 +10:00
vl_v . setContentsMargins ( 0 , 0 , 0 , 0 )
self . variables_editor = QgsVariableEditorWidget ( )
vl_v . addWidget ( self . variables_editor )
self . variables_dock_contents . setLayout ( vl_v )
self . variables_dock . setWidget ( self . variables_dock_contents )
self . addDockWidget ( Qt . DockWidgetArea ( 1 ) , self . variables_dock )
self . variables_dock . setWindowTitle ( self . tr ( " Variables " ) )
self . addDockWidget ( Qt . DockWidgetArea ( 1 ) , self . propertiesDock )
self . tabifyDockWidget ( self . propertiesDock , self . variables_dock )
self . variables_editor . scopeChanged . connect ( self . variables_changed )
2016-09-12 08:00:16 +02:00
self . bar = QgsMessageBar ( )
self . bar . setSizePolicy ( QSizePolicy . Minimum , QSizePolicy . Fixed )
2016-11-23 17:28:34 +07:00
self . centralWidget ( ) . layout ( ) . insertWidget ( 0 , self . bar )
2016-09-12 08:00:16 +02:00
2016-11-24 11:37:53 +07:00
try :
self . setDockOptions ( self . dockOptions ( ) | QMainWindow . GroupedDragging )
except :
pass
2018-07-18 07:18:08 +10:00
if iface is not None :
self . mToolbar . setIconSize ( iface . iconSize ( ) )
2018-08-15 19:06:48 +10:00
self . setStyleSheet ( iface . mainWindow ( ) . styleSheet ( ) )
2019-02-01 12:22:59 +10:00
self . toolbutton_export_to_script = QToolButton ( )
self . toolbutton_export_to_script . setPopupMode ( QToolButton . InstantPopup )
2019-02-04 00:52:10 +01:00
self . export_to_script_algorithm_action = QAction ( QCoreApplication . translate ( ' ModelerDialog ' , ' Export as Script Algorithm… ' ) )
2019-02-01 12:22:59 +10:00
self . toolbutton_export_to_script . addActions ( [ self . export_to_script_algorithm_action ] )
2019-09-29 15:59:00 +02:00
self . toolbutton_export_to_script . setDefaultAction ( self . export_to_script_algorithm_action )
2019-02-01 12:22:59 +10:00
self . mToolbar . insertWidget ( self . mActionExportImage , self . toolbutton_export_to_script )
self . export_to_script_algorithm_action . triggered . connect ( self . export_as_script_algorithm )
2017-08-07 01:33:43 +10:00
self . mActionOpen . setIcon (
QgsApplication . getThemeIcon ( ' /mActionFileOpen.svg ' ) )
self . mActionSave . setIcon (
QgsApplication . getThemeIcon ( ' /mActionFileSave.svg ' ) )
self . mActionSaveAs . setIcon (
QgsApplication . getThemeIcon ( ' /mActionFileSaveAs.svg ' ) )
2018-07-18 07:18:08 +10:00
self . mActionSaveInProject . setIcon (
2018-08-13 15:03:06 +10:00
QgsApplication . getThemeIcon ( ' /mAddToProject.svg ' ) )
2017-08-07 01:33:43 +10:00
self . mActionZoomActual . setIcon (
QgsApplication . getThemeIcon ( ' /mActionZoomActual.svg ' ) )
self . mActionZoomIn . setIcon (
QgsApplication . getThemeIcon ( ' /mActionZoomIn.svg ' ) )
self . mActionZoomOut . setIcon (
QgsApplication . getThemeIcon ( ' /mActionZoomOut.svg ' ) )
self . mActionExportImage . setIcon (
QgsApplication . getThemeIcon ( ' /mActionSaveMapAsImage.svg ' ) )
self . mActionZoomToItems . setIcon (
QgsApplication . getThemeIcon ( ' /mActionZoomFullExtent.svg ' ) )
self . mActionExportPdf . setIcon (
QgsApplication . getThemeIcon ( ' /mActionSaveAsPDF.svg ' ) )
self . mActionExportSvg . setIcon (
QgsApplication . getThemeIcon ( ' /mActionSaveAsSVG.svg ' ) )
2019-02-01 12:22:59 +10:00
self . toolbutton_export_to_script . setIcon (
QgsApplication . getThemeIcon ( ' /mActionSaveAsPython.svg ' ) )
2017-08-07 01:33:43 +10:00
self . mActionEditHelp . setIcon (
QgsApplication . getThemeIcon ( ' /mActionEditHelpContent.svg ' ) )
self . mActionRun . setIcon (
QgsApplication . getThemeIcon ( ' /mActionStart.svg ' ) )
2016-11-24 11:37:53 +07:00
self . addDockWidget ( Qt . LeftDockWidgetArea , self . propertiesDock )
self . addDockWidget ( Qt . LeftDockWidgetArea , self . inputsDock )
self . addDockWidget ( Qt . LeftDockWidgetArea , self . algorithmsDock )
self . tabifyDockWidget ( self . inputsDock , self . algorithmsDock )
self . inputsDock . raise_ ( )
2016-05-26 14:43:18 +03:00
self . setWindowFlags ( Qt . WindowMinimizeButtonHint |
Qt . WindowMaximizeButtonHint |
2014-01-01 12:22:40 +01:00
Qt . WindowCloseButtonHint )
2015-09-12 12:22:20 +02:00
2017-03-03 20:39:17 +01:00
settings = QgsSettings ( )
2016-11-23 12:05:58 +07:00
self . restoreState ( settings . value ( " /Processing/stateModeler " , QByteArray ( ) ) )
2015-09-10 18:06:56 +02:00
self . restoreGeometry ( settings . value ( " /Processing/geometryModeler " , QByteArray ( ) ) )
2014-01-01 12:22:40 +01:00
2017-06-15 07:24:15 +10:00
self . scene = ModelerScene ( self , dialog = self )
2014-06-08 00:21:12 +02:00
self . scene . setSceneRect ( QRectF ( 0 , 0 , self . CANVAS_SIZE , self . CANVAS_SIZE ) )
2014-07-02 07:46:03 +02:00
2012-12-01 20:21:42 +02:00
self . view . setScene ( self . scene )
2014-03-26 13:37:40 +01:00
self . view . setAcceptDrops ( True )
2012-12-01 20:21:42 +02:00
self . view . ensureVisible ( 0 , 0 , 10 , 10 )
2019-03-04 21:19:24 +10:00
self . view . scale ( QgsApplication . desktop ( ) . logicalDpiX ( ) / 96 , QgsApplication . desktop ( ) . logicalDpiX ( ) / 96 )
2014-03-26 16:47:06 +02:00
2014-03-26 13:37:40 +01:00
def _dragEnterEvent ( event ) :
2018-07-10 10:07:33 +10:00
if event . mimeData ( ) . hasText ( ) or event . mimeData ( ) . hasFormat ( ' application/x-vnd.qgis.qgis.algorithmid ' ) :
2014-03-26 13:37:40 +01:00
event . acceptProposedAction ( )
else :
2014-03-26 16:47:06 +02:00
event . ignore ( )
2014-03-26 13:37:40 +01:00
def _dropEvent ( event ) :
2019-03-04 20:12:29 +10:00
def alg_dropped ( algorithm_id , pos ) :
2018-07-10 10:07:33 +10:00
alg = QgsApplication . processingRegistry ( ) . createAlgorithmById ( algorithm_id )
if alg is not None :
2019-03-04 20:12:29 +10:00
self . _addAlgorithm ( alg , pos )
2018-07-10 10:07:33 +10:00
else :
assert False , algorithm_id
2019-03-04 20:12:29 +10:00
def input_dropped ( id , pos ) :
if id in [ param . id ( ) for param in QgsApplication . instance ( ) . processingRegistry ( ) . parameterTypes ( ) ] :
self . addInputOfType ( itemId , pos )
if event . mimeData ( ) . hasFormat ( ' application/x-vnd.qgis.qgis.algorithmid ' ) :
data = event . mimeData ( ) . data ( ' application/x-vnd.qgis.qgis.algorithmid ' )
stream = QDataStream ( data , QIODevice . ReadOnly )
algorithm_id = stream . readQString ( )
2019-03-04 20:27:20 +10:00
QTimer . singleShot ( 0 , lambda id = algorithm_id , pos = self . view . mapToScene ( event . pos ( ) ) : alg_dropped ( id , pos ) )
2019-03-04 20:12:29 +10:00
event . accept ( )
2018-07-10 10:07:33 +10:00
elif event . mimeData ( ) . hasText ( ) :
2018-02-28 16:48:50 -05:00
itemId = event . mimeData ( ) . text ( )
2019-03-04 20:27:20 +10:00
QTimer . singleShot ( 0 , lambda id = itemId , pos = self . view . mapToScene ( event . pos ( ) ) : input_dropped ( id , pos ) )
2014-03-26 13:37:40 +01:00
event . accept ( )
else :
event . ignore ( )
2014-03-26 16:47:06 +02:00
2014-03-26 13:37:40 +01:00
def _dragMoveEvent ( event ) :
2018-07-10 10:07:33 +10:00
if event . mimeData ( ) . hasText ( ) or event . mimeData ( ) . hasFormat ( ' application/x-vnd.qgis.qgis.algorithmid ' ) :
2014-03-26 13:37:40 +01:00
event . accept ( )
else :
2014-03-26 16:47:06 +02:00
event . ignore ( )
2014-06-08 00:21:12 +02:00
def _wheelEvent ( event ) :
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
self . view . setTransformationAnchor ( QGraphicsView . AnchorUnderMouse )
2016-11-11 15:00:52 +07:00
2017-03-03 20:39:17 +01:00
settings = QgsSettings ( )
2016-11-11 15:00:52 +07:00
factor = settings . value ( ' /qgis/zoom_favor ' , 2.0 )
2017-06-13 16:05:59 +10:00
# "Normal" mouse has an angle delta of 120, precision mouses provide data
# faster, in smaller steps
factor = 1.0 + ( factor - 1.0 ) / 120.0 * abs ( event . angleDelta ( ) . y ( ) )
2016-11-11 15:00:52 +07:00
if ( event . modifiers ( ) == Qt . ControlModifier ) :
factor = 1.0 + ( factor - 1.0 ) / 20.0
if event . angleDelta ( ) . y ( ) < 0 :
2015-08-22 14:29:41 +02:00
factor = 1 / factor
2016-11-11 15:00:52 +07:00
2014-06-08 00:21:12 +02:00
self . view . scale ( factor , factor )
2014-07-02 07:46:03 +02:00
def _enterEvent ( e ) :
QGraphicsView . enterEvent ( self . view , e )
2014-06-08 00:21:12 +02:00
self . view . viewport ( ) . setCursor ( Qt . ArrowCursor )
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
2014-07-02 07:46:03 +02:00
def _mouseReleaseEvent ( e ) :
QGraphicsView . mouseReleaseEvent ( self . view , e )
2014-06-08 00:21:12 +02:00
self . view . viewport ( ) . setCursor ( Qt . ArrowCursor )
2014-07-02 07:46:03 +02:00
2016-11-11 15:00:52 +07:00
def _mousePressEvent ( e ) :
if e . button ( ) == Qt . MidButton :
self . previousMousePos = e . pos ( )
else :
QGraphicsView . mousePressEvent ( self . view , e )
def _mouseMoveEvent ( e ) :
if e . buttons ( ) == Qt . MidButton :
offset = self . previousMousePos - e . pos ( )
self . previousMousePos = e . pos ( )
self . view . verticalScrollBar ( ) . setValue ( self . view . verticalScrollBar ( ) . value ( ) + offset . y ( ) )
self . view . horizontalScrollBar ( ) . setValue ( self . view . horizontalScrollBar ( ) . value ( ) + offset . x ( ) )
else :
QGraphicsView . mouseMoveEvent ( self . view , e )
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
self . view . setDragMode ( QGraphicsView . ScrollHandDrag )
2014-03-26 13:37:40 +01:00
self . view . dragEnterEvent = _dragEnterEvent
self . view . dropEvent = _dropEvent
self . view . dragMoveEvent = _dragMoveEvent
2014-06-08 00:21:12 +02:00
self . view . wheelEvent = _wheelEvent
self . view . enterEvent = _enterEvent
self . view . mousePressEvent = _mousePressEvent
2016-11-11 15:00:52 +07:00
self . view . mouseMoveEvent = _mouseMoveEvent
2014-03-26 13:37:40 +01:00
2014-03-26 16:47:06 +02:00
def _mimeDataInput ( items ) :
mimeData = QMimeData ( )
2018-03-01 09:47:35 -05:00
text = items [ 0 ] . data ( 0 , Qt . UserRole )
2014-03-26 16:47:06 +02:00
mimeData . setText ( text )
2014-03-26 13:37:40 +01:00
return mimeData
2014-03-26 16:47:06 +02:00
2014-03-26 13:37:40 +01:00
self . inputsTree . mimeData = _mimeDataInput
2014-03-26 16:47:06 +02:00
self . inputsTree . setDragDropMode ( QTreeWidget . DragOnly )
2014-03-26 13:37:40 +01:00
self . inputsTree . setDropIndicatorShown ( True )
2014-03-26 16:47:06 +02:00
2018-07-10 10:07:33 +10:00
self . algorithms_model = ModelerToolboxModel ( self , QgsApplication . processingRegistry ( ) )
self . algorithmTree . setToolboxProxyModel ( self . algorithms_model )
2014-03-26 16:47:06 +02:00
self . algorithmTree . setDragDropMode ( QTreeWidget . DragOnly )
2014-03-26 13:37:40 +01:00
self . algorithmTree . setDropIndicatorShown ( True )
2012-12-01 20:21:42 +02:00
2019-03-05 17:04:00 +10:00
filters = QgsProcessingToolboxProxyModel . Filters ( QgsProcessingToolboxProxyModel . FilterModeler )
if ProcessingConfig . getSetting ( ProcessingConfig . SHOW_ALGORITHMS_KNOWN_ISSUES ) :
filters | = QgsProcessingToolboxProxyModel . FilterShowKnownIssues
self . algorithmTree . setFilters ( filters )
2018-07-10 10:07:33 +10:00
2012-12-01 20:21:42 +02:00
if hasattr ( self . searchBox , ' setPlaceholderText ' ) :
2018-02-15 22:30:52 +01:00
self . searchBox . setPlaceholderText ( QCoreApplication . translate ( ' ModelerDialog ' , ' Search… ' ) )
2012-12-01 20:21:42 +02:00
if hasattr ( self . textName , ' setPlaceholderText ' ) :
2016-11-23 17:28:34 +07:00
self . textName . setPlaceholderText ( self . tr ( ' Enter model name here ' ) )
2012-12-01 20:21:42 +02:00
if hasattr ( self . textGroup , ' setPlaceholderText ' ) :
2016-11-23 17:28:34 +07:00
self . textGroup . setPlaceholderText ( self . tr ( ' Enter group name here ' ) )
2012-12-01 20:21:42 +02:00
2013-10-01 20:52:22 +03:00
# Connect signals and slots
2012-12-01 20:21:42 +02:00
self . inputsTree . doubleClicked . connect ( self . addInput )
2018-07-10 10:07:33 +10:00
self . searchBox . textChanged . connect ( self . algorithmTree . setFilterString )
2012-12-01 20:21:42 +02:00
self . algorithmTree . doubleClicked . connect ( self . addAlgorithm )
2017-01-03 12:15:03 +07:00
# Ctrl+= should also trigger a zoom in action
ctrlEquals = QShortcut ( QKeySequence ( " Ctrl+= " ) , self )
ctrlEquals . activated . connect ( self . zoomIn )
2016-11-23 12:05:58 +07:00
self . mActionOpen . triggered . connect ( self . openModel )
self . mActionSave . triggered . connect ( self . save )
self . mActionSaveAs . triggered . connect ( self . saveAs )
2018-07-18 07:18:08 +10:00
self . mActionSaveInProject . triggered . connect ( self . saveInProject )
2017-01-03 12:15:03 +07:00
self . mActionZoomIn . triggered . connect ( self . zoomIn )
self . mActionZoomOut . triggered . connect ( self . zoomOut )
self . mActionZoomActual . triggered . connect ( self . zoomActual )
self . mActionZoomToItems . triggered . connect ( self . zoomToItems )
2016-11-23 12:05:58 +07:00
self . mActionExportImage . triggered . connect ( self . exportAsImage )
2017-01-02 10:27:41 +07:00
self . mActionExportPdf . triggered . connect ( self . exportAsPdf )
self . mActionExportSvg . triggered . connect ( self . exportAsSvg )
2018-02-02 09:29:40 +02:00
#self.mActionExportPython.triggered.connect(self.exportAsPython)
2016-11-23 12:05:58 +07:00
self . mActionEditHelp . triggered . connect ( self . editHelp )
self . mActionRun . triggered . connect ( self . runModel )
2012-12-01 20:21:42 +02:00
2017-06-13 16:05:59 +10:00
if model is not None :
2017-06-28 23:05:12 +10:00
self . model = model . create ( )
2017-06-27 13:38:57 +10:00
self . model . setSourceFilePath ( model . sourceFilePath ( ) )
2017-06-27 10:34:55 +10:00
self . textGroup . setText ( self . model . group ( ) )
self . textName . setText ( self . model . displayName ( ) )
2012-12-01 20:33:28 +02:00
self . repaintModel ( )
2014-05-21 21:25:18 +02:00
2012-09-15 18:25:25 +03:00
else :
2017-07-03 21:25:04 +10:00
self . model = QgsProcessingModelAlgorithm ( )
2017-06-24 13:33:39 +10:00
self . model . setProvider ( QgsApplication . processingRegistry ( ) . providerById ( ' model ' ) )
2019-02-19 16:27:50 +10:00
self . update_variables_gui ( )
2012-12-01 20:21:42 +02:00
2016-01-19 09:39:31 +01:00
self . fillInputsTree ( )
2012-12-01 20:33:28 +02:00
self . view . centerOn ( 0 , 0 )
2012-09-15 18:25:25 +03:00
self . help = None
2014-05-21 21:25:18 +02:00
self . hasChanged = False
2012-09-15 18:25:25 +03:00
2013-09-22 14:37:30 +03:00
def closeEvent ( self , evt ) :
2017-03-03 20:39:17 +01:00
settings = QgsSettings ( )
2016-11-23 12:05:58 +07:00
settings . setValue ( " /Processing/stateModeler " , self . saveState ( ) )
2015-09-10 18:06:56 +02:00
settings . setValue ( " /Processing/geometryModeler " , self . saveGeometry ( ) )
2015-09-12 12:22:20 +02:00
2013-09-21 12:45:25 +02:00
if self . hasChanged :
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
ret = QMessageBox . question (
2017-11-18 18:52:45 +01:00
self , self . tr ( ' Save Model? ' ) ,
self . tr ( ' There are unsaved changes in this model. Do you want to keep those? ' ) ,
2016-11-11 15:00:52 +07:00
QMessageBox . Save | QMessageBox . Cancel | QMessageBox . Discard , QMessageBox . Cancel )
2013-09-21 12:45:25 +02:00
2016-11-11 15:00:52 +07:00
if ret == QMessageBox . Save :
self . saveModel ( False )
evt . accept ( )
elif ret == QMessageBox . Discard :
2013-09-21 12:45:25 +02:00
evt . accept ( )
else :
2013-09-22 14:37:30 +03:00
evt . ignore ( )
2013-09-21 12:45:25 +02:00
else :
2013-09-22 14:37:30 +03:00
evt . accept ( )
2012-09-15 18:25:25 +03:00
def editHelp ( self ) :
2017-06-13 16:05:59 +10:00
alg = self . model
2015-01-22 12:51:06 +01:00
dlg = HelpEditionDialog ( alg )
2012-09-15 18:25:25 +03:00
dlg . exec_ ( )
2014-11-23 14:03:32 +01:00
if dlg . descriptions :
2017-06-23 15:48:00 +10:00
self . model . setHelpContent ( dlg . descriptions )
2014-11-23 14:03:32 +01:00
self . hasChanged = True
2013-10-01 20:52:22 +03:00
2019-02-19 16:27:50 +10:00
def update_variables_gui ( self ) :
variables_scope = QgsExpressionContextScope ( self . tr ( ' Model Variables ' ) )
for k , v in self . model . variables ( ) . items ( ) :
variables_scope . setVariable ( k , v )
variables_context = QgsExpressionContext ( )
variables_context . appendScope ( variables_scope )
self . variables_editor . setContext ( variables_context )
self . variables_editor . setEditableScopeIndex ( 0 )
def variables_changed ( self ) :
self . model . setVariables ( self . variables_editor . variablesInActiveScope ( ) )
2012-09-15 18:25:25 +03:00
def runModel ( self ) :
2017-06-13 16:05:59 +10:00
if len ( self . model . childAlgorithms ( ) ) == 0 :
2018-03-09 17:18:37 +01:00
self . bar . pushMessage ( " " , self . tr ( " Model doesn ' t contain any algorithm and/or parameter and can ' t be executed " ) , level = Qgis . Warning , duration = 5 )
2013-04-10 17:37:50 +04:00
return
2018-11-30 08:25:21 +10:00
dlg = AlgorithmDialog ( self . model . create ( ) , parent = iface . mainWindow ( ) )
2014-06-08 00:21:12 +02:00
dlg . exec_ ( )
2013-03-31 21:18:27 +02:00
2013-03-30 18:58:53 +01:00
def save ( self ) :
self . saveModel ( False )
2013-03-31 21:18:27 +02:00
2013-03-30 18:58:53 +01:00
def saveAs ( self ) :
2013-03-31 21:18:27 +02:00
self . saveModel ( True )
2013-03-30 18:58:53 +01:00
2018-07-18 07:18:08 +10:00
def saveInProject ( self ) :
if not self . can_save ( ) :
return
self . model . setName ( str ( self . textName . text ( ) ) )
self . model . setGroup ( str ( self . textGroup . text ( ) ) )
self . model . setSourceFilePath ( None )
project_provider = QgsApplication . processingRegistry ( ) . providerById ( PROJECT_PROVIDER_ID )
project_provider . add_model ( self . model )
self . update_model . emit ( )
self . bar . pushMessage ( " " , self . tr ( " Model was saved inside current project " ) , level = Qgis . Success , duration = 5 )
self . hasChanged = False
QgsProject . instance ( ) . setDirty ( True )
2017-01-03 12:15:03 +07:00
def zoomIn ( self ) :
self . view . setTransformationAnchor ( QGraphicsView . NoAnchor )
point = self . view . mapToScene ( QPoint ( self . view . viewport ( ) . width ( ) / 2 , self . view . viewport ( ) . height ( ) / 2 ) )
2017-03-03 20:39:17 +01:00
settings = QgsSettings ( )
2017-01-03 12:15:03 +07:00
factor = settings . value ( ' /qgis/zoom_favor ' , 2.0 )
self . view . scale ( factor , factor )
self . view . centerOn ( point )
self . repaintModel ( )
def zoomOut ( self ) :
self . view . setTransformationAnchor ( QGraphicsView . NoAnchor )
point = self . view . mapToScene ( QPoint ( self . view . viewport ( ) . width ( ) / 2 , self . view . viewport ( ) . height ( ) / 2 ) )
2017-03-03 20:39:17 +01:00
settings = QgsSettings ( )
2017-01-03 12:15:03 +07:00
factor = settings . value ( ' /qgis/zoom_favor ' , 2.0 )
factor = 1 / factor
self . view . scale ( factor , factor )
self . view . centerOn ( point )
self . repaintModel ( )
def zoomActual ( self ) :
point = self . view . mapToScene ( QPoint ( self . view . viewport ( ) . width ( ) / 2 , self . view . viewport ( ) . height ( ) / 2 ) )
self . view . resetTransform ( )
2019-03-04 21:19:24 +10:00
self . view . scale ( QgsApplication . desktop ( ) . logicalDpiX ( ) / 96 , QgsApplication . desktop ( ) . logicalDpiX ( ) / 96 )
2017-01-03 12:15:03 +07:00
self . view . centerOn ( point )
def zoomToItems ( self ) :
totalRect = self . scene . itemsBoundingRect ( )
totalRect . adjust ( - 10 , - 10 , 10 , 10 )
self . view . fitInView ( totalRect , Qt . KeepAspectRatio )
2013-09-13 16:25:41 +03:00
def exportAsImage ( self ) :
2017-01-02 10:27:41 +07:00
self . repaintModel ( controls = False )
2016-11-24 12:34:59 +02:00
filename , fileFilter = QFileDialog . getSaveFileName ( self ,
self . tr ( ' Save Model As Image ' ) , ' ' ,
self . tr ( ' PNG files (*.png *.PNG) ' ) )
2013-09-13 16:25:41 +03:00
if not filename :
return
2013-10-01 20:52:22 +03:00
if not filename . lower ( ) . endswith ( ' .png ' ) :
filename + = ' .png '
2013-09-13 16:25:41 +03:00
2017-01-02 10:27:41 +07:00
totalRect = self . scene . itemsBoundingRect ( )
2013-09-13 16:25:41 +03:00
totalRect . adjust ( - 10 , - 10 , 10 , 10 )
2017-01-02 10:27:41 +07:00
imgRect = QRectF ( 0 , 0 , totalRect . width ( ) , totalRect . height ( ) )
2013-09-13 16:25:41 +03:00
2013-10-01 20:52:22 +03:00
img = QImage ( totalRect . width ( ) , totalRect . height ( ) ,
QImage . Format_ARGB32_Premultiplied )
2013-09-13 16:25:41 +03:00
img . fill ( Qt . white )
painter = QPainter ( )
painter . setRenderHint ( QPainter . Antialiasing )
painter . begin ( img )
2017-01-02 10:27:41 +07:00
self . scene . render ( painter , imgRect , totalRect )
2013-09-13 16:25:41 +03:00
painter . end ( )
img . save ( filename )
2018-09-11 09:42:56 +07:00
self . bar . pushMessage ( " " , self . tr ( " Successfully exported model as image to <a href= \" {} \" > {} </a> " ) . format ( QUrl . fromLocalFile ( filename ) . toString ( ) , QDir . toNativeSeparators ( filename ) ) , level = Qgis . Success , duration = 5 )
2017-01-02 10:27:41 +07:00
self . repaintModel ( controls = True )
def exportAsPdf ( self ) :
self . repaintModel ( controls = False )
filename , fileFilter = QFileDialog . getSaveFileName ( self ,
self . tr ( ' Save Model As PDF ' ) , ' ' ,
2017-01-25 02:19:33 +01:00
self . tr ( ' PDF files (*.pdf *.PDF) ' ) )
2017-01-02 10:27:41 +07:00
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 ( )
2018-09-11 09:42:56 +07:00
self . bar . pushMessage ( " " , self . tr ( " Successfully exported model as PDF to <a href= \" {} \" > {} </a> " ) . format ( QUrl . fromLocalFile ( filename ) . toString ( ) , QDir . toNativeSeparators ( filename ) ) , level = Qgis . Success , duration = 5 )
2017-01-02 10:27:41 +07:00
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 )
2017-06-13 16:05:59 +10:00
svg . setTitle ( self . model . displayName ( ) )
2017-01-02 10:27:41 +07:00
painter = QPainter ( svg )
self . scene . render ( painter , svgRect , totalRect )
painter . end ( )
2018-09-11 09:42:56 +07:00
self . bar . pushMessage ( " " , self . tr ( " Successfully exported model as SVG to <a href= \" {} \" > {} </a> " ) . format ( QUrl . fromLocalFile ( filename ) . toString ( ) , QDir . toNativeSeparators ( filename ) ) , level = Qgis . Success , duration = 5 )
2017-01-02 10:27:41 +07:00
self . repaintModel ( controls = True )
2016-11-23 17:28:34 +07:00
2015-11-06 14:02:11 +01:00
def exportAsPython ( self ) :
2016-09-05 11:39:43 +10:00
filename , filter = QFileDialog . getSaveFileName ( self ,
2016-01-08 12:52:19 +01:00
self . tr ( ' Save Model As Python Script ' ) , ' ' ,
2018-08-03 08:34:05 +03:00
self . tr ( ' Processing scripts (*.py *.PY) ' ) )
2015-11-06 14:02:11 +01:00
if not filename :
return
if not filename . lower ( ) . endswith ( ' .py ' ) :
filename + = ' .py '
2017-06-26 14:27:31 +10:00
text = self . model . asPythonCode ( )
2015-11-06 14:02:11 +01:00
with codecs . open ( filename , ' w ' , encoding = ' utf-8 ' ) as fout :
fout . write ( text )
2016-11-23 17:28:34 +07:00
2018-09-11 09:42:56 +07:00
self . bar . pushMessage ( " " , self . tr ( " Successfully exported model as python script to <a href= \" {} \" > {} </a> " ) . format ( QUrl . fromLocalFile ( filename ) . toString ( ) , QDir . toNativeSeparators ( filename ) ) , level = Qgis . Success , duration = 5 )
2015-11-06 14:02:11 +01:00
2018-07-18 07:18:08 +10:00
def can_save ( self ) :
"""
Tests whether a model can be saved , or if it is not yet valid
: return : bool
"""
2018-07-18 07:35:41 +10:00
if str ( self . textName . text ( ) ) . strip ( ) == ' ' :
self . bar . pushWarning (
" " , self . tr ( ' Please a enter model name before saving ' )
fix python pep8 warnings and fix some revealed errors
pep8 --ignore=E111,E128,E201,E202,E203,E211,E221,E222,E225,E226,E227,E231,E241,E261,E265,E272,E302,E303,E501,E701 \
--exclude="ui_*.py,debian/*,python/ext-libs/*" \
.
2015-02-01 14:15:42 +01:00
)
2018-07-18 07:18:08 +10:00
return False
return True
def saveModel ( self , saveAs ) :
if not self . can_save ( ) :
2012-09-15 18:25:25 +03:00
return
2017-06-20 17:26:57 +10:00
self . model . setName ( str ( self . textName . text ( ) ) )
self . model . setGroup ( str ( self . textGroup . text ( ) ) )
2017-06-27 10:34:55 +10:00
if self . model . sourceFilePath ( ) and not saveAs :
2017-06-23 15:53:26 +10:00
filename = self . model . sourceFilePath ( )
2012-09-15 18:25:25 +03:00
else :
2016-09-05 11:39:43 +10:00
filename , filter = QFileDialog . getSaveFileName ( self ,
2016-01-08 12:52:19 +01:00
self . tr ( ' Save Model ' ) ,
2016-07-25 16:22:21 +03:00
ModelerUtils . modelsFolders ( ) [ 0 ] ,
2018-08-03 08:34:05 +03:00
self . tr ( ' Processing models (*.model3 *.MODEL3) ' ) )
2012-09-15 18:25:25 +03:00
if filename :
2017-06-20 17:26:57 +10:00
if not filename . endswith ( ' .model3 ' ) :
filename + = ' .model3 '
2017-06-23 15:53:26 +10:00
self . model . setSourceFilePath ( filename )
2012-09-15 18:25:25 +03:00
if filename :
2017-06-20 17:26:57 +10:00
if not self . model . toFile ( filename ) :
2013-04-30 19:41:35 +02:00
if saveAs :
2013-10-01 20:52:22 +03:00
QMessageBox . warning ( self , self . tr ( ' I/O error ' ) ,
2017-03-04 16:23:36 +01:00
self . tr ( ' Unable to save edits. Reason: \n {0} ' ) . format ( str ( sys . exc_info ( ) [ 1 ] ) ) )
2013-04-30 19:41:35 +02:00
else :
2018-02-15 22:30:52 +01:00
QMessageBox . warning ( self , self . tr ( " Can ' t save model " ) , QCoreApplication . translate ( ' QgsPluginInstallerInstallingDialog ' , (
" This model can ' t be saved in its original location (probably you do not "
" have permission to do it). Please, use the ' Save as… ' option. " ) )
)
2013-04-30 19:41:35 +02:00
return
2016-10-31 14:37:12 +10:00
self . update_model . emit ( )
2018-09-11 09:42:56 +07:00
if saveAs :
self . bar . pushMessage ( " " , self . tr ( " Model was correctly saved to <a href= \" {} \" > {} </a> " ) . format ( QUrl . fromLocalFile ( filename ) . toString ( ) , QDir . toNativeSeparators ( filename ) ) , level = Qgis . Success , duration = 5 )
else :
self . bar . pushMessage ( " " , self . tr ( " Model was correctly saved " ) , level = Qgis . Success , duration = 5 )
2016-09-19 11:52:31 +03:00
2013-09-21 12:45:25 +02:00
self . hasChanged = False
2012-09-15 18:25:25 +03:00
def openModel ( self ) :
2017-02-09 11:05:09 +01:00
filename , selected_filter = QFileDialog . getOpenFileName ( self ,
self . tr ( ' Open Model ' ) ,
ModelerUtils . modelsFolders ( ) [ 0 ] ,
2017-06-24 13:34:14 +10:00
self . tr ( ' Processing models (*.model3 *.MODEL3) ' ) )
2012-09-15 18:25:25 +03:00
if filename :
2017-09-20 14:05:31 +10:00
self . loadModel ( filename )
def loadModel ( self , filename ) :
alg = QgsProcessingModelAlgorithm ( )
if alg . fromFile ( filename ) :
self . model = alg
self . model . setProvider ( QgsApplication . processingRegistry ( ) . providerById ( ' model ' ) )
self . textGroup . setText ( alg . group ( ) )
self . textName . setText ( alg . name ( ) )
self . repaintModel ( )
2019-02-19 16:27:50 +10:00
self . update_variables_gui ( )
2017-09-20 14:05:31 +10:00
self . view . centerOn ( 0 , 0 )
self . hasChanged = False
else :
QgsMessageLog . logMessage ( self . tr ( ' Could not load model {0} ' ) . format ( filename ) ,
self . tr ( ' Processing ' ) ,
2018-02-05 22:11:34 -04:00
Qgis . Critical )
2018-02-21 18:29:42 +10:00
QMessageBox . critical ( self , self . tr ( ' Open Model ' ) ,
2017-09-20 14:05:31 +10:00
self . tr ( ' The selected model could not be loaded. \n '
' See the log for more information. ' ) )
2012-09-15 18:25:25 +03:00
2017-01-02 10:27:41 +07:00
def repaintModel ( self , controls = True ) :
2017-06-15 07:24:15 +10:00
self . scene = ModelerScene ( self , dialog = self )
2017-06-23 15:58:50 +10:00
self . scene . setSceneRect ( QRectF ( 0 , 0 , self . CANVAS_SIZE ,
self . CANVAS_SIZE ) )
2017-06-13 16:05:59 +10:00
self . scene . paintModel ( self . model , controls )
2013-03-31 21:18:27 +02:00
self . view . setScene ( self . scene )
2012-09-15 18:25:25 +03:00
def addInput ( self ) :
item = self . inputsTree . currentItem ( )
2018-02-28 16:48:50 -05:00
param = item . data ( 0 , Qt . UserRole )
self . addInputOfType ( param )
2014-03-26 16:47:06 +02:00
2019-06-30 20:08:11 +10:00
def create_widget_context ( self ) :
"""
Returns a new widget context for use in the model editor
"""
widget_context = QgsProcessingParameterWidgetContext ( )
widget_context . setProject ( QgsProject . instance ( ) )
if iface is not None :
widget_context . setMapCanvas ( iface . mapCanvas ( ) )
widget_context . setModel ( self . model )
return widget_context
def autogenerate_parameter_name ( self , parameter ) :
"""
Automatically generates and sets a new parameter ' s name, based on the parameter ' s
description and ensuring that it is unique for the model .
"""
validChars = \
' abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 '
safeName = ' ' . join ( c for c in parameter . description ( ) if c in validChars )
name = safeName . lower ( )
i = 2
while self . model . parameterDefinition ( name ) :
name = safeName . lower ( ) + str ( i )
i + = 1
parameter . setName ( safeName )
2014-06-08 00:21:12 +02:00
def addInputOfType ( self , paramType , pos = None ) :
2019-06-30 20:08:11 +10:00
new_param = None
if ModelerParameterDefinitionDialog . use_legacy_dialog ( paramType = paramType ) :
dlg = ModelerParameterDefinitionDialog ( self . model , paramType )
if dlg . exec_ ( ) :
new_param = dlg . param
else :
# yay, use new API!
context = createContext ( )
widget_context = self . create_widget_context ( )
dlg = QgsProcessingParameterDefinitionDialog ( type = paramType ,
context = context ,
widgetContext = widget_context ,
algorithm = self . model )
if dlg . exec_ ( ) :
new_param = dlg . createParameter ( )
self . autogenerate_parameter_name ( new_param )
if new_param is not None :
2018-02-28 16:48:50 -05:00
if pos is None :
pos = self . getPositionForParameterItem ( )
if isinstance ( pos , QPoint ) :
pos = QPointF ( pos )
2019-06-30 20:08:11 +10:00
component = QgsProcessingModelParameter ( new_param . name ( ) )
component . setDescription ( new_param . name ( ) )
2018-02-28 16:48:50 -05:00
component . setPosition ( pos )
2019-06-30 20:08:11 +10:00
self . model . addModelParameter ( new_param , component )
2018-02-28 16:48:50 -05:00
self . repaintModel ( )
# self.view.ensureVisible(self.scene.getLastParameterItem())
self . hasChanged = True
2014-07-02 07:46:03 +02:00
2014-06-08 00:21:12 +02:00
def getPositionForParameterItem ( self ) :
MARGIN = 20
BOX_WIDTH = 200
BOX_HEIGHT = 80
2017-06-22 12:21:25 +10:00
if len ( self . model . parameterComponents ( ) ) > 0 :
2017-06-13 16:05:59 +10:00
maxX = max ( [ i . position ( ) . x ( ) for i in list ( self . model . parameterComponents ( ) . values ( ) ) ] )
2014-06-08 00:21:12 +02:00
newX = min ( MARGIN + BOX_WIDTH + maxX , self . CANVAS_SIZE - BOX_WIDTH )
else :
newX = MARGIN + BOX_WIDTH / 2
2014-07-02 07:46:03 +02:00
return QPointF ( newX , MARGIN + BOX_HEIGHT / 2 )
2012-09-15 18:25:25 +03:00
def fillInputsTree ( self ) :
2016-12-31 11:17:50 +07:00
icon = QIcon ( os . path . join ( pluginPath , ' images ' , ' input.svg ' ) )
2014-03-26 16:47:06 +02:00
parametersItem = QTreeWidgetItem ( )
2013-10-01 20:52:22 +03:00
parametersItem . setText ( 0 , self . tr ( ' Parameters ' ) )
2018-03-03 12:41:33 -05:00
sortedParams = sorted ( QgsApplication . instance ( ) . processingRegistry ( ) . parameterTypes ( ) , key = lambda pt : pt . name ( ) )
2018-02-28 16:48:50 -05:00
for param in sortedParams :
2018-03-06 07:18:36 -05:00
if param . flags ( ) & QgsProcessingParameterType . ExposeToModeler :
2018-03-01 10:20:08 -05:00
paramItem = QTreeWidgetItem ( )
2018-03-03 11:16:35 -05:00
paramItem . setText ( 0 , param . name ( ) )
paramItem . setData ( 0 , Qt . UserRole , param . id ( ) )
2018-03-01 10:20:08 -05:00
paramItem . setIcon ( 0 , icon )
paramItem . setFlags ( Qt . ItemIsEnabled | Qt . ItemIsSelectable | Qt . ItemIsDragEnabled )
2018-03-03 11:16:35 -05:00
paramItem . setToolTip ( 0 , param . description ( ) )
2018-03-01 10:20:08 -05:00
parametersItem . addChild ( paramItem )
2012-09-15 18:25:25 +03:00
self . inputsTree . addTopLevelItem ( parametersItem )
parametersItem . setExpanded ( True )
def addAlgorithm ( self ) :
2018-07-10 10:07:33 +10:00
algorithm = self . algorithmTree . selectedAlgorithm ( )
if algorithm is not None :
alg = QgsApplication . processingRegistry ( ) . createAlgorithmById ( algorithm . id ( ) )
2017-05-19 10:24:59 +10:00
self . _addAlgorithm ( alg )
2014-03-26 16:47:06 +02:00
2014-06-08 00:21:12 +02:00
def _addAlgorithm ( self , alg , pos = None ) :
2018-04-09 09:15:15 +02:00
dlg = ModelerParametersDialog ( alg , self . model )
2017-09-12 14:21:15 +10:00
if dlg . exec_ ( ) :
alg = dlg . createAlgorithm ( )
2016-01-08 12:52:19 +01:00
if pos is None :
2017-09-12 14:21:15 +10:00
alg . setPosition ( self . getPositionForAlgorithmItem ( ) )
2016-01-08 12:52:19 +01:00
else :
2017-09-12 14:21:15 +10:00
alg . setPosition ( pos )
2016-01-08 12:52:19 +01:00
from processing . modeler . ModelerGraphicItem import ModelerGraphicItem
2017-09-12 14:21:15 +10:00
for i , out in enumerate ( alg . modelOutputs ( ) ) :
alg . modelOutput ( out ) . setPosition ( alg . position ( ) + QPointF ( ModelerGraphicItem . BOX_WIDTH , ( i + 1.5 ) *
2017-09-12 19:23:37 +10:00
ModelerGraphicItem . BOX_HEIGHT ) )
2017-09-12 14:21:15 +10:00
self . model . addChildAlgorithm ( alg )
2016-01-08 12:52:19 +01:00
self . repaintModel ( )
self . hasChanged = True
2014-07-02 07:46:03 +02:00
2014-06-08 00:21:12 +02:00
def getPositionForAlgorithmItem ( self ) :
MARGIN = 20
BOX_WIDTH = 200
BOX_HEIGHT = 80
2017-06-13 16:05:59 +10:00
if self . model . childAlgorithms ( ) :
maxX = max ( [ alg . position ( ) . x ( ) for alg in list ( self . model . childAlgorithms ( ) . values ( ) ) ] )
maxY = max ( [ alg . position ( ) . y ( ) for alg in list ( self . model . childAlgorithms ( ) . values ( ) ) ] )
2014-06-08 00:21:12 +02:00
newX = min ( MARGIN + BOX_WIDTH + maxX , self . CANVAS_SIZE - BOX_WIDTH )
2017-03-03 20:44:03 +01:00
newY = min ( MARGIN + BOX_HEIGHT + maxY , self . CANVAS_SIZE -
BOX_HEIGHT )
2014-06-08 00:21:12 +02:00
else :
newX = MARGIN + BOX_WIDTH / 2
newY = MARGIN * 2 + BOX_HEIGHT + BOX_HEIGHT / 2
return QPointF ( newX , newY )
2019-02-01 12:22:59 +10:00
def export_as_script_algorithm ( self ) :
dlg = ScriptEditorDialog ( None )
dlg . editor . setText ( ' \n ' . join ( self . model . asPythonCode ( QgsProcessing . PythonQgsProcessingAlgorithmSubclass , 4 ) ) )
dlg . show ( )