17 #include "qgssettings.h" 
   21 #include "qgsprocessingmodelalgorithm.h" 
   31 #include "processing/models/qgsprocessingmodelgroupbox.h" 
   39 #include <QDesktopWidget> 
   40 #include <QKeySequence> 
   41 #include <QFileDialog> 
   43 #include <QSvgGenerator> 
   44 #include <QToolButton> 
   45 #include <QCloseEvent> 
   46 #include <QMessageBox> 
   48 #include <QPushButton> 
   50 #include <QTextStream> 
   55 QgsModelerToolboxModel::QgsModelerToolboxModel( QObject *parent )
 
   61 Qt::ItemFlags QgsModelerToolboxModel::flags( 
const QModelIndex &index )
 const 
   63   Qt::ItemFlags f = QgsProcessingToolboxProxyModel::flags( index );
 
   64   const QModelIndex sourceIndex = mapToSource( index );
 
   65   if ( toolboxModel()->isAlgorithm( sourceIndex ) )
 
   67     f = f | Qt::ItemIsDragEnabled;
 
   72 Qt::DropActions QgsModelerToolboxModel::supportedDragActions()
 const 
   74   return Qt::CopyAction;
 
   79 QgsModelDesignerDialog::QgsModelDesignerDialog( QWidget *parent, Qt::WindowFlags flags )
 
   80   : QMainWindow( parent, flags )
 
   81   , mToolsActionGroup( new QActionGroup( this ) )
 
   85   setAttribute( Qt::WA_DeleteOnClose );
 
   86   setDockOptions( dockOptions() | QMainWindow::GroupedDragging );
 
   87   setWindowFlags( Qt::WindowMinimizeButtonHint |
 
   88                   Qt::WindowMaximizeButtonHint |
 
   89                   Qt::WindowCloseButtonHint );
 
   93   mModel = std::make_unique< QgsProcessingModelAlgorithm >();
 
   96   mUndoStack = 
new QUndoStack( 
this );
 
   97   connect( mUndoStack, &QUndoStack::indexChanged, 
this, [ = ]
 
   99     if ( mIgnoreUndoStackChanges )
 
  102     mBlockUndoCommands++;
 
  103     updateVariablesGui();
 
  104     mGroupEdit->setText( mModel->group() );
 
  105     mNameEdit->setText( mModel->displayName() );
 
  106     mBlockUndoCommands--;
 
  110   mPropertiesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
 
  111   mInputsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
 
  112   mAlgorithmsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
 
  113   mVariablesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
 
  115   mAlgorithmsTree->header()->setVisible( 
false );
 
  116   mAlgorithmSearchEdit->setShowSearchIcon( 
true );
 
  117   mAlgorithmSearchEdit->setPlaceholderText( tr( 
"Search…" ) );
 
  118   connect( mAlgorithmSearchEdit, &QgsFilterLineEdit::textChanged, mAlgorithmsTree, &QgsProcessingToolboxTreeView::setFilterString );
 
  120   mInputsTreeWidget->header()->setVisible( 
false );
 
  121   mInputsTreeWidget->setAlternatingRowColors( 
true );
 
  122   mInputsTreeWidget->setDragDropMode( QTreeWidget::DragOnly );
 
  123   mInputsTreeWidget->setDropIndicatorShown( 
true );
 
  125   mNameEdit->setPlaceholderText( tr( 
"Enter model name here" ) );
 
  126   mGroupEdit->setPlaceholderText( tr( 
"Enter group name here" ) );
 
  129   mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
 
  130   mainLayout->insertWidget( 0, mMessageBar );
 
  132   mView->setAcceptDrops( 
true );
 
  133   QgsSettings settings;
 
  135   connect( mActionClose, &QAction::triggered, 
this, &QWidget::close );
 
  136   connect( mActionZoomIn, &QAction::triggered, 
this, &QgsModelDesignerDialog::zoomIn );
 
  137   connect( mActionZoomOut, &QAction::triggered, 
this, &QgsModelDesignerDialog::zoomOut );
 
  138   connect( mActionZoomActual, &QAction::triggered, 
this, &QgsModelDesignerDialog::zoomActual );
 
  139   connect( mActionZoomToItems, &QAction::triggered, 
this, &QgsModelDesignerDialog::zoomFull );
 
  140   connect( mActionExportImage, &QAction::triggered, 
this, &QgsModelDesignerDialog::exportToImage );
 
  141   connect( mActionExportPdf, &QAction::triggered, 
this, &QgsModelDesignerDialog::exportToPdf );
 
  142   connect( mActionExportSvg, &QAction::triggered, 
this, &QgsModelDesignerDialog::exportToSvg );
 
  143   connect( mActionExportPython, &QAction::triggered, 
this, &QgsModelDesignerDialog::exportAsPython );
 
  144   connect( mActionSave, &QAction::triggered, 
this, [ = ] { saveModel( 
false ); } );
 
  145   connect( mActionSaveAs, &QAction::triggered, 
this, [ = ] { saveModel( 
true ); } );
 
  146   connect( mActionDeleteComponents, &QAction::triggered, 
this, &QgsModelDesignerDialog::deleteSelected );
 
  147   connect( mActionSnapSelected, &QAction::triggered, mView, &QgsModelGraphicsView::snapSelected );
 
  148   connect( mActionValidate, &QAction::triggered, 
this, &QgsModelDesignerDialog::validate );
 
  149   connect( mActionReorderInputs, &QAction::triggered, 
this, &QgsModelDesignerDialog::reorderInputs );
 
  150   connect( mReorderInputsButton, &QPushButton::clicked, 
this, &QgsModelDesignerDialog::reorderInputs );
 
  152   mActionSnappingEnabled->setChecked( settings.value( QStringLiteral( 
"/Processing/Modeler/enableSnapToGrid" ), 
false ).toBool() );
 
  153   connect( mActionSnappingEnabled, &QAction::toggled, 
this, [ = ]( 
bool enabled )
 
  155     mView->snapper()->setSnapToGrid( enabled );
 
  156     QgsSettings().setValue( QStringLiteral( 
"/Processing/Modeler/enableSnapToGrid" ), enabled );
 
  158   mView->snapper()->setSnapToGrid( mActionSnappingEnabled->isChecked() );
 
  160   connect( mActionSelectAll, &QAction::triggered, 
this, [ = ]
 
  165   QStringList docksTitle = settings.value( QStringLiteral( 
"ModelDesigner/hiddenDocksTitle" ), QStringList(), QgsSettings::App ).toStringList();
 
  166   QStringList docksActive = settings.value( QStringLiteral( 
"ModelDesigner/hiddenDocksActive" ), QStringList(), QgsSettings::App ).toStringList();
 
  167   if ( !docksTitle.isEmpty() )
 
  169     for ( 
const auto &title : docksTitle )
 
  171       mPanelStatus.insert( title, PanelStatus( 
true, docksActive.contains( title ) ) );
 
  174   mActionHidePanels->setChecked( !docksTitle.isEmpty() );
 
  175   connect( mActionHidePanels, &QAction::toggled, 
this, &QgsModelDesignerDialog::setPanelVisibility );
 
  177   mUndoAction = mUndoStack->createUndoAction( 
this );
 
  179   mUndoAction->setShortcuts( QKeySequence::Undo );
 
  180   mRedoAction = mUndoStack->createRedoAction( 
this );
 
  182   mRedoAction->setShortcuts( QKeySequence::Redo );
 
  184   mMenuEdit->insertAction( mActionDeleteComponents, mRedoAction );
 
  185   mMenuEdit->insertAction( mActionDeleteComponents, mUndoAction );
 
  186   mMenuEdit->insertSeparator( mActionDeleteComponents );
 
  187   mToolbar->insertAction( mActionZoomIn, mUndoAction );
 
  188   mToolbar->insertAction( mActionZoomIn, mRedoAction );
 
  189   mToolbar->insertSeparator( mActionZoomIn );
 
  191   mGroupMenu = 
new QMenu( tr( 
"Zoom To" ), 
this );
 
  192   mMenuView->insertMenu( mActionZoomIn, mGroupMenu );
 
  193   connect( mGroupMenu, &QMenu::aboutToShow, 
this, &QgsModelDesignerDialog::populateZoomToMenu );
 
  197   mActionCut = 
new QAction( tr( 
"Cu&t" ), 
this );
 
  198   mActionCut->setShortcuts( QKeySequence::Cut );
 
  199   mActionCut->setStatusTip( tr( 
"Cut" ) );
 
  201   connect( mActionCut, &QAction::triggered, 
this, [ = ]
 
  203     mView->copySelectedItems( QgsModelGraphicsView::ClipboardCut );
 
  206   mActionCopy = 
new QAction( tr( 
"&Copy" ), 
this );
 
  207   mActionCopy->setShortcuts( QKeySequence::Copy );
 
  208   mActionCopy->setStatusTip( tr( 
"Copy" ) );
 
  210   connect( mActionCopy, &QAction::triggered, 
this, [ = ]
 
  212     mView->copySelectedItems( QgsModelGraphicsView::ClipboardCopy );
 
  215   mActionPaste = 
new QAction( tr( 
"&Paste" ), 
this );
 
  216   mActionPaste->setShortcuts( QKeySequence::Paste );
 
  217   mActionPaste->setStatusTip( tr( 
"Paste" ) );
 
  219   connect( mActionPaste, &QAction::triggered, 
this, [ = ]
 
  221     mView->pasteItems( QgsModelGraphicsView::PasteModeCursor );
 
  223   mMenuEdit->insertAction( mActionDeleteComponents, mActionCut );
 
  224   mMenuEdit->insertAction( mActionDeleteComponents, mActionCopy );
 
  225   mMenuEdit->insertAction( mActionDeleteComponents, mActionPaste );
 
  226   mMenuEdit->insertSeparator( mActionDeleteComponents );
 
  229   if ( settings.value( QStringLiteral( 
"Processing/Configuration/SHOW_ALGORITHMS_KNOWN_ISSUES" ), 
false ).toBool() )
 
  233   mAlgorithmsTree->setFilters( filters );
 
  234   mAlgorithmsTree->setDragDropMode( QTreeWidget::DragOnly );
 
  235   mAlgorithmsTree->setDropIndicatorShown( 
true );
 
  237   mAlgorithmsModel = 
new QgsModelerToolboxModel( 
this );
 
  238   mAlgorithmsTree->setToolboxProxyModel( mAlgorithmsModel );
 
  240   connect( mView, &QgsModelGraphicsView::algorithmDropped, 
this, [ = ]( 
const QString & algorithmId, 
const QPointF & pos )
 
  242     addAlgorithm( algorithmId, pos );
 
  244   connect( mAlgorithmsTree, &QgsProcessingToolboxTreeView::doubleClicked, 
this, [ = ]()
 
  246     if ( mAlgorithmsTree->selectedAlgorithm() )
 
  247       addAlgorithm( mAlgorithmsTree->selectedAlgorithm()->id(), QPointF() );
 
  249   connect( mInputsTreeWidget, &QgsModelDesignerInputsTreeWidget::doubleClicked, 
this, [ = ]( 
const QModelIndex & )
 
  251     const QString parameterType = mInputsTreeWidget->currentItem()->data( 0, Qt::UserRole ).toString();
 
  252     addInput( parameterType, QPointF() );
 
  255   connect( mView, &QgsModelGraphicsView::inputDropped, 
this, &QgsModelDesignerDialog::addInput );
 
  258   QShortcut *ctrlEquals = 
new QShortcut( QKeySequence( QStringLiteral( 
"Ctrl+=" ) ), 
this );
 
  259   connect( ctrlEquals, &QShortcut::activated, 
this, &QgsModelDesignerDialog::zoomIn );
 
  262   mUndoDock->setObjectName( QStringLiteral( 
"UndoDock" ) );
 
  263   mUndoView = 
new QUndoView( mUndoStack, 
this );
 
  264   mUndoDock->setWidget( mUndoView );
 
  265   mUndoDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
 
  266   addDockWidget( Qt::DockWidgetArea::LeftDockWidgetArea, mUndoDock );
 
  268   tabifyDockWidget( mUndoDock, mPropertiesDock );
 
  269   tabifyDockWidget( mVariablesDock, mPropertiesDock );
 
  270   mPropertiesDock->raise();
 
  271   tabifyDockWidget( mInputsDock, mAlgorithmsDock );
 
  272   mInputsDock->raise();
 
  278       beginUndoCommand( tr( 
"Change Model Variables" ) );
 
  279       mModel->setVariables( mVariablesEditor->variablesInActiveScope() );
 
  283   connect( mNameEdit, &QLineEdit::textChanged, 
this, [ = ]( 
const QString & name )
 
  287       beginUndoCommand( tr( 
"Change Model Name" ), NameChanged );
 
  288       mModel->setName( name );
 
  293   connect( mGroupEdit, &QLineEdit::textChanged, 
this, [ = ]( 
const QString & group )
 
  297       beginUndoCommand( tr( 
"Change Model Group" ), GroupChanged );
 
  298       mModel->setGroup( group );
 
  305   QToolButton *toolbuttonExportToScript = 
new QToolButton();
 
  306   toolbuttonExportToScript->setPopupMode( QToolButton::InstantPopup );
 
  307   toolbuttonExportToScript->addAction( mActionExportAsScriptAlgorithm );
 
  308   toolbuttonExportToScript->setDefaultAction( mActionExportAsScriptAlgorithm );
 
  309   mToolbar->insertWidget( mActionExportImage, toolbuttonExportToScript );
 
  310   connect( mActionExportAsScriptAlgorithm, &QAction::triggered, 
this, &QgsModelDesignerDialog::exportAsScriptAlgorithm );
 
  312   mActionShowComments->setChecked( settings.value( QStringLiteral( 
"/Processing/Modeler/ShowComments" ), 
true ).toBool() );
 
  313   connect( mActionShowComments, &QAction::toggled, 
this, &QgsModelDesignerDialog::toggleComments );
 
  316   mPanTool->setAction( mActionPan );
 
  318   mToolsActionGroup->addAction( mActionPan );
 
  319   connect( mActionPan, &QAction::triggered, mPanTool, [ = ] { mView->setTool( mPanTool ); } );
 
  322   mSelectTool->setAction( mActionSelectMoveItem );
 
  324   mToolsActionGroup->addAction( mActionSelectMoveItem );
 
  325   connect( mActionSelectMoveItem, &QAction::triggered, mSelectTool, [ = ] { mView->setTool( mSelectTool ); } );
 
  327   mView->setTool( mSelectTool );
 
  330   connect( mView, &QgsModelGraphicsView::macroCommandStarted, 
this, [ = ]( 
const QString & text )
 
  332     mIgnoreUndoStackChanges++;
 
  333     mUndoStack->beginMacro( text );
 
  334     mIgnoreUndoStackChanges--;
 
  336   connect( mView, &QgsModelGraphicsView::macroCommandEnded, 
this, [ = ]
 
  338     mIgnoreUndoStackChanges++;
 
  339     mUndoStack->endMacro();
 
  340     mIgnoreUndoStackChanges--;
 
  342   connect( mView, &QgsModelGraphicsView::beginCommand, 
this, [ = ]( 
const QString & text )
 
  344     beginUndoCommand( text );
 
  346   connect( mView, &QgsModelGraphicsView::endCommand, 
this, [ = ]
 
  350   connect( mView, &QgsModelGraphicsView::deleteSelectedItems, 
this, [ = ]
 
  355   connect( mActionAddGroupBox, &QAction::triggered, 
this, [ = ]
 
  357     const QPointF viewCenter = mView->mapToScene( mView->viewport()->rect().center() );
 
  358     QgsProcessingModelGroupBox group;
 
  359     group.setPosition( viewCenter );
 
  360     group.setDescription( tr( 
"New Group" ) );
 
  362     beginUndoCommand( tr( 
"Add Group Box" ) );
 
  363     model()->addGroupBox( group );
 
  371   restoreState( settings.value( QStringLiteral( 
"ModelDesigner/state" ), QByteArray(), QgsSettings::App ).toByteArray() );
 
  374 QgsModelDesignerDialog::~QgsModelDesignerDialog()
 
  376   QgsSettings settings;
 
  377   if ( !mPanelStatus.isEmpty() )
 
  379     QStringList docksTitle;
 
  380     QStringList docksActive;
 
  382     for ( 
const auto &panel : mPanelStatus.toStdMap() )
 
  384       if ( panel.second.isVisible )
 
  385         docksTitle << panel.first;
 
  386       if ( panel.second.isActive )
 
  387         docksActive << panel.first;
 
  389     settings.setValue( QStringLiteral( 
"ModelDesigner/hiddenDocksTitle" ), docksTitle, QgsSettings::App );
 
  390     settings.setValue( QStringLiteral( 
"ModelDesigner/hiddenDocksActive" ), docksActive, QgsSettings::App );
 
  394     settings.remove( QStringLiteral( 
"ModelDesigner/hiddenDocksTitle" ), QgsSettings::App );
 
  395     settings.remove( QStringLiteral( 
"ModelDesigner/hiddenDocksActive" ), QgsSettings::App );
 
  399   settings.setValue( QStringLiteral( 
"ModelDesigner/state" ), saveState(), QgsSettings::App );
 
  401   mIgnoreUndoStackChanges++;
 
  405 void QgsModelDesignerDialog::closeEvent( QCloseEvent *event )
 
  407   if ( checkForUnsavedChanges() )
 
  413 void QgsModelDesignerDialog::beginUndoCommand( 
const QString &text, 
int id )
 
  415   if ( mBlockUndoCommands || !mUndoStack )
 
  418   if ( mActiveCommand )
 
  421   mActiveCommand = std::make_unique< QgsModelUndoCommand >( mModel.get(), text, 
id );
 
  424 void QgsModelDesignerDialog::endUndoCommand()
 
  426   if ( mBlockUndoCommands || !mActiveCommand || !mUndoStack )
 
  429   mActiveCommand->saveAfterState();
 
  430   mIgnoreUndoStackChanges++;
 
  431   mUndoStack->push( mActiveCommand.release() );
 
  432   mIgnoreUndoStackChanges--;
 
  436 QgsProcessingModelAlgorithm *QgsModelDesignerDialog::model()
 
  441 void QgsModelDesignerDialog::setModel( QgsProcessingModelAlgorithm *model )
 
  443   mModel.reset( model );
 
  445   mGroupEdit->setText( mModel->group() );
 
  446   mNameEdit->setText( mModel->displayName() );
 
  448   updateVariablesGui();
 
  450   mView->centerOn( 0, 0 );
 
  453   mIgnoreUndoStackChanges++;
 
  455   mIgnoreUndoStackChanges--;
 
  460 void QgsModelDesignerDialog::loadModel( 
const QString &path )
 
  462   std::unique_ptr< QgsProcessingModelAlgorithm > alg = std::make_unique< QgsProcessingModelAlgorithm >();
 
  463   if ( alg->fromFile( path ) )
 
  466     setModel( alg.release() );
 
  470     QgsMessageLog::logMessage( tr( 
"Could not load model %1" ).arg( path ), tr( 
"Processing" ), Qgis::MessageLevel::Critical );
 
  471     QMessageBox::critical( 
this, tr( 
"Open Model" ), tr( 
"The selected model could not be loaded.\n" 
  472                            "See the log for more information." ) );
 
  476 void QgsModelDesignerDialog::setModelScene( QgsModelGraphicsScene *scene )
 
  478   QgsModelGraphicsScene *oldScene = mScene;
 
  481   mScene->setParent( 
this );
 
  482   mScene->setChildAlgorithmResults( mChildResults );
 
  483   mScene->setModel( mModel.get() );
 
  484   mScene->setMessageBar( mMessageBar );
 
  486   const QPointF center = mView->mapToScene( mView->viewport()->rect().center() );
 
  487   mView->setModelScene( mScene );
 
  489   mSelectTool->resetCache();
 
  490   mSelectTool->setScene( mScene );
 
  492   connect( mScene, &QgsModelGraphicsScene::rebuildRequired, 
this, [ = ]
 
  494     if ( mBlockRepaints )
 
  499   connect( mScene, &QgsModelGraphicsScene::componentAboutToChange, 
this, [ = ]( 
const QString & description, 
int id ) { beginUndoCommand( description, 
id ); } );
 
  500   connect( mScene, &QgsModelGraphicsScene::componentChanged, 
this, [ = ] { endUndoCommand(); } );
 
  502   mView->centerOn( center );
 
  505     oldScene->deleteLater();
 
  508 void QgsModelDesignerDialog::updateVariablesGui()
 
  510   mBlockUndoCommands++;
 
  512   std::unique_ptr< QgsExpressionContextScope > variablesScope = std::make_unique< QgsExpressionContextScope >( tr( 
"Model Variables" ) );
 
  513   const QVariantMap modelVars = mModel->variables();
 
  514   for ( 
auto it = modelVars.constBegin(); it != modelVars.constEnd(); ++it )
 
  516     variablesScope->setVariable( it.key(), it.value() );
 
  519   variablesContext.
appendScope( variablesScope.release() );
 
  520   mVariablesEditor->setContext( &variablesContext );
 
  521   mVariablesEditor->setEditableScopeIndex( 0 );
 
  523   mBlockUndoCommands--;
 
  526 void QgsModelDesignerDialog::setDirty( 
bool dirty )
 
  532 bool QgsModelDesignerDialog::validateSave()
 
  534   if ( mNameEdit->text().trimmed().isEmpty() )
 
  536     mMessageBar->pushWarning( QString(), tr( 
"Please a enter model name before saving" ) );
 
  543 bool QgsModelDesignerDialog::checkForUnsavedChanges()
 
  547     QMessageBox::StandardButton ret = QMessageBox::question( 
this, tr( 
"Save Model?" ),
 
  548                                       tr( 
"There are unsaved changes in this model. Do you want to keep those?" ),
 
  549                                       QMessageBox::Save | QMessageBox::Cancel | QMessageBox::Discard, QMessageBox::Cancel );
 
  552       case QMessageBox::Save:
 
  556       case QMessageBox::Discard:
 
  569 void QgsModelDesignerDialog::setLastRunChildAlgorithmResults( 
const QVariantMap &results )
 
  571   mChildResults = results;
 
  573     mScene->setChildAlgorithmResults( mChildResults );
 
  576 void QgsModelDesignerDialog::setLastRunChildAlgorithmInputs( 
const QVariantMap &inputs )
 
  578   mChildInputs = inputs;
 
  580     mScene->setChildAlgorithmInputs( mChildInputs );
 
  583 void QgsModelDesignerDialog::zoomIn()
 
  585   mView->setTransformationAnchor( QGraphicsView::NoAnchor );
 
  586   QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
 
  587   QgsSettings settings;
 
  588   const double factor = settings.value( QStringLiteral( 
"/qgis/zoom_favor" ), 2.0 ).toDouble();
 
  589   mView->scale( factor, factor );
 
  590   mView->centerOn( point );
 
  593 void QgsModelDesignerDialog::zoomOut()
 
  595   mView->setTransformationAnchor( QGraphicsView::NoAnchor );
 
  596   QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
 
  597   QgsSettings settings;
 
  598   const double factor = 1.0 / settings.value( QStringLiteral( 
"/qgis/zoom_favor" ), 2.0 ).toDouble();
 
  599   mView->scale( factor, factor );
 
  600   mView->centerOn( point );
 
  603 void QgsModelDesignerDialog::zoomActual()
 
  605   QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
 
  606   mView->resetTransform();
 
  607   mView->scale( QgsApplication::desktop()->logicalDpiX() / 96, QgsApplication::desktop()->logicalDpiX() / 96 );
 
  608   mView->centerOn( point );
 
  611 void QgsModelDesignerDialog::zoomFull()
 
  613   QRectF totalRect = mView->scene()->itemsBoundingRect();
 
  614   totalRect.adjust( -10, -10, 10, 10 );
 
  615   mView->fitInView( totalRect, Qt::KeepAspectRatio );
 
  618 void QgsModelDesignerDialog::exportToImage()
 
  620   QString filename = QFileDialog::getSaveFileName( 
this, tr( 
"Save Model as Image" ), tr( 
"PNG files (*.png *.PNG)" ) );
 
  621   if ( filename.isEmpty() )
 
  626   repaintModel( 
false );
 
  628   QRectF totalRect = mView->scene()->itemsBoundingRect();
 
  629   totalRect.adjust( -10, -10, 10, 10 );
 
  630   const QRectF imageRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
 
  632   QImage img( totalRect.width(), totalRect.height(),
 
  633               QImage::Format_ARGB32_Premultiplied );
 
  634   img.fill( Qt::white );
 
  636   painter.setRenderHint( QPainter::Antialiasing );
 
  637   painter.begin( &img );
 
  638   mView->scene()->render( &painter, imageRect, totalRect );
 
  641   img.save( filename );
 
  643   mMessageBar->pushMessage( QString(), tr( 
"Successfully exported model as image to <a href=\"{}\">{}</a>" ).arg( QUrl::fromLocalFile( filename ).toString(), QDir::toNativeSeparators( filename ) ), Qgis::MessageLevel::Success, 0 );
 
  644   repaintModel( 
true );
 
  647 void QgsModelDesignerDialog::exportToPdf()
 
  649   QString filename = QFileDialog::getSaveFileName( 
this, tr( 
"Save Model as PDF" ), tr( 
"PDF files (*.pdf *.PDF)" ) );
 
  650   if ( filename.isEmpty() )
 
  655   repaintModel( 
false );
 
  657   QRectF totalRect = mView->scene()->itemsBoundingRect();
 
  658   totalRect.adjust( -10, -10, 10, 10 );
 
  659   const QRectF printerRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
 
  662   printer.setOutputFormat( QPrinter::PdfFormat );
 
  663   printer.setOutputFileName( filename );
 
  664   printer.setPaperSize( QSizeF( printerRect.width(), printerRect.height() ), QPrinter::DevicePixel );
 
  665   printer.setFullPage( 
true );
 
  667   QPainter painter( &printer );
 
  668   mView->scene()->render( &painter, printerRect, totalRect );
 
  671   mMessageBar->pushMessage( QString(), tr( 
"Successfully exported model as PDF to <a href=\"{}\">{}</a>" ).arg( QUrl::fromLocalFile( filename ).toString(), QDir::toNativeSeparators( filename ) ), Qgis::MessageLevel::Success, 0 );
 
  672   repaintModel( 
true );
 
  675 void QgsModelDesignerDialog::exportToSvg()
 
  677   QString filename = QFileDialog::getSaveFileName( 
this, tr( 
"Save Model as SVG" ), tr( 
"SVG files (*.svg *.SVG)" ) );
 
  678   if ( filename.isEmpty() )
 
  683   repaintModel( 
false );
 
  685   QRectF totalRect = mView->scene()->itemsBoundingRect();
 
  686   totalRect.adjust( -10, -10, 10, 10 );
 
  687   const QRectF svgRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
 
  690   svg.setFileName( filename );
 
  691   svg.setSize( QSize( totalRect.width(), totalRect.height() ) );
 
  692   svg.setViewBox( svgRect );
 
  693   svg.setTitle( mModel->displayName() );
 
  695   QPainter painter( &svg );
 
  696   mView->scene()->render( &painter, svgRect, totalRect );
 
  699   mMessageBar->pushMessage( QString(), tr( 
"Successfully exported model as SVG to <a href=\"{}\">{}</a>" ).arg( QUrl::fromLocalFile( filename ).toString(), QDir::toNativeSeparators( filename ) ), Qgis::MessageLevel::Success, 0 );
 
  700   repaintModel( 
true );
 
  703 void QgsModelDesignerDialog::exportAsPython()
 
  705   QString filename = QFileDialog::getSaveFileName( 
this, tr( 
"Save Model as Python Script" ), tr( 
"Processing scripts (*.py *.PY)" ) );
 
  706   if ( filename.isEmpty() )
 
  713   QFile outFile( filename );
 
  714   if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
 
  718   QTextStream fout( &outFile );
 
  722   mMessageBar->pushMessage( QString(), tr( 
"Successfully exported model as Python script to <a href=\"{}\">{}</a>" ).arg( QUrl::fromLocalFile( filename ).toString(), QDir::toNativeSeparators( filename ) ), Qgis::MessageLevel::Success, 0 );
 
  725 void QgsModelDesignerDialog::toggleComments( 
bool show )
 
  727   QgsSettings().setValue( QStringLiteral( 
"/Processing/Modeler/ShowComments" ), show );
 
  729   repaintModel( 
true );
 
  732 void QgsModelDesignerDialog::updateWindowTitle()
 
  734   QString title = tr( 
"Model Designer" );
 
  735   if ( !mModel->name().isEmpty() )
 
  736     title = QStringLiteral( 
"%1 - %2" ).arg( title, mModel->name() );
 
  739     title.prepend( 
'*' );
 
  741   setWindowTitle( title );
 
  744 void QgsModelDesignerDialog::deleteSelected()
 
  746   QList< QgsModelComponentGraphicItem * > items = mScene->selectedComponentItems();
 
  750   if ( items.size() == 1 )
 
  752     items.at( 0 )->deleteComponent();
 
  756   std::sort( items.begin(), items.end(), []( QgsModelComponentGraphicItem * p1, QgsModelComponentGraphicItem * p2 )
 
  759     if ( dynamic_cast< QgsModelCommentGraphicItem *>( p1 ) )
 
  761     else if ( dynamic_cast< QgsModelCommentGraphicItem *>( p2 ) )
 
  763     else if ( dynamic_cast< QgsModelGroupBoxGraphicItem *>( p1 ) )
 
  765     else if ( dynamic_cast< QgsModelGroupBoxGraphicItem *>( p2 ) )
 
  767     else if ( dynamic_cast< QgsModelOutputGraphicItem *>( p1 ) )
 
  769     else if ( dynamic_cast< QgsModelOutputGraphicItem *>( p2 ) )
 
  771     else if ( dynamic_cast< QgsModelChildAlgorithmGraphicItem *>( p1 ) )
 
  773     else if ( dynamic_cast< QgsModelChildAlgorithmGraphicItem *>( p2 ) )
 
  779   beginUndoCommand( tr( 
"Delete Components" ) );
 
  781   QVariant prevState = mModel->toVariant();
 
  782   mBlockUndoCommands++;
 
  783   mBlockRepaints = 
true;
 
  785   while ( !items.empty() )
 
  787     QgsModelComponentGraphicItem *toDelete = 
nullptr;
 
  788     for ( QgsModelComponentGraphicItem *item : items )
 
  790       if ( item->canDeleteComponent() )
 
  803     toDelete->deleteComponent();
 
  804     items.removeAll( toDelete );
 
  809     mModel->loadVariant( prevState );
 
  810     QMessageBox::warning( 
nullptr, QObject::tr( 
"Could not remove components" ),
 
  811                           QObject::tr( 
"Components depend on the selected items.\n" 
  812                                        "Try to remove them before trying deleting these components." ) );
 
  813     mBlockUndoCommands--;
 
  814     mActiveCommand.reset();
 
  818     mBlockUndoCommands--;
 
  822   mBlockRepaints = 
false;
 
  826 void QgsModelDesignerDialog::populateZoomToMenu()
 
  829   for ( 
const QgsProcessingModelGroupBox &box : model()->groupBoxes() )
 
  831     if ( QgsModelComponentGraphicItem *item = mScene->groupBoxItem( box.uuid() ) )
 
  833       QAction *zoomAction = 
new QAction( box.description(), mGroupMenu );
 
  834       connect( zoomAction, &QAction::triggered, 
this, [ = ]
 
  836         mView->centerOn( item );
 
  838       mGroupMenu->addAction( zoomAction );
 
  843 void QgsModelDesignerDialog::setPanelVisibility( 
bool hidden )
 
  845   const QList<QDockWidget *> docks = findChildren<QDockWidget *>();
 
  846   const QList<QTabBar *> tabBars = findChildren<QTabBar *>();
 
  850     mPanelStatus.clear();
 
  852     for ( QDockWidget *dock : docks )
 
  854       mPanelStatus.insert( dock->windowTitle(), PanelStatus( dock->isVisible(), 
false ) );
 
  855       dock->setVisible( 
false );
 
  859     for ( QTabBar *tabBar : tabBars )
 
  861       QString currentTabTitle = tabBar->tabText( tabBar->currentIndex() );
 
  862       mPanelStatus[ currentTabTitle ].isActive = 
true;
 
  868     for ( QDockWidget *dock : docks )
 
  870       if ( mPanelStatus.contains( dock->windowTitle() ) )
 
  872         dock->setVisible( mPanelStatus.value( dock->windowTitle() ).isVisible );
 
  877     for ( QTabBar *tabBar : tabBars )
 
  880       for ( 
int i = 0; i < tabBar->count(); ++i )
 
  882         QString tabTitle = tabBar->tabText( i );
 
  883         if ( mPanelStatus.contains( tabTitle ) && mPanelStatus.value( tabTitle ).isActive )
 
  885           tabBar->setCurrentIndex( i );
 
  889     mPanelStatus.clear();
 
  893 void QgsModelDesignerDialog::validate()
 
  896   if ( model()->validate( issues ) )
 
  898     mMessageBar->pushSuccess( QString(), tr( 
"Model is valid!" ) );
 
  902     QgsMessageBarItem *messageWidget = mMessageBar->createMessage( QString(), tr( 
"Model is invalid!" ) );
 
  903     QPushButton *detailsButton = 
new QPushButton( tr( 
"Details" ) );
 
  904     connect( detailsButton, &QPushButton::clicked, detailsButton, [ = ]
 
  907       dialog->
setTitle( tr( 
"Model is Invalid" ) );
 
  909       QString longMessage = tr( 
"<p>This model is not valid:</p>" ) + QStringLiteral( 
"<ul>" );
 
  910       for ( 
const QString &issue : issues )
 
  912         longMessage += QStringLiteral( 
"<li>%1</li>" ).arg( issue );
 
  914       longMessage += QLatin1String( 
"</ul>" );
 
  919     messageWidget->layout()->addWidget( detailsButton );
 
  920     mMessageBar->clearWidgets();
 
  921     mMessageBar->pushWidget( messageWidget, Qgis::MessageLevel::Warning, 0 );
 
  925 void QgsModelDesignerDialog::reorderInputs()
 
  927   QgsModelInputReorderDialog dlg( 
this );
 
  928   dlg.setModel( mModel.get() );
 
  931     const QStringList inputOrder = dlg.inputOrder();
 
  932     beginUndoCommand( tr( 
"Reorder Inputs" ) );
 
  933     mModel->setParameterOrder( inputOrder );
 
  938 bool QgsModelDesignerDialog::isDirty()
 const 
  940   return mHasChanged && mUndoStack->index() != -1;
 
  943 void QgsModelDesignerDialog::fillInputsTree()
 
  946   std::unique_ptr< QTreeWidgetItem > parametersItem = std::make_unique< QTreeWidgetItem >();
 
  947   parametersItem->setText( 0, tr( 
"Parameters" ) );
 
  951     return QString::localeAwareCompare( a->name(), b->name() ) < 0;
 
  958       std::unique_ptr< QTreeWidgetItem > paramItem = std::make_unique< QTreeWidgetItem >();
 
  959       paramItem->setText( 0, param->name() );
 
  960       paramItem->setData( 0, Qt::UserRole, param->id() );
 
  961       paramItem->setIcon( 0, icon );
 
  962       paramItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
 
  963       paramItem->setToolTip( 0, param->description() );
 
  964       parametersItem->addChild( paramItem.release() );
 
  967   mInputsTreeWidget->addTopLevelItem( parametersItem.release() );
 
  968   mInputsTreeWidget->topLevelItem( 0 )->setExpanded( 
true );
 
  976 QgsModelChildDependenciesWidget::QgsModelChildDependenciesWidget( QWidget *parent,  QgsProcessingModelAlgorithm *model, 
const QString &childId )
 
  979   , mChildId( childId )
 
  981   QHBoxLayout *hl = 
new QHBoxLayout();
 
  982   hl->setContentsMargins( 0, 0, 0, 0 );
 
  984   mLineEdit = 
new QLineEdit();
 
  985   mLineEdit->setEnabled( 
false );
 
  986   hl->addWidget( mLineEdit, 1 );
 
  988   mToolButton = 
new QToolButton();
 
  989   mToolButton->setText( QString( QChar( 0x2026 ) ) );
 
  990   hl->addWidget( mToolButton );
 
  994   mLineEdit->setText( tr( 
"%1 dependencies selected" ).arg( 0 ) );
 
  996   connect( mToolButton, &QToolButton::clicked, 
this, &QgsModelChildDependenciesWidget::showDialog );
 
  999 void QgsModelChildDependenciesWidget::setValue( 
const QList<QgsProcessingModelChildDependency> &value )
 
 1003   updateSummaryText();
 
 1006 void QgsModelChildDependenciesWidget::showDialog()
 
 1008   const QList<QgsProcessingModelChildDependency> available = mModel->availableDependenciesForChildAlgorithm( mChildId );
 
 1010   QVariantList availableOptions;
 
 1011   for ( 
const QgsProcessingModelChildDependency &dep : available )
 
 1012     availableOptions << QVariant::fromValue( dep );
 
 1013   QVariantList selectedOptions;
 
 1014   for ( 
const QgsProcessingModelChildDependency &dep : mValue )
 
 1015     selectedOptions << QVariant::fromValue( dep );
 
 1020     QgsProcessingMultipleSelectionPanelWidget *widget = 
new QgsProcessingMultipleSelectionPanelWidget( availableOptions, selectedOptions );
 
 1021     widget->setPanelTitle( tr( 
"Algorithm Dependencies" ) );
 
 1023     widget->setValueFormatter( [ = ]( 
const QVariant & v ) -> QString
 
 1025       const QgsProcessingModelChildDependency dep = v.value< QgsProcessingModelChildDependency >();
 
 1027       const QString description = mModel->childAlgorithm( dep.childId ).description();
 
 1028       if ( dep.conditionalBranch.isEmpty() )
 
 1031         return tr( 
"Condition “%1” from algorithm “%2”" ).arg( dep.conditionalBranch, description );
 
 1034     connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged, 
this, [ = ]()
 
 1036       QList< QgsProcessingModelChildDependency > res;
 
 1037       for ( 
const QVariant &v : widget->selectedOptions() )
 
 1039         res << v.value< QgsProcessingModelChildDependency >();
 
 1048 void QgsModelChildDependenciesWidget::updateSummaryText()
 
 1050   mLineEdit->setText( tr( 
"%1 dependencies selected" ).arg( mValue.count() ) );
 
static QgsProcessingRegistry * processingRegistry()
Returns the application's processing registry, used for managing processing providers,...
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
static void enableAutoGeometryRestore(QWidget *widget, const QString &key=QString())
Register the widget to allow its position to be automatically saved and restored when open and closed...
Represents an item shown within a QgsMessageBar widget.
A bar for displaying non-blocking messages to the user.
static void logMessage(const QString &message, const QString &tag=QString(), Qgis::MessageLevel level=Qgis::MessageLevel::Warning, bool notifyUser=true)
Adds a message to the log instance (and creates it if necessary).
A generic message view for displaying QGIS messages.
void setTitle(const QString &title) override
Sets title for the messages.
void setMessage(const QString &message, MessageType msgType) override
Sets message, it won't be displayed until.
void showMessage(bool blocking=true) override
display the message to the user and deletes itself
Makes metadata of processing parameters available.
@ ExposeToModeler
Is this parameter available in the modeler. Is set to on by default.
QList< QgsProcessingParameterType * > parameterTypes() const
Returns a list with all known parameter types.
@ PythonQgsProcessingAlgorithmSubclass
Full Python QgsProcessingAlgorithm subclass.