49#include <QActionGroup>
52#include <QKeySequence>
58#include <QSvgGenerator>
64#include "moc_qgsmodeldesignerdialog.cpp"
66using namespace Qt::StringLiterals;
71QgsModelerToolboxModel::QgsModelerToolboxModel( QObject *parent )
75Qt::ItemFlags QgsModelerToolboxModel::flags(
const QModelIndex &index )
const
77 Qt::ItemFlags f = QgsProcessingToolboxProxyModel::flags( index );
78 const QModelIndex sourceIndex = mapToSource( index );
79 if ( toolboxModel()->isAlgorithm( sourceIndex ) || toolboxModel()->isParameter( sourceIndex ) )
81 f = f | Qt::ItemIsDragEnabled;
86Qt::DropActions QgsModelerToolboxModel::supportedDragActions()
const
88 return Qt::CopyAction;
91QgsModelDesignerDialog::QgsModelDesignerDialog( QWidget *parent, Qt::WindowFlags flags )
92 : QMainWindow( parent, flags )
93 , mToolsActionGroup( new QActionGroup( this ) )
101 setAttribute( Qt::WA_DeleteOnClose );
102 setDockOptions( dockOptions() | QMainWindow::GroupedDragging );
103 setWindowFlags( Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint );
107 mModel = std::make_unique<QgsProcessingModelAlgorithm>();
110 mUndoStack =
new QUndoStack(
this );
111 connect( mUndoStack, &QUndoStack::indexChanged,
this, [
this] {
112 if ( mIgnoreUndoStackChanges )
115 mBlockUndoCommands++;
116 updateVariablesGui();
117 mGroupEdit->setText( mModel->group() );
118 mNameEdit->setText( mModel->displayName() );
119 mBlockUndoCommands--;
124 mConfigWidgetDock->setWindowTitle( tr(
"Configuration" ) );
125 mConfigWidgetDock->setObjectName( u
"ModelConfigDock"_s );
128 mConfigWidgetDock->setWidget( mConfigWidget );
129 mConfigWidgetDock->setFeatures( QDockWidget::NoDockWidgetFeatures );
132 mPropertiesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
133 mInputsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
134 mAlgorithmsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
135 mVariablesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
137 mToolboxTree->header()->setVisible(
false );
138 mToolboxSearchEdit->setShowSearchIcon(
true );
139 mToolboxSearchEdit->setPlaceholderText( tr(
"Search…" ) );
140 connect( mToolboxSearchEdit, &QgsFilterLineEdit::textChanged, mToolboxTree, &QgsProcessingToolboxTreeView::setFilterString );
142 mInputsTreeWidget->header()->setVisible(
false );
143 mInputsTreeWidget->setAlternatingRowColors(
true );
144 mInputsTreeWidget->setDragDropMode( QTreeWidget::DragOnly );
145 mInputsTreeWidget->setDropIndicatorShown(
true );
147 mNameEdit->setPlaceholderText( tr(
"Enter model name here" ) );
148 mGroupEdit->setPlaceholderText( tr(
"Enter group name here" ) );
151 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
152 mainLayout->insertWidget( 0, mMessageBar );
154 mView->setAcceptDrops(
true );
157 connect( mActionClose, &QAction::triggered,
this, &QWidget::close );
158 connect( mActionNew, &QAction::triggered,
this, &QgsModelDesignerDialog::newModel );
159 connect( mActionZoomIn, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomIn );
160 connect( mActionZoomOut, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomOut );
161 connect( mActionZoomActual, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomActual );
162 connect( mActionZoomToItems, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomFull );
163 connect( mActionExportImage, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToImage );
164 connect( mActionExportPdf, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToPdf );
165 connect( mActionExportSvg, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToSvg );
166 connect( mActionExportPython, &QAction::triggered,
this, &QgsModelDesignerDialog::exportAsPython );
167 connect( mActionSave, &QAction::triggered,
this, [
this] { saveModel(
false ); } );
168 connect( mActionSaveAs, &QAction::triggered,
this, [
this] { saveModel(
true ); } );
169 connect( mActionDeleteComponents, &QAction::triggered,
this, &QgsModelDesignerDialog::deleteSelected );
170 connect( mActionSnapSelected, &QAction::triggered, mView, &QgsModelGraphicsView::snapSelected );
171 connect( mActionValidate, &QAction::triggered,
this, &QgsModelDesignerDialog::validate );
172 connect( mActionReorderInputs, &QAction::triggered,
this, &QgsModelDesignerDialog::reorderInputs );
173 connect( mActionReorderOutputs, &QAction::triggered,
this, &QgsModelDesignerDialog::reorderOutputs );
174 connect( mActionEditHelp, &QAction::triggered,
this, &QgsModelDesignerDialog::editHelp );
175 connect( mReorderInputsButton, &QPushButton::clicked,
this, &QgsModelDesignerDialog::reorderInputs );
176 connect( mActionRun, &QAction::triggered,
this, [
this] { run(); } );
177 connect( mActionRunSelectedSteps, &QAction::triggered,
this, &QgsModelDesignerDialog::runSelectedSteps );
179 mActionSnappingEnabled->setChecked( settings.
value( u
"/Processing/Modeler/enableSnapToGrid"_s,
false ).toBool() );
180 connect( mActionSnappingEnabled, &QAction::toggled,
this, [
this](
bool enabled ) {
181 mView->snapper()->setSnapToGrid( enabled );
184 mView->snapper()->setSnapToGrid( mActionSnappingEnabled->isChecked() );
186 connect( mView, &QgsModelGraphicsView::itemFocused,
this, &QgsModelDesignerDialog::onItemFocused );
188 connect( mActionSelectAll, &QAction::triggered,
this, [
this] { mScene->selectAll(); } );
190 QStringList docksTitle = settings.
value( u
"ModelDesigner/hiddenDocksTitle"_s, QStringList(),
QgsSettings::App ).toStringList();
191 QStringList docksActive = settings.
value( u
"ModelDesigner/hiddenDocksActive"_s, QStringList(),
QgsSettings::App ).toStringList();
192 if ( !docksTitle.isEmpty() )
194 for (
const auto &title : docksTitle )
196 mPanelStatus.insert( title, PanelStatus(
true, docksActive.contains( title ) ) );
199 mActionHidePanels->setChecked( !docksTitle.isEmpty() );
200 connect( mActionHidePanels, &QAction::toggled,
this, &QgsModelDesignerDialog::setPanelVisibility );
202 mUndoAction = mUndoStack->createUndoAction(
this );
204 mUndoAction->setShortcuts( QKeySequence::Undo );
205 mRedoAction = mUndoStack->createRedoAction(
this );
207 mRedoAction->setShortcuts( QKeySequence::Redo );
209 mMenuEdit->insertAction( mActionDeleteComponents, mRedoAction );
210 mMenuEdit->insertAction( mActionDeleteComponents, mUndoAction );
211 mMenuEdit->insertSeparator( mActionDeleteComponents );
212 mToolbar->insertAction( mActionZoomIn, mUndoAction );
213 mToolbar->insertAction( mActionZoomIn, mRedoAction );
214 mToolbar->insertSeparator( mActionZoomIn );
216 mGroupMenu =
new QMenu( tr(
"Zoom To" ),
this );
217 mMenuView->insertMenu( mActionZoomIn, mGroupMenu );
218 connect( mGroupMenu, &QMenu::aboutToShow,
this, &QgsModelDesignerDialog::populateZoomToMenu );
222 mActionCut =
new QAction( tr(
"Cu&t" ),
this );
223 mActionCut->setShortcuts( QKeySequence::Cut );
224 mActionCut->setStatusTip( tr(
"Cut" ) );
226 connect( mActionCut, &QAction::triggered,
this, [
this] { mView->copySelectedItems( QgsModelGraphicsView::ClipboardCut ); } );
228 mActionCopy =
new QAction( tr(
"&Copy" ),
this );
229 mActionCopy->setShortcuts( QKeySequence::Copy );
230 mActionCopy->setStatusTip( tr(
"Copy" ) );
232 connect( mActionCopy, &QAction::triggered,
this, [
this] { mView->copySelectedItems( QgsModelGraphicsView::ClipboardCopy ); } );
234 mActionPaste =
new QAction( tr(
"&Paste" ),
this );
235 mActionPaste->setShortcuts( QKeySequence::Paste );
236 mActionPaste->setStatusTip( tr(
"Paste" ) );
238 connect( mActionPaste, &QAction::triggered,
this, [
this] { mView->pasteItems( QgsModelGraphicsView::PasteModeCursor ); } );
239 mMenuEdit->insertAction( mActionDeleteComponents, mActionCut );
240 mMenuEdit->insertAction( mActionDeleteComponents, mActionCopy );
241 mMenuEdit->insertAction( mActionDeleteComponents, mActionPaste );
242 mMenuEdit->insertSeparator( mActionDeleteComponents );
244 mAlgorithmsModel =
new QgsModelerToolboxModel(
this );
245 mToolboxTree->setToolboxProxyModel( mAlgorithmsModel );
248 if ( settings.
value( u
"Processing/Configuration/SHOW_ALGORITHMS_KNOWN_ISSUES"_s,
false ).toBool() )
252 mToolboxTree->setFilters( filters );
253 mToolboxTree->setDragDropMode( QTreeWidget::DragOnly );
254 mToolboxTree->setDropIndicatorShown(
true );
256 connect( mView, &QgsModelGraphicsView::algorithmDropped,
this, [
this](
const QString &algorithmId,
const QPointF &pos ) { addAlgorithm( algorithmId, pos ); } );
257 connect( mView, &QgsModelGraphicsView::inputDropped,
this, &QgsModelDesignerDialog::addInput );
259 connect( mToolboxTree, &QgsProcessingToolboxTreeView::doubleClicked,
this, [
this](
const QModelIndex & ) {
260 if ( mToolboxTree->selectedAlgorithm() )
261 addAlgorithm( mToolboxTree->selectedAlgorithm()->id(), QPointF() );
262 if ( mToolboxTree->selectedParameterType() )
263 addInput( mToolboxTree->selectedParameterType()->id(), QPointF() );
266 connect( mInputsTreeWidget, &QgsModelDesignerInputsTreeWidget::doubleClicked,
this, [
this](
const QModelIndex & ) {
267 const QString parameterType = mInputsTreeWidget->currentItem()->data( 0, Qt::UserRole ).toString();
268 addInput( parameterType, QPointF() );
272 QShortcut *ctrlEquals =
new QShortcut( QKeySequence( u
"Ctrl+="_s ),
this );
273 connect( ctrlEquals, &QShortcut::activated,
this, &QgsModelDesignerDialog::zoomIn );
276 mUndoDock->setObjectName( u
"UndoDock"_s );
277 mUndoView =
new QUndoView( mUndoStack,
this );
278 mUndoDock->setWidget( mUndoView );
279 mUndoDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
280 addDockWidget( Qt::DockWidgetArea::LeftDockWidgetArea, mUndoDock );
282 tabifyDockWidget( mUndoDock, mPropertiesDock );
283 tabifyDockWidget( mVariablesDock, mPropertiesDock );
284 mPropertiesDock->raise();
285 tabifyDockWidget( mInputsDock, mAlgorithmsDock );
286 mInputsDock->raise();
291 beginUndoCommand( tr(
"Change Model Variables" ) );
292 mModel->setVariables( mVariablesEditor->variablesInActiveScope() );
296 connect( mNameEdit, &QLineEdit::textChanged,
this, [
this](
const QString &name ) {
299 beginUndoCommand( tr(
"Change Model Name" ), QString(), QgsModelUndoCommand::CommandOperation::NameChanged );
300 mModel->setName( name );
305 connect( mGroupEdit, &QLineEdit::textChanged,
this, [
this](
const QString &group ) {
308 beginUndoCommand( tr(
"Change Model Group" ), QString(), QgsModelUndoCommand::CommandOperation::GroupChanged );
309 mModel->setGroup( group );
317 QToolButton *toolbuttonExportToScript =
new QToolButton();
318 toolbuttonExportToScript->setPopupMode( QToolButton::InstantPopup );
319 toolbuttonExportToScript->addAction( mActionExportAsScriptAlgorithm );
320 toolbuttonExportToScript->setDefaultAction( mActionExportAsScriptAlgorithm );
321 mToolbar->insertWidget( mActionExportImage, toolbuttonExportToScript );
322 connect( mActionExportAsScriptAlgorithm, &QAction::triggered,
this, &QgsModelDesignerDialog::exportAsScriptAlgorithm );
324 mActionShowComments->setChecked( settings.
value( u
"/Processing/Modeler/ShowComments"_s,
true ).toBool() );
325 connect( mActionShowComments, &QAction::toggled,
this, &QgsModelDesignerDialog::toggleComments );
327 mActionShowFeatureCount->setChecked( settings.
value( u
"/Processing/Modeler/ShowFeatureCount"_s,
true ).toBool() );
328 connect( mActionShowFeatureCount, &QAction::toggled,
this, &QgsModelDesignerDialog::toggleFeatureCount );
331 mPanTool->setAction( mActionPan );
333 mToolsActionGroup->addAction( mActionPan );
334 connect( mActionPan, &QAction::triggered, mPanTool, [
this] { mView->setTool( mPanTool ); } );
339 mSelectTool->setAction( mActionSelectMoveItem );
341 mToolsActionGroup->addAction( mActionSelectMoveItem );
342 connect( mActionSelectMoveItem, &QAction::triggered, mSelectTool, [
this] { mView->setTool( mSelectTool ); } );
344 mView->setTool( mSelectTool );
347 connect( mView, &QgsModelGraphicsView::macroCommandStarted,
this, [
this](
const QString &text ) {
348 mIgnoreUndoStackChanges++;
349 mUndoStack->beginMacro( text );
350 mIgnoreUndoStackChanges--;
352 connect( mView, &QgsModelGraphicsView::macroCommandEnded,
this, [
this] {
353 mIgnoreUndoStackChanges++;
354 mUndoStack->endMacro();
355 mIgnoreUndoStackChanges--;
357 connect( mView, &QgsModelGraphicsView::commandBegun,
this, [
this](
const QString &text ) { beginUndoCommand( text ); } );
358 connect( mView, &QgsModelGraphicsView::commandEnded,
this, [
this] { endUndoCommand(); } );
359 connect( mView, &QgsModelGraphicsView::commandAborted,
this, [
this] { abortUndoCommand(); } );
360 connect( mView, &QgsModelGraphicsView::deleteSelectedItems,
this, [
this] { deleteSelected(); } );
362 connect( mActionAddGroupBox, &QAction::triggered,
this, [
this] {
363 const QPointF viewCenter = mView->mapToScene( mView->viewport()->rect().center() );
364 QgsProcessingModelGroupBox group;
365 group.setPosition( viewCenter );
366 group.setDescription( tr(
"New Group" ) );
368 beginUndoCommand( tr(
"Add Group Box" ) );
369 model()->addGroupBox( group );
377 restoreState( settings.
value( u
"ModelDesigner/state"_s, QByteArray(),
QgsSettings::App ).toByteArray() );
380QgsModelDesignerDialog::~QgsModelDesignerDialog()
382 if ( mAlgorithmWidget )
384 delete mAlgorithmWidget;
386 for (
const QPointer<QgsProcessingAlgorithmWidgetBase> &widget : std::as_const( mAlgorithmWidgetsToCleanUp ) )
399 if ( !mPanelStatus.isEmpty() )
401 QStringList docksTitle;
402 QStringList docksActive;
404 for (
const auto &panel : mPanelStatus.toStdMap() )
406 if ( panel.second.isVisible )
407 docksTitle << panel.first;
408 if ( panel.second.isActive )
409 docksActive << panel.first;
423 mIgnoreUndoStackChanges++;
426void QgsModelDesignerDialog::closeEvent( QCloseEvent *event )
428 if ( checkForUnsavedChanges() )
434void QgsModelDesignerDialog::beginUndoCommand(
const QString &text,
const QString &
id, QgsModelUndoCommand::CommandOperation operation )
436 if ( mBlockUndoCommands || !mUndoStack )
439 if ( mActiveCommand )
444 mActiveCommand = std::make_unique<QgsModelUndoCommand>( mModel.get(), text,
id );
448 mActiveCommand = std::make_unique<QgsModelUndoCommand>( mModel.get(), text, operation );
452void QgsModelDesignerDialog::endUndoCommand()
454 if ( mBlockUndoCommands || !mActiveCommand || !mUndoStack )
457 mActiveCommand->saveAfterState();
458 mIgnoreUndoStackChanges++;
459 mUndoStack->push( mActiveCommand.release() );
460 mIgnoreUndoStackChanges--;
464void QgsModelDesignerDialog::abortUndoCommand()
466 if ( mActiveCommand )
467 mActiveCommand->setObsolete(
true );
470QgsProcessingModelAlgorithm *QgsModelDesignerDialog::model()
475void QgsModelDesignerDialog::setModel( QgsProcessingModelAlgorithm *model )
477 mModel.reset( model );
479 mGroupEdit->setText( mModel->group() );
480 mNameEdit->setText( mModel->displayName() );
481 repaintModel(
true );
482 updateVariablesGui();
486 mIgnoreUndoStackChanges++;
488 mIgnoreUndoStackChanges--;
494 QMetaObject::invokeMethod(
this, &QgsModelDesignerDialog::zoomFull, Qt::QueuedConnection );
497void QgsModelDesignerDialog::loadModel(
const QString &path )
499 auto alg = std::make_unique<QgsProcessingModelAlgorithm>();
500 if ( alg->fromFile( path ) )
503 alg->setSourceFilePath( path );
504 setModel( alg.release() );
509 QMessageBox::critical(
513 "The selected model could not be loaded.\n"
514 "See the log for more information."
520void QgsModelDesignerDialog::setModelScene( QgsModelGraphicsScene *scene )
522 QgsModelGraphicsScene *oldScene = mScene;
525 mScene->setParent(
this );
526 mScene->setLastRunResult( mLastResult, mLayerStore );
527 mScene->setModel( mModel.get() );
528 mScene->setMessageBar( mMessageBar );
531 const bool showFeatureCount = settings.
value( u
"/Processing/Modeler/ShowFeatureCount"_s,
true ).toBool();
532 if ( !showFeatureCount )
533 mScene->setFlag( QgsModelGraphicsScene::FlagHideFeatureCount );
535 mView->setModelScene( mScene );
537 mSelectTool->resetCache();
538 mSelectTool->setScene( mScene );
540 connect( mScene, &QgsModelGraphicsScene::rebuildRequired,
this, [
this] {
541 if ( mBlockRepaints )
545 mScene->flagChildrenAsOutdated( mOutdatedChildResults );
547 connect( mScene, &QgsModelGraphicsScene::componentAboutToChange,
this, [
this](
const QString &description,
const QString &
id ) { beginUndoCommand( description,
id ); } );
548 connect( mScene, &QgsModelGraphicsScene::componentChanged,
this, [
this] { endUndoCommand(); } );
549 connect( mScene, &QgsModelGraphicsScene::runFromChild,
this, &QgsModelDesignerDialog::runFromChild );
550 connect( mScene, &QgsModelGraphicsScene::runSelected,
this, &QgsModelDesignerDialog::runSelectedSteps );
551 connect( mScene, &QgsModelGraphicsScene::showChildAlgorithmOutputs,
this, &QgsModelDesignerDialog::showChildAlgorithmOutputs );
552 connect( mScene, &QgsModelGraphicsScene::showChildAlgorithmLog,
this, &QgsModelDesignerDialog::showChildAlgorithmLog );
555 oldScene->deleteLater();
558QgsModelGraphicsScene *QgsModelDesignerDialog::modelScene()
565 auto result = std::make_unique< QgsProcessingModelFeedback >();
566 mScene->setupFeedbackConnections( result.get() );
568 mOutdatedChildResults.remove( childId );
570 return result.release();
573void QgsModelDesignerDialog::activate()
577 setWindowState( windowState() & ~Qt::WindowMinimized );
583 mProcessingContextGenerator = generator;
586void QgsModelDesignerDialog::updateVariablesGui()
588 mBlockUndoCommands++;
590 auto variablesScope = std::make_unique<QgsExpressionContextScope>( tr(
"Model Variables" ) );
591 const QVariantMap modelVars = mModel->variables();
592 for (
auto it = modelVars.constBegin(); it != modelVars.constEnd(); ++it )
594 variablesScope->setVariable( it.key(), it.value() );
597 variablesContext.
appendScope( variablesScope.release() );
598 mVariablesEditor->setContext( &variablesContext );
599 mVariablesEditor->setEditableScopeIndex( 0 );
601 mBlockUndoCommands--;
604void QgsModelDesignerDialog::setDirty(
bool dirty )
608 if ( mAlgorithmWidget )
610 if (
QgsMessageBar *messageBar = mAlgorithmWidget->messageBar() )
612 QgsMessageBarItem *messageBarItem = messageBar->createMessage( QString(), tr(
"The model has changed, this panel should be reloaded." ) );
613 auto reloadButton =
new QPushButton( tr(
"Reload Now" ) );
614 connect( reloadButton, &QPushButton::clicked, reloadButton, [
this] {
615 if ( mAlgorithmWidget && mAlgorithmWidget->isRunning() )
617 QMessageBox messageBox;
618 messageBox.setIcon( QMessageBox::Icon::Warning );
619 messageBox.setWindowTitle( tr(
"Run Model" ) );
620 messageBox.setText( tr(
"This model is currently running." ) );
621 messageBox.setStandardButtons( QMessageBox::StandardButton::Cancel | QMessageBox::StandardButton::RestoreDefaults );
623 QAbstractButton *buttonReRun = messageBox.button( QMessageBox::StandardButton::RestoreDefaults );
624 buttonReRun->setText( tr(
"Terminate and Reload" ) );
626 int r = messageBox.exec();
630 case QMessageBox::StandardButton::Cancel:
632 case QMessageBox::StandardButton::RestoreDefaults:
638 cancelRunningModel();
641 messageBarItem->layout()->addWidget( reloadButton );
647bool QgsModelDesignerDialog::validateSave( SaveAction action )
651 case QgsModelDesignerDialog::SaveAction::SaveAsFile:
653 case QgsModelDesignerDialog::SaveAction::SaveInProject:
654 if ( mNameEdit->text().trimmed().isEmpty() )
656 mMessageBar->pushWarning( QString(), tr(
"Please enter a model name before saving" ) );
665bool QgsModelDesignerDialog::checkForUnsavedChanges()
669 QMessageBox::StandardButton ret = QMessageBox::
670 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 );
673 case QMessageBox::Save:
674 return saveModel(
false );
676 case QMessageBox::Discard:
691 mLastResult.mergeWith( result );
693 mScene->setLastRunResult( mLastResult, mLayerStore );
696void QgsModelDesignerDialog::setModelName(
const QString &name )
698 mNameEdit->setText( name );
701void QgsModelDesignerDialog::zoomIn()
703 mView->setTransformationAnchor( QGraphicsView::NoAnchor );
704 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
706 const double factor = settings.
value( u
"/qgis/zoom_favor"_s, 2.0 ).toDouble();
707 mView->scale( factor, factor );
708 mView->centerOn( point );
711void QgsModelDesignerDialog::zoomOut()
713 mView->setTransformationAnchor( QGraphicsView::NoAnchor );
714 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
716 const double factor = 1.0 / settings.
value( u
"/qgis/zoom_favor"_s, 2.0 ).toDouble();
717 mView->scale( factor, factor );
718 mView->centerOn( point );
721void QgsModelDesignerDialog::zoomActual()
723 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
724 mView->resetTransform();
725 mView->scale( mScreenHelper->screenDpi() / 96, mScreenHelper->screenDpi() / 96 );
726 mView->centerOn( point );
729void QgsModelDesignerDialog::zoomFull()
731 QRectF totalRect = mView->scene()->itemsBoundingRect();
732 totalRect.adjust( -10, -10, 10, 10 );
733 mView->fitInView( totalRect, Qt::KeepAspectRatio );
736void QgsModelDesignerDialog::newModel()
738 if ( !checkForUnsavedChanges() )
741 auto alg = std::make_unique<QgsProcessingModelAlgorithm>();
743 setModel( alg.release() );
746void QgsModelDesignerDialog::exportToImage()
749 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
751 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as Image" ), lastExportDir, tr(
"PNG files (*.png *.PNG)" ) );
755 if ( filename.isEmpty() )
760 const QFileInfo saveFileInfo( filename );
763 repaintModel(
false );
765 QRectF totalRect = mView->scene()->itemsBoundingRect();
766 totalRect.adjust( -10, -10, 10, 10 );
767 const QRectF imageRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
769 QImage img( totalRect.width(), totalRect.height(), QImage::Format_ARGB32_Premultiplied );
770 img.fill( Qt::white );
772 painter.setRenderHint( QPainter::Antialiasing );
773 painter.begin( &img );
774 mView->scene()->render( &painter, imageRect, totalRect );
777 img.save( filename );
780 ->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 );
781 repaintModel(
true );
784void QgsModelDesignerDialog::exportToPdf()
787 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
789 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as PDF" ), lastExportDir, tr(
"PDF files (*.pdf *.PDF)" ) );
793 if ( filename.isEmpty() )
798 const QFileInfo saveFileInfo( filename );
801 repaintModel(
false );
803 QRectF totalRect = mView->scene()->itemsBoundingRect();
804 totalRect.adjust( -10, -10, 10, 10 );
805 const QRectF printerRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
807 QPdfWriter pdfWriter( filename );
809 const double scaleFactor = 96 / 25.4;
811 QPageLayout pageLayout( QPageSize( totalRect.size() / scaleFactor, QPageSize::Millimeter ), QPageLayout::Portrait, QMarginsF( 0, 0, 0, 0 ) );
812 pageLayout.setMode( QPageLayout::FullPageMode );
813 pdfWriter.setPageLayout( pageLayout );
815 QPainter painter( &pdfWriter );
816 mView->scene()->render( &painter, printerRect, totalRect );
820 ->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 );
821 repaintModel(
true );
824void QgsModelDesignerDialog::exportToSvg()
827 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
829 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as SVG" ), lastExportDir, tr(
"SVG files (*.svg *.SVG)" ) );
833 if ( filename.isEmpty() )
838 const QFileInfo saveFileInfo( filename );
841 repaintModel(
false );
843 QRectF totalRect = mView->scene()->itemsBoundingRect();
844 totalRect.adjust( -10, -10, 10, 10 );
845 const QRectF svgRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
848 svg.setFileName( filename );
849 svg.setSize( QSize( totalRect.width(), totalRect.height() ) );
850 svg.setViewBox( svgRect );
851 svg.setTitle( mModel->displayName() );
853 QPainter painter( &svg );
854 mView->scene()->render( &painter, svgRect, totalRect );
858 ->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 );
859 repaintModel(
true );
862void QgsModelDesignerDialog::exportAsPython()
865 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
867 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as Python Script" ), lastExportDir, tr(
"Processing scripts (*.py *.PY)" ) );
871 if ( filename.isEmpty() )
876 const QFileInfo saveFileInfo( filename );
881 QFile outFile( filename );
882 if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
886 QTextStream fout( &outFile );
891 ->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 );
894void QgsModelDesignerDialog::toggleComments(
bool show )
898 repaintModel(
true );
901void QgsModelDesignerDialog::toggleFeatureCount(
bool show )
905 repaintModel(
true );
908void QgsModelDesignerDialog::updateWindowTitle()
910 QString title = tr(
"Model Designer" );
911 if ( !mModel->name().isEmpty() )
912 title = mModel->group().isEmpty() ? u
"%1: %2"_s.arg( title, mModel->name() ) : u
"%1: %2 - %3"_s.arg( title, mModel->group(), mModel->name() );
915 title.prepend(
'*' );
917 setWindowTitle( title );
920void QgsModelDesignerDialog::deleteSelected()
922 QList<QgsModelComponentGraphicItem *> items = mScene->selectedComponentItems();
926 if ( items.size() == 1 )
928 items.at( 0 )->deleteComponent();
932 std::sort( items.begin(), items.end(), []( QgsModelComponentGraphicItem *p1, QgsModelComponentGraphicItem *p2 ) {
937 if ( dynamic_cast<QgsModelCommentGraphicItem *>( p1 ) && dynamic_cast<QgsModelCommentGraphicItem *>( p2 ) )
939 else if ( dynamic_cast<QgsModelCommentGraphicItem *>( p1 ) )
941 else if ( dynamic_cast<QgsModelCommentGraphicItem *>( p2 ) )
944 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p1 ) && dynamic_cast<QgsModelGroupBoxGraphicItem *>( p2 ) )
946 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p1 ) )
948 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p2 ) )
951 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p1 ) && dynamic_cast<QgsModelOutputGraphicItem *>( p2 ) )
953 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p1 ) )
955 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p2 ) )
958 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p1 ) && dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p2 ) )
960 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p1 ) )
962 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p2 ) )
969 beginUndoCommand( tr(
"Delete Components" ) );
971 QVariant prevState = mModel->toVariant();
972 mBlockUndoCommands++;
973 mBlockRepaints =
true;
975 while ( !items.empty() )
977 QgsModelComponentGraphicItem *toDelete =
nullptr;
978 for ( QgsModelComponentGraphicItem *item : items )
980 if ( item->canDeleteComponent() )
993 toDelete->deleteComponent();
994 items.removeAll( toDelete );
999 mModel->loadVariant( prevState );
1000 QMessageBox::warning(
1002 QObject::tr(
"Could not remove components" ),
1004 "Components depend on the selected items.\n"
1005 "Try to remove them before trying deleting these components."
1008 mBlockUndoCommands--;
1009 mActiveCommand.reset();
1013 mBlockUndoCommands--;
1017 mBlockRepaints =
false;
1021void QgsModelDesignerDialog::populateZoomToMenu()
1023 mGroupMenu->clear();
1024 for (
const QgsProcessingModelGroupBox &box : model()->groupBoxes() )
1026 if ( QgsModelComponentGraphicItem *item = mScene->groupBoxItem( box.uuid() ) )
1028 QAction *zoomAction =
new QAction( box.description(), mGroupMenu );
1029 connect( zoomAction, &QAction::triggered,
this, [
this, item] {
1030 QRectF groupRect = item->mapToScene( item->boundingRect() ).boundingRect();
1031 groupRect.adjust( -10, -10, 10, 10 );
1032 mView->fitInView( groupRect, Qt::KeepAspectRatio );
1033 mView->centerOn( item );
1035 mGroupMenu->addAction( zoomAction );
1040void QgsModelDesignerDialog::setPanelVisibility(
bool hidden )
1042 const QList<QDockWidget *> docks = findChildren<QDockWidget *>();
1043 const QList<QTabBar *> tabBars = findChildren<QTabBar *>();
1047 mPanelStatus.clear();
1049 for ( QDockWidget *dock : docks )
1051 mPanelStatus.insert( dock->windowTitle(), PanelStatus( dock->isVisible(),
false ) );
1052 dock->setVisible(
false );
1056 for ( QTabBar *tabBar : tabBars )
1058 QString currentTabTitle = tabBar->tabText( tabBar->currentIndex() );
1059 mPanelStatus[currentTabTitle].isActive =
true;
1065 for ( QDockWidget *dock : docks )
1067 if ( mPanelStatus.contains( dock->windowTitle() ) )
1069 dock->setVisible( mPanelStatus.value( dock->windowTitle() ).isVisible );
1074 for ( QTabBar *tabBar : tabBars )
1077 for (
int i = 0; i < tabBar->count(); ++i )
1079 QString tabTitle = tabBar->tabText( i );
1080 if ( mPanelStatus.contains( tabTitle ) && mPanelStatus.value( tabTitle ).isActive )
1082 tabBar->setCurrentIndex( i );
1086 mPanelStatus.clear();
1090void QgsModelDesignerDialog::editHelp()
1092 QgsProcessingHelpEditorDialog dialog(
this );
1093 dialog.setWindowTitle( tr(
"Edit Model Help" ) );
1094 dialog.setAlgorithm( mModel.get() );
1095 if ( dialog.exec() )
1097 beginUndoCommand( tr(
"Edit Model Help" ) );
1098 mModel->setHelpContent( dialog.helpContent() );
1103void QgsModelDesignerDialog::runSelectedSteps()
1105 QSet<QString> children;
1106 const QList<QgsModelComponentGraphicItem *> items = mScene->selectedComponentItems();
1107 for ( QgsModelComponentGraphicItem *item : items )
1109 if ( QgsProcessingModelChildAlgorithm *childAlgorithm =
dynamic_cast<QgsProcessingModelChildAlgorithm *
>( item->component() ) )
1111 children.insert( childAlgorithm->childId() );
1115 if ( children.isEmpty() )
1117 mMessageBar->pushWarning( QString(), tr(
"No steps are selected" ) );
1124void QgsModelDesignerDialog::runFromChild(
const QString &
id )
1126 QSet<QString> children = mModel->dependentChildAlgorithms(
id );
1127 children.insert(
id );
1131void QgsModelDesignerDialog::cancelRunningModel()
1133 if ( !mAlgorithmWidget )
1139 mAlgorithmWidget->cancel();
1141 mAlgorithmWidget->forceClose();
1144 if ( mAlgorithmWidget )
1150 mAlgorithmWidgetsToCleanUp << mAlgorithmWidget;
1152 mAlgorithmWidget.clear();
1155void QgsModelDesignerDialog::run(
const QSet<QString> &childAlgorithmSubset )
1158 const bool isValid = model()->validate( errors );
1161 QMessageBox messageBox;
1162 messageBox.setWindowTitle( tr(
"Model is Invalid" ) );
1163 messageBox.setIcon( QMessageBox::Icon::Warning );
1164 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?" ) );
1165 messageBox.setStandardButtons( QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::Cancel );
1166 messageBox.setDefaultButton( QMessageBox::StandardButton::Cancel );
1168 QString errorString;
1169 for (
const QString &error : std::as_const( errors ) )
1171 QString cleanedError = error;
1172 const thread_local QRegularExpression re( u
"<[^>]*>"_s );
1173 cleanedError.replace( re, QString() );
1174 errorString += u
"• %1\n"_s.arg( cleanedError );
1177 messageBox.setDetailedText( errorString );
1178 if ( messageBox.exec() == QMessageBox::StandardButton::Cancel )
1182 if ( !childAlgorithmSubset.isEmpty() )
1184 for (
const QString &child : childAlgorithmSubset )
1187 const QSet<QString> requirements = mModel->dependsOnChildAlgorithms( child );
1188 for (
const QString &requirement : requirements )
1190 if ( !mLastResult.executedChildIds().contains( requirement ) )
1192 QMessageBox messageBox;
1193 messageBox.setWindowTitle( tr(
"Run Model" ) );
1194 messageBox.setIcon( QMessageBox::Icon::Warning );
1195 messageBox.setText( tr(
"Prerequisite parts of this model have not yet been run (try running the full model first)." ) );
1196 messageBox.setStandardButtons( QMessageBox::StandardButton::Ok );
1204 if ( mAlgorithmWidget && mAlgorithmWidget->isRunning() )
1206 QMessageBox messageBox;
1207 messageBox.setIcon( QMessageBox::Icon::Warning );
1208 messageBox.setWindowTitle( tr(
"Run Model" ) );
1209 messageBox.setText( tr(
"This model is already running." ) );
1210 messageBox.setStandardButtons( QMessageBox::StandardButton::Cancel | QMessageBox::StandardButton::RestoreDefaults | QMessageBox::StandardButton::Ok );
1212 QAbstractButton *buttonShowRunningAlg = messageBox.button( QMessageBox::StandardButton::Ok );
1213 buttonShowRunningAlg->setText( tr(
"Show Progress" ) );
1215 QAbstractButton *buttonReRun = messageBox.button( QMessageBox::StandardButton::RestoreDefaults );
1216 buttonReRun->setText( tr(
"Cancel and Restart Model" ) );
1218 int r = messageBox.exec();
1222 case QMessageBox::StandardButton::Cancel:
1224 case QMessageBox::StandardButton::RestoreDefaults:
1225 cancelRunningModel();
1227 case QMessageBox::StandardButton::Ok:
1228 mAlgorithmWidget->showWidget();
1234 else if ( mAlgorithmWidget )
1237 mAlgorithmWidget->close();
1238 if ( mAlgorithmWidget )
1244 mAlgorithmWidgetsToCleanUp << mAlgorithmWidget;
1247 mAlgorithmWidget.clear();
1250 if ( !mAlgorithmWidget )
1252 mAlgorithmWidget = createExecutionWidget();
1253 mAlgorithmWidget->hideShortHelp();
1254 mAlgorithmWidget->setTitle( tr(
"Run Model" ) );
1257 mAlgorithmWidget->setParameters( mModel->designerParameterValues() );
1259 if ( !childAlgorithmSubset.isEmpty() )
1261 mAlgorithmWidget->runButton()->setText( tr(
"Run Subset" ) );
1262 mAlgorithmWidget->runButton()->setToolTip( tr(
"Runs a subset of the child algorithms from this model" ) );
1265 connect( mAlgorithmWidget.get(), &QgsProcessingAlgorithmWidgetBase::algorithmAboutToRun,
this, [
this, childAlgorithmSubset](
QgsProcessingContext *context ) {
1266 if ( !childAlgorithmSubset.empty() )
1269 auto modelConfig = std::make_unique<QgsProcessingModelInitialRunConfig>();
1270 modelConfig->setChildAlgorithmSubset( childAlgorithmSubset );
1271 modelConfig->setPreviouslyExecutedChildAlgorithms( mLastResult.executedChildIds() );
1272 modelConfig->setInitialChildInputs( mLastResult.rawChildInputs() );
1273 modelConfig->setInitialChildOutputs( mLastResult.rawChildOutputs() );
1277 const QMap<QString, QgsMapLayer *> previousOutputLayers = mLayerStore.temporaryLayerStore()->mapLayers();
1278 auto previousResultStore = std::make_unique<QgsMapLayerStore>();
1279 for ( auto it = previousOutputLayers.constBegin(); it != previousOutputLayers.constEnd(); ++it )
1281 std::unique_ptr<QgsMapLayer> clone( it.value()->clone() );
1282 clone->setId( it.value()->id() );
1283 previousResultStore->addMapLayer( clone.release() );
1285 previousResultStore->moveToThread( nullptr );
1286 modelConfig->setPreviousLayerStore( std::move( previousResultStore ) );
1287 context->setModelInitialRunConfig( std::move( modelConfig ) );
1289 mScene->resetChildAlgorithmItems( childAlgorithmSubset );
1292 for ( const QString &child : childAlgorithmSubset )
1294 const QSet< QString > outdated = mModel->dependentChildAlgorithms( child );
1295 mScene->flagChildrenAsOutdated( outdated );
1296 mOutdatedChildResults.unite( outdated );
1302 mScene->resetChildAlgorithmItems();
1306 connect( mAlgorithmWidget, &QgsProcessingAlgorithmWidgetBase::algorithmFinished,
this, [
this](
bool,
const QVariantMap & ) {
1310 mLayerStore.takeResultsFrom( *context );
1318void QgsModelDesignerDialog::showChildAlgorithmOutputs(
const QString &childId )
1320 const bool isOutdated = mOutdatedChildResults.contains( childId );
1321 const QString childDescription = mModel->childAlgorithm( childId ).description();
1324 const QVariantMap childAlgorithmOutputs = result.
outputs();
1325 if ( childAlgorithmOutputs.isEmpty() )
1327 mMessageBar->pushWarning( QString(), tr(
"No results are available for %1" ).arg( childDescription ) );
1334 mMessageBar->pushCritical( QString(), tr(
"Results cannot be shown for an invalid model component" ) );
1338 const QList<const QgsProcessingParameterDefinition *> outputParams =
algorithm->destinationParameterDefinitions();
1339 if ( outputParams.isEmpty() )
1342 QgsDebugError(
"Cannot show results for algorithms with no outputs" );
1346 bool foundResults =
false;
1349 const QVariant output = childAlgorithmOutputs.value( outputParam->name() );
1350 if ( !output.isValid() )
1353 if ( output.type() == QVariant::String )
1357 QgsDebugMsgLevel( u
"Loading previous result for %1: %2"_s.arg( outputParam->name(), output.toString() ), 2 );
1359 std::unique_ptr<QgsMapLayer> layer( resultLayer->clone() );
1362 if ( outputParams.size() > 1 )
1363 baseName = tr(
"%1 — %2" ).arg( childDescription, outputParam->name() );
1365 baseName = childDescription;
1369 QString name = baseName;
1374 name = tr(
"%1 (%2)" ).arg( baseName ).arg( counter );
1377 layer->setName( name );
1380 foundResults =
true;
1385 QgsDebugError( u
"Could not load previous result for %1: %2"_s.arg( outputParam->name(), output.toString() ) );
1390 if ( !foundResults )
1392 mMessageBar->pushWarning( QString(), tr(
"No results are available for %1" ).arg( childDescription ) );
1395 else if ( isOutdated )
1397 mMessageBar->pushWarning( QString(), tr(
"These results are outdated, and may not reflect the most recent model execution" ) );
1402void QgsModelDesignerDialog::showChildAlgorithmLog(
const QString &childId )
1404 const QString childDescription = mModel->childAlgorithm( childId ).description();
1409 if ( QgsModelChildAlgorithmGraphicItem *item = mScene->childAlgorithmItem( childId ) )
1411 result = item->results();
1413 if ( result.
htmlLog().isEmpty() )
1415 result = mLastResult.childResults().value( childId );
1418 if ( result.
htmlLog().isEmpty() )
1420 mMessageBar->pushWarning( QString(), tr(
"No log is available for %1" ).arg( childDescription ) );
1425 m.setWindowTitle( childDescription );
1426 m.setCheckBoxVisible(
false );
1427 m.setMessageAsHtml( result.
htmlLog() );
1431void QgsModelDesignerDialog::onItemFocused( QgsModelComponentGraphicItem *item )
1438 if ( !item || !item->component() )
1440 mConfigWidget->showComponentConfig(
nullptr, *context, widgetContext );
1444 mConfigWidget->showComponentConfig( item->component(), *context, widgetContext );
1446 if (
auto childAlgorithmItem = qobject_cast< QgsModelChildAlgorithmGraphicItem * >( item ) )
1448 connect( childAlgorithmItem, &QgsModelChildAlgorithmGraphicItem::rebuildConfigurationDockWidget, childAlgorithmItem, [
this] {
1453 mConfigWidget->showComponentConfig(
nullptr, *context, widgetContext );
1459void QgsModelDesignerDialog::validate()
1462 if ( model()->validate( issues ) )
1464 mMessageBar->pushSuccess( QString(), tr(
"Model is valid!" ) );
1469 QPushButton *detailsButton =
new QPushButton( tr(
"Details" ) );
1470 connect( detailsButton, &QPushButton::clicked, detailsButton, [detailsButton, issues] {
1472 dialog->
setTitle( tr(
"Model is Invalid" ) );
1474 QString longMessage = tr(
"<p>This model is not valid:</p>" ) + u
"<ul>"_s;
1475 for (
const QString &issue : issues )
1477 longMessage += u
"<li>%1</li>"_s.arg( issue );
1479 longMessage +=
"</ul>"_L1;
1484 messageWidget->layout()->addWidget( detailsButton );
1485 mMessageBar->clearWidgets();
1490void QgsModelDesignerDialog::reorderInputs()
1492 QgsModelInputReorderDialog dlg(
this );
1493 dlg.setModel( mModel.get() );
1496 const QStringList inputOrder = dlg.inputOrder();
1497 beginUndoCommand( tr(
"Reorder Inputs" ) );
1498 mModel->setParameterOrder( inputOrder );
1503void QgsModelDesignerDialog::reorderOutputs()
1505 QgsModelOutputReorderDialog dlg(
this );
1506 dlg.setModel( mModel.get() );
1509 const QStringList outputOrder = dlg.outputOrder();
1510 beginUndoCommand( tr(
"Reorder Outputs" ) );
1511 mModel->setOutputOrder( outputOrder );
1512 mModel->setOutputGroup( dlg.outputGroup() );
1517bool QgsModelDesignerDialog::isDirty()
const
1519 return mHasChanged && mUndoStack->index() != -1;
1522void QgsModelDesignerDialog::fillInputsTree()
1525 auto parametersItem = std::make_unique<QTreeWidgetItem>();
1526 parametersItem->setText( 0, tr(
"Parameters" ) );
1529 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
1536 auto paramItem = std::make_unique<QTreeWidgetItem>();
1537 paramItem->setText( 0, param->name() );
1538 paramItem->setData( 0, Qt::UserRole, param->id() );
1539 paramItem->setIcon( 0, icon );
1540 paramItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1541 paramItem->setToolTip( 0, param->description() );
1542 parametersItem->addChild( paramItem.release() );
1545 mInputsTreeWidget->addTopLevelItem( parametersItem.release() );
1546 mInputsTreeWidget->topLevelItem( 0 )->setExpanded(
true );
1554QgsModelChildDependenciesWidget::QgsModelChildDependenciesWidget( QWidget *parent, QgsProcessingModelAlgorithm *model,
const QString &childId )
1557 , mChildId( childId )
1559 QHBoxLayout *hl =
new QHBoxLayout();
1560 hl->setContentsMargins( 0, 0, 0, 0 );
1562 mLineEdit =
new QLineEdit();
1563 mLineEdit->setEnabled(
false );
1564 hl->addWidget( mLineEdit, 1 );
1566 mToolButton =
new QToolButton();
1567 mToolButton->setText( QString( QChar( 0x2026 ) ) );
1568 hl->addWidget( mToolButton );
1572 mLineEdit->setText( tr(
"%1 dependencies selected" ).arg( 0 ) );
1574 connect( mToolButton, &QToolButton::clicked,
this, &QgsModelChildDependenciesWidget::showDialog );
1577void QgsModelChildDependenciesWidget::setValue(
const QList<QgsProcessingModelChildDependency> &value )
1581 updateSummaryText();
1584void QgsModelChildDependenciesWidget::showDialog()
1586 const QList<QgsProcessingModelChildDependency> available = mModel->availableDependenciesForChildAlgorithm( mChildId );
1588 QVariantList availableOptions;
1589 for (
const QgsProcessingModelChildDependency &dep : available )
1590 availableOptions << QVariant::fromValue( dep );
1591 QVariantList selectedOptions;
1592 for (
const QgsProcessingModelChildDependency &dep : mValue )
1593 selectedOptions << QVariant::fromValue( dep );
1598 QgsProcessingMultipleSelectionPanelWidget *widget =
new QgsProcessingMultipleSelectionPanelWidget( availableOptions, selectedOptions );
1599 widget->setPanelTitle( tr(
"Algorithm Dependencies" ) );
1601 widget->setValueFormatter( [
this](
const QVariant &v ) -> QString {
1602 const QgsProcessingModelChildDependency dep = v.value<QgsProcessingModelChildDependency>();
1604 const QString description = mModel->childAlgorithm( dep.childId ).description();
1605 if ( dep.conditionalBranch.isEmpty() )
1608 return tr(
"Condition “%1” from algorithm “%2”" ).arg( dep.conditionalBranch, description );
1611 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged,
this, [
this, widget]() {
1612 QList<QgsProcessingModelChildDependency> res;
1613 for (
const QVariant &v : widget->selectedOptions() )
1615 res << v.value<QgsProcessingModelChildDependency>();
1624void QgsModelChildDependenciesWidget::updateSummaryText()
1626 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 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...
void removeAllMapLayers()
Removes all registered layers.
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.
QgsMapLayerStore * temporaryLayerStore()
Returns a reference to the layer store used for storing temporary layers during algorithm execution.
Base class for providing feedback from a processing algorithm.
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 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)