49#include <QActionGroup>
52#include <QKeySequence>
58#include <QSvgGenerator>
65#include "moc_qgsmodeldesignerdialog.cpp"
67using namespace Qt::StringLiterals;
72QgsModelerToolboxModel::QgsModelerToolboxModel( QObject *parent )
76Qt::ItemFlags QgsModelerToolboxModel::flags(
const QModelIndex &index )
const
78 Qt::ItemFlags f = QgsProcessingToolboxProxyModel::flags( index );
79 const QModelIndex sourceIndex = mapToSource( index );
80 if ( toolboxModel()->isAlgorithm( sourceIndex ) || toolboxModel()->isParameter( sourceIndex ) )
82 f = f | Qt::ItemIsDragEnabled;
87Qt::DropActions QgsModelerToolboxModel::supportedDragActions()
const
89 return Qt::CopyAction;
92QgsModelDesignerDialog::QgsModelDesignerDialog( QWidget *parent, Qt::WindowFlags flags )
93 : QMainWindow( parent, flags )
94 , mToolsActionGroup( new QActionGroup( this ) )
102 setAttribute( Qt::WA_DeleteOnClose );
103 setDockOptions( dockOptions() | QMainWindow::GroupedDragging );
104 setWindowFlags( Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint );
108 mModel = std::make_unique<QgsProcessingModelAlgorithm>();
111 mUndoStack =
new QUndoStack(
this );
112 connect( mUndoStack, &QUndoStack::indexChanged,
this, [
this] {
113 if ( mIgnoreUndoStackChanges )
116 mBlockUndoCommands++;
117 updateVariablesGui();
118 mGroupEdit->setText( mModel->group() );
119 mNameEdit->setText( mModel->displayName() );
120 mBlockUndoCommands--;
125 mConfigWidgetDock->setWindowTitle( tr(
"Configuration" ) );
126 mConfigWidgetDock->setObjectName( u
"ModelConfigDock"_s );
129 mConfigWidgetDock->setWidget( mConfigWidget );
130 mConfigWidgetDock->setFeatures( QDockWidget::NoDockWidgetFeatures );
133 mPropertiesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
134 mInputsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
135 mAlgorithmsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
136 mVariablesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
138 mToolboxTree->header()->setVisible(
false );
139 mToolboxSearchEdit->setShowSearchIcon(
true );
140 mToolboxSearchEdit->setPlaceholderText( tr(
"Search…" ) );
141 connect( mToolboxSearchEdit, &QgsFilterLineEdit::textChanged, mToolboxTree, &QgsProcessingToolboxTreeView::setFilterString );
143 mInputsTreeWidget->header()->setVisible(
false );
144 mInputsTreeWidget->setAlternatingRowColors(
true );
145 mInputsTreeWidget->setDragDropMode( QTreeWidget::DragOnly );
146 mInputsTreeWidget->setDropIndicatorShown(
true );
148 mNameEdit->setPlaceholderText( tr(
"Enter model name here" ) );
149 mGroupEdit->setPlaceholderText( tr(
"Enter group name here" ) );
152 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
153 mainLayout->insertWidget( 0, mMessageBar );
155 mView->setAcceptDrops(
true );
158 connect( mActionClose, &QAction::triggered,
this, &QWidget::close );
159 connect( mActionNew, &QAction::triggered,
this, &QgsModelDesignerDialog::newModel );
160 connect( mActionZoomIn, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomIn );
161 connect( mActionZoomOut, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomOut );
162 connect( mActionZoomActual, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomActual );
163 connect( mActionZoomToItems, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomFull );
164 connect( mActionExportImage, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToImage );
165 connect( mActionExportPdf, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToPdf );
166 connect( mActionExportSvg, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToSvg );
167 connect( mActionExportPython, &QAction::triggered,
this, &QgsModelDesignerDialog::exportAsPython );
168 connect( mActionSave, &QAction::triggered,
this, [
this] { saveModel(
false ); } );
169 connect( mActionSaveAs, &QAction::triggered,
this, [
this] { saveModel(
true ); } );
170 connect( mActionDeleteComponents, &QAction::triggered,
this, &QgsModelDesignerDialog::deleteSelected );
171 connect( mActionSnapSelected, &QAction::triggered, mView, &QgsModelGraphicsView::snapSelected );
172 connect( mActionValidate, &QAction::triggered,
this, &QgsModelDesignerDialog::validate );
173 connect( mActionReorderInputs, &QAction::triggered,
this, &QgsModelDesignerDialog::reorderInputs );
174 connect( mActionReorderOutputs, &QAction::triggered,
this, &QgsModelDesignerDialog::reorderOutputs );
175 connect( mActionEditHelp, &QAction::triggered,
this, &QgsModelDesignerDialog::editHelp );
176 connect( mReorderInputsButton, &QPushButton::clicked,
this, &QgsModelDesignerDialog::reorderInputs );
177 connect( mActionRun, &QAction::triggered,
this, [
this] { run(); } );
178 connect( mActionRunSelectedSteps, &QAction::triggered,
this, &QgsModelDesignerDialog::runSelectedSteps );
180 mActionSnappingEnabled->setChecked( settings.
value( u
"/Processing/Modeler/enableSnapToGrid"_s,
false ).toBool() );
181 connect( mActionSnappingEnabled, &QAction::toggled,
this, [
this](
bool enabled ) {
182 mView->snapper()->setSnapToGrid( enabled );
185 mView->snapper()->setSnapToGrid( mActionSnappingEnabled->isChecked() );
187 connect( mView, &QgsModelGraphicsView::itemFocused,
this, &QgsModelDesignerDialog::onItemFocused );
189 connect( mActionSelectAll, &QAction::triggered,
this, [
this] { mScene->selectAll(); } );
191 QStringList docksTitle = settings.
value( u
"ModelDesigner/hiddenDocksTitle"_s, QStringList(),
QgsSettings::App ).toStringList();
192 QStringList docksActive = settings.
value( u
"ModelDesigner/hiddenDocksActive"_s, QStringList(),
QgsSettings::App ).toStringList();
193 if ( !docksTitle.isEmpty() )
195 for (
const auto &title : docksTitle )
197 mPanelStatus.insert( title, PanelStatus(
true, docksActive.contains( title ) ) );
200 mActionHidePanels->setChecked( !docksTitle.isEmpty() );
201 connect( mActionHidePanels, &QAction::toggled,
this, &QgsModelDesignerDialog::setPanelVisibility );
203 mUndoAction = mUndoStack->createUndoAction(
this );
205 mUndoAction->setShortcuts( QKeySequence::Undo );
206 mRedoAction = mUndoStack->createRedoAction(
this );
208 mRedoAction->setShortcuts( QKeySequence::Redo );
210 mMenuEdit->insertAction( mActionDeleteComponents, mRedoAction );
211 mMenuEdit->insertAction( mActionDeleteComponents, mUndoAction );
212 mMenuEdit->insertSeparator( mActionDeleteComponents );
213 mToolbar->insertAction( mActionZoomIn, mUndoAction );
214 mToolbar->insertAction( mActionZoomIn, mRedoAction );
215 mToolbar->insertSeparator( mActionZoomIn );
217 mGroupMenu =
new QMenu( tr(
"Zoom To" ),
this );
218 mMenuView->insertMenu( mActionZoomIn, mGroupMenu );
219 connect( mGroupMenu, &QMenu::aboutToShow,
this, &QgsModelDesignerDialog::populateZoomToMenu );
223 mActionCut =
new QAction( tr(
"Cu&t" ),
this );
224 mActionCut->setShortcuts( QKeySequence::Cut );
225 mActionCut->setStatusTip( tr(
"Cut" ) );
227 connect( mActionCut, &QAction::triggered,
this, [
this] { mView->copySelectedItems( QgsModelGraphicsView::ClipboardCut ); } );
229 mActionCopy =
new QAction( tr(
"&Copy" ),
this );
230 mActionCopy->setShortcuts( QKeySequence::Copy );
231 mActionCopy->setStatusTip( tr(
"Copy" ) );
233 connect( mActionCopy, &QAction::triggered,
this, [
this] { mView->copySelectedItems( QgsModelGraphicsView::ClipboardCopy ); } );
235 mActionPaste =
new QAction( tr(
"&Paste" ),
this );
236 mActionPaste->setShortcuts( QKeySequence::Paste );
237 mActionPaste->setStatusTip( tr(
"Paste" ) );
239 connect( mActionPaste, &QAction::triggered,
this, [
this] { mView->pasteItems( QgsModelGraphicsView::PasteModeCursor ); } );
240 mMenuEdit->insertAction( mActionDeleteComponents, mActionCut );
241 mMenuEdit->insertAction( mActionDeleteComponents, mActionCopy );
242 mMenuEdit->insertAction( mActionDeleteComponents, mActionPaste );
243 mMenuEdit->insertSeparator( mActionDeleteComponents );
245 mAlgorithmsModel =
new QgsModelerToolboxModel(
this );
246 mToolboxTree->setToolboxProxyModel( mAlgorithmsModel );
249 if ( settings.
value( u
"Processing/Configuration/SHOW_ALGORITHMS_KNOWN_ISSUES"_s,
false ).toBool() )
253 mToolboxTree->setFilters( filters );
254 mToolboxTree->setDragDropMode( QTreeWidget::DragOnly );
255 mToolboxTree->setDropIndicatorShown(
true );
257 connect( mView, &QgsModelGraphicsView::algorithmDropped,
this, [
this](
const QString &algorithmId,
const QPointF &pos ) { addAlgorithm( algorithmId, pos ); } );
258 connect( mView, &QgsModelGraphicsView::inputDropped,
this, &QgsModelDesignerDialog::addInput );
260 connect( mToolboxTree, &QgsProcessingToolboxTreeView::doubleClicked,
this, [
this](
const QModelIndex & ) {
261 if ( mToolboxTree->selectedAlgorithm() )
262 addAlgorithm( mToolboxTree->selectedAlgorithm()->id(), QPointF() );
263 if ( mToolboxTree->selectedParameterType() )
264 addInput( mToolboxTree->selectedParameterType()->id(), QPointF() );
267 connect( mInputsTreeWidget, &QgsModelDesignerInputsTreeWidget::doubleClicked,
this, [
this](
const QModelIndex & ) {
268 const QString parameterType = mInputsTreeWidget->currentItem()->data( 0, Qt::UserRole ).toString();
269 addInput( parameterType, QPointF() );
273 QShortcut *ctrlEquals =
new QShortcut( QKeySequence( u
"Ctrl+="_s ),
this );
274 connect( ctrlEquals, &QShortcut::activated,
this, &QgsModelDesignerDialog::zoomIn );
277 mUndoDock->setObjectName( u
"UndoDock"_s );
278 mUndoView =
new QUndoView( mUndoStack,
this );
279 mUndoDock->setWidget( mUndoView );
280 mUndoDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
281 addDockWidget( Qt::DockWidgetArea::LeftDockWidgetArea, mUndoDock );
283 tabifyDockWidget( mUndoDock, mPropertiesDock );
284 tabifyDockWidget( mVariablesDock, mPropertiesDock );
285 mPropertiesDock->raise();
286 tabifyDockWidget( mInputsDock, mAlgorithmsDock );
287 mInputsDock->raise();
292 beginUndoCommand( tr(
"Change Model Variables" ) );
293 mModel->setVariables( mVariablesEditor->variablesInActiveScope() );
297 connect( mNameEdit, &QLineEdit::textChanged,
this, [
this](
const QString &name ) {
300 beginUndoCommand( tr(
"Change Model Name" ), QString(), QgsModelUndoCommand::CommandOperation::NameChanged );
301 mModel->setName( name );
306 connect( mGroupEdit, &QLineEdit::textChanged,
this, [
this](
const QString &group ) {
309 beginUndoCommand( tr(
"Change Model Group" ), QString(), QgsModelUndoCommand::CommandOperation::GroupChanged );
310 mModel->setGroup( group );
318 QToolButton *toolbuttonExportToScript =
new QToolButton();
319 toolbuttonExportToScript->setPopupMode( QToolButton::InstantPopup );
320 toolbuttonExportToScript->addAction( mActionExportAsScriptAlgorithm );
321 toolbuttonExportToScript->setDefaultAction( mActionExportAsScriptAlgorithm );
322 mToolbar->insertWidget( mActionExportImage, toolbuttonExportToScript );
323 connect( mActionExportAsScriptAlgorithm, &QAction::triggered,
this, &QgsModelDesignerDialog::exportAsScriptAlgorithm );
325 mActionShowComments->setChecked( settings.
value( u
"/Processing/Modeler/ShowComments"_s,
true ).toBool() );
326 connect( mActionShowComments, &QAction::toggled,
this, &QgsModelDesignerDialog::toggleComments );
328 mActionShowFeatureCount->setChecked( settings.
value( u
"/Processing/Modeler/ShowFeatureCount"_s,
true ).toBool() );
329 connect( mActionShowFeatureCount, &QAction::toggled,
this, &QgsModelDesignerDialog::toggleFeatureCount );
332 mPanTool->setAction( mActionPan );
334 mToolsActionGroup->addAction( mActionPan );
335 connect( mActionPan, &QAction::triggered, mPanTool, [
this] { mView->setTool( mPanTool ); } );
340 mSelectTool->setAction( mActionSelectMoveItem );
342 mToolsActionGroup->addAction( mActionSelectMoveItem );
343 connect( mActionSelectMoveItem, &QAction::triggered, mSelectTool, [
this] { mView->setTool( mSelectTool ); } );
345 mView->setTool( mSelectTool );
348 connect( mView, &QgsModelGraphicsView::macroCommandStarted,
this, [
this](
const QString &text ) {
349 mIgnoreUndoStackChanges++;
350 mUndoStack->beginMacro( text );
351 mIgnoreUndoStackChanges--;
353 connect( mView, &QgsModelGraphicsView::macroCommandEnded,
this, [
this] {
354 mIgnoreUndoStackChanges++;
355 mUndoStack->endMacro();
356 mIgnoreUndoStackChanges--;
358 connect( mView, &QgsModelGraphicsView::commandBegun,
this, [
this](
const QString &text ) { beginUndoCommand( text ); } );
359 connect( mView, &QgsModelGraphicsView::commandEnded,
this, [
this] { endUndoCommand(); } );
360 connect( mView, &QgsModelGraphicsView::commandAborted,
this, [
this] { abortUndoCommand(); } );
361 connect( mView, &QgsModelGraphicsView::deleteSelectedItems,
this, [
this] { deleteSelected(); } );
363 connect( mActionAddGroupBox, &QAction::triggered,
this, [
this] {
364 const QPointF viewCenter = mView->mapToScene( mView->viewport()->rect().center() );
365 QgsProcessingModelGroupBox group;
366 group.setPosition( viewCenter );
367 group.setDescription( tr(
"New Group" ) );
369 beginUndoCommand( tr(
"Add Group Box" ) );
370 model()->addGroupBox( group );
378 restoreState( settings.
value( u
"ModelDesigner/state"_s, QByteArray(),
QgsSettings::App ).toByteArray() );
381QgsModelDesignerDialog::~QgsModelDesignerDialog()
383 if ( mAlgorithmWidget )
385 delete mAlgorithmWidget;
387 for (
const QPointer<QgsProcessingAlgorithmWidgetBase> &widget : std::as_const( mAlgorithmWidgetsToCleanUp ) )
400 if ( !mPanelStatus.isEmpty() )
402 QStringList docksTitle;
403 QStringList docksActive;
405 for (
const auto &panel : mPanelStatus.toStdMap() )
407 if ( panel.second.isVisible )
408 docksTitle << panel.first;
409 if ( panel.second.isActive )
410 docksActive << panel.first;
424 mIgnoreUndoStackChanges++;
427void QgsModelDesignerDialog::closeEvent( QCloseEvent *event )
429 if ( checkForUnsavedChanges() )
435void QgsModelDesignerDialog::beginUndoCommand(
const QString &text,
const QString &
id, QgsModelUndoCommand::CommandOperation operation )
437 if ( mBlockUndoCommands || !mUndoStack )
440 if ( mActiveCommand )
445 mActiveCommand = std::make_unique<QgsModelUndoCommand>( mModel.get(), text,
id );
449 mActiveCommand = std::make_unique<QgsModelUndoCommand>( mModel.get(), text, operation );
453void QgsModelDesignerDialog::endUndoCommand()
455 if ( mBlockUndoCommands || !mActiveCommand || !mUndoStack )
458 mActiveCommand->saveAfterState();
459 mIgnoreUndoStackChanges++;
460 mUndoStack->push( mActiveCommand.release() );
461 mIgnoreUndoStackChanges--;
465void QgsModelDesignerDialog::abortUndoCommand()
467 if ( mActiveCommand )
468 mActiveCommand->setObsolete(
true );
471QgsProcessingModelAlgorithm *QgsModelDesignerDialog::model()
476void QgsModelDesignerDialog::setModel( QgsProcessingModelAlgorithm *model )
478 mModel.reset( model );
480 mGroupEdit->setText( mModel->group() );
481 mNameEdit->setText( mModel->displayName() );
482 repaintModel(
true );
483 updateVariablesGui();
487 mIgnoreUndoStackChanges++;
489 mIgnoreUndoStackChanges--;
495 QTimer::singleShot( 100,
this, [
this] { zoomFull(); } );
498void QgsModelDesignerDialog::loadModel(
const QString &path )
500 auto alg = std::make_unique<QgsProcessingModelAlgorithm>();
501 if ( alg->fromFile( path ) )
504 alg->setSourceFilePath( path );
505 setModel( alg.release() );
510 QMessageBox::critical(
514 "The selected model could not be loaded.\n"
515 "See the log for more information."
521void QgsModelDesignerDialog::setModelScene( QgsModelGraphicsScene *scene )
523 QgsModelGraphicsScene *oldScene = mScene;
526 mScene->setParent(
this );
527 mScene->setLastRunResult( mLastResult, mLayerStore );
528 mScene->setModel( mModel.get() );
529 mScene->setMessageBar( mMessageBar );
532 const bool showFeatureCount = settings.
value( u
"/Processing/Modeler/ShowFeatureCount"_s,
true ).toBool();
533 if ( !showFeatureCount )
534 mScene->setFlag( QgsModelGraphicsScene::FlagHideFeatureCount );
536 mView->setModelScene( mScene );
538 mSelectTool->resetCache();
539 mSelectTool->setScene( mScene );
541 connect( mScene, &QgsModelGraphicsScene::rebuildRequired,
this, [
this] {
542 if ( mBlockRepaints )
546 mScene->flagChildrenAsOutdated( mOutdatedChildResults );
548 connect( mScene, &QgsModelGraphicsScene::componentAboutToChange,
this, [
this](
const QString &description,
const QString &
id ) { beginUndoCommand( description,
id ); } );
549 connect( mScene, &QgsModelGraphicsScene::componentChanged,
this, [
this] { endUndoCommand(); } );
550 connect( mScene, &QgsModelGraphicsScene::runFromChild,
this, &QgsModelDesignerDialog::runFromChild );
551 connect( mScene, &QgsModelGraphicsScene::runSelected,
this, &QgsModelDesignerDialog::runSelectedSteps );
552 connect( mScene, &QgsModelGraphicsScene::showChildAlgorithmOutputs,
this, &QgsModelDesignerDialog::showChildAlgorithmOutputs );
553 connect( mScene, &QgsModelGraphicsScene::showChildAlgorithmLog,
this, &QgsModelDesignerDialog::showChildAlgorithmLog );
556 oldScene->deleteLater();
559QgsModelGraphicsScene *QgsModelDesignerDialog::modelScene()
566 auto result = std::make_unique< QgsProcessingModelFeedback >();
567 mScene->setupFeedbackConnections( result.get() );
569 mOutdatedChildResults.remove( childId );
571 return result.release();
574void QgsModelDesignerDialog::activate()
578 setWindowState( windowState() & ~Qt::WindowMinimized );
584 mProcessingContextGenerator = generator;
587void QgsModelDesignerDialog::updateVariablesGui()
589 mBlockUndoCommands++;
591 auto variablesScope = std::make_unique<QgsExpressionContextScope>( tr(
"Model Variables" ) );
592 const QVariantMap modelVars = mModel->variables();
593 for (
auto it = modelVars.constBegin(); it != modelVars.constEnd(); ++it )
595 variablesScope->setVariable( it.key(), it.value() );
598 variablesContext.
appendScope( variablesScope.release() );
599 mVariablesEditor->setContext( &variablesContext );
600 mVariablesEditor->setEditableScopeIndex( 0 );
602 mBlockUndoCommands--;
605void QgsModelDesignerDialog::setDirty(
bool dirty )
609 if ( mAlgorithmWidget )
611 if (
QgsMessageBar *messageBar = mAlgorithmWidget->messageBar() )
613 QgsMessageBarItem *messageBarItem = messageBar->createMessage( QString(), tr(
"The model has changed, this panel should be reloaded." ) );
614 auto reloadButton =
new QPushButton( tr(
"Reload Now" ) );
615 connect( reloadButton, &QPushButton::clicked, reloadButton, [
this] {
616 if ( mAlgorithmWidget && mAlgorithmWidget->isRunning() )
618 QMessageBox messageBox;
619 messageBox.setIcon( QMessageBox::Icon::Warning );
620 messageBox.setWindowTitle( tr(
"Run Model" ) );
621 messageBox.setText( tr(
"This model is currently running." ) );
622 messageBox.setStandardButtons( QMessageBox::StandardButton::Cancel | QMessageBox::StandardButton::RestoreDefaults );
624 QAbstractButton *buttonReRun = messageBox.button( QMessageBox::StandardButton::RestoreDefaults );
625 buttonReRun->setText( tr(
"Terminate and Reload" ) );
627 int r = messageBox.exec();
631 case QMessageBox::StandardButton::Cancel:
633 case QMessageBox::StandardButton::RestoreDefaults:
639 cancelRunningModel();
642 messageBarItem->layout()->addWidget( reloadButton );
648bool QgsModelDesignerDialog::validateSave( SaveAction action )
652 case QgsModelDesignerDialog::SaveAction::SaveAsFile:
654 case QgsModelDesignerDialog::SaveAction::SaveInProject:
655 if ( mNameEdit->text().trimmed().isEmpty() )
657 mMessageBar->pushWarning( QString(), tr(
"Please enter a model name before saving" ) );
666bool QgsModelDesignerDialog::checkForUnsavedChanges()
670 QMessageBox::StandardButton ret = QMessageBox::
671 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 );
674 case QMessageBox::Save:
675 return saveModel(
false );
677 case QMessageBox::Discard:
692 mLastResult.mergeWith( result );
694 mScene->setLastRunResult( mLastResult, mLayerStore );
697void QgsModelDesignerDialog::setModelName(
const QString &name )
699 mNameEdit->setText( name );
702void QgsModelDesignerDialog::zoomIn()
704 mView->setTransformationAnchor( QGraphicsView::NoAnchor );
705 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
707 const double factor = settings.
value( u
"/qgis/zoom_favor"_s, 2.0 ).toDouble();
708 mView->scale( factor, factor );
709 mView->centerOn( point );
712void QgsModelDesignerDialog::zoomOut()
714 mView->setTransformationAnchor( QGraphicsView::NoAnchor );
715 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
717 const double factor = 1.0 / settings.
value( u
"/qgis/zoom_favor"_s, 2.0 ).toDouble();
718 mView->scale( factor, factor );
719 mView->centerOn( point );
722void QgsModelDesignerDialog::zoomActual()
724 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
725 mView->resetTransform();
726 mView->scale( mScreenHelper->screenDpi() / 96, mScreenHelper->screenDpi() / 96 );
727 mView->centerOn( point );
730void QgsModelDesignerDialog::zoomFull()
732 QRectF totalRect = mView->scene()->itemsBoundingRect();
733 totalRect.adjust( -10, -10, 10, 10 );
734 mView->fitInView( totalRect, Qt::KeepAspectRatio );
737void QgsModelDesignerDialog::newModel()
739 if ( !checkForUnsavedChanges() )
742 auto alg = std::make_unique<QgsProcessingModelAlgorithm>();
744 setModel( alg.release() );
747void QgsModelDesignerDialog::exportToImage()
750 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
752 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as Image" ), lastExportDir, tr(
"PNG files (*.png *.PNG)" ) );
756 if ( filename.isEmpty() )
761 const QFileInfo saveFileInfo( filename );
764 repaintModel(
false );
766 QRectF totalRect = mView->scene()->itemsBoundingRect();
767 totalRect.adjust( -10, -10, 10, 10 );
768 const QRectF imageRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
770 QImage img( totalRect.width(), totalRect.height(), QImage::Format_ARGB32_Premultiplied );
771 img.fill( Qt::white );
773 painter.setRenderHint( QPainter::Antialiasing );
774 painter.begin( &img );
775 mView->scene()->render( &painter, imageRect, totalRect );
778 img.save( filename );
781 ->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 );
782 repaintModel(
true );
785void QgsModelDesignerDialog::exportToPdf()
788 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
790 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as PDF" ), lastExportDir, tr(
"PDF files (*.pdf *.PDF)" ) );
794 if ( filename.isEmpty() )
799 const QFileInfo saveFileInfo( filename );
802 repaintModel(
false );
804 QRectF totalRect = mView->scene()->itemsBoundingRect();
805 totalRect.adjust( -10, -10, 10, 10 );
806 const QRectF printerRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
808 QPdfWriter pdfWriter( filename );
810 const double scaleFactor = 96 / 25.4;
812 QPageLayout pageLayout( QPageSize( totalRect.size() / scaleFactor, QPageSize::Millimeter ), QPageLayout::Portrait, QMarginsF( 0, 0, 0, 0 ) );
813 pageLayout.setMode( QPageLayout::FullPageMode );
814 pdfWriter.setPageLayout( pageLayout );
816 QPainter painter( &pdfWriter );
817 mView->scene()->render( &painter, printerRect, totalRect );
821 ->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 );
822 repaintModel(
true );
825void QgsModelDesignerDialog::exportToSvg()
828 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
830 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as SVG" ), lastExportDir, tr(
"SVG files (*.svg *.SVG)" ) );
834 if ( filename.isEmpty() )
839 const QFileInfo saveFileInfo( filename );
842 repaintModel(
false );
844 QRectF totalRect = mView->scene()->itemsBoundingRect();
845 totalRect.adjust( -10, -10, 10, 10 );
846 const QRectF svgRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
849 svg.setFileName( filename );
850 svg.setSize( QSize( totalRect.width(), totalRect.height() ) );
851 svg.setViewBox( svgRect );
852 svg.setTitle( mModel->displayName() );
854 QPainter painter( &svg );
855 mView->scene()->render( &painter, svgRect, totalRect );
859 ->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 );
860 repaintModel(
true );
863void QgsModelDesignerDialog::exportAsPython()
866 QString lastExportDir = settings.
value( u
"lastModelDesignerExportDir"_s, QDir::homePath(),
QgsSettings::App ).toString();
868 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as Python Script" ), lastExportDir, tr(
"Processing scripts (*.py *.PY)" ) );
872 if ( filename.isEmpty() )
877 const QFileInfo saveFileInfo( filename );
882 QFile outFile( filename );
883 if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
887 QTextStream fout( &outFile );
892 ->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 );
895void QgsModelDesignerDialog::toggleComments(
bool show )
899 repaintModel(
true );
902void QgsModelDesignerDialog::toggleFeatureCount(
bool show )
906 repaintModel(
true );
909void QgsModelDesignerDialog::updateWindowTitle()
911 QString title = tr(
"Model Designer" );
912 if ( !mModel->name().isEmpty() )
913 title = mModel->group().isEmpty() ? u
"%1: %2"_s.arg( title, mModel->name() ) : u
"%1: %2 - %3"_s.arg( title, mModel->group(), mModel->name() );
916 title.prepend(
'*' );
918 setWindowTitle( title );
921void QgsModelDesignerDialog::deleteSelected()
923 QList<QgsModelComponentGraphicItem *> items = mScene->selectedComponentItems();
927 if ( items.size() == 1 )
929 items.at( 0 )->deleteComponent();
933 std::sort( items.begin(), items.end(), []( QgsModelComponentGraphicItem *p1, QgsModelComponentGraphicItem *p2 ) {
938 if ( dynamic_cast<QgsModelCommentGraphicItem *>( p1 ) && dynamic_cast<QgsModelCommentGraphicItem *>( p2 ) )
940 else if ( dynamic_cast<QgsModelCommentGraphicItem *>( p1 ) )
942 else if ( dynamic_cast<QgsModelCommentGraphicItem *>( p2 ) )
945 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p1 ) && dynamic_cast<QgsModelGroupBoxGraphicItem *>( p2 ) )
947 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p1 ) )
949 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p2 ) )
952 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p1 ) && dynamic_cast<QgsModelOutputGraphicItem *>( p2 ) )
954 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p1 ) )
956 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p2 ) )
959 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p1 ) && dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p2 ) )
961 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p1 ) )
963 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p2 ) )
970 beginUndoCommand( tr(
"Delete Components" ) );
972 QVariant prevState = mModel->toVariant();
973 mBlockUndoCommands++;
974 mBlockRepaints =
true;
976 while ( !items.empty() )
978 QgsModelComponentGraphicItem *toDelete =
nullptr;
979 for ( QgsModelComponentGraphicItem *item : items )
981 if ( item->canDeleteComponent() )
994 toDelete->deleteComponent();
995 items.removeAll( toDelete );
1000 mModel->loadVariant( prevState );
1001 QMessageBox::warning(
1003 QObject::tr(
"Could not remove components" ),
1005 "Components depend on the selected items.\n"
1006 "Try to remove them before trying deleting these components."
1009 mBlockUndoCommands--;
1010 mActiveCommand.reset();
1014 mBlockUndoCommands--;
1018 mBlockRepaints =
false;
1022void QgsModelDesignerDialog::populateZoomToMenu()
1024 mGroupMenu->clear();
1025 for (
const QgsProcessingModelGroupBox &box : model()->groupBoxes() )
1027 if ( QgsModelComponentGraphicItem *item = mScene->groupBoxItem( box.uuid() ) )
1029 QAction *zoomAction =
new QAction( box.description(), mGroupMenu );
1030 connect( zoomAction, &QAction::triggered,
this, [
this, item] {
1031 QRectF groupRect = item->mapToScene( item->boundingRect() ).boundingRect();
1032 groupRect.adjust( -10, -10, 10, 10 );
1033 mView->fitInView( groupRect, Qt::KeepAspectRatio );
1034 mView->centerOn( item );
1036 mGroupMenu->addAction( zoomAction );
1041void QgsModelDesignerDialog::setPanelVisibility(
bool hidden )
1043 const QList<QDockWidget *> docks = findChildren<QDockWidget *>();
1044 const QList<QTabBar *> tabBars = findChildren<QTabBar *>();
1048 mPanelStatus.clear();
1050 for ( QDockWidget *dock : docks )
1052 mPanelStatus.insert( dock->windowTitle(), PanelStatus( dock->isVisible(),
false ) );
1053 dock->setVisible(
false );
1057 for ( QTabBar *tabBar : tabBars )
1059 QString currentTabTitle = tabBar->tabText( tabBar->currentIndex() );
1060 mPanelStatus[currentTabTitle].isActive =
true;
1066 for ( QDockWidget *dock : docks )
1068 if ( mPanelStatus.contains( dock->windowTitle() ) )
1070 dock->setVisible( mPanelStatus.value( dock->windowTitle() ).isVisible );
1075 for ( QTabBar *tabBar : tabBars )
1078 for (
int i = 0; i < tabBar->count(); ++i )
1080 QString tabTitle = tabBar->tabText( i );
1081 if ( mPanelStatus.contains( tabTitle ) && mPanelStatus.value( tabTitle ).isActive )
1083 tabBar->setCurrentIndex( i );
1087 mPanelStatus.clear();
1091void QgsModelDesignerDialog::editHelp()
1093 QgsProcessingHelpEditorDialog dialog(
this );
1094 dialog.setWindowTitle( tr(
"Edit Model Help" ) );
1095 dialog.setAlgorithm( mModel.get() );
1096 if ( dialog.exec() )
1098 beginUndoCommand( tr(
"Edit Model Help" ) );
1099 mModel->setHelpContent( dialog.helpContent() );
1104void QgsModelDesignerDialog::runSelectedSteps()
1106 QSet<QString> children;
1107 const QList<QgsModelComponentGraphicItem *> items = mScene->selectedComponentItems();
1108 for ( QgsModelComponentGraphicItem *item : items )
1110 if ( QgsProcessingModelChildAlgorithm *childAlgorithm =
dynamic_cast<QgsProcessingModelChildAlgorithm *
>( item->component() ) )
1112 children.insert( childAlgorithm->childId() );
1116 if ( children.isEmpty() )
1118 mMessageBar->pushWarning( QString(), tr(
"No steps are selected" ) );
1125void QgsModelDesignerDialog::runFromChild(
const QString &
id )
1127 QSet<QString> children = mModel->dependentChildAlgorithms(
id );
1128 children.insert(
id );
1132void QgsModelDesignerDialog::cancelRunningModel()
1134 if ( !mAlgorithmWidget )
1140 mAlgorithmWidget->cancel();
1142 mAlgorithmWidget->forceClose();
1145 if ( mAlgorithmWidget )
1151 mAlgorithmWidgetsToCleanUp << mAlgorithmWidget;
1153 mAlgorithmWidget.clear();
1156void QgsModelDesignerDialog::run(
const QSet<QString> &childAlgorithmSubset )
1159 const bool isValid = model()->validate( errors );
1162 QMessageBox messageBox;
1163 messageBox.setWindowTitle( tr(
"Model is Invalid" ) );
1164 messageBox.setIcon( QMessageBox::Icon::Warning );
1165 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?" ) );
1166 messageBox.setStandardButtons( QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::Cancel );
1167 messageBox.setDefaultButton( QMessageBox::StandardButton::Cancel );
1169 QString errorString;
1170 for (
const QString &error : std::as_const( errors ) )
1172 QString cleanedError = error;
1173 const thread_local QRegularExpression re( u
"<[^>]*>"_s );
1174 cleanedError.replace( re, QString() );
1175 errorString += u
"• %1\n"_s.arg( cleanedError );
1178 messageBox.setDetailedText( errorString );
1179 if ( messageBox.exec() == QMessageBox::StandardButton::Cancel )
1183 if ( !childAlgorithmSubset.isEmpty() )
1185 for (
const QString &child : childAlgorithmSubset )
1188 const QSet<QString> requirements = mModel->dependsOnChildAlgorithms( child );
1189 for (
const QString &requirement : requirements )
1191 if ( !mLastResult.executedChildIds().contains( requirement ) )
1193 QMessageBox messageBox;
1194 messageBox.setWindowTitle( tr(
"Run Model" ) );
1195 messageBox.setIcon( QMessageBox::Icon::Warning );
1196 messageBox.setText( tr(
"Prerequisite parts of this model have not yet been run (try running the full model first)." ) );
1197 messageBox.setStandardButtons( QMessageBox::StandardButton::Ok );
1205 if ( mAlgorithmWidget && mAlgorithmWidget->isRunning() )
1207 QMessageBox messageBox;
1208 messageBox.setIcon( QMessageBox::Icon::Warning );
1209 messageBox.setWindowTitle( tr(
"Run Model" ) );
1210 messageBox.setText( tr(
"This model is already running." ) );
1211 messageBox.setStandardButtons( QMessageBox::StandardButton::Cancel | QMessageBox::StandardButton::RestoreDefaults | QMessageBox::StandardButton::Ok );
1213 QAbstractButton *buttonShowRunningAlg = messageBox.button( QMessageBox::StandardButton::Ok );
1214 buttonShowRunningAlg->setText( tr(
"Show Progress" ) );
1216 QAbstractButton *buttonReRun = messageBox.button( QMessageBox::StandardButton::RestoreDefaults );
1217 buttonReRun->setText( tr(
"Cancel and Restart Model" ) );
1219 int r = messageBox.exec();
1223 case QMessageBox::StandardButton::Cancel:
1225 case QMessageBox::StandardButton::RestoreDefaults:
1226 cancelRunningModel();
1228 case QMessageBox::StandardButton::Ok:
1229 mAlgorithmWidget->showWidget();
1235 else if ( mAlgorithmWidget )
1238 mAlgorithmWidget->close();
1239 if ( mAlgorithmWidget )
1245 mAlgorithmWidgetsToCleanUp << mAlgorithmWidget;
1248 mAlgorithmWidget.clear();
1251 if ( !mAlgorithmWidget )
1253 mAlgorithmWidget = createExecutionWidget();
1254 mAlgorithmWidget->hideShortHelp();
1255 mAlgorithmWidget->setTitle( tr(
"Run Model" ) );
1258 mAlgorithmWidget->setParameters( mModel->designerParameterValues() );
1260 if ( !childAlgorithmSubset.isEmpty() )
1262 mAlgorithmWidget->runButton()->setText( tr(
"Run Subset" ) );
1263 mAlgorithmWidget->runButton()->setToolTip( tr(
"Runs a subset of the child algorithms from this model" ) );
1266 connect( mAlgorithmWidget.get(), &QgsProcessingAlgorithmWidgetBase::algorithmAboutToRun,
this, [
this, childAlgorithmSubset](
QgsProcessingContext *context ) {
1267 if ( !childAlgorithmSubset.empty() )
1270 auto modelConfig = std::make_unique<QgsProcessingModelInitialRunConfig>();
1271 modelConfig->setChildAlgorithmSubset( childAlgorithmSubset );
1272 modelConfig->setPreviouslyExecutedChildAlgorithms( mLastResult.executedChildIds() );
1273 modelConfig->setInitialChildInputs( mLastResult.rawChildInputs() );
1274 modelConfig->setInitialChildOutputs( mLastResult.rawChildOutputs() );
1278 const QMap<QString, QgsMapLayer *> previousOutputLayers = mLayerStore.temporaryLayerStore()->mapLayers();
1279 auto previousResultStore = std::make_unique<QgsMapLayerStore>();
1280 for ( auto it = previousOutputLayers.constBegin(); it != previousOutputLayers.constEnd(); ++it )
1282 std::unique_ptr<QgsMapLayer> clone( it.value()->clone() );
1283 clone->setId( it.value()->id() );
1284 previousResultStore->addMapLayer( clone.release() );
1286 previousResultStore->moveToThread( nullptr );
1287 modelConfig->setPreviousLayerStore( std::move( previousResultStore ) );
1288 context->setModelInitialRunConfig( std::move( modelConfig ) );
1290 mScene->resetChildAlgorithmItems( childAlgorithmSubset );
1293 for ( const QString &child : childAlgorithmSubset )
1295 const QSet< QString > outdated = mModel->dependentChildAlgorithms( child );
1296 mScene->flagChildrenAsOutdated( outdated );
1297 mOutdatedChildResults.unite( outdated );
1303 mScene->resetChildAlgorithmItems();
1307 connect( mAlgorithmWidget, &QgsProcessingAlgorithmWidgetBase::algorithmFinished,
this, [
this](
bool,
const QVariantMap & ) {
1311 mLayerStore.takeResultsFrom( *context );
1319void QgsModelDesignerDialog::showChildAlgorithmOutputs(
const QString &childId )
1321 const bool isOutdated = mOutdatedChildResults.contains( childId );
1322 const QString childDescription = mModel->childAlgorithm( childId ).description();
1325 const QVariantMap childAlgorithmOutputs = result.
outputs();
1326 if ( childAlgorithmOutputs.isEmpty() )
1328 mMessageBar->pushWarning( QString(), tr(
"No results are available for %1" ).arg( childDescription ) );
1335 mMessageBar->pushCritical( QString(), tr(
"Results cannot be shown for an invalid model component" ) );
1339 const QList<const QgsProcessingParameterDefinition *> outputParams =
algorithm->destinationParameterDefinitions();
1340 if ( outputParams.isEmpty() )
1343 QgsDebugError(
"Cannot show results for algorithms with no outputs" );
1347 bool foundResults =
false;
1350 const QVariant output = childAlgorithmOutputs.value( outputParam->name() );
1351 if ( !output.isValid() )
1354 if ( output.type() == QVariant::String )
1358 QgsDebugMsgLevel( u
"Loading previous result for %1: %2"_s.arg( outputParam->name(), output.toString() ), 2 );
1360 std::unique_ptr<QgsMapLayer> layer( resultLayer->clone() );
1363 if ( outputParams.size() > 1 )
1364 baseName = tr(
"%1 — %2" ).arg( childDescription, outputParam->name() );
1366 baseName = childDescription;
1370 QString name = baseName;
1375 name = tr(
"%1 (%2)" ).arg( baseName ).arg( counter );
1378 layer->setName( name );
1381 foundResults =
true;
1386 QgsDebugError( u
"Could not load previous result for %1: %2"_s.arg( outputParam->name(), output.toString() ) );
1391 if ( !foundResults )
1393 mMessageBar->pushWarning( QString(), tr(
"No results are available for %1" ).arg( childDescription ) );
1396 else if ( isOutdated )
1398 mMessageBar->pushWarning( QString(), tr(
"These results are outdated, and may not reflect the most recent model execution" ) );
1403void QgsModelDesignerDialog::showChildAlgorithmLog(
const QString &childId )
1405 const QString childDescription = mModel->childAlgorithm( childId ).description();
1410 if ( QgsModelChildAlgorithmGraphicItem *item = mScene->childAlgorithmItem( childId ) )
1412 result = item->results();
1414 if ( result.
htmlLog().isEmpty() )
1416 result = mLastResult.childResults().value( childId );
1419 if ( result.
htmlLog().isEmpty() )
1421 mMessageBar->pushWarning( QString(), tr(
"No log is available for %1" ).arg( childDescription ) );
1426 m.setWindowTitle( childDescription );
1427 m.setCheckBoxVisible(
false );
1428 m.setMessageAsHtml( result.
htmlLog() );
1432void QgsModelDesignerDialog::onItemFocused( QgsModelComponentGraphicItem *item )
1439 if ( !item || !item->component() )
1441 mConfigWidget->showComponentConfig(
nullptr, *context, widgetContext );
1445 mConfigWidget->showComponentConfig( item->component(), *context, widgetContext );
1447 if (
auto childAlgorithmItem = qobject_cast< QgsModelChildAlgorithmGraphicItem * >( item ) )
1449 connect( childAlgorithmItem, &QgsModelChildAlgorithmGraphicItem::rebuildConfigurationDockWidget, childAlgorithmItem, [
this] {
1454 mConfigWidget->showComponentConfig(
nullptr, *context, widgetContext );
1460void QgsModelDesignerDialog::validate()
1463 if ( model()->validate( issues ) )
1465 mMessageBar->pushSuccess( QString(), tr(
"Model is valid!" ) );
1470 QPushButton *detailsButton =
new QPushButton( tr(
"Details" ) );
1471 connect( detailsButton, &QPushButton::clicked, detailsButton, [detailsButton, issues] {
1473 dialog->
setTitle( tr(
"Model is Invalid" ) );
1475 QString longMessage = tr(
"<p>This model is not valid:</p>" ) + u
"<ul>"_s;
1476 for (
const QString &issue : issues )
1478 longMessage += u
"<li>%1</li>"_s.arg( issue );
1480 longMessage +=
"</ul>"_L1;
1485 messageWidget->layout()->addWidget( detailsButton );
1486 mMessageBar->clearWidgets();
1491void QgsModelDesignerDialog::reorderInputs()
1493 QgsModelInputReorderDialog dlg(
this );
1494 dlg.setModel( mModel.get() );
1497 const QStringList inputOrder = dlg.inputOrder();
1498 beginUndoCommand( tr(
"Reorder Inputs" ) );
1499 mModel->setParameterOrder( inputOrder );
1504void QgsModelDesignerDialog::reorderOutputs()
1506 QgsModelOutputReorderDialog dlg(
this );
1507 dlg.setModel( mModel.get() );
1510 const QStringList outputOrder = dlg.outputOrder();
1511 beginUndoCommand( tr(
"Reorder Outputs" ) );
1512 mModel->setOutputOrder( outputOrder );
1513 mModel->setOutputGroup( dlg.outputGroup() );
1518bool QgsModelDesignerDialog::isDirty()
const
1520 return mHasChanged && mUndoStack->index() != -1;
1523void QgsModelDesignerDialog::fillInputsTree()
1526 auto parametersItem = std::make_unique<QTreeWidgetItem>();
1527 parametersItem->setText( 0, tr(
"Parameters" ) );
1530 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
1537 auto paramItem = std::make_unique<QTreeWidgetItem>();
1538 paramItem->setText( 0, param->name() );
1539 paramItem->setData( 0, Qt::UserRole, param->id() );
1540 paramItem->setIcon( 0, icon );
1541 paramItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1542 paramItem->setToolTip( 0, param->description() );
1543 parametersItem->addChild( paramItem.release() );
1546 mInputsTreeWidget->addTopLevelItem( parametersItem.release() );
1547 mInputsTreeWidget->topLevelItem( 0 )->setExpanded(
true );
1555QgsModelChildDependenciesWidget::QgsModelChildDependenciesWidget( QWidget *parent, QgsProcessingModelAlgorithm *model,
const QString &childId )
1558 , mChildId( childId )
1560 QHBoxLayout *hl =
new QHBoxLayout();
1561 hl->setContentsMargins( 0, 0, 0, 0 );
1563 mLineEdit =
new QLineEdit();
1564 mLineEdit->setEnabled(
false );
1565 hl->addWidget( mLineEdit, 1 );
1567 mToolButton =
new QToolButton();
1568 mToolButton->setText( QString( QChar( 0x2026 ) ) );
1569 hl->addWidget( mToolButton );
1573 mLineEdit->setText( tr(
"%1 dependencies selected" ).arg( 0 ) );
1575 connect( mToolButton, &QToolButton::clicked,
this, &QgsModelChildDependenciesWidget::showDialog );
1578void QgsModelChildDependenciesWidget::setValue(
const QList<QgsProcessingModelChildDependency> &value )
1582 updateSummaryText();
1585void QgsModelChildDependenciesWidget::showDialog()
1587 const QList<QgsProcessingModelChildDependency> available = mModel->availableDependenciesForChildAlgorithm( mChildId );
1589 QVariantList availableOptions;
1590 for (
const QgsProcessingModelChildDependency &dep : available )
1591 availableOptions << QVariant::fromValue( dep );
1592 QVariantList selectedOptions;
1593 for (
const QgsProcessingModelChildDependency &dep : mValue )
1594 selectedOptions << QVariant::fromValue( dep );
1599 QgsProcessingMultipleSelectionPanelWidget *widget =
new QgsProcessingMultipleSelectionPanelWidget( availableOptions, selectedOptions );
1600 widget->setPanelTitle( tr(
"Algorithm Dependencies" ) );
1602 widget->setValueFormatter( [
this](
const QVariant &v ) -> QString {
1603 const QgsProcessingModelChildDependency dep = v.value<QgsProcessingModelChildDependency>();
1605 const QString description = mModel->childAlgorithm( dep.childId ).description();
1606 if ( dep.conditionalBranch.isEmpty() )
1609 return tr(
"Condition “%1” from algorithm “%2”" ).arg( dep.conditionalBranch, description );
1612 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged,
this, [
this, widget]() {
1613 QList<QgsProcessingModelChildDependency> res;
1614 for (
const QVariant &v : widget->selectedOptions() )
1616 res << v.value<QgsProcessingModelChildDependency>();
1625void QgsModelChildDependenciesWidget::updateSummaryText()
1627 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)
constexpr QObjectUniquePtr< Tp > make_qobject_unique(Args &&...args)
Create an object owned by a QObjectUniquePtr.