50#include <QActionGroup>
53#include <QKeySequence>
59#include <QSvgGenerator>
66#include "moc_qgsmodeldesignerdialog.cpp"
68using namespace Qt::StringLiterals;
73QgsModelerToolboxModel::QgsModelerToolboxModel( QObject *parent )
77Qt::ItemFlags QgsModelerToolboxModel::flags(
const QModelIndex &index )
const
79 Qt::ItemFlags f = QgsProcessingToolboxProxyModel::flags( index );
80 const QModelIndex sourceIndex = mapToSource( index );
81 if ( toolboxModel()->isAlgorithm( sourceIndex ) || toolboxModel()->isParameter( sourceIndex ) )
83 f = f | Qt::ItemIsDragEnabled;
88Qt::DropActions QgsModelerToolboxModel::supportedDragActions()
const
90 return Qt::CopyAction;
93QgsModelDesignerDialog::QgsModelDesignerDialog( QWidget *parent, Qt::WindowFlags flags )
94 : QMainWindow( parent, flags )
95 , mToolsActionGroup( new QActionGroup( this ) )
103 setAttribute( Qt::WA_DeleteOnClose );
104 setDockOptions( dockOptions() | QMainWindow::GroupedDragging );
105 setWindowFlags( Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint );
109 mModel = std::make_unique<QgsProcessingModelAlgorithm>();
112 mUndoStack =
new QUndoStack(
this );
113 connect( mUndoStack, &QUndoStack::indexChanged,
this, [
this] {
114 if ( mIgnoreUndoStackChanges )
117 mBlockUndoCommands++;
118 updateVariablesGui();
119 mGroupEdit->setText( mModel->group() );
120 mNameEdit->setText( mModel->displayName() );
121 mBlockUndoCommands--;
126 mConfigWidgetDock->setWindowTitle( tr(
"Configuration" ) );
127 mConfigWidgetDock->setObjectName( u
"ModelConfigDock"_s );
130 mConfigWidgetDock->setWidget( mConfigWidget );
131 mConfigWidgetDock->setFeatures( QDockWidget::NoDockWidgetFeatures );
134 mPropertiesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
135 mInputsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
136 mAlgorithmsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
137 mVariablesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
139 mToolboxTree->header()->setVisible(
false );
140 mToolboxSearchEdit->setShowSearchIcon(
true );
141 mToolboxSearchEdit->setPlaceholderText( tr(
"Search…" ) );
142 connect( mToolboxSearchEdit, &QgsFilterLineEdit::textChanged, mToolboxTree, &QgsProcessingToolboxTreeView::setFilterString );
144 mInputsTreeWidget->header()->setVisible(
false );
145 mInputsTreeWidget->setAlternatingRowColors(
true );
146 mInputsTreeWidget->setDragDropMode( QTreeWidget::DragOnly );
147 mInputsTreeWidget->setDropIndicatorShown(
true );
149 mNameEdit->setPlaceholderText( tr(
"Enter model name here" ) );
150 mGroupEdit->setPlaceholderText( tr(
"Enter group name here" ) );
153 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
154 mainLayout->insertWidget( 0, mMessageBar );
156 mView->setAcceptDrops(
true );
159 connect( mActionClose, &QAction::triggered,
this, &QWidget::close );
160 connect( mActionNew, &QAction::triggered,
this, &QgsModelDesignerDialog::newModel );
161 connect( mActionZoomIn, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomIn );
162 connect( mActionZoomOut, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomOut );
163 connect( mActionZoomActual, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomActual );
164 connect( mActionZoomToItems, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomFull );
165 connect( mActionExportImage, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToImage );
166 connect( mActionExportPdf, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToPdf );
167 connect( mActionExportSvg, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToSvg );
168 connect( mActionExportPython, &QAction::triggered,
this, &QgsModelDesignerDialog::exportAsPython );
169 connect( mActionSave, &QAction::triggered,
this, [
this] { saveModel(
false ); } );
170 connect( mActionSaveAs, &QAction::triggered,
this, [
this] { saveModel(
true ); } );
171 connect( mActionDeleteComponents, &QAction::triggered,
this, &QgsModelDesignerDialog::deleteSelected );
172 connect( mActionSnapSelected, &QAction::triggered, mView, &QgsModelGraphicsView::snapSelected );
173 connect( mActionValidate, &QAction::triggered,
this, &QgsModelDesignerDialog::validate );
174 connect( mActionReorderInputs, &QAction::triggered,
this, &QgsModelDesignerDialog::reorderInputs );
175 connect( mActionReorderOutputs, &QAction::triggered,
this, &QgsModelDesignerDialog::reorderOutputs );
176 connect( mActionEditHelp, &QAction::triggered,
this, &QgsModelDesignerDialog::editHelp );
177 connect( mReorderInputsButton, &QPushButton::clicked,
this, &QgsModelDesignerDialog::reorderInputs );
178 connect( mActionRun, &QAction::triggered,
this, [
this] { run(); } );
179 connect( mActionRunSelectedSteps, &QAction::triggered,
this, &QgsModelDesignerDialog::runSelectedSteps );
181 mActionSnappingEnabled->setChecked( settings.
value( u
"/Processing/Modeler/enableSnapToGrid"_s,
false ).toBool() );
182 connect( mActionSnappingEnabled, &QAction::toggled,
this, [
this](
bool enabled ) {
183 mView->snapper()->setSnapToGrid( enabled );
186 mView->snapper()->setSnapToGrid( mActionSnappingEnabled->isChecked() );
188 connect( mView, &QgsModelGraphicsView::itemFocused,
this, &QgsModelDesignerDialog::onItemFocused );
190 connect( mActionSelectAll, &QAction::triggered,
this, [
this] { mScene->selectAll(); } );
192 QStringList docksTitle = settings.
value( u
"ModelDesigner/hiddenDocksTitle"_s, QStringList(),
QgsSettings::App ).toStringList();
193 QStringList docksActive = settings.
value( u
"ModelDesigner/hiddenDocksActive"_s, QStringList(),
QgsSettings::App ).toStringList();
194 if ( !docksTitle.isEmpty() )
196 for (
const auto &title : docksTitle )
198 mPanelStatus.insert( title, PanelStatus(
true, docksActive.contains( title ) ) );
201 mActionHidePanels->setChecked( !docksTitle.isEmpty() );
202 connect( mActionHidePanels, &QAction::toggled,
this, &QgsModelDesignerDialog::setPanelVisibility );
204 mUndoAction = mUndoStack->createUndoAction(
this );
206 mUndoAction->setShortcuts( QKeySequence::Undo );
207 mRedoAction = mUndoStack->createRedoAction(
this );
209 mRedoAction->setShortcuts( QKeySequence::Redo );
211 mMenuEdit->insertAction( mActionDeleteComponents, mRedoAction );
212 mMenuEdit->insertAction( mActionDeleteComponents, mUndoAction );
213 mMenuEdit->insertSeparator( mActionDeleteComponents );
214 mToolbar->insertAction( mActionZoomIn, mUndoAction );
215 mToolbar->insertAction( mActionZoomIn, mRedoAction );
216 mToolbar->insertSeparator( mActionZoomIn );
218 mGroupMenu =
new QMenu( tr(
"Zoom To" ),
this );
219 mMenuView->insertMenu( mActionZoomIn, mGroupMenu );
220 connect( mGroupMenu, &QMenu::aboutToShow,
this, &QgsModelDesignerDialog::populateZoomToMenu );
224 mActionCut =
new QAction( tr(
"Cu&t" ),
this );
225 mActionCut->setShortcuts( QKeySequence::Cut );
226 mActionCut->setStatusTip( tr(
"Cut" ) );
228 connect( mActionCut, &QAction::triggered,
this, [
this] { mView->copySelectedItems( QgsModelGraphicsView::ClipboardCut ); } );
230 mActionCopy =
new QAction( tr(
"&Copy" ),
this );
231 mActionCopy->setShortcuts( QKeySequence::Copy );
232 mActionCopy->setStatusTip( tr(
"Copy" ) );
234 connect( mActionCopy, &QAction::triggered,
this, [
this] { mView->copySelectedItems( QgsModelGraphicsView::ClipboardCopy ); } );
236 mActionPaste =
new QAction( tr(
"&Paste" ),
this );
237 mActionPaste->setShortcuts( QKeySequence::Paste );
238 mActionPaste->setStatusTip( tr(
"Paste" ) );
240 connect( mActionPaste, &QAction::triggered,
this, [
this] { mView->pasteItems( QgsModelGraphicsView::PasteModeCursor ); } );
241 mMenuEdit->insertAction( mActionDeleteComponents, mActionCut );
242 mMenuEdit->insertAction( mActionDeleteComponents, mActionCopy );
243 mMenuEdit->insertAction( mActionDeleteComponents, mActionPaste );
244 mMenuEdit->insertSeparator( mActionDeleteComponents );
246 mAlgorithmsModel =
new QgsModelerToolboxModel(
this );
247 mToolboxTree->setToolboxProxyModel( mAlgorithmsModel );
250 if ( settings.
value( u
"Processing/Configuration/SHOW_ALGORITHMS_KNOWN_ISSUES"_s,
false ).toBool() )
254 mToolboxTree->setFilters( filters );
255 mToolboxTree->setDragDropMode( QTreeWidget::DragOnly );
256 mToolboxTree->setDropIndicatorShown(
true );
258 connect( mView, &QgsModelGraphicsView::algorithmDropped,
this, [
this](
const QString &algorithmId,
const QPointF &pos ) { addAlgorithm( algorithmId, pos ); } );
259 connect( mView, &QgsModelGraphicsView::inputDropped,
this, &QgsModelDesignerDialog::addInput );
261 connect( mToolboxTree, &QgsProcessingToolboxTreeView::doubleClicked,
this, [
this](
const QModelIndex & ) {
262 if ( mToolboxTree->selectedAlgorithm() )
263 addAlgorithm( mToolboxTree->selectedAlgorithm()->id(), QPointF() );
264 if ( mToolboxTree->selectedParameterType() )
265 addInput( mToolboxTree->selectedParameterType()->id(), QPointF() );
268 connect( mInputsTreeWidget, &QgsModelDesignerInputsTreeWidget::doubleClicked,
this, [
this](
const QModelIndex & ) {
269 const QString parameterType = mInputsTreeWidget->currentItem()->data( 0, Qt::UserRole ).toString();
270 addInput( parameterType, QPointF() );
274 QShortcut *ctrlEquals =
new QShortcut( QKeySequence( u
"Ctrl+="_s ),
this );
275 connect( ctrlEquals, &QShortcut::activated,
this, &QgsModelDesignerDialog::zoomIn );
278 mUndoDock->setObjectName( u
"UndoDock"_s );
279 mUndoView =
new QUndoView( mUndoStack,
this );
280 mUndoDock->setWidget( mUndoView );
281 mUndoDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
282 addDockWidget( Qt::DockWidgetArea::LeftDockWidgetArea, mUndoDock );
284 tabifyDockWidget( mUndoDock, mPropertiesDock );
285 tabifyDockWidget( mVariablesDock, mPropertiesDock );
286 mPropertiesDock->raise();
287 tabifyDockWidget( mInputsDock, mAlgorithmsDock );
288 mInputsDock->raise();
293 beginUndoCommand( tr(
"Change Model Variables" ) );
294 mModel->setVariables( mVariablesEditor->variablesInActiveScope() );
298 connect( mNameEdit, &QLineEdit::textChanged,
this, [
this](
const QString &name ) {
301 beginUndoCommand( tr(
"Change Model Name" ), QString(), QgsModelUndoCommand::CommandOperation::NameChanged );
302 mModel->setName( name );
307 connect( mGroupEdit, &QLineEdit::textChanged,
this, [
this](
const QString &group ) {
310 beginUndoCommand( tr(
"Change Model Group" ), QString(), QgsModelUndoCommand::CommandOperation::GroupChanged );
311 mModel->setGroup( group );
319 QToolButton *toolbuttonExportToScript =
new QToolButton();
320 toolbuttonExportToScript->setPopupMode( QToolButton::InstantPopup );
321 toolbuttonExportToScript->addAction( mActionExportAsScriptAlgorithm );
322 toolbuttonExportToScript->setDefaultAction( mActionExportAsScriptAlgorithm );
323 mToolbar->insertWidget( mActionExportImage, toolbuttonExportToScript );
324 connect( mActionExportAsScriptAlgorithm, &QAction::triggered,
this, &QgsModelDesignerDialog::exportAsScriptAlgorithm );
326 mActionShowComments->setChecked( settings.
value( u
"/Processing/Modeler/ShowComments"_s,
true ).toBool() );
327 connect( mActionShowComments, &QAction::toggled,
this, &QgsModelDesignerDialog::toggleComments );
329 mActionShowFeatureCount->setChecked( settings.
value( u
"/Processing/Modeler/ShowFeatureCount"_s,
true ).toBool() );
330 connect( mActionShowFeatureCount, &QAction::toggled,
this, &QgsModelDesignerDialog::toggleFeatureCount );
333 mPanTool->setAction( mActionPan );
335 mToolsActionGroup->addAction( mActionPan );
336 connect( mActionPan, &QAction::triggered, mPanTool, [
this] { mView->setTool( mPanTool ); } );
341 mSelectTool->setAction( mActionSelectMoveItem );
343 mToolsActionGroup->addAction( mActionSelectMoveItem );
344 connect( mActionSelectMoveItem, &QAction::triggered, mSelectTool, [
this] { mView->setTool( mSelectTool ); } );
346 mView->setTool( mSelectTool );
349 connect( mView, &QgsModelGraphicsView::macroCommandStarted,
this, [
this](
const QString &text ) {
350 mIgnoreUndoStackChanges++;
351 mUndoStack->beginMacro( text );
352 mIgnoreUndoStackChanges--;
354 connect( mView, &QgsModelGraphicsView::macroCommandEnded,
this, [
this] {
355 mIgnoreUndoStackChanges++;
356 mUndoStack->endMacro();
357 mIgnoreUndoStackChanges--;
359 connect( mView, &QgsModelGraphicsView::commandBegun,
this, [
this](
const QString &text ) { beginUndoCommand( text ); } );
360 connect( mView, &QgsModelGraphicsView::commandEnded,
this, [
this] { endUndoCommand(); } );
361 connect( mView, &QgsModelGraphicsView::commandAborted,
this, [
this] { abortUndoCommand(); } );
362 connect( mView, &QgsModelGraphicsView::deleteSelectedItems,
this, [
this] { deleteSelected(); } );
364 connect( mActionAddGroupBox, &QAction::triggered,
this, [
this] {
365 const QPointF viewCenter = mView->mapToScene( mView->viewport()->rect().center() );
366 QgsProcessingModelGroupBox group;
367 group.setPosition( viewCenter );
368 group.setDescription( tr(
"New Group" ) );
370 beginUndoCommand( tr(
"Add Group Box" ) );
371 model()->addGroupBox( group );
379 restoreState( settings.
value( u
"ModelDesigner/state"_s, QByteArray(),
QgsSettings::App ).toByteArray() );
382QgsModelDesignerDialog::~QgsModelDesignerDialog()
384 if ( mAlgorithmWidget )
386 delete mAlgorithmWidget;
388 for (
const QPointer<QgsProcessingAlgorithmWidgetBase> &widget : std::as_const( mAlgorithmWidgetsToCleanUp ) )
401 if ( !mPanelStatus.isEmpty() )
403 QStringList docksTitle;
404 QStringList docksActive;
406 for (
const auto &panel : mPanelStatus.toStdMap() )
408 if ( panel.second.isVisible )
409 docksTitle << panel.first;
410 if ( panel.second.isActive )
411 docksActive << panel.first;
425 mIgnoreUndoStackChanges++;
428void QgsModelDesignerDialog::closeEvent( QCloseEvent *event )
430 if ( checkForUnsavedChanges() )
436void QgsModelDesignerDialog::beginUndoCommand(
const QString &text,
const QString &
id, QgsModelUndoCommand::CommandOperation operation )
438 if ( mBlockUndoCommands || !mUndoStack )
441 if ( mActiveCommand )
446 mActiveCommand = std::make_unique<QgsModelUndoCommand>( mModel.get(), text,
id );
450 mActiveCommand = std::make_unique<QgsModelUndoCommand>( mModel.get(), text, operation );
454void QgsModelDesignerDialog::endUndoCommand()
456 if ( mBlockUndoCommands || !mActiveCommand || !mUndoStack )
459 mActiveCommand->saveAfterState();
460 mIgnoreUndoStackChanges++;
461 mUndoStack->push( mActiveCommand.release() );
462 mIgnoreUndoStackChanges--;
466void QgsModelDesignerDialog::abortUndoCommand()
468 if ( mActiveCommand )
469 mActiveCommand->setObsolete(
true );
472QgsProcessingModelAlgorithm *QgsModelDesignerDialog::model()
477void QgsModelDesignerDialog::setModel( QgsProcessingModelAlgorithm *model )
479 mModel.reset( model );
481 mGroupEdit->setText( mModel->group() );
482 mNameEdit->setText( mModel->displayName() );
483 repaintModel(
true );
484 updateVariablesGui();
488 mIgnoreUndoStackChanges++;
490 mIgnoreUndoStackChanges--;
496 QTimer::singleShot( 100,
this, [
this] { zoomFull(); } );
499void QgsModelDesignerDialog::loadModel(
const QString &path )
501 auto alg = std::make_unique<QgsProcessingModelAlgorithm>();
502 if ( alg->fromFile( path ) )
505 alg->setSourceFilePath( path );
506 setModel( alg.release() );
511 QMessageBox::critical(
515 "The selected model could not be loaded.\n"
516 "See the log for more information."
522void QgsModelDesignerDialog::setModelScene( QgsModelGraphicsScene *scene )
524 QgsModelGraphicsScene *oldScene = mScene;
527 mScene->setParent(
this );
528 mScene->setLastRunResult( mLastResult, mLayerStore );
529 mScene->setModel( mModel.get() );
530 mScene->setMessageBar( mMessageBar );
531 mScene->registerWidgetContextGenerator(
this );
534 const bool showFeatureCount = settings.
value( u
"/Processing/Modeler/ShowFeatureCount"_s,
true ).toBool();
535 if ( !showFeatureCount )
536 mScene->setFlag( QgsModelGraphicsScene::FlagHideFeatureCount );
538 mView->setModelScene( mScene );
540 mSelectTool->resetCache();
541 mSelectTool->setScene( mScene );
543 connect( mScene, &QgsModelGraphicsScene::rebuildRequired,
this, [
this] {
544 if ( mBlockRepaints )
548 mScene->flagChildrenAsOutdated( mOutdatedChildResults );
550 connect( mScene, &QgsModelGraphicsScene::componentAboutToChange,
this, [
this](
const QString &description,
const QString &
id ) { beginUndoCommand( description,
id ); } );
551 connect( mScene, &QgsModelGraphicsScene::componentChanged,
this, [
this] { endUndoCommand(); } );
552 connect( mScene, &QgsModelGraphicsScene::runFromChild,
this, &QgsModelDesignerDialog::runFromChild );
553 connect( mScene, &QgsModelGraphicsScene::runSelected,
this, &QgsModelDesignerDialog::runSelectedSteps );
554 connect( mScene, &QgsModelGraphicsScene::showChildAlgorithmOutputs,
this, &QgsModelDesignerDialog::showChildAlgorithmOutputs );
555 connect( mScene, &QgsModelGraphicsScene::showChildAlgorithmLog,
this, &QgsModelDesignerDialog::showChildAlgorithmLog );
558 oldScene->deleteLater();
561QgsModelGraphicsScene *QgsModelDesignerDialog::modelScene()
568 auto result = std::make_unique< QgsProcessingModelFeedback >();
569 mScene->setupFeedbackConnections( result.get() );
571 mOutdatedChildResults.remove( childId );
574 return result.release();
586void QgsModelDesignerDialog::activate()
590 setWindowState( windowState() & ~Qt::WindowMinimized );
596 mProcessingContextGenerator = generator;
601 if ( mProcessingContextGenerator )
603 return mProcessingContextGenerator->processingContext();
608void QgsModelDesignerDialog::updateVariablesGui()
610 mBlockUndoCommands++;
612 auto variablesScope = std::make_unique<QgsExpressionContextScope>( tr(
"Model Variables" ) );
613 const QVariantMap modelVars = mModel->variables();
614 for (
auto it = modelVars.constBegin(); it != modelVars.constEnd(); ++it )
616 variablesScope->setVariable( it.key(), it.value() );
619 variablesContext.
appendScope( variablesScope.release() );
620 mVariablesEditor->setContext( &variablesContext );
621 mVariablesEditor->setEditableScopeIndex( 0 );
623 mBlockUndoCommands--;
626void QgsModelDesignerDialog::setDirty(
bool dirty )
630 if ( mAlgorithmWidget )
632 if (
QgsMessageBar *messageBar = mAlgorithmWidget->messageBar() )
634 QgsMessageBarItem *messageBarItem = messageBar->createMessage( QString(), tr(
"The model has changed, this panel should be reloaded." ) );
635 auto reloadButton =
new QPushButton( tr(
"Reload Now" ) );
636 connect( reloadButton, &QPushButton::clicked, reloadButton, [
this] {
637 if ( mAlgorithmWidget && mAlgorithmWidget->isRunning() )
639 QMessageBox messageBox;
640 messageBox.setIcon( QMessageBox::Icon::Warning );
641 messageBox.setWindowTitle( tr(
"Run Model" ) );
642 messageBox.setText( tr(
"This model is currently running." ) );
643 messageBox.setStandardButtons( QMessageBox::StandardButton::Cancel | QMessageBox::StandardButton::RestoreDefaults );
645 QAbstractButton *buttonReRun = messageBox.button( QMessageBox::StandardButton::RestoreDefaults );
646 buttonReRun->setText( tr(
"Terminate and Reload" ) );
648 int r = messageBox.exec();
652 case QMessageBox::StandardButton::Cancel:
654 case QMessageBox::StandardButton::RestoreDefaults:
660 cancelRunningModel();
663 messageBarItem->layout()->addWidget( reloadButton );
669bool QgsModelDesignerDialog::validateSave( SaveAction action )
673 case QgsModelDesignerDialog::SaveAction::SaveAsFile:
675 case QgsModelDesignerDialog::SaveAction::SaveInProject:
676 if ( mNameEdit->text().trimmed().isEmpty() )
678 mMessageBar->pushWarning( QString(), tr(
"Please enter a model name before saving" ) );
687bool QgsModelDesignerDialog::checkForUnsavedChanges()
691 QMessageBox::StandardButton ret = QMessageBox::
692 question(
this, tr(
"Save Model?" ), tr(
"There are unsaved changes in this model. Do you want to keep those?" ), QMessageBox::Save | QMessageBox::Cancel | QMessageBox::Discard, QMessageBox::Cancel );
695 case QMessageBox::Save:
696 return saveModel(
false );
698 case QMessageBox::Discard:
713 mLastResult.mergeWith( result );
715 mScene->setLastRunResult( mLastResult, mLayerStore );
718void QgsModelDesignerDialog::setModelName(
const QString &name )
720 mNameEdit->setText( name );
723void QgsModelDesignerDialog::zoomIn()
725 mView->setTransformationAnchor( QGraphicsView::NoAnchor );
726 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
728 const double factor = settings.
value( u
"/qgis/zoom_favor"_s, 2.0 ).toDouble();
729 mView->scale( factor, factor );
730 mView->centerOn( point );
733void QgsModelDesignerDialog::zoomOut()
735 mView->setTransformationAnchor( QGraphicsView::NoAnchor );
736 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
738 const double factor = 1.0 / settings.
value( u
"/qgis/zoom_favor"_s, 2.0 ).toDouble();
739 mView->scale( factor, factor );
740 mView->centerOn( point );
743void QgsModelDesignerDialog::zoomActual()
745 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
746 mView->resetTransform();
747 mView->scale( mScreenHelper->screenDpi() / 96, mScreenHelper->screenDpi() / 96 );
748 mView->centerOn( point );
751void QgsModelDesignerDialog::zoomFull()
753 QRectF totalRect = mView->scene()->itemsBoundingRect();
754 totalRect.adjust( -10, -10, 10, 10 );
755 mView->fitInView( totalRect, Qt::KeepAspectRatio );
758void QgsModelDesignerDialog::newModel()
760 if ( !checkForUnsavedChanges() )
763 auto alg = std::make_unique<QgsProcessingModelAlgorithm>();
765 setModel( alg.release() );
768void QgsModelDesignerDialog::exportToImage()
771 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
773 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as Image" ), lastExportDir, tr(
"PNG files (*.png *.PNG)" ) );
777 if ( filename.isEmpty() )
782 const QFileInfo saveFileInfo( filename );
785 repaintModel(
false );
787 QRectF totalRect = mView->scene()->itemsBoundingRect();
788 totalRect.adjust( -10, -10, 10, 10 );
789 const QRectF imageRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
791 QImage img( totalRect.width(), totalRect.height(), QImage::Format_ARGB32_Premultiplied );
792 img.fill( Qt::white );
794 painter.setRenderHint( QPainter::Antialiasing );
795 painter.begin( &img );
796 mView->scene()->render( &painter, imageRect, totalRect );
799 img.save( filename );
802 ->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 );
803 repaintModel(
true );
806void QgsModelDesignerDialog::exportToPdf()
809 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
811 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as PDF" ), lastExportDir, tr(
"PDF files (*.pdf *.PDF)" ) );
815 if ( filename.isEmpty() )
820 const QFileInfo saveFileInfo( filename );
823 repaintModel(
false );
825 QRectF totalRect = mView->scene()->itemsBoundingRect();
826 totalRect.adjust( -10, -10, 10, 10 );
827 const QRectF printerRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
829 QPdfWriter pdfWriter( filename );
831 const double scaleFactor = 96 / 25.4;
833 QPageLayout pageLayout( QPageSize( totalRect.size() / scaleFactor, QPageSize::Millimeter ), QPageLayout::Portrait, QMarginsF( 0, 0, 0, 0 ) );
834 pageLayout.setMode( QPageLayout::FullPageMode );
835 pdfWriter.setPageLayout( pageLayout );
837 QPainter painter( &pdfWriter );
838 mView->scene()->render( &painter, printerRect, totalRect );
842 ->pushMessage( QString(), tr(
"Successfully exported model as PDF to <a href=\"%1\">%2</a>" ).arg( QUrl::fromLocalFile( filename ).toString(), QDir::toNativeSeparators( filename ) ),
Qgis::MessageLevel::Success, 0 );
843 repaintModel(
true );
846void QgsModelDesignerDialog::exportToSvg()
849 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
851 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as SVG" ), lastExportDir, tr(
"SVG files (*.svg *.SVG)" ) );
855 if ( filename.isEmpty() )
860 const QFileInfo saveFileInfo( filename );
863 repaintModel(
false );
865 QRectF totalRect = mView->scene()->itemsBoundingRect();
866 totalRect.adjust( -10, -10, 10, 10 );
867 const QRectF svgRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
870 svg.setFileName( filename );
871 svg.setSize( QSize( totalRect.width(), totalRect.height() ) );
872 svg.setViewBox( svgRect );
873 svg.setTitle( mModel->displayName() );
875 QPainter painter( &svg );
876 mView->scene()->render( &painter, svgRect, totalRect );
880 ->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 );
881 repaintModel(
true );
884void QgsModelDesignerDialog::exportAsPython()
887 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
889 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as Python Script" ), lastExportDir, tr(
"Processing scripts (*.py *.PY)" ) );
893 if ( filename.isEmpty() )
898 const QFileInfo saveFileInfo( filename );
903 QFile outFile( filename );
904 if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
908 QTextStream fout( &outFile );
913 ->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 );
916void QgsModelDesignerDialog::toggleComments(
bool show )
920 repaintModel(
true );
923void QgsModelDesignerDialog::toggleFeatureCount(
bool show )
927 repaintModel(
true );
930void QgsModelDesignerDialog::updateWindowTitle()
932 QString title = tr(
"Model Designer" );
933 if ( !mModel->name().isEmpty() )
934 title = mModel->group().isEmpty() ? u
"%1: %2"_s.arg( title, mModel->name() ) : u
"%1: %2 - %3"_s.arg( title, mModel->group(), mModel->name() );
937 title.prepend(
'*' );
939 setWindowTitle( title );
942void QgsModelDesignerDialog::deleteSelected()
944 QList<QgsModelComponentGraphicItem *> items = mScene->selectedComponentItems();
948 if ( items.size() == 1 )
950 items.at( 0 )->deleteComponent();
954 std::sort( items.begin(), items.end(), []( QgsModelComponentGraphicItem *p1, QgsModelComponentGraphicItem *p2 ) {
959 if ( dynamic_cast<QgsModelCommentGraphicItem *>( p1 ) && dynamic_cast<QgsModelCommentGraphicItem *>( p2 ) )
961 else if ( dynamic_cast<QgsModelCommentGraphicItem *>( p1 ) )
963 else if ( dynamic_cast<QgsModelCommentGraphicItem *>( p2 ) )
966 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p1 ) && dynamic_cast<QgsModelGroupBoxGraphicItem *>( p2 ) )
968 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p1 ) )
970 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p2 ) )
973 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p1 ) && dynamic_cast<QgsModelOutputGraphicItem *>( p2 ) )
975 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p1 ) )
977 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p2 ) )
980 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p1 ) && dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p2 ) )
982 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p1 ) )
984 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p2 ) )
991 beginUndoCommand( tr(
"Delete Components" ) );
993 QVariant prevState = mModel->toVariant();
994 mBlockUndoCommands++;
995 mBlockRepaints =
true;
997 while ( !items.empty() )
999 QgsModelComponentGraphicItem *toDelete =
nullptr;
1000 for ( QgsModelComponentGraphicItem *item : items )
1002 if ( item->canDeleteComponent() )
1015 toDelete->deleteComponent();
1016 items.removeAll( toDelete );
1021 mModel->loadVariant( prevState );
1022 QMessageBox::warning(
1024 QObject::tr(
"Could not remove components" ),
1026 "Components depend on the selected items.\n"
1027 "Try to remove them before trying deleting these components."
1030 mBlockUndoCommands--;
1031 mActiveCommand.reset();
1035 mBlockUndoCommands--;
1039 mBlockRepaints =
false;
1043void QgsModelDesignerDialog::populateZoomToMenu()
1045 mGroupMenu->clear();
1046 for (
const QgsProcessingModelGroupBox &box : model()->groupBoxes() )
1048 if ( QgsModelComponentGraphicItem *item = mScene->groupBoxItem( box.uuid() ) )
1050 QAction *zoomAction =
new QAction( box.description(), mGroupMenu );
1051 connect( zoomAction, &QAction::triggered,
this, [
this, item] {
1052 QRectF groupRect = item->mapToScene( item->boundingRect() ).boundingRect();
1053 groupRect.adjust( -10, -10, 10, 10 );
1054 mView->fitInView( groupRect, Qt::KeepAspectRatio );
1055 mView->centerOn( item );
1057 mGroupMenu->addAction( zoomAction );
1062void QgsModelDesignerDialog::setPanelVisibility(
bool hidden )
1064 const QList<QDockWidget *> docks = findChildren<QDockWidget *>();
1065 const QList<QTabBar *> tabBars = findChildren<QTabBar *>();
1069 mPanelStatus.clear();
1071 for ( QDockWidget *dock : docks )
1073 mPanelStatus.insert( dock->windowTitle(), PanelStatus( dock->isVisible(),
false ) );
1074 dock->setVisible(
false );
1078 for ( QTabBar *tabBar : tabBars )
1080 QString currentTabTitle = tabBar->tabText( tabBar->currentIndex() );
1081 mPanelStatus[currentTabTitle].isActive =
true;
1087 for ( QDockWidget *dock : docks )
1089 if ( mPanelStatus.contains( dock->windowTitle() ) )
1091 dock->setVisible( mPanelStatus.value( dock->windowTitle() ).isVisible );
1096 for ( QTabBar *tabBar : tabBars )
1099 for (
int i = 0; i < tabBar->count(); ++i )
1101 QString tabTitle = tabBar->tabText( i );
1102 if ( mPanelStatus.contains( tabTitle ) && mPanelStatus.value( tabTitle ).isActive )
1104 tabBar->setCurrentIndex( i );
1108 mPanelStatus.clear();
1112void QgsModelDesignerDialog::editHelp()
1114 QgsProcessingHelpEditorDialog dialog(
this );
1115 dialog.setWindowTitle( tr(
"Edit Model Help" ) );
1116 dialog.setAlgorithm( mModel.get() );
1117 if ( dialog.exec() )
1119 beginUndoCommand( tr(
"Edit Model Help" ) );
1120 mModel->setHelpContent( dialog.helpContent() );
1125void QgsModelDesignerDialog::runSelectedSteps()
1127 QSet<QString> children;
1128 const QList<QgsModelComponentGraphicItem *> items = mScene->selectedComponentItems();
1129 for ( QgsModelComponentGraphicItem *item : items )
1131 if ( QgsProcessingModelChildAlgorithm *childAlgorithm =
dynamic_cast<QgsProcessingModelChildAlgorithm *
>( item->component() ) )
1133 children.insert( childAlgorithm->childId() );
1137 if ( children.isEmpty() )
1139 mMessageBar->pushWarning( QString(), tr(
"No steps are selected" ) );
1146void QgsModelDesignerDialog::runFromChild(
const QString &
id )
1148 QSet<QString> children = mModel->dependentChildAlgorithms(
id );
1149 children.insert(
id );
1153void QgsModelDesignerDialog::cancelRunningModel()
1155 if ( !mAlgorithmWidget )
1161 mAlgorithmWidget->cancel();
1163 mAlgorithmWidget->forceClose();
1166 if ( mAlgorithmWidget )
1172 mAlgorithmWidgetsToCleanUp << mAlgorithmWidget;
1174 mAlgorithmWidget.clear();
1177void QgsModelDesignerDialog::run(
const QSet<QString> &childAlgorithmSubset )
1180 const bool isValid = model()->validate( errors );
1183 QMessageBox messageBox;
1184 messageBox.setWindowTitle( tr(
"Model is Invalid" ) );
1185 messageBox.setIcon( QMessageBox::Icon::Warning );
1186 messageBox.setText( tr(
"This model is not valid and contains one or more issues. Are you sure you want to run it in this state?" ) );
1187 messageBox.setStandardButtons( QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::Cancel );
1188 messageBox.setDefaultButton( QMessageBox::StandardButton::Cancel );
1190 QString errorString;
1191 for (
const QString &error : std::as_const( errors ) )
1193 QString cleanedError = error;
1194 const thread_local QRegularExpression re( u
"<[^>]*>"_s );
1195 cleanedError.replace( re, QString() );
1196 errorString += u
"• %1\n"_s.arg( cleanedError );
1199 messageBox.setDetailedText( errorString );
1200 if ( messageBox.exec() == QMessageBox::StandardButton::Cancel )
1204 if ( !childAlgorithmSubset.isEmpty() )
1206 for (
const QString &child : childAlgorithmSubset )
1209 const QSet<QString> requirements = mModel->dependsOnChildAlgorithms( child );
1210 for (
const QString &requirement : requirements )
1212 if ( !mLastResult.executedChildIds().contains( requirement ) )
1214 QMessageBox messageBox;
1215 messageBox.setWindowTitle( tr(
"Run Model" ) );
1216 messageBox.setIcon( QMessageBox::Icon::Warning );
1217 messageBox.setText( tr(
"Prerequisite parts of this model have not yet been run (try running the full model first)." ) );
1218 messageBox.setStandardButtons( QMessageBox::StandardButton::Ok );
1226 if ( mAlgorithmWidget && mAlgorithmWidget->isRunning() )
1228 QMessageBox messageBox;
1229 messageBox.setIcon( QMessageBox::Icon::Warning );
1230 messageBox.setWindowTitle( tr(
"Run Model" ) );
1231 messageBox.setText( tr(
"This model is already running." ) );
1232 messageBox.setStandardButtons( QMessageBox::StandardButton::Cancel | QMessageBox::StandardButton::RestoreDefaults | QMessageBox::StandardButton::Ok );
1234 QAbstractButton *buttonShowRunningAlg = messageBox.button( QMessageBox::StandardButton::Ok );
1235 buttonShowRunningAlg->setText( tr(
"Show Progress" ) );
1237 QAbstractButton *buttonReRun = messageBox.button( QMessageBox::StandardButton::RestoreDefaults );
1238 buttonReRun->setText( tr(
"Cancel and Restart Model" ) );
1240 int r = messageBox.exec();
1244 case QMessageBox::StandardButton::Cancel:
1246 case QMessageBox::StandardButton::RestoreDefaults:
1247 cancelRunningModel();
1249 case QMessageBox::StandardButton::Ok:
1250 mAlgorithmWidget->showWidget();
1256 else if ( mAlgorithmWidget )
1259 mAlgorithmWidget->close();
1260 if ( mAlgorithmWidget )
1266 mAlgorithmWidgetsToCleanUp << mAlgorithmWidget;
1269 mAlgorithmWidget.clear();
1272 if ( !mAlgorithmWidget )
1274 mAlgorithmWidget = createExecutionWidget();
1275 mAlgorithmWidget->hideShortHelp();
1276 mAlgorithmWidget->setTitle( tr(
"Run Model" ) );
1279 mAlgorithmWidget->setParameters( mModel->designerParameterValues() );
1281 if ( !childAlgorithmSubset.isEmpty() )
1283 mAlgorithmWidget->runButton()->setText( tr(
"Run Subset" ) );
1284 mAlgorithmWidget->runButton()->setToolTip( tr(
"Runs a subset of the child algorithms from this model" ) );
1287 connect( mAlgorithmWidget.get(), &QgsProcessingAlgorithmWidgetBase::algorithmAboutToRun,
this, [
this, childAlgorithmSubset](
QgsProcessingContext *context ) {
1288 if ( !childAlgorithmSubset.empty() )
1291 auto modelConfig = std::make_unique<QgsProcessingModelInitialRunConfig>();
1292 modelConfig->setChildAlgorithmSubset( childAlgorithmSubset );
1293 modelConfig->setPreviouslyExecutedChildAlgorithms( mLastResult.executedChildIds() );
1294 modelConfig->setInitialChildInputs( mLastResult.rawChildInputs() );
1295 modelConfig->setInitialChildOutputs( mLastResult.rawChildOutputs() );
1299 const QMap<QString, QgsMapLayer *> previousOutputLayers = mLayerStore.temporaryLayerStore()->mapLayers();
1300 auto previousResultStore = std::make_unique<QgsMapLayerStore>();
1301 for ( auto it = previousOutputLayers.constBegin(); it != previousOutputLayers.constEnd(); ++it )
1303 std::unique_ptr<QgsMapLayer> clone( it.value()->clone() );
1304 clone->setId( it.value()->id() );
1305 previousResultStore->addMapLayer( clone.release() );
1307 previousResultStore->moveToThread( nullptr );
1308 modelConfig->setPreviousLayerStore( std::move( previousResultStore ) );
1309 context->setModelInitialRunConfig( std::move( modelConfig ) );
1311 mScene->resetChildAlgorithmItems( childAlgorithmSubset );
1314 for ( const QString &child : childAlgorithmSubset )
1316 const QSet< QString > outdated = mModel->dependentChildAlgorithms( child );
1317 mScene->flagChildrenAsOutdated( outdated );
1318 mOutdatedChildResults.unite( outdated );
1324 mScene->resetChildAlgorithmItems();
1328 connect( mAlgorithmWidget, &QgsProcessingAlgorithmWidgetBase::algorithmFinished,
this, [
this](
bool,
const QVariantMap & ) {
1331 mLayerStore.temporaryLayerStore()->removeAllMapLayers();
1332 mLayerStore.takeResultsFrom( *context );
1340void QgsModelDesignerDialog::showChildAlgorithmOutputs(
const QString &childId )
1342 const bool isOutdated = mOutdatedChildResults.contains( childId );
1343 const QString childDescription = mModel->childAlgorithm( childId ).description();
1346 const QVariantMap childAlgorithmOutputs = result.
outputs();
1347 if ( childAlgorithmOutputs.isEmpty() )
1349 mMessageBar->pushWarning( QString(), tr(
"No results are available for %1" ).arg( childDescription ) );
1356 mMessageBar->pushCritical( QString(), tr(
"Results cannot be shown for an invalid model component" ) );
1360 const QList<const QgsProcessingParameterDefinition *> outputParams =
algorithm->destinationParameterDefinitions();
1361 if ( outputParams.isEmpty() )
1364 QgsDebugError(
"Cannot show results for algorithms with no outputs" );
1368 bool foundResults =
false;
1371 const QVariant output = childAlgorithmOutputs.value( outputParam->name() );
1372 if ( !output.isValid() )
1375 if ( output.type() == QVariant::String )
1379 QgsDebugMsgLevel( u
"Loading previous result for %1: %2"_s.arg( outputParam->name(), output.toString() ), 2 );
1381 std::unique_ptr<QgsMapLayer> layer( resultLayer->clone() );
1384 if ( outputParams.size() > 1 )
1385 baseName = tr(
"%1 — %2" ).arg( childDescription, outputParam->name() );
1387 baseName = childDescription;
1391 QString name = baseName;
1396 name = tr(
"%1 (%2)" ).arg( baseName ).arg( counter );
1399 layer->setName( name );
1402 foundResults =
true;
1407 QgsDebugError( u
"Could not load previous result for %1: %2"_s.arg( outputParam->name(), output.toString() ) );
1412 if ( !foundResults )
1414 mMessageBar->pushWarning( QString(), tr(
"No results are available for %1" ).arg( childDescription ) );
1417 else if ( isOutdated )
1419 mMessageBar->pushWarning( QString(), tr(
"These results are outdated, and may not reflect the most recent model execution" ) );
1424void QgsModelDesignerDialog::showChildAlgorithmLog(
const QString &childId )
1426 const QString childDescription = mModel->childAlgorithm( childId ).description();
1431 if ( QgsModelChildAlgorithmGraphicItem *item = mScene->childAlgorithmItem( childId ) )
1433 result = item->results();
1435 if ( result.
htmlLog().isEmpty() )
1437 result = mLastResult.childResults().value( childId );
1440 if ( result.
htmlLog().isEmpty() )
1442 mMessageBar->pushWarning( QString(), tr(
"No log is available for %1" ).arg( childDescription ) );
1447 m.setWindowTitle( childDescription );
1448 m.setCheckBoxVisible(
false );
1449 m.setMessageAsHtml( result.
htmlLog() );
1453void QgsModelDesignerDialog::onItemFocused( QgsModelComponentGraphicItem *item )
1460 if ( !item || !item->component() )
1462 mConfigWidget->showComponentConfig(
nullptr, *context, widgetContext );
1466 mConfigWidget->showComponentConfig( item->component(), *context, widgetContext );
1468 if (
auto childAlgorithmItem = qobject_cast< QgsModelChildAlgorithmGraphicItem * >( item ) )
1470 connect( childAlgorithmItem, &QgsModelChildAlgorithmGraphicItem::rebuildConfigurationDockWidget, childAlgorithmItem, [
this] {
1475 mConfigWidget->showComponentConfig(
nullptr, *context, widgetContext );
1481void QgsModelDesignerDialog::validate()
1484 if ( model()->validate( issues ) )
1486 mMessageBar->pushSuccess( QString(), tr(
"Model is valid!" ) );
1491 QPushButton *detailsButton =
new QPushButton( tr(
"Details" ) );
1492 connect( detailsButton, &QPushButton::clicked, detailsButton, [detailsButton, issues] {
1494 dialog->
setTitle( tr(
"Model is Invalid" ) );
1496 QString longMessage = tr(
"<p>This model is not valid:</p>" ) + u
"<ul>"_s;
1497 for (
const QString &issue : issues )
1499 longMessage += u
"<li>%1</li>"_s.arg( issue );
1501 longMessage +=
"</ul>"_L1;
1506 messageWidget->layout()->addWidget( detailsButton );
1507 mMessageBar->clearWidgets();
1512void QgsModelDesignerDialog::reorderInputs()
1514 QgsModelInputReorderDialog dlg(
this );
1515 dlg.setModel( mModel.get() );
1518 const QStringList inputOrder = dlg.inputOrder();
1519 beginUndoCommand( tr(
"Reorder Inputs" ) );
1520 mModel->setParameterOrder( inputOrder );
1525void QgsModelDesignerDialog::reorderOutputs()
1527 QgsModelOutputReorderDialog dlg(
this );
1528 dlg.setModel( mModel.get() );
1531 const QStringList outputOrder = dlg.outputOrder();
1532 beginUndoCommand( tr(
"Reorder Outputs" ) );
1533 mModel->setOutputOrder( outputOrder );
1534 mModel->setOutputGroup( dlg.outputGroup() );
1539bool QgsModelDesignerDialog::isDirty()
const
1541 return mHasChanged && mUndoStack->index() != -1;
1544void QgsModelDesignerDialog::fillInputsTree()
1547 auto parametersItem = std::make_unique<QTreeWidgetItem>();
1548 parametersItem->setText( 0, tr(
"Parameters" ) );
1551 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
1558 auto paramItem = std::make_unique<QTreeWidgetItem>();
1559 paramItem->setText( 0, param->name() );
1560 paramItem->setData( 0, Qt::UserRole, param->id() );
1561 paramItem->setIcon( 0, icon );
1562 paramItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1563 paramItem->setToolTip( 0, param->description() );
1564 parametersItem->addChild( paramItem.release() );
1567 mInputsTreeWidget->addTopLevelItem( parametersItem.release() );
1568 mInputsTreeWidget->topLevelItem( 0 )->setExpanded(
true );
1576QgsModelChildDependenciesWidget::QgsModelChildDependenciesWidget( QWidget *parent, QgsProcessingModelAlgorithm *model,
const QString &childId )
1579 , mChildId( childId )
1581 QHBoxLayout *hl =
new QHBoxLayout();
1582 hl->setContentsMargins( 0, 0, 0, 0 );
1584 mLineEdit =
new QLineEdit();
1585 mLineEdit->setEnabled(
false );
1586 hl->addWidget( mLineEdit, 1 );
1588 mToolButton =
new QToolButton();
1589 mToolButton->setText( QString( QChar( 0x2026 ) ) );
1590 hl->addWidget( mToolButton );
1594 mLineEdit->setText( tr(
"%1 dependencies selected" ).arg( 0 ) );
1596 connect( mToolButton, &QToolButton::clicked,
this, &QgsModelChildDependenciesWidget::showDialog );
1599void QgsModelChildDependenciesWidget::setValue(
const QList<QgsProcessingModelChildDependency> &value )
1601 const bool hasChanged = value != mValue;
1604 updateSummaryText();
1611void QgsModelChildDependenciesWidget::showDialog()
1613 const QList<QgsProcessingModelChildDependency> available = mModel->availableDependenciesForChildAlgorithm( mChildId );
1615 QVariantList availableOptions;
1616 for (
const QgsProcessingModelChildDependency &dep : available )
1617 availableOptions << QVariant::fromValue( dep );
1618 QVariantList selectedOptions;
1619 for (
const QgsProcessingModelChildDependency &dep : mValue )
1620 selectedOptions << QVariant::fromValue( dep );
1625 QgsProcessingMultipleSelectionPanelWidget *widget =
new QgsProcessingMultipleSelectionPanelWidget( availableOptions, selectedOptions );
1626 widget->setPanelTitle( tr(
"Algorithm Dependencies" ) );
1628 widget->setValueFormatter( [
this](
const QVariant &v ) -> QString {
1629 const QgsProcessingModelChildDependency dep = v.value<QgsProcessingModelChildDependency>();
1631 const QString description = mModel->childAlgorithm( dep.childId ).description();
1632 if ( dep.conditionalBranch.isEmpty() )
1635 return tr(
"Condition “%1” from algorithm “%2”" ).arg( dep.conditionalBranch, description );
1638 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged,
this, [
this, widget]() {
1639 QList<QgsProcessingModelChildDependency> res;
1640 for (
const QVariant &v : widget->selectedOptions() )
1642 res << v.value<QgsProcessingModelChildDependency>();
1651void QgsModelChildDependenciesWidget::updateSummaryText()
1653 mLineEdit->setText( tr(
"%n dependencies selected",
nullptr, mValue.count() ) );
@ ExposeToModeler
Is this parameter available in the modeler. Is set to on by default.
@ Warning
Warning message.
@ Critical
Critical/error message.
@ Success
Used for reporting a successful operation.
@ ModelDebug
Model debug level logging. Includes verbose logging and other outputs useful for debugging models.
static QgsProcessingRegistry * processingRegistry()
Returns the application's processing registry, used for managing processing providers,...
static QIcon getThemeIcon(const QString &name, const QColor &fillColor=QColor(), const QColor &strokeColor=QColor())
Helper to get a theme icon.
Expression contexts are used to encapsulate the parameters around which a QgsExpression should be eva...
void appendScope(QgsExpressionContextScope *scope)
Appends a scope to the end of the context.
static QString ensureFileNameHasExtension(const QString &fileName, const QStringList &extensions)
Ensures that a fileName ends with an extension from the provided list of extensions.
static QgsProcessingGuiRegistry * processingGuiRegistry()
Returns the global processing gui registry, used for registering the GUI behavior of processing algor...
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...
Base class for all map layer types.
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, const char *file=__builtin_FILE(), const char *function=__builtin_FUNCTION(), int line=__builtin_LINE(), Qgis::StringFormat format=Qgis::StringFormat::PlainText)
Adds a message to the log instance (and creates it if necessary).
A generic message view for displaying QGIS messages.
void setMessage(const QString &message, Qgis::StringFormat format) override
Sets message, it won't be displayed until.
void setTitle(const QString &title) override
Sets title for the messages.
void showMessage(bool blocking=true) override
display the message to the user and deletes itself
Abstract base class for processing algorithms.
An interface for objects which can create Processing contexts.
Contains information about the context in which a processing algorithm is executed.
QgsProcessingModelResult modelResult() const
Returns the model results, populated when the context is used to run a model algorithm.
Base class for providing feedback from a processing algorithm.
QgsProcessingParameterWidgetContext createWidgetContext() override
Register a Processing widget context.
Encapsulates the results of running a child algorithm within a model.
QString htmlLog() const
Returns the HTML formatted contents of logged messages which occurred while running the child.
QVariantMap outputs() const
Returns the outputs generated by the child algorithm.
void childResultReported(const QString &childId, const QgsProcessingModelChildAlgorithmResult &result)
Emitted when the result of a child algorithm has been reported.
Encapsulates the results of running a Processing model.
QMap< QString, QgsProcessingModelChildAlgorithmResult > childResults() const
Returns the map of child algorithm results.
Base class for the definition of processing parameters.
Makes metadata of processing parameters available.
Contains settings which reflect the context in which a Processing parameter widget is shown.
void setModelDesignerDialog(QgsModelDesignerDialog *dialog)
Sets the associated model designer dialog, if applicable.
void setModel(QgsProcessingModelAlgorithm *model)
Sets the model which the parameter widget is associated with.
void registerProcessingContextGenerator(QgsProcessingContextGenerator *generator)
Registers a Processing context generator class that will be used to retrieve a Processing context for...
@ SkipDefaultValueParameters
Parameters which are unchanged from their default values should not be included.
QList< QgsProcessingParameterType * > parameterTypes() const
Returns a list with all known parameter types.
static QgsMapLayer * mapLayerFromString(const QString &string, QgsProcessingContext &context, bool allowLoadingNewLayers=true, QgsProcessingUtils::LayerHint typeHint=QgsProcessingUtils::LayerHint::UnknownType, QgsProcessing::LayerOptionsFlags flags=QgsProcessing::LayerOptionsFlags())
Interprets a string as a map layer within the supplied context.
@ PythonQgsProcessingAlgorithmSubclass
Full Python QgsProcessingAlgorithm subclass.
static QgsProject * instance()
Returns the QgsProject singleton instance.
QgsMapLayer * addMapLayer(QgsMapLayer *mapLayer, bool addToLegend=true, bool takeOwnership=true)
Add a layer to the map of loaded layers.
A utility class for dynamic handling of changes to screen properties.
Stores settings for use within QGIS.
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 addDockWidget(QMainWindow *window, Qt::DockWidgetArea area, QDockWidget *dockwidget)
Add a dock widget to a main window.
As part of the API refactoring and improvements which landed in the Processing API was substantially reworked from the x version This was done in order to allow much of the underlying Processing framework to be ported into allowing algorithms to be written in pure substantial changes are required in order to port existing x Processing algorithms for QGIS x The most significant changes are outlined not GeoAlgorithm For algorithms which operate on features one by consider subclassing the QgsProcessingFeatureBasedAlgorithm class This class allows much of the boilerplate code for looping over features from a vector layer to be bypassed and instead requires implementation of a processFeature method Ensure that your algorithm(or algorithm 's parent class) implements the new pure virtual createInstance(self) call
#define QgsDebugMsgLevel(str, level)
#define QgsDebugError(str)
constexpr QObjectUniquePtr< Tp > make_qobject_unique(Args &&...args)
Create an object owned by a QObjectUniquePtr.