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