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