QGIS API Documentation  3.26.3-Buenos Aires (65e4edfdad)
qgsmodeldesignerdialog.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsmodeldesignerdialog.cpp
3  ------------------------
4  Date : March 2020
5  Copyright : (C) 2020 Nyall Dawson
6  Email : nyall dot dawson at gmail dot com
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
16 #include "qgsmodeldesignerdialog.h"
17 #include "qgssettings.h"
18 #include "qgsapplication.h"
19 #include "qgsfileutils.h"
20 #include "qgsmessagebar.h"
22 #include "qgsprocessingregistry.h"
23 #include "qgsprocessingalgorithm.h"
24 #include "qgsgui.h"
26 #include "qgsmodelundocommand.h"
27 #include "qgsmodelviewtoolselect.h"
28 #include "qgsmodelviewtoolpan.h"
29 #include "qgsmodelgraphicsscene.h"
33 #include "qgsmessageviewer.h"
34 #include "qgsmessagebaritem.h"
35 #include "qgspanelwidget.h"
38 
39 #include <QShortcut>
40 #include <QDesktopWidget>
41 #include <QKeySequence>
42 #include <QFileDialog>
43 #include <QPrinter>
44 #include <QSvgGenerator>
45 #include <QToolButton>
46 #include <QCloseEvent>
47 #include <QMessageBox>
48 #include <QUndoView>
49 #include <QPushButton>
50 #include <QUrl>
51 #include <QTextStream>
52 #include <QActionGroup>
53 
55 
56 
57 QgsModelerToolboxModel::QgsModelerToolboxModel( QObject *parent )
59 {
60 
61 }
62 
63 Qt::ItemFlags QgsModelerToolboxModel::flags( const QModelIndex &index ) const
64 {
65  Qt::ItemFlags f = QgsProcessingToolboxProxyModel::flags( index );
66  const QModelIndex sourceIndex = mapToSource( index );
67  if ( toolboxModel()->isAlgorithm( sourceIndex ) )
68  {
69  f = f | Qt::ItemIsDragEnabled;
70  }
71  return f;
72 }
73 
74 Qt::DropActions QgsModelerToolboxModel::supportedDragActions() const
75 {
76  return Qt::CopyAction;
77 }
78 
79 
80 
81 QgsModelDesignerDialog::QgsModelDesignerDialog( QWidget *parent, Qt::WindowFlags flags )
82  : QMainWindow( parent, flags )
83  , mToolsActionGroup( new QActionGroup( this ) )
84 {
85  setupUi( this );
86 
87  setAttribute( Qt::WA_DeleteOnClose );
88  setDockOptions( dockOptions() | QMainWindow::GroupedDragging );
89  setWindowFlags( Qt::WindowMinimizeButtonHint |
90  Qt::WindowMaximizeButtonHint |
91  Qt::WindowCloseButtonHint );
92 
94 
95  mModel = std::make_unique< QgsProcessingModelAlgorithm >();
96  mModel->setProvider( QgsApplication::processingRegistry()->providerById( QStringLiteral( "model" ) ) );
97 
98  mUndoStack = new QUndoStack( this );
99  connect( mUndoStack, &QUndoStack::indexChanged, this, [ = ]
100  {
101  if ( mIgnoreUndoStackChanges )
102  return;
103 
104  mBlockUndoCommands++;
105  updateVariablesGui();
106  mGroupEdit->setText( mModel->group() );
107  mNameEdit->setText( mModel->displayName() );
108  mBlockUndoCommands--;
109  repaintModel();
110  } );
111 
112  mPropertiesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
113  mInputsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
114  mAlgorithmsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
115  mVariablesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
116 
117  mAlgorithmsTree->header()->setVisible( false );
118  mAlgorithmSearchEdit->setShowSearchIcon( true );
119  mAlgorithmSearchEdit->setPlaceholderText( tr( "Search…" ) );
120  connect( mAlgorithmSearchEdit, &QgsFilterLineEdit::textChanged, mAlgorithmsTree, &QgsProcessingToolboxTreeView::setFilterString );
121 
122  mInputsTreeWidget->header()->setVisible( false );
123  mInputsTreeWidget->setAlternatingRowColors( true );
124  mInputsTreeWidget->setDragDropMode( QTreeWidget::DragOnly );
125  mInputsTreeWidget->setDropIndicatorShown( true );
126 
127  mNameEdit->setPlaceholderText( tr( "Enter model name here" ) );
128  mGroupEdit->setPlaceholderText( tr( "Enter group name here" ) );
129 
130  mMessageBar = new QgsMessageBar();
131  mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
132  mainLayout->insertWidget( 0, mMessageBar );
133 
134  mView->setAcceptDrops( true );
135  QgsSettings settings;
136 
137  connect( mActionClose, &QAction::triggered, this, &QWidget::close );
138  connect( mActionNew, &QAction::triggered, this, &QgsModelDesignerDialog::newModel );
139  connect( mActionZoomIn, &QAction::triggered, this, &QgsModelDesignerDialog::zoomIn );
140  connect( mActionZoomOut, &QAction::triggered, this, &QgsModelDesignerDialog::zoomOut );
141  connect( mActionZoomActual, &QAction::triggered, this, &QgsModelDesignerDialog::zoomActual );
142  connect( mActionZoomToItems, &QAction::triggered, this, &QgsModelDesignerDialog::zoomFull );
143  connect( mActionExportImage, &QAction::triggered, this, &QgsModelDesignerDialog::exportToImage );
144  connect( mActionExportPdf, &QAction::triggered, this, &QgsModelDesignerDialog::exportToPdf );
145  connect( mActionExportSvg, &QAction::triggered, this, &QgsModelDesignerDialog::exportToSvg );
146  connect( mActionExportPython, &QAction::triggered, this, &QgsModelDesignerDialog::exportAsPython );
147  connect( mActionSave, &QAction::triggered, this, [ = ] { saveModel( false ); } );
148  connect( mActionSaveAs, &QAction::triggered, this, [ = ] { saveModel( true ); } );
149  connect( mActionDeleteComponents, &QAction::triggered, this, &QgsModelDesignerDialog::deleteSelected );
150  connect( mActionSnapSelected, &QAction::triggered, mView, &QgsModelGraphicsView::snapSelected );
151  connect( mActionValidate, &QAction::triggered, this, &QgsModelDesignerDialog::validate );
152  connect( mActionReorderInputs, &QAction::triggered, this, &QgsModelDesignerDialog::reorderInputs );
153  connect( mActionEditHelp, &QAction::triggered, this, &QgsModelDesignerDialog::editHelp );
154  connect( mReorderInputsButton, &QPushButton::clicked, this, &QgsModelDesignerDialog::reorderInputs );
155 
156  mActionSnappingEnabled->setChecked( settings.value( QStringLiteral( "/Processing/Modeler/enableSnapToGrid" ), false ).toBool() );
157  connect( mActionSnappingEnabled, &QAction::toggled, this, [ = ]( bool enabled )
158  {
159  mView->snapper()->setSnapToGrid( enabled );
160  QgsSettings().setValue( QStringLiteral( "/Processing/Modeler/enableSnapToGrid" ), enabled );
161  } );
162  mView->snapper()->setSnapToGrid( mActionSnappingEnabled->isChecked() );
163 
164  connect( mActionSelectAll, &QAction::triggered, this, [ = ]
165  {
166  mScene->selectAll();
167  } );
168 
169  QStringList docksTitle = settings.value( QStringLiteral( "ModelDesigner/hiddenDocksTitle" ), QStringList(), QgsSettings::App ).toStringList();
170  QStringList docksActive = settings.value( QStringLiteral( "ModelDesigner/hiddenDocksActive" ), QStringList(), QgsSettings::App ).toStringList();
171  if ( !docksTitle.isEmpty() )
172  {
173  for ( const auto &title : docksTitle )
174  {
175  mPanelStatus.insert( title, PanelStatus( true, docksActive.contains( title ) ) );
176  }
177  }
178  mActionHidePanels->setChecked( !docksTitle.isEmpty() );
179  connect( mActionHidePanels, &QAction::toggled, this, &QgsModelDesignerDialog::setPanelVisibility );
180 
181  mUndoAction = mUndoStack->createUndoAction( this );
182  mUndoAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionUndo.svg" ) ) );
183  mUndoAction->setShortcuts( QKeySequence::Undo );
184  mRedoAction = mUndoStack->createRedoAction( this );
185  mRedoAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionRedo.svg" ) ) );
186  mRedoAction->setShortcuts( QKeySequence::Redo );
187 
188  mMenuEdit->insertAction( mActionDeleteComponents, mRedoAction );
189  mMenuEdit->insertAction( mActionDeleteComponents, mUndoAction );
190  mMenuEdit->insertSeparator( mActionDeleteComponents );
191  mToolbar->insertAction( mActionZoomIn, mUndoAction );
192  mToolbar->insertAction( mActionZoomIn, mRedoAction );
193  mToolbar->insertSeparator( mActionZoomIn );
194 
195  mGroupMenu = new QMenu( tr( "Zoom To" ), this );
196  mMenuView->insertMenu( mActionZoomIn, mGroupMenu );
197  connect( mGroupMenu, &QMenu::aboutToShow, this, &QgsModelDesignerDialog::populateZoomToMenu );
198 
199  //cut/copy/paste actions. Note these are not included in the ui file
200  //as ui files have no support for QKeySequence shortcuts
201  mActionCut = new QAction( tr( "Cu&t" ), this );
202  mActionCut->setShortcuts( QKeySequence::Cut );
203  mActionCut->setStatusTip( tr( "Cut" ) );
204  mActionCut->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditCut.svg" ) ) );
205  connect( mActionCut, &QAction::triggered, this, [ = ]
206  {
207  mView->copySelectedItems( QgsModelGraphicsView::ClipboardCut );
208  } );
209 
210  mActionCopy = new QAction( tr( "&Copy" ), this );
211  mActionCopy->setShortcuts( QKeySequence::Copy );
212  mActionCopy->setStatusTip( tr( "Copy" ) );
213  mActionCopy->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditCopy.svg" ) ) );
214  connect( mActionCopy, &QAction::triggered, this, [ = ]
215  {
216  mView->copySelectedItems( QgsModelGraphicsView::ClipboardCopy );
217  } );
218 
219  mActionPaste = new QAction( tr( "&Paste" ), this );
220  mActionPaste->setShortcuts( QKeySequence::Paste );
221  mActionPaste->setStatusTip( tr( "Paste" ) );
222  mActionPaste->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionEditPaste.svg" ) ) );
223  connect( mActionPaste, &QAction::triggered, this, [ = ]
224  {
225  mView->pasteItems( QgsModelGraphicsView::PasteModeCursor );
226  } );
227  mMenuEdit->insertAction( mActionDeleteComponents, mActionCut );
228  mMenuEdit->insertAction( mActionDeleteComponents, mActionCopy );
229  mMenuEdit->insertAction( mActionDeleteComponents, mActionPaste );
230  mMenuEdit->insertSeparator( mActionDeleteComponents );
231 
232  mAlgorithmsModel = new QgsModelerToolboxModel( this );
233  mAlgorithmsTree->setToolboxProxyModel( mAlgorithmsModel );
234 
235  QgsProcessingToolboxProxyModel::Filters filters = QgsProcessingToolboxProxyModel::FilterModeler;
236  if ( settings.value( QStringLiteral( "Processing/Configuration/SHOW_ALGORITHMS_KNOWN_ISSUES" ), false ).toBool() )
237  {
239  }
240  mAlgorithmsTree->setFilters( filters );
241  mAlgorithmsTree->setDragDropMode( QTreeWidget::DragOnly );
242  mAlgorithmsTree->setDropIndicatorShown( true );
243 
244  connect( mView, &QgsModelGraphicsView::algorithmDropped, this, [ = ]( const QString & algorithmId, const QPointF & pos )
245  {
246  addAlgorithm( algorithmId, pos );
247  } );
248  connect( mAlgorithmsTree, &QgsProcessingToolboxTreeView::doubleClicked, this, [ = ]()
249  {
250  if ( mAlgorithmsTree->selectedAlgorithm() )
251  addAlgorithm( mAlgorithmsTree->selectedAlgorithm()->id(), QPointF() );
252  } );
253  connect( mInputsTreeWidget, &QgsModelDesignerInputsTreeWidget::doubleClicked, this, [ = ]( const QModelIndex & )
254  {
255  const QString parameterType = mInputsTreeWidget->currentItem()->data( 0, Qt::UserRole ).toString();
256  addInput( parameterType, QPointF() );
257  } );
258 
259  connect( mView, &QgsModelGraphicsView::inputDropped, this, &QgsModelDesignerDialog::addInput );
260 
261  // Ctrl+= should also trigger a zoom in action
262  QShortcut *ctrlEquals = new QShortcut( QKeySequence( QStringLiteral( "Ctrl+=" ) ), this );
263  connect( ctrlEquals, &QShortcut::activated, this, &QgsModelDesignerDialog::zoomIn );
264 
265  mUndoDock = new QgsDockWidget( tr( "Undo History" ), this );
266  mUndoDock->setObjectName( QStringLiteral( "UndoDock" ) );
267  mUndoView = new QUndoView( mUndoStack, this );
268  mUndoDock->setWidget( mUndoView );
269  mUndoDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
270  addDockWidget( Qt::DockWidgetArea::LeftDockWidgetArea, mUndoDock );
271 
272  tabifyDockWidget( mUndoDock, mPropertiesDock );
273  tabifyDockWidget( mVariablesDock, mPropertiesDock );
274  mPropertiesDock->raise();
275  tabifyDockWidget( mInputsDock, mAlgorithmsDock );
276  mInputsDock->raise();
277 
278  connect( mVariablesEditor, &QgsVariableEditorWidget::scopeChanged, this, [ = ]
279  {
280  if ( mModel )
281  {
282  beginUndoCommand( tr( "Change Model Variables" ) );
283  mModel->setVariables( mVariablesEditor->variablesInActiveScope() );
284  endUndoCommand();
285  }
286  } );
287  connect( mNameEdit, &QLineEdit::textChanged, this, [ = ]( const QString & name )
288  {
289  if ( mModel )
290  {
291  beginUndoCommand( tr( "Change Model Name" ), NameChanged );
292  mModel->setName( name );
293  endUndoCommand();
294  updateWindowTitle();
295  }
296  } );
297  connect( mGroupEdit, &QLineEdit::textChanged, this, [ = ]( const QString & group )
298  {
299  if ( mModel )
300  {
301  beginUndoCommand( tr( "Change Model Group" ), GroupChanged );
302  mModel->setGroup( group );
303  endUndoCommand();
304  }
305  } );
306 
307  fillInputsTree();
308 
309  QToolButton *toolbuttonExportToScript = new QToolButton();
310  toolbuttonExportToScript->setPopupMode( QToolButton::InstantPopup );
311  toolbuttonExportToScript->addAction( mActionExportAsScriptAlgorithm );
312  toolbuttonExportToScript->setDefaultAction( mActionExportAsScriptAlgorithm );
313  mToolbar->insertWidget( mActionExportImage, toolbuttonExportToScript );
314  connect( mActionExportAsScriptAlgorithm, &QAction::triggered, this, &QgsModelDesignerDialog::exportAsScriptAlgorithm );
315 
316  mActionShowComments->setChecked( settings.value( QStringLiteral( "/Processing/Modeler/ShowComments" ), true ).toBool() );
317  connect( mActionShowComments, &QAction::toggled, this, &QgsModelDesignerDialog::toggleComments );
318 
319  mPanTool = new QgsModelViewToolPan( mView );
320  mPanTool->setAction( mActionPan );
321 
322  mToolsActionGroup->addAction( mActionPan );
323  connect( mActionPan, &QAction::triggered, mPanTool, [ = ] { mView->setTool( mPanTool ); } );
324 
325  mSelectTool = new QgsModelViewToolSelect( mView );
326  mSelectTool->setAction( mActionSelectMoveItem );
327 
328  mToolsActionGroup->addAction( mActionSelectMoveItem );
329  connect( mActionSelectMoveItem, &QAction::triggered, mSelectTool, [ = ] { mView->setTool( mSelectTool ); } );
330 
331  mView->setTool( mSelectTool );
332  mView->setFocus();
333 
334  connect( mView, &QgsModelGraphicsView::macroCommandStarted, this, [ = ]( const QString & text )
335  {
336  mIgnoreUndoStackChanges++;
337  mUndoStack->beginMacro( text );
338  mIgnoreUndoStackChanges--;
339  } );
340  connect( mView, &QgsModelGraphicsView::macroCommandEnded, this, [ = ]
341  {
342  mIgnoreUndoStackChanges++;
343  mUndoStack->endMacro();
344  mIgnoreUndoStackChanges--;
345  } );
346  connect( mView, &QgsModelGraphicsView::beginCommand, this, [ = ]( const QString & text )
347  {
348  beginUndoCommand( text );
349  } );
350  connect( mView, &QgsModelGraphicsView::endCommand, this, [ = ]
351  {
352  endUndoCommand();
353  } );
354  connect( mView, &QgsModelGraphicsView::deleteSelectedItems, this, [ = ]
355  {
356  deleteSelected();
357  } );
358 
359  connect( mActionAddGroupBox, &QAction::triggered, this, [ = ]
360  {
361  const QPointF viewCenter = mView->mapToScene( mView->viewport()->rect().center() );
362  QgsProcessingModelGroupBox group;
363  group.setPosition( viewCenter );
364  group.setDescription( tr( "New Group" ) );
365 
366  beginUndoCommand( tr( "Add Group Box" ) );
367  model()->addGroupBox( group );
368  repaintModel();
369  endUndoCommand();
370  } );
371 
372  updateWindowTitle();
373 
374  // restore the toolbar and dock widgets positions using Qt settings API
375  restoreState( settings.value( QStringLiteral( "ModelDesigner/state" ), QByteArray(), QgsSettings::App ).toByteArray() );
376 }
377 
378 QgsModelDesignerDialog::~QgsModelDesignerDialog()
379 {
380  QgsSettings settings;
381  if ( !mPanelStatus.isEmpty() )
382  {
383  QStringList docksTitle;
384  QStringList docksActive;
385 
386  for ( const auto &panel : mPanelStatus.toStdMap() )
387  {
388  if ( panel.second.isVisible )
389  docksTitle << panel.first;
390  if ( panel.second.isActive )
391  docksActive << panel.first;
392  }
393  settings.setValue( QStringLiteral( "ModelDesigner/hiddenDocksTitle" ), docksTitle, QgsSettings::App );
394  settings.setValue( QStringLiteral( "ModelDesigner/hiddenDocksActive" ), docksActive, QgsSettings::App );
395  }
396  else
397  {
398  settings.remove( QStringLiteral( "ModelDesigner/hiddenDocksTitle" ), QgsSettings::App );
399  settings.remove( QStringLiteral( "ModelDesigner/hiddenDocksActive" ), QgsSettings::App );
400  }
401 
402  // store the toolbar/dock widget settings using Qt settings API
403  settings.setValue( QStringLiteral( "ModelDesigner/state" ), saveState(), QgsSettings::App );
404 
405  mIgnoreUndoStackChanges++;
406  delete mSelectTool; // delete mouse handles before everything else
407 }
408 
409 void QgsModelDesignerDialog::closeEvent( QCloseEvent *event )
410 {
411  if ( checkForUnsavedChanges() )
412  event->accept();
413  else
414  event->ignore();
415 }
416 
417 void QgsModelDesignerDialog::beginUndoCommand( const QString &text, int id )
418 {
419  if ( mBlockUndoCommands || !mUndoStack )
420  return;
421 
422  if ( mActiveCommand )
423  endUndoCommand();
424 
425  mActiveCommand = std::make_unique< QgsModelUndoCommand >( mModel.get(), text, id );
426 }
427 
428 void QgsModelDesignerDialog::endUndoCommand()
429 {
430  if ( mBlockUndoCommands || !mActiveCommand || !mUndoStack )
431  return;
432 
433  mActiveCommand->saveAfterState();
434  mIgnoreUndoStackChanges++;
435  mUndoStack->push( mActiveCommand.release() );
436  mIgnoreUndoStackChanges--;
437  setDirty( true );
438 }
439 
440 QgsProcessingModelAlgorithm *QgsModelDesignerDialog::model()
441 {
442  return mModel.get();
443 }
444 
445 void QgsModelDesignerDialog::setModel( QgsProcessingModelAlgorithm *model )
446 {
447  mModel.reset( model );
448 
449  mGroupEdit->setText( mModel->group() );
450  mNameEdit->setText( mModel->displayName() );
451  repaintModel();
452  updateVariablesGui();
453 
454  mView->centerOn( 0, 0 );
455  setDirty( false );
456 
457  mIgnoreUndoStackChanges++;
458  mUndoStack->clear();
459  mIgnoreUndoStackChanges--;
460 
461  updateWindowTitle();
462 }
463 
464 void QgsModelDesignerDialog::loadModel( const QString &path )
465 {
466  std::unique_ptr< QgsProcessingModelAlgorithm > alg = std::make_unique< QgsProcessingModelAlgorithm >();
467  if ( alg->fromFile( path ) )
468  {
469  alg->setProvider( QgsApplication::processingRegistry()->providerById( QStringLiteral( "model" ) ) );
470  alg->setSourceFilePath( path );
471  setModel( alg.release() );
472  }
473  else
474  {
475  QgsMessageLog::logMessage( tr( "Could not load model %1" ).arg( path ), tr( "Processing" ), Qgis::MessageLevel::Critical );
476  QMessageBox::critical( this, tr( "Open Model" ), tr( "The selected model could not be loaded.\n"
477  "See the log for more information." ) );
478  }
479 }
480 
481 void QgsModelDesignerDialog::setModelScene( QgsModelGraphicsScene *scene )
482 {
483  QgsModelGraphicsScene *oldScene = mScene;
484 
485  mScene = scene;
486  mScene->setParent( this );
487  mScene->setChildAlgorithmResults( mChildResults );
488  mScene->setModel( mModel.get() );
489  mScene->setMessageBar( mMessageBar );
490 
491  const QPointF center = mView->mapToScene( mView->viewport()->rect().center() );
492  mView->setModelScene( mScene );
493 
494  mSelectTool->resetCache();
495  mSelectTool->setScene( mScene );
496 
497  connect( mScene, &QgsModelGraphicsScene::rebuildRequired, this, [ = ]
498  {
499  if ( mBlockRepaints )
500  return;
501 
502  repaintModel();
503  } );
504  connect( mScene, &QgsModelGraphicsScene::componentAboutToChange, this, [ = ]( const QString & description, int id ) { beginUndoCommand( description, id ); } );
505  connect( mScene, &QgsModelGraphicsScene::componentChanged, this, [ = ] { endUndoCommand(); } );
506 
507  mView->centerOn( center );
508 
509  if ( oldScene )
510  oldScene->deleteLater();
511 }
512 
513 void QgsModelDesignerDialog::activate()
514 {
515  show();
516  raise();
517  setWindowState( windowState() & ~Qt::WindowMinimized );
518  activateWindow();
519 }
520 
521 void QgsModelDesignerDialog::updateVariablesGui()
522 {
523  mBlockUndoCommands++;
524 
525  std::unique_ptr< QgsExpressionContextScope > variablesScope = std::make_unique< QgsExpressionContextScope >( tr( "Model Variables" ) );
526  const QVariantMap modelVars = mModel->variables();
527  for ( auto it = modelVars.constBegin(); it != modelVars.constEnd(); ++it )
528  {
529  variablesScope->setVariable( it.key(), it.value() );
530  }
531  QgsExpressionContext variablesContext;
532  variablesContext.appendScope( variablesScope.release() );
533  mVariablesEditor->setContext( &variablesContext );
534  mVariablesEditor->setEditableScopeIndex( 0 );
535 
536  mBlockUndoCommands--;
537 }
538 
539 void QgsModelDesignerDialog::setDirty( bool dirty )
540 {
541  mHasChanged = dirty;
542  updateWindowTitle();
543 }
544 
545 bool QgsModelDesignerDialog::validateSave( SaveAction action )
546 {
547  switch ( action )
548  {
549  case QgsModelDesignerDialog::SaveAction::SaveAsFile:
550  break;
551  case QgsModelDesignerDialog::SaveAction::SaveInProject:
552  if ( mNameEdit->text().trimmed().isEmpty() )
553  {
554  mMessageBar->pushWarning( QString(), tr( "Please enter a model name before saving" ) );
555  return false;
556  }
557  break;
558  }
559 
560  return true;
561 }
562 
563 bool QgsModelDesignerDialog::checkForUnsavedChanges()
564 {
565  if ( isDirty() )
566  {
567  QMessageBox::StandardButton ret = QMessageBox::question( this, tr( "Save Model?" ),
568  tr( "There are unsaved changes in this model. Do you want to keep those?" ),
569  QMessageBox::Save | QMessageBox::Cancel | QMessageBox::Discard, QMessageBox::Cancel );
570  switch ( ret )
571  {
572  case QMessageBox::Save:
573  return saveModel( false );
574 
575  case QMessageBox::Discard:
576  return true;
577 
578  default:
579  return false;
580  }
581  }
582  else
583  {
584  return true;
585  }
586 }
587 
588 void QgsModelDesignerDialog::setLastRunChildAlgorithmResults( const QVariantMap &results )
589 {
590  mChildResults = results;
591  if ( mScene )
592  mScene->setChildAlgorithmResults( mChildResults );
593 }
594 
595 void QgsModelDesignerDialog::setLastRunChildAlgorithmInputs( const QVariantMap &inputs )
596 {
597  mChildInputs = inputs;
598  if ( mScene )
599  mScene->setChildAlgorithmInputs( mChildInputs );
600 }
601 
602 void QgsModelDesignerDialog::setModelName( const QString &name )
603 {
604  mNameEdit->setText( name );
605 }
606 
607 void QgsModelDesignerDialog::zoomIn()
608 {
609  mView->setTransformationAnchor( QGraphicsView::NoAnchor );
610  QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
611  QgsSettings settings;
612  const double factor = settings.value( QStringLiteral( "/qgis/zoom_favor" ), 2.0 ).toDouble();
613  mView->scale( factor, factor );
614  mView->centerOn( point );
615 }
616 
617 void QgsModelDesignerDialog::zoomOut()
618 {
619  mView->setTransformationAnchor( QGraphicsView::NoAnchor );
620  QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
621  QgsSettings settings;
622  const double factor = 1.0 / settings.value( QStringLiteral( "/qgis/zoom_favor" ), 2.0 ).toDouble();
623  mView->scale( factor, factor );
624  mView->centerOn( point );
625 }
626 
627 void QgsModelDesignerDialog::zoomActual()
628 {
629  QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
630  mView->resetTransform();
631  mView->scale( QgsApplication::desktop()->logicalDpiX() / 96, QgsApplication::desktop()->logicalDpiX() / 96 );
632  mView->centerOn( point );
633 }
634 
635 void QgsModelDesignerDialog::zoomFull()
636 {
637  QRectF totalRect = mView->scene()->itemsBoundingRect();
638  totalRect.adjust( -10, -10, 10, 10 );
639  mView->fitInView( totalRect, Qt::KeepAspectRatio );
640 }
641 
642 void QgsModelDesignerDialog::newModel()
643 {
644  if ( !checkForUnsavedChanges() )
645  return;
646 
647  std::unique_ptr< QgsProcessingModelAlgorithm > alg = std::make_unique< QgsProcessingModelAlgorithm >();
648  alg->setProvider( QgsApplication::processingRegistry()->providerById( QStringLiteral( "model" ) ) );
649  setModel( alg.release() );
650 }
651 
652 void QgsModelDesignerDialog::exportToImage()
653 {
654  QgsSettings settings;
655  QString lastExportDir = settings.value( QStringLiteral( "lastModelDesignerExportDir" ), QDir::homePath(), QgsSettings::App ).toString();
656 
657  QString filename = QFileDialog::getSaveFileName( this, tr( "Save Model as Image" ),
658  lastExportDir,
659  tr( "PNG files (*.png *.PNG)" ) );
660  if ( filename.isEmpty() )
661  return;
662 
663  filename = QgsFileUtils::ensureFileNameHasExtension( filename, QStringList() << QStringLiteral( "png" ) );
664 
665  const QFileInfo saveFileInfo( filename );
666  settings.setValue( QStringLiteral( "lastModelDesignerExportDir" ), saveFileInfo.absolutePath(), QgsSettings::App );
667 
668  repaintModel( false );
669 
670  QRectF totalRect = mView->scene()->itemsBoundingRect();
671  totalRect.adjust( -10, -10, 10, 10 );
672  const QRectF imageRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
673 
674  QImage img( totalRect.width(), totalRect.height(),
675  QImage::Format_ARGB32_Premultiplied );
676  img.fill( Qt::white );
677  QPainter painter;
678  painter.setRenderHint( QPainter::Antialiasing );
679  painter.begin( &img );
680  mView->scene()->render( &painter, imageRect, totalRect );
681  painter.end();
682 
683  img.save( filename );
684 
685  mMessageBar->pushMessage( QString(), tr( "Successfully exported model as image to <a href=\"%1\">%2</a>" ).arg( QUrl::fromLocalFile( filename ).toString(), QDir::toNativeSeparators( filename ) ), Qgis::MessageLevel::Success, 0 );
686  repaintModel( true );
687 }
688 
689 void QgsModelDesignerDialog::exportToPdf()
690 {
691  QgsSettings settings;
692  QString lastExportDir = settings.value( QStringLiteral( "lastModelDesignerExportDir" ), QDir::homePath(), QgsSettings::App ).toString();
693 
694  QString filename = QFileDialog::getSaveFileName( this, tr( "Save Model as PDF" ),
695  lastExportDir,
696  tr( "PDF files (*.pdf *.PDF)" ) );
697  if ( filename.isEmpty() )
698  return;
699 
700  filename = QgsFileUtils::ensureFileNameHasExtension( filename, QStringList() << QStringLiteral( "pdf" ) );
701 
702  const QFileInfo saveFileInfo( filename );
703  settings.setValue( QStringLiteral( "lastModelDesignerExportDir" ), saveFileInfo.absolutePath(), QgsSettings::App );
704 
705  repaintModel( false );
706 
707  QRectF totalRect = mView->scene()->itemsBoundingRect();
708  totalRect.adjust( -10, -10, 10, 10 );
709  const QRectF printerRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
710 
711  QPrinter printer;
712  printer.setOutputFormat( QPrinter::PdfFormat );
713  printer.setOutputFileName( filename );
714  printer.setPaperSize( QSizeF( printerRect.width(), printerRect.height() ), QPrinter::DevicePixel );
715  printer.setFullPage( true );
716 
717  QPainter painter( &printer );
718  mView->scene()->render( &painter, printerRect, totalRect );
719  painter.end();
720 
721  mMessageBar->pushMessage( QString(), tr( "Successfully exported model as PDF to <a href=\"%1\">%2</a>" ).arg( QUrl::fromLocalFile( filename ).toString(),
722  QDir::toNativeSeparators( filename ) ), Qgis::MessageLevel::Success, 0 );
723  repaintModel( true );
724 }
725 
726 void QgsModelDesignerDialog::exportToSvg()
727 {
728  QgsSettings settings;
729  QString lastExportDir = settings.value( QStringLiteral( "lastModelDesignerExportDir" ), QDir::homePath(), QgsSettings::App ).toString();
730 
731  QString filename = QFileDialog::getSaveFileName( this, tr( "Save Model as SVG" ),
732  lastExportDir,
733  tr( "SVG files (*.svg *.SVG)" ) );
734  if ( filename.isEmpty() )
735  return;
736 
737  filename = QgsFileUtils::ensureFileNameHasExtension( filename, QStringList() << QStringLiteral( "svg" ) );
738 
739  const QFileInfo saveFileInfo( filename );
740  settings.setValue( QStringLiteral( "lastModelDesignerExportDir" ), saveFileInfo.absolutePath(), QgsSettings::App );
741 
742  repaintModel( false );
743 
744  QRectF totalRect = mView->scene()->itemsBoundingRect();
745  totalRect.adjust( -10, -10, 10, 10 );
746  const QRectF svgRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
747 
748  QSvgGenerator svg;
749  svg.setFileName( filename );
750  svg.setSize( QSize( totalRect.width(), totalRect.height() ) );
751  svg.setViewBox( svgRect );
752  svg.setTitle( mModel->displayName() );
753 
754  QPainter painter( &svg );
755  mView->scene()->render( &painter, svgRect, totalRect );
756  painter.end();
757 
758  mMessageBar->pushMessage( QString(), tr( "Successfully exported model as SVG to <a href=\"%1\">%2</a>" ).arg( QUrl::fromLocalFile( filename ).toString(), QDir::toNativeSeparators( filename ) ), Qgis::MessageLevel::Success, 0 );
759  repaintModel( true );
760 }
761 
762 void QgsModelDesignerDialog::exportAsPython()
763 {
764  QgsSettings settings;
765  QString lastExportDir = settings.value( QStringLiteral( "lastModelDesignerExportDir" ), QDir::homePath(), QgsSettings::App ).toString();
766 
767  QString filename = QFileDialog::getSaveFileName( this, tr( "Save Model as Python Script" ),
768  lastExportDir,
769  tr( "Processing scripts (*.py *.PY)" ) );
770  if ( filename.isEmpty() )
771  return;
772 
773  filename = QgsFileUtils::ensureFileNameHasExtension( filename, QStringList() << QStringLiteral( "py" ) );
774 
775  const QFileInfo saveFileInfo( filename );
776  settings.setValue( QStringLiteral( "lastModelDesignerExportDir" ), saveFileInfo.absolutePath(), QgsSettings::App );
777 
778  const QString text = mModel->asPythonCode( QgsProcessing::PythonQgsProcessingAlgorithmSubclass, 4 ).join( '\n' );
779 
780  QFile outFile( filename );
781  if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
782  {
783  return;
784  }
785  QTextStream fout( &outFile );
786  fout << text;
787  outFile.close();
788 
789  mMessageBar->pushMessage( QString(), tr( "Successfully exported model as Python script to <a href=\"%1\">%2</a>" ).arg( QUrl::fromLocalFile( filename ).toString(), QDir::toNativeSeparators( filename ) ), Qgis::MessageLevel::Success, 0 );
790 }
791 
792 void QgsModelDesignerDialog::toggleComments( bool show )
793 {
794  QgsSettings().setValue( QStringLiteral( "/Processing/Modeler/ShowComments" ), show );
795 
796  repaintModel( true );
797 }
798 
799 void QgsModelDesignerDialog::updateWindowTitle()
800 {
801  QString title = tr( "Model Designer" );
802  if ( !mModel->name().isEmpty() )
803  title = QStringLiteral( "%1 - %2" ).arg( title, mModel->name() );
804 
805  if ( isDirty() )
806  title.prepend( '*' );
807 
808  setWindowTitle( title );
809 }
810 
811 void QgsModelDesignerDialog::deleteSelected()
812 {
813  QList< QgsModelComponentGraphicItem * > items = mScene->selectedComponentItems();
814  if ( items.empty() )
815  return;
816 
817  if ( items.size() == 1 )
818  {
819  items.at( 0 )->deleteComponent();
820  return;
821  }
822 
823  std::sort( items.begin(), items.end(), []( QgsModelComponentGraphicItem * p1, QgsModelComponentGraphicItem * p2 )
824  {
825  // try to delete the easy stuff first, so comments, then outputs, as nothing will depend on these...
826  if ( dynamic_cast< QgsModelCommentGraphicItem *>( p1 ) )
827  return true;
828  else if ( dynamic_cast< QgsModelCommentGraphicItem *>( p2 ) )
829  return false;
830  else if ( dynamic_cast< QgsModelGroupBoxGraphicItem *>( p1 ) )
831  return true;
832  else if ( dynamic_cast< QgsModelGroupBoxGraphicItem *>( p2 ) )
833  return false;
834  else if ( dynamic_cast< QgsModelOutputGraphicItem *>( p1 ) )
835  return true;
836  else if ( dynamic_cast< QgsModelOutputGraphicItem *>( p2 ) )
837  return false;
838  else if ( dynamic_cast< QgsModelChildAlgorithmGraphicItem *>( p1 ) )
839  return true;
840  else if ( dynamic_cast< QgsModelChildAlgorithmGraphicItem *>( p2 ) )
841  return false;
842  return false;
843  } );
844 
845 
846  beginUndoCommand( tr( "Delete Components" ) );
847 
848  QVariant prevState = mModel->toVariant();
849  mBlockUndoCommands++;
850  mBlockRepaints = true;
851  bool failed = false;
852  while ( !items.empty() )
853  {
854  QgsModelComponentGraphicItem *toDelete = nullptr;
855  for ( QgsModelComponentGraphicItem *item : items )
856  {
857  if ( item->canDeleteComponent() )
858  {
859  toDelete = item;
860  break;
861  }
862  }
863 
864  if ( !toDelete )
865  {
866  failed = true;
867  break;
868  }
869 
870  toDelete->deleteComponent();
871  items.removeAll( toDelete );
872  }
873 
874  if ( failed )
875  {
876  mModel->loadVariant( prevState );
877  QMessageBox::warning( nullptr, QObject::tr( "Could not remove components" ),
878  QObject::tr( "Components depend on the selected items.\n"
879  "Try to remove them before trying deleting these components." ) );
880  mBlockUndoCommands--;
881  mActiveCommand.reset();
882  }
883  else
884  {
885  mBlockUndoCommands--;
886  endUndoCommand();
887  }
888 
889  mBlockRepaints = false;
890  repaintModel();
891 }
892 
893 void QgsModelDesignerDialog::populateZoomToMenu()
894 {
895  mGroupMenu->clear();
896  for ( const QgsProcessingModelGroupBox &box : model()->groupBoxes() )
897  {
898  if ( QgsModelComponentGraphicItem *item = mScene->groupBoxItem( box.uuid() ) )
899  {
900  QAction *zoomAction = new QAction( box.description(), mGroupMenu );
901  connect( zoomAction, &QAction::triggered, this, [ = ]
902  {
903  QRectF groupRect = item->mapToScene( item->boundingRect() ).boundingRect();
904  groupRect.adjust( -10, -10, 10, 10 );
905  mView->fitInView( groupRect, Qt::KeepAspectRatio );
906  mView->centerOn( item );
907  } );
908  mGroupMenu->addAction( zoomAction );
909  }
910  }
911 }
912 
913 void QgsModelDesignerDialog::setPanelVisibility( bool hidden )
914 {
915  const QList<QDockWidget *> docks = findChildren<QDockWidget *>();
916  const QList<QTabBar *> tabBars = findChildren<QTabBar *>();
917 
918  if ( hidden )
919  {
920  mPanelStatus.clear();
921  //record status of all docks
922  for ( QDockWidget *dock : docks )
923  {
924  mPanelStatus.insert( dock->windowTitle(), PanelStatus( dock->isVisible(), false ) );
925  dock->setVisible( false );
926  }
927 
928  //record active dock tabs
929  for ( QTabBar *tabBar : tabBars )
930  {
931  QString currentTabTitle = tabBar->tabText( tabBar->currentIndex() );
932  mPanelStatus[ currentTabTitle ].isActive = true;
933  }
934  }
935  else
936  {
937  //restore visibility of all docks
938  for ( QDockWidget *dock : docks )
939  {
940  if ( mPanelStatus.contains( dock->windowTitle() ) )
941  {
942  dock->setVisible( mPanelStatus.value( dock->windowTitle() ).isVisible );
943  }
944  }
945 
946  //restore previously active dock tabs
947  for ( QTabBar *tabBar : tabBars )
948  {
949  //loop through all tabs in tab bar
950  for ( int i = 0; i < tabBar->count(); ++i )
951  {
952  QString tabTitle = tabBar->tabText( i );
953  if ( mPanelStatus.contains( tabTitle ) && mPanelStatus.value( tabTitle ).isActive )
954  {
955  tabBar->setCurrentIndex( i );
956  }
957  }
958  }
959  mPanelStatus.clear();
960  }
961 }
962 
963 void QgsModelDesignerDialog::editHelp()
964 {
965  QgsProcessingHelpEditorDialog dialog( this );
966  dialog.setWindowTitle( tr( "Edit Model Help" ) );
967  dialog.setAlgorithm( mModel.get() );
968  if ( dialog.exec() )
969  {
970  beginUndoCommand( tr( "Edit Model Help" ) );
971  mModel->setHelpContent( dialog.helpContent() );
972  endUndoCommand();
973  }
974 }
975 
976 void QgsModelDesignerDialog::validate()
977 {
978  QStringList issues;
979  if ( model()->validate( issues ) )
980  {
981  mMessageBar->pushSuccess( QString(), tr( "Model is valid!" ) );
982  }
983  else
984  {
985  QgsMessageBarItem *messageWidget = QgsMessageBar::createMessage( QString(), tr( "Model is invalid!" ) );
986  QPushButton *detailsButton = new QPushButton( tr( "Details" ) );
987  connect( detailsButton, &QPushButton::clicked, detailsButton, [ = ]
988  {
989  QgsMessageViewer *dialog = new QgsMessageViewer( detailsButton );
990  dialog->setTitle( tr( "Model is Invalid" ) );
991 
992  QString longMessage = tr( "<p>This model is not valid:</p>" ) + QStringLiteral( "<ul>" );
993  for ( const QString &issue : issues )
994  {
995  longMessage += QStringLiteral( "<li>%1</li>" ).arg( issue );
996  }
997  longMessage += QLatin1String( "</ul>" );
998 
999  dialog->setMessage( longMessage, QgsMessageOutput::MessageHtml );
1000  dialog->showMessage();
1001  } );
1002  messageWidget->layout()->addWidget( detailsButton );
1003  mMessageBar->clearWidgets();
1004  mMessageBar->pushWidget( messageWidget, Qgis::MessageLevel::Warning, 0 );
1005  }
1006 }
1007 
1008 void QgsModelDesignerDialog::reorderInputs()
1009 {
1010  QgsModelInputReorderDialog dlg( this );
1011  dlg.setModel( mModel.get() );
1012  if ( dlg.exec() )
1013  {
1014  const QStringList inputOrder = dlg.inputOrder();
1015  beginUndoCommand( tr( "Reorder Inputs" ) );
1016  mModel->setParameterOrder( inputOrder );
1017  endUndoCommand();
1018  }
1019 }
1020 
1021 bool QgsModelDesignerDialog::isDirty() const
1022 {
1023  return mHasChanged && mUndoStack->index() != -1;
1024 }
1025 
1026 void QgsModelDesignerDialog::fillInputsTree()
1027 {
1028  const QIcon icon = QgsApplication::getThemeIcon( QStringLiteral( "mIconModelInput.svg" ) );
1029  std::unique_ptr< QTreeWidgetItem > parametersItem = std::make_unique< QTreeWidgetItem >();
1030  parametersItem->setText( 0, tr( "Parameters" ) );
1031  QList<QgsProcessingParameterType *> available = QgsApplication::processingRegistry()->parameterTypes();
1032  std::sort( available.begin(), available.end(), []( const QgsProcessingParameterType * a, const QgsProcessingParameterType * b ) -> bool
1033  {
1034  return QString::localeAwareCompare( a->name(), b->name() ) < 0;
1035  } );
1036 
1037  for ( QgsProcessingParameterType *param : std::as_const( available ) )
1038  {
1039  if ( param->flags() & QgsProcessingParameterType::ExposeToModeler )
1040  {
1041  std::unique_ptr< QTreeWidgetItem > paramItem = std::make_unique< QTreeWidgetItem >();
1042  paramItem->setText( 0, param->name() );
1043  paramItem->setData( 0, Qt::UserRole, param->id() );
1044  paramItem->setIcon( 0, icon );
1045  paramItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1046  paramItem->setToolTip( 0, param->description() );
1047  parametersItem->addChild( paramItem.release() );
1048  }
1049  }
1050  mInputsTreeWidget->addTopLevelItem( parametersItem.release() );
1051  mInputsTreeWidget->topLevelItem( 0 )->setExpanded( true );
1052 }
1053 
1054 
1055 //
1056 // QgsModelChildDependenciesWidget
1057 //
1058 
1059 QgsModelChildDependenciesWidget::QgsModelChildDependenciesWidget( QWidget *parent, QgsProcessingModelAlgorithm *model, const QString &childId )
1060  : QWidget( parent )
1061  , mModel( model )
1062  , mChildId( childId )
1063 {
1064  QHBoxLayout *hl = new QHBoxLayout();
1065  hl->setContentsMargins( 0, 0, 0, 0 );
1066 
1067  mLineEdit = new QLineEdit();
1068  mLineEdit->setEnabled( false );
1069  hl->addWidget( mLineEdit, 1 );
1070 
1071  mToolButton = new QToolButton();
1072  mToolButton->setText( QString( QChar( 0x2026 ) ) );
1073  hl->addWidget( mToolButton );
1074 
1075  setLayout( hl );
1076 
1077  mLineEdit->setText( tr( "%1 dependencies selected" ).arg( 0 ) );
1078 
1079  connect( mToolButton, &QToolButton::clicked, this, &QgsModelChildDependenciesWidget::showDialog );
1080 }
1081 
1082 void QgsModelChildDependenciesWidget::setValue( const QList<QgsProcessingModelChildDependency> &value )
1083 {
1084  mValue = value;
1085 
1086  updateSummaryText();
1087 }
1088 
1089 void QgsModelChildDependenciesWidget::showDialog()
1090 {
1091  const QList<QgsProcessingModelChildDependency> available = mModel->availableDependenciesForChildAlgorithm( mChildId );
1092 
1093  QVariantList availableOptions;
1094  for ( const QgsProcessingModelChildDependency &dep : available )
1095  availableOptions << QVariant::fromValue( dep );
1096  QVariantList selectedOptions;
1097  for ( const QgsProcessingModelChildDependency &dep : mValue )
1098  selectedOptions << QVariant::fromValue( dep );
1099 
1101  if ( panel )
1102  {
1103  QgsProcessingMultipleSelectionPanelWidget *widget = new QgsProcessingMultipleSelectionPanelWidget( availableOptions, selectedOptions );
1104  widget->setPanelTitle( tr( "Algorithm Dependencies" ) );
1105 
1106  widget->setValueFormatter( [ = ]( const QVariant & v ) -> QString
1107  {
1108  const QgsProcessingModelChildDependency dep = v.value< QgsProcessingModelChildDependency >();
1109 
1110  const QString description = mModel->childAlgorithm( dep.childId ).description();
1111  if ( dep.conditionalBranch.isEmpty() )
1112  return description;
1113  else
1114  return tr( "Condition “%1” from algorithm “%2”" ).arg( dep.conditionalBranch, description );
1115  } );
1116 
1117  connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged, this, [ = ]()
1118  {
1119  QList< QgsProcessingModelChildDependency > res;
1120  for ( const QVariant &v : widget->selectedOptions() )
1121  {
1122  res << v.value< QgsProcessingModelChildDependency >();
1123  }
1124  setValue( res );
1125  } );
1126  connect( widget, &QgsProcessingMultipleSelectionPanelWidget::acceptClicked, widget, &QgsPanelWidget::acceptPanel );
1127  panel->openPanel( widget );
1128  }
1129 }
1130 
1131 void QgsModelChildDependenciesWidget::updateSummaryText()
1132 {
1133  mLineEdit->setText( tr( "%n dependencies selected", nullptr, mValue.count() ) );
1134 }
1135 
QgsExpressionContext
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
Definition: qgsexpressioncontext.h:406
QgsApplication::processingRegistry
static QgsProcessingRegistry * processingRegistry()
Returns the application's processing registry, used for managing processing providers,...
Definition: qgsapplication.cpp:2455
qgsprocessingparametertype.h
QgsSettings::remove
void remove(const QString &key, QgsSettings::Section section=QgsSettings::NoSection)
Removes the setting key and any sub-settings of key in a section.
Definition: qgssettings.cpp:192
qgsprocessinghelpeditorwidget.h
QgsModelViewToolSelect
Model designer view tool for selecting items in the model.
Definition: qgsmodelviewtoolselect.h:36
qgsmodelviewtoolselect.h
qgsmessagebaritem.h
QgsSettings::value
QVariant value(const QString &key, const QVariant &defaultValue=QVariant(), Section section=NoSection) const
Returns the value for setting key.
Definition: qgssettings.cpp:161
QgsSettings::App
@ App
Definition: qgssettings.h:75
QgsPanelWidget::findParentPanel
static QgsPanelWidget * findParentPanel(QWidget *widget)
Traces through the parents of a widget to find if it is contained within a QgsPanelWidget widget.
Definition: qgspanelwidget.cpp:54
QgsModelViewToolPan
Model designer view tool for panning a model.
Definition: qgsmodelviewtoolpan.h:30
qgsgui.h
QgsPanelWidget::openPanel
void openPanel(QgsPanelWidget *panel)
Open a panel or dialog depending on dock mode setting If dock mode is true this method will emit the ...
Definition: qgspanelwidget.cpp:84
QgsProcessingRegistry::parameterTypes
QList< QgsProcessingParameterType * > parameterTypes() const
Returns a list with all known parameter types.
Definition: qgsprocessingregistry.cpp:232
qgsprocessingmodelalgorithm.h
qgsmodelviewtoolpan.h
qgsprocessingmodelgroupbox.h
QgsSettings
This class is a composition of two QSettings instances:
Definition: qgssettings.h:61
QgsProcessingParameterType::ExposeToModeler
@ ExposeToModeler
Is this parameter available in the modeler. Is set to on by default.
Definition: qgsprocessingparametertype.h:69
QgsVariableEditorWidget::scopeChanged
void scopeChanged()
Emitted when the user has modified a scope using the widget.
QgsProcessingParameterType
Makes metadata of processing parameters available.
Definition: qgsprocessingparametertype.h:33
qgsapplication.h
QgsGui::enableAutoGeometryRestore
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...
Definition: qgsgui.cpp:180
qgsmodelinputreorderwidget.h
QgsProcessing::PythonQgsProcessingAlgorithmSubclass
@ PythonQgsProcessingAlgorithmSubclass
Full Python QgsProcessingAlgorithm subclass.
Definition: qgsprocessing.h:64
QgsPanelWidget
Base class for any widget that can be shown as a inline panel.
Definition: qgspanelwidget.h:29
QgsProcessingToolboxProxyModel
A sort/filter proxy model for providers and algorithms shown within the Processing toolbox,...
Definition: qgsprocessingtoolboxmodel.h:417
QgsProcessingToolboxProxyModel::FilterModeler
@ FilterModeler
Filters out any algorithms and content which should not be shown in the modeler.
Definition: qgsprocessingtoolboxmodel.h:427
QgsMessageBarItem
Represents an item shown within a QgsMessageBar widget.
Definition: qgsmessagebaritem.h:38
qgsprocessingalgorithm.h
QgsPanelWidget::acceptPanel
void acceptPanel()
Accept the panel.
Definition: qgspanelwidget.cpp:112
qgsmessageviewer.h
qgsmodelundocommand.h
QgsMessageBar
A bar for displaying non-blocking messages to the user.
Definition: qgsmessagebar.h:60
QgsMessageLog::logMessage
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).
Definition: qgsmessagelog.cpp:27
qgsmodeldesignerdialog.h
qgsmessagebar.h
QgsSettings::setValue
void setValue(const QString &key, const QVariant &value, QgsSettings::Section section=QgsSettings::NoSection)
Sets the value of setting key to value.
Definition: qgssettings.cpp:279
QgsProcessingToolboxProxyModel::FilterShowKnownIssues
@ FilterShowKnownIssues
Show algorithms with known issues (hidden by default)
Definition: qgsprocessingtoolboxmodel.h:429
qgsmodelcomponentgraphicitem.h
qgsfileutils.h
qgsmodelgraphicsscene.h
QgsExpressionContext::appendScope
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
Definition: qgsexpressioncontext.cpp:494
QgsFileUtils::ensureFileNameHasExtension
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
Definition: qgsfileutils.cpp:111
qgssettings.h
QgsDockWidget
QgsDockWidget subclass with more fine-grained control over how the widget is closed or opened.
Definition: qgsdockwidget.h:31
QgsMessageOutput::MessageHtml
@ MessageHtml
Definition: qgsmessageoutput.h:48
QgsApplication::getThemeIcon
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Definition: qgsapplication.cpp:693
QgsMessageBar::createMessage
static QgsMessageBarItem * createMessage(const QString &text, QWidget *parent=nullptr)
Creates message bar item widget containing a message text to be displayed on the bar.
Definition: qgsmessagebar.cpp:389
qgsprocessingmultipleselectiondialog.h
qgspanelwidget.h
qgsprocessingregistry.h