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();
584void QgsModelDesignerDialog::activate()
588 setWindowState( windowState() & ~Qt::WindowMinimized );
594 mProcessingContextGenerator = generator;
597void QgsModelDesignerDialog::updateVariablesGui()
599 mBlockUndoCommands++;
601 auto variablesScope = std::make_unique<QgsExpressionContextScope>( tr(
"Model Variables" ) );
602 const QVariantMap modelVars = mModel->variables();
603 for (
auto it = modelVars.constBegin(); it != modelVars.constEnd(); ++it )
605 variablesScope->setVariable( it.key(), it.value() );
608 variablesContext.
appendScope( variablesScope.release() );
609 mVariablesEditor->setContext( &variablesContext );
610 mVariablesEditor->setEditableScopeIndex( 0 );
612 mBlockUndoCommands--;
615void QgsModelDesignerDialog::setDirty(
bool dirty )
619 if ( mAlgorithmWidget )
621 if (
QgsMessageBar *messageBar = mAlgorithmWidget->messageBar() )
623 QgsMessageBarItem *messageBarItem = messageBar->createMessage( QString(), tr(
"The model has changed, this panel should be reloaded." ) );
624 auto reloadButton =
new QPushButton( tr(
"Reload Now" ) );
625 connect( reloadButton, &QPushButton::clicked, reloadButton, [
this] {
626 if ( mAlgorithmWidget && mAlgorithmWidget->isRunning() )
628 QMessageBox messageBox;
629 messageBox.setIcon( QMessageBox::Icon::Warning );
630 messageBox.setWindowTitle( tr(
"Run Model" ) );
631 messageBox.setText( tr(
"This model is currently running." ) );
632 messageBox.setStandardButtons( QMessageBox::StandardButton::Cancel | QMessageBox::StandardButton::RestoreDefaults );
634 QAbstractButton *buttonReRun = messageBox.button( QMessageBox::StandardButton::RestoreDefaults );
635 buttonReRun->setText( tr(
"Terminate and Reload" ) );
637 int r = messageBox.exec();
641 case QMessageBox::StandardButton::Cancel:
643 case QMessageBox::StandardButton::RestoreDefaults:
649 cancelRunningModel();
652 messageBarItem->layout()->addWidget( reloadButton );
658bool QgsModelDesignerDialog::validateSave( SaveAction action )
662 case QgsModelDesignerDialog::SaveAction::SaveAsFile:
664 case QgsModelDesignerDialog::SaveAction::SaveInProject:
665 if ( mNameEdit->text().trimmed().isEmpty() )
667 mMessageBar->pushWarning( QString(), tr(
"Please enter a model name before saving" ) );
676bool QgsModelDesignerDialog::checkForUnsavedChanges()
680 QMessageBox::StandardButton ret = QMessageBox::
681 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 );
684 case QMessageBox::Save:
685 return saveModel(
false );
687 case QMessageBox::Discard:
702 mLastResult.mergeWith( result );
704 mScene->setLastRunResult( mLastResult, mLayerStore );
707void QgsModelDesignerDialog::setModelName(
const QString &name )
709 mNameEdit->setText( name );
712void QgsModelDesignerDialog::zoomIn()
714 mView->setTransformationAnchor( QGraphicsView::NoAnchor );
715 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
717 const double factor = settings.
value( u
"/qgis/zoom_favor"_s, 2.0 ).toDouble();
718 mView->scale( factor, factor );
719 mView->centerOn( point );
722void QgsModelDesignerDialog::zoomOut()
724 mView->setTransformationAnchor( QGraphicsView::NoAnchor );
725 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
727 const double factor = 1.0 / settings.
value( u
"/qgis/zoom_favor"_s, 2.0 ).toDouble();
728 mView->scale( factor, factor );
729 mView->centerOn( point );
732void QgsModelDesignerDialog::zoomActual()
734 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
735 mView->resetTransform();
736 mView->scale( mScreenHelper->screenDpi() / 96, mScreenHelper->screenDpi() / 96 );
737 mView->centerOn( point );
740void QgsModelDesignerDialog::zoomFull()
742 QRectF totalRect = mView->scene()->itemsBoundingRect();
743 totalRect.adjust( -10, -10, 10, 10 );
744 mView->fitInView( totalRect, Qt::KeepAspectRatio );
747void QgsModelDesignerDialog::newModel()
749 if ( !checkForUnsavedChanges() )
752 auto alg = std::make_unique<QgsProcessingModelAlgorithm>();
754 setModel( alg.release() );
757void QgsModelDesignerDialog::exportToImage()
760 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
762 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as Image" ), lastExportDir, tr(
"PNG files (*.png *.PNG)" ) );
766 if ( filename.isEmpty() )
771 const QFileInfo saveFileInfo( filename );
774 repaintModel(
false );
776 QRectF totalRect = mView->scene()->itemsBoundingRect();
777 totalRect.adjust( -10, -10, 10, 10 );
778 const QRectF imageRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
780 QImage img( totalRect.width(), totalRect.height(), QImage::Format_ARGB32_Premultiplied );
781 img.fill( Qt::white );
783 painter.setRenderHint( QPainter::Antialiasing );
784 painter.begin( &img );
785 mView->scene()->render( &painter, imageRect, totalRect );
788 img.save( filename );
791 ->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 );
792 repaintModel(
true );
795void QgsModelDesignerDialog::exportToPdf()
798 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
800 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as PDF" ), lastExportDir, tr(
"PDF files (*.pdf *.PDF)" ) );
804 if ( filename.isEmpty() )
809 const QFileInfo saveFileInfo( filename );
812 repaintModel(
false );
814 QRectF totalRect = mView->scene()->itemsBoundingRect();
815 totalRect.adjust( -10, -10, 10, 10 );
816 const QRectF printerRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
818 QPdfWriter pdfWriter( filename );
820 const double scaleFactor = 96 / 25.4;
822 QPageLayout pageLayout( QPageSize( totalRect.size() / scaleFactor, QPageSize::Millimeter ), QPageLayout::Portrait, QMarginsF( 0, 0, 0, 0 ) );
823 pageLayout.setMode( QPageLayout::FullPageMode );
824 pdfWriter.setPageLayout( pageLayout );
826 QPainter painter( &pdfWriter );
827 mView->scene()->render( &painter, printerRect, totalRect );
831 ->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 );
832 repaintModel(
true );
835void QgsModelDesignerDialog::exportToSvg()
838 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
840 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as SVG" ), lastExportDir, tr(
"SVG files (*.svg *.SVG)" ) );
844 if ( filename.isEmpty() )
849 const QFileInfo saveFileInfo( filename );
852 repaintModel(
false );
854 QRectF totalRect = mView->scene()->itemsBoundingRect();
855 totalRect.adjust( -10, -10, 10, 10 );
856 const QRectF svgRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
859 svg.setFileName( filename );
860 svg.setSize( QSize( totalRect.width(), totalRect.height() ) );
861 svg.setViewBox( svgRect );
862 svg.setTitle( mModel->displayName() );
864 QPainter painter( &svg );
865 mView->scene()->render( &painter, svgRect, totalRect );
869 ->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 );
870 repaintModel(
true );
873void QgsModelDesignerDialog::exportAsPython()
876 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
878 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as Python Script" ), lastExportDir, tr(
"Processing scripts (*.py *.PY)" ) );
882 if ( filename.isEmpty() )
887 const QFileInfo saveFileInfo( filename );
892 QFile outFile( filename );
893 if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
897 QTextStream fout( &outFile );
902 ->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 );
905void QgsModelDesignerDialog::toggleComments(
bool show )
909 repaintModel(
true );
912void QgsModelDesignerDialog::toggleFeatureCount(
bool show )
916 repaintModel(
true );
919void QgsModelDesignerDialog::updateWindowTitle()
921 QString title = tr(
"Model Designer" );
922 if ( !mModel->name().isEmpty() )
923 title = mModel->group().isEmpty() ? u
"%1: %2"_s.arg( title, mModel->name() ) : u
"%1: %2 - %3"_s.arg( title, mModel->group(), mModel->name() );
926 title.prepend(
'*' );
928 setWindowTitle( title );
931void QgsModelDesignerDialog::deleteSelected()
933 QList<QgsModelComponentGraphicItem *> items = mScene->selectedComponentItems();
937 if ( items.size() == 1 )
939 items.at( 0 )->deleteComponent();
943 std::sort( items.begin(), items.end(), []( QgsModelComponentGraphicItem *p1, QgsModelComponentGraphicItem *p2 ) {
948 if ( dynamic_cast<QgsModelCommentGraphicItem *>( p1 ) && dynamic_cast<QgsModelCommentGraphicItem *>( p2 ) )
950 else if ( dynamic_cast<QgsModelCommentGraphicItem *>( p1 ) )
952 else if ( dynamic_cast<QgsModelCommentGraphicItem *>( p2 ) )
955 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p1 ) && dynamic_cast<QgsModelGroupBoxGraphicItem *>( p2 ) )
957 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p1 ) )
959 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p2 ) )
962 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p1 ) && dynamic_cast<QgsModelOutputGraphicItem *>( p2 ) )
964 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p1 ) )
966 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p2 ) )
969 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p1 ) && dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p2 ) )
971 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p1 ) )
973 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p2 ) )
980 beginUndoCommand( tr(
"Delete Components" ) );
982 QVariant prevState = mModel->toVariant();
983 mBlockUndoCommands++;
984 mBlockRepaints =
true;
986 while ( !items.empty() )
988 QgsModelComponentGraphicItem *toDelete =
nullptr;
989 for ( QgsModelComponentGraphicItem *item : items )
991 if ( item->canDeleteComponent() )
1004 toDelete->deleteComponent();
1005 items.removeAll( toDelete );
1010 mModel->loadVariant( prevState );
1011 QMessageBox::warning(
1013 QObject::tr(
"Could not remove components" ),
1015 "Components depend on the selected items.\n"
1016 "Try to remove them before trying deleting these components."
1019 mBlockUndoCommands--;
1020 mActiveCommand.reset();
1024 mBlockUndoCommands--;
1028 mBlockRepaints =
false;
1032void QgsModelDesignerDialog::populateZoomToMenu()
1034 mGroupMenu->clear();
1035 for (
const QgsProcessingModelGroupBox &box : model()->groupBoxes() )
1037 if ( QgsModelComponentGraphicItem *item = mScene->groupBoxItem( box.uuid() ) )
1039 QAction *zoomAction =
new QAction( box.description(), mGroupMenu );
1040 connect( zoomAction, &QAction::triggered,
this, [
this, item] {
1041 QRectF groupRect = item->mapToScene( item->boundingRect() ).boundingRect();
1042 groupRect.adjust( -10, -10, 10, 10 );
1043 mView->fitInView( groupRect, Qt::KeepAspectRatio );
1044 mView->centerOn( item );
1046 mGroupMenu->addAction( zoomAction );
1051void QgsModelDesignerDialog::setPanelVisibility(
bool hidden )
1053 const QList<QDockWidget *> docks = findChildren<QDockWidget *>();
1054 const QList<QTabBar *> tabBars = findChildren<QTabBar *>();
1058 mPanelStatus.clear();
1060 for ( QDockWidget *dock : docks )
1062 mPanelStatus.insert( dock->windowTitle(), PanelStatus( dock->isVisible(),
false ) );
1063 dock->setVisible(
false );
1067 for ( QTabBar *tabBar : tabBars )
1069 QString currentTabTitle = tabBar->tabText( tabBar->currentIndex() );
1070 mPanelStatus[currentTabTitle].isActive =
true;
1076 for ( QDockWidget *dock : docks )
1078 if ( mPanelStatus.contains( dock->windowTitle() ) )
1080 dock->setVisible( mPanelStatus.value( dock->windowTitle() ).isVisible );
1085 for ( QTabBar *tabBar : tabBars )
1088 for (
int i = 0; i < tabBar->count(); ++i )
1090 QString tabTitle = tabBar->tabText( i );
1091 if ( mPanelStatus.contains( tabTitle ) && mPanelStatus.value( tabTitle ).isActive )
1093 tabBar->setCurrentIndex( i );
1097 mPanelStatus.clear();
1101void QgsModelDesignerDialog::editHelp()
1103 QgsProcessingHelpEditorDialog dialog(
this );
1104 dialog.setWindowTitle( tr(
"Edit Model Help" ) );
1105 dialog.setAlgorithm( mModel.get() );
1106 if ( dialog.exec() )
1108 beginUndoCommand( tr(
"Edit Model Help" ) );
1109 mModel->setHelpContent( dialog.helpContent() );
1114void QgsModelDesignerDialog::runSelectedSteps()
1116 QSet<QString> children;
1117 const QList<QgsModelComponentGraphicItem *> items = mScene->selectedComponentItems();
1118 for ( QgsModelComponentGraphicItem *item : items )
1120 if ( QgsProcessingModelChildAlgorithm *childAlgorithm =
dynamic_cast<QgsProcessingModelChildAlgorithm *
>( item->component() ) )
1122 children.insert( childAlgorithm->childId() );
1126 if ( children.isEmpty() )
1128 mMessageBar->pushWarning( QString(), tr(
"No steps are selected" ) );
1135void QgsModelDesignerDialog::runFromChild(
const QString &
id )
1137 QSet<QString> children = mModel->dependentChildAlgorithms(
id );
1138 children.insert(
id );
1142void QgsModelDesignerDialog::cancelRunningModel()
1144 if ( !mAlgorithmWidget )
1150 mAlgorithmWidget->cancel();
1152 mAlgorithmWidget->forceClose();
1155 if ( mAlgorithmWidget )
1161 mAlgorithmWidgetsToCleanUp << mAlgorithmWidget;
1163 mAlgorithmWidget.clear();
1166void QgsModelDesignerDialog::run(
const QSet<QString> &childAlgorithmSubset )
1169 const bool isValid = model()->validate( errors );
1172 QMessageBox messageBox;
1173 messageBox.setWindowTitle( tr(
"Model is Invalid" ) );
1174 messageBox.setIcon( QMessageBox::Icon::Warning );
1175 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?" ) );
1176 messageBox.setStandardButtons( QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::Cancel );
1177 messageBox.setDefaultButton( QMessageBox::StandardButton::Cancel );
1179 QString errorString;
1180 for (
const QString &error : std::as_const( errors ) )
1182 QString cleanedError = error;
1183 const thread_local QRegularExpression re( u
"<[^>]*>"_s );
1184 cleanedError.replace( re, QString() );
1185 errorString += u
"• %1\n"_s.arg( cleanedError );
1188 messageBox.setDetailedText( errorString );
1189 if ( messageBox.exec() == QMessageBox::StandardButton::Cancel )
1193 if ( !childAlgorithmSubset.isEmpty() )
1195 for (
const QString &child : childAlgorithmSubset )
1198 const QSet<QString> requirements = mModel->dependsOnChildAlgorithms( child );
1199 for (
const QString &requirement : requirements )
1201 if ( !mLastResult.executedChildIds().contains( requirement ) )
1203 QMessageBox messageBox;
1204 messageBox.setWindowTitle( tr(
"Run Model" ) );
1205 messageBox.setIcon( QMessageBox::Icon::Warning );
1206 messageBox.setText( tr(
"Prerequisite parts of this model have not yet been run (try running the full model first)." ) );
1207 messageBox.setStandardButtons( QMessageBox::StandardButton::Ok );
1215 if ( mAlgorithmWidget && mAlgorithmWidget->isRunning() )
1217 QMessageBox messageBox;
1218 messageBox.setIcon( QMessageBox::Icon::Warning );
1219 messageBox.setWindowTitle( tr(
"Run Model" ) );
1220 messageBox.setText( tr(
"This model is already running." ) );
1221 messageBox.setStandardButtons( QMessageBox::StandardButton::Cancel | QMessageBox::StandardButton::RestoreDefaults | QMessageBox::StandardButton::Ok );
1223 QAbstractButton *buttonShowRunningAlg = messageBox.button( QMessageBox::StandardButton::Ok );
1224 buttonShowRunningAlg->setText( tr(
"Show Progress" ) );
1226 QAbstractButton *buttonReRun = messageBox.button( QMessageBox::StandardButton::RestoreDefaults );
1227 buttonReRun->setText( tr(
"Cancel and Restart Model" ) );
1229 int r = messageBox.exec();
1233 case QMessageBox::StandardButton::Cancel:
1235 case QMessageBox::StandardButton::RestoreDefaults:
1236 cancelRunningModel();
1238 case QMessageBox::StandardButton::Ok:
1239 mAlgorithmWidget->showWidget();
1245 else if ( mAlgorithmWidget )
1248 mAlgorithmWidget->close();
1249 if ( mAlgorithmWidget )
1255 mAlgorithmWidgetsToCleanUp << mAlgorithmWidget;
1258 mAlgorithmWidget.clear();
1261 if ( !mAlgorithmWidget )
1263 mAlgorithmWidget = createExecutionWidget();
1264 mAlgorithmWidget->hideShortHelp();
1265 mAlgorithmWidget->setTitle( tr(
"Run Model" ) );
1268 mAlgorithmWidget->setParameters( mModel->designerParameterValues() );
1270 if ( !childAlgorithmSubset.isEmpty() )
1272 mAlgorithmWidget->runButton()->setText( tr(
"Run Subset" ) );
1273 mAlgorithmWidget->runButton()->setToolTip( tr(
"Runs a subset of the child algorithms from this model" ) );
1276 connect( mAlgorithmWidget.get(), &QgsProcessingAlgorithmWidgetBase::algorithmAboutToRun,
this, [
this, childAlgorithmSubset](
QgsProcessingContext *context ) {
1277 if ( !childAlgorithmSubset.empty() )
1280 auto modelConfig = std::make_unique<QgsProcessingModelInitialRunConfig>();
1281 modelConfig->setChildAlgorithmSubset( childAlgorithmSubset );
1282 modelConfig->setPreviouslyExecutedChildAlgorithms( mLastResult.executedChildIds() );
1283 modelConfig->setInitialChildInputs( mLastResult.rawChildInputs() );
1284 modelConfig->setInitialChildOutputs( mLastResult.rawChildOutputs() );
1288 const QMap<QString, QgsMapLayer *> previousOutputLayers = mLayerStore.temporaryLayerStore()->mapLayers();
1289 auto previousResultStore = std::make_unique<QgsMapLayerStore>();
1290 for ( auto it = previousOutputLayers.constBegin(); it != previousOutputLayers.constEnd(); ++it )
1292 std::unique_ptr<QgsMapLayer> clone( it.value()->clone() );
1293 clone->setId( it.value()->id() );
1294 previousResultStore->addMapLayer( clone.release() );
1296 previousResultStore->moveToThread( nullptr );
1297 modelConfig->setPreviousLayerStore( std::move( previousResultStore ) );
1298 context->setModelInitialRunConfig( std::move( modelConfig ) );
1300 mScene->resetChildAlgorithmItems( childAlgorithmSubset );
1303 for ( const QString &child : childAlgorithmSubset )
1305 const QSet< QString > outdated = mModel->dependentChildAlgorithms( child );
1306 mScene->flagChildrenAsOutdated( outdated );
1307 mOutdatedChildResults.unite( outdated );
1313 mScene->resetChildAlgorithmItems();
1317 connect( mAlgorithmWidget, &QgsProcessingAlgorithmWidgetBase::algorithmFinished,
this, [
this](
bool,
const QVariantMap & ) {
1320 mLayerStore.temporaryLayerStore()->removeAllMapLayers();
1321 mLayerStore.takeResultsFrom( *context );
1329void QgsModelDesignerDialog::showChildAlgorithmOutputs(
const QString &childId )
1331 const bool isOutdated = mOutdatedChildResults.contains( childId );
1332 const QString childDescription = mModel->childAlgorithm( childId ).description();
1335 const QVariantMap childAlgorithmOutputs = result.
outputs();
1336 if ( childAlgorithmOutputs.isEmpty() )
1338 mMessageBar->pushWarning( QString(), tr(
"No results are available for %1" ).arg( childDescription ) );
1345 mMessageBar->pushCritical( QString(), tr(
"Results cannot be shown for an invalid model component" ) );
1349 const QList<const QgsProcessingParameterDefinition *> outputParams =
algorithm->destinationParameterDefinitions();
1350 if ( outputParams.isEmpty() )
1353 QgsDebugError(
"Cannot show results for algorithms with no outputs" );
1357 bool foundResults =
false;
1360 const QVariant output = childAlgorithmOutputs.value( outputParam->name() );
1361 if ( !output.isValid() )
1364 if ( output.type() == QVariant::String )
1368 QgsDebugMsgLevel( u
"Loading previous result for %1: %2"_s.arg( outputParam->name(), output.toString() ), 2 );
1370 std::unique_ptr<QgsMapLayer> layer( resultLayer->clone() );
1373 if ( outputParams.size() > 1 )
1374 baseName = tr(
"%1 — %2" ).arg( childDescription, outputParam->name() );
1376 baseName = childDescription;
1380 QString name = baseName;
1385 name = tr(
"%1 (%2)" ).arg( baseName ).arg( counter );
1388 layer->setName( name );
1391 foundResults =
true;
1396 QgsDebugError( u
"Could not load previous result for %1: %2"_s.arg( outputParam->name(), output.toString() ) );
1401 if ( !foundResults )
1403 mMessageBar->pushWarning( QString(), tr(
"No results are available for %1" ).arg( childDescription ) );
1406 else if ( isOutdated )
1408 mMessageBar->pushWarning( QString(), tr(
"These results are outdated, and may not reflect the most recent model execution" ) );
1413void QgsModelDesignerDialog::showChildAlgorithmLog(
const QString &childId )
1415 const QString childDescription = mModel->childAlgorithm( childId ).description();
1420 if ( QgsModelChildAlgorithmGraphicItem *item = mScene->childAlgorithmItem( childId ) )
1422 result = item->results();
1424 if ( result.
htmlLog().isEmpty() )
1426 result = mLastResult.childResults().value( childId );
1429 if ( result.
htmlLog().isEmpty() )
1431 mMessageBar->pushWarning( QString(), tr(
"No log is available for %1" ).arg( childDescription ) );
1436 m.setWindowTitle( childDescription );
1437 m.setCheckBoxVisible(
false );
1438 m.setMessageAsHtml( result.
htmlLog() );
1442void QgsModelDesignerDialog::onItemFocused( QgsModelComponentGraphicItem *item )
1449 if ( !item || !item->component() )
1451 mConfigWidget->showComponentConfig(
nullptr, *context, widgetContext );
1455 mConfigWidget->showComponentConfig( item->component(), *context, widgetContext );
1457 if (
auto childAlgorithmItem = qobject_cast< QgsModelChildAlgorithmGraphicItem * >( item ) )
1459 connect( childAlgorithmItem, &QgsModelChildAlgorithmGraphicItem::rebuildConfigurationDockWidget, childAlgorithmItem, [
this] {
1464 mConfigWidget->showComponentConfig(
nullptr, *context, widgetContext );
1470void QgsModelDesignerDialog::validate()
1473 if ( model()->validate( issues ) )
1475 mMessageBar->pushSuccess( QString(), tr(
"Model is valid!" ) );
1480 QPushButton *detailsButton =
new QPushButton( tr(
"Details" ) );
1481 connect( detailsButton, &QPushButton::clicked, detailsButton, [detailsButton, issues] {
1483 dialog->
setTitle( tr(
"Model is Invalid" ) );
1485 QString longMessage = tr(
"<p>This model is not valid:</p>" ) + u
"<ul>"_s;
1486 for (
const QString &issue : issues )
1488 longMessage += u
"<li>%1</li>"_s.arg( issue );
1490 longMessage +=
"</ul>"_L1;
1495 messageWidget->layout()->addWidget( detailsButton );
1496 mMessageBar->clearWidgets();
1501void QgsModelDesignerDialog::reorderInputs()
1503 QgsModelInputReorderDialog dlg(
this );
1504 dlg.setModel( mModel.get() );
1507 const QStringList inputOrder = dlg.inputOrder();
1508 beginUndoCommand( tr(
"Reorder Inputs" ) );
1509 mModel->setParameterOrder( inputOrder );
1514void QgsModelDesignerDialog::reorderOutputs()
1516 QgsModelOutputReorderDialog dlg(
this );
1517 dlg.setModel( mModel.get() );
1520 const QStringList outputOrder = dlg.outputOrder();
1521 beginUndoCommand( tr(
"Reorder Outputs" ) );
1522 mModel->setOutputOrder( outputOrder );
1523 mModel->setOutputGroup( dlg.outputGroup() );
1528bool QgsModelDesignerDialog::isDirty()
const
1530 return mHasChanged && mUndoStack->index() != -1;
1533void QgsModelDesignerDialog::fillInputsTree()
1536 auto parametersItem = std::make_unique<QTreeWidgetItem>();
1537 parametersItem->setText( 0, tr(
"Parameters" ) );
1540 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
1547 auto paramItem = std::make_unique<QTreeWidgetItem>();
1548 paramItem->setText( 0, param->name() );
1549 paramItem->setData( 0, Qt::UserRole, param->id() );
1550 paramItem->setIcon( 0, icon );
1551 paramItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1552 paramItem->setToolTip( 0, param->description() );
1553 parametersItem->addChild( paramItem.release() );
1556 mInputsTreeWidget->addTopLevelItem( parametersItem.release() );
1557 mInputsTreeWidget->topLevelItem( 0 )->setExpanded(
true );
1565QgsModelChildDependenciesWidget::QgsModelChildDependenciesWidget( QWidget *parent, QgsProcessingModelAlgorithm *model,
const QString &childId )
1568 , mChildId( childId )
1570 QHBoxLayout *hl =
new QHBoxLayout();
1571 hl->setContentsMargins( 0, 0, 0, 0 );
1573 mLineEdit =
new QLineEdit();
1574 mLineEdit->setEnabled(
false );
1575 hl->addWidget( mLineEdit, 1 );
1577 mToolButton =
new QToolButton();
1578 mToolButton->setText( QString( QChar( 0x2026 ) ) );
1579 hl->addWidget( mToolButton );
1583 mLineEdit->setText( tr(
"%1 dependencies selected" ).arg( 0 ) );
1585 connect( mToolButton, &QToolButton::clicked,
this, &QgsModelChildDependenciesWidget::showDialog );
1588void QgsModelChildDependenciesWidget::setValue(
const QList<QgsProcessingModelChildDependency> &value )
1592 updateSummaryText();
1595void QgsModelChildDependenciesWidget::showDialog()
1597 const QList<QgsProcessingModelChildDependency> available = mModel->availableDependenciesForChildAlgorithm( mChildId );
1599 QVariantList availableOptions;
1600 for (
const QgsProcessingModelChildDependency &dep : available )
1601 availableOptions << QVariant::fromValue( dep );
1602 QVariantList selectedOptions;
1603 for (
const QgsProcessingModelChildDependency &dep : mValue )
1604 selectedOptions << QVariant::fromValue( dep );
1609 QgsProcessingMultipleSelectionPanelWidget *widget =
new QgsProcessingMultipleSelectionPanelWidget( availableOptions, selectedOptions );
1610 widget->setPanelTitle( tr(
"Algorithm Dependencies" ) );
1612 widget->setValueFormatter( [
this](
const QVariant &v ) -> QString {
1613 const QgsProcessingModelChildDependency dep = v.value<QgsProcessingModelChildDependency>();
1615 const QString description = mModel->childAlgorithm( dep.childId ).description();
1616 if ( dep.conditionalBranch.isEmpty() )
1619 return tr(
"Condition “%1” from algorithm “%2”" ).arg( dep.conditionalBranch, description );
1622 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged,
this, [
this, widget]() {
1623 QList<QgsProcessingModelChildDependency> res;
1624 for (
const QVariant &v : widget->selectedOptions() )
1626 res << v.value<QgsProcessingModelChildDependency>();
1635void QgsModelChildDependenciesWidget::updateSummaryText()
1637 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.