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