21 #include "qgsprocessingmodelalgorithm.h"
30 #include "processing/models/qgsprocessingmodelgroupbox.h"
38 #include <QDesktopWidget>
39 #include <QKeySequence>
40 #include <QFileDialog>
42 #include <QSvgGenerator>
43 #include <QToolButton>
44 #include <QCloseEvent>
45 #include <QMessageBox>
47 #include <QPushButton>
52 QgsModelerToolboxModel::QgsModelerToolboxModel( QObject *parent )
58 Qt::ItemFlags QgsModelerToolboxModel::flags(
const QModelIndex &index )
const
60 Qt::ItemFlags f = QgsProcessingToolboxProxyModel::flags( index );
61 const QModelIndex sourceIndex = mapToSource( index );
62 if ( toolboxModel()->isAlgorithm( sourceIndex ) )
64 f = f | Qt::ItemIsDragEnabled;
69 Qt::DropActions QgsModelerToolboxModel::supportedDragActions()
const
71 return Qt::CopyAction;
76 QgsModelDesignerDialog::QgsModelDesignerDialog( QWidget *parent, Qt::WindowFlags flags )
77 : QMainWindow( parent, flags )
82 setAttribute( Qt::WA_DeleteOnClose );
83 setDockOptions( dockOptions() | QMainWindow::GroupedDragging );
84 setWindowFlags( Qt::WindowMinimizeButtonHint |
85 Qt::WindowMaximizeButtonHint |
86 Qt::WindowCloseButtonHint );
88 mModel = qgis::make_unique< QgsProcessingModelAlgorithm >();
91 mUndoStack =
new QUndoStack(
this );
92 connect( mUndoStack, &QUndoStack::indexChanged,
this, [ = ]
94 if ( mIgnoreUndoStackChanges )
99 mGroupEdit->setText( mModel->group() );
100 mNameEdit->setText( mModel->displayName() );
101 mBlockUndoCommands--;
105 mPropertiesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
106 mInputsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
107 mAlgorithmsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
108 mVariablesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
110 mAlgorithmsTree->header()->setVisible(
false );
111 mAlgorithmSearchEdit->setShowSearchIcon(
true );
112 mAlgorithmSearchEdit->setPlaceholderText( tr(
"Search…" ) );
113 connect( mAlgorithmSearchEdit, &QgsFilterLineEdit::textChanged, mAlgorithmsTree, &QgsProcessingToolboxTreeView::setFilterString );
115 mInputsTreeWidget->header()->setVisible(
false );
116 mInputsTreeWidget->setAlternatingRowColors(
true );
117 mInputsTreeWidget->setDragDropMode( QTreeWidget::DragOnly );
118 mInputsTreeWidget->setDropIndicatorShown(
true );
120 mNameEdit->setPlaceholderText( tr(
"Enter model name here" ) );
121 mGroupEdit->setPlaceholderText( tr(
"Enter group name here" ) );
124 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
125 mainLayout->insertWidget( 0, mMessageBar );
127 mView->setAcceptDrops(
true );
130 connect( mActionClose, &QAction::triggered,
this, &QWidget::close );
131 connect( mActionZoomIn, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomIn );
132 connect( mActionZoomOut, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomOut );
133 connect( mActionZoomActual, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomActual );
134 connect( mActionZoomToItems, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomFull );
135 connect( mActionExportImage, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToImage );
136 connect( mActionExportPdf, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToPdf );
137 connect( mActionExportSvg, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToSvg );
138 connect( mActionExportPython, &QAction::triggered,
this, &QgsModelDesignerDialog::exportAsPython );
139 connect( mActionSave, &QAction::triggered,
this, [ = ] { saveModel(
false ); } );
140 connect( mActionSaveAs, &QAction::triggered,
this, [ = ] { saveModel(
true ); } );
141 connect( mActionDeleteComponents, &QAction::triggered,
this, &QgsModelDesignerDialog::deleteSelected );
142 connect( mActionSnapSelected, &QAction::triggered, mView, &QgsModelGraphicsView::snapSelected );
143 connect( mActionValidate, &QAction::triggered,
this, &QgsModelDesignerDialog::validate );
144 connect( mActionReorderInputs, &QAction::triggered,
this, &QgsModelDesignerDialog::reorderInputs );
145 connect( mReorderInputsButton, &QPushButton::clicked,
this, &QgsModelDesignerDialog::reorderInputs );
147 mActionSnappingEnabled->setChecked( settings.
value( QStringLiteral(
"/Processing/Modeler/enableSnapToGrid" ),
false ).toBool() );
148 connect( mActionSnappingEnabled, &QAction::toggled,
this, [ = ](
bool enabled )
150 mView->snapper()->setSnapToGrid( enabled );
151 QgsSettings().
setValue( QStringLiteral(
"/Processing/Modeler/enableSnapToGrid" ), enabled );
153 mView->snapper()->setSnapToGrid( mActionSnappingEnabled->isChecked() );
155 connect( mActionSelectAll, &QAction::triggered,
this, [ = ]
160 mUndoAction = mUndoStack->createUndoAction(
this );
162 mUndoAction->setShortcuts( QKeySequence::Undo );
163 mRedoAction = mUndoStack->createRedoAction(
this );
165 mRedoAction->setShortcuts( QKeySequence::Redo );
167 mMenuEdit->insertAction( mActionDeleteComponents, mRedoAction );
168 mMenuEdit->insertAction( mActionDeleteComponents, mUndoAction );
169 mMenuEdit->insertSeparator( mActionDeleteComponents );
170 mToolbar->insertAction( mActionZoomIn, mUndoAction );
171 mToolbar->insertAction( mActionZoomIn, mRedoAction );
172 mToolbar->insertSeparator( mActionZoomIn );
174 mGroupMenu =
new QMenu( tr(
"Zoom To" ),
this );
175 mMenuView->insertMenu( mActionZoomIn, mGroupMenu );
176 connect( mGroupMenu, &QMenu::aboutToShow,
this, &QgsModelDesignerDialog::populateZoomToMenu );
180 mActionCut =
new QAction( tr(
"Cu&t" ),
this );
181 mActionCut->setShortcuts( QKeySequence::Cut );
182 mActionCut->setStatusTip( tr(
"Cut" ) );
184 connect( mActionCut, &QAction::triggered,
this, [ = ]
186 mView->copySelectedItems( QgsModelGraphicsView::ClipboardCut );
189 mActionCopy =
new QAction( tr(
"&Copy" ),
this );
190 mActionCopy->setShortcuts( QKeySequence::Copy );
191 mActionCopy->setStatusTip( tr(
"Copy" ) );
193 connect( mActionCopy, &QAction::triggered,
this, [ = ]
195 mView->copySelectedItems( QgsModelGraphicsView::ClipboardCopy );
198 mActionPaste =
new QAction( tr(
"&Paste" ),
this );
199 mActionPaste->setShortcuts( QKeySequence::Paste );
200 mActionPaste->setStatusTip( tr(
"Paste" ) );
202 connect( mActionPaste, &QAction::triggered,
this, [ = ]
204 mView->pasteItems( QgsModelGraphicsView::PasteModeCursor );
206 mMenuEdit->insertAction( mActionDeleteComponents, mActionCut );
207 mMenuEdit->insertAction( mActionDeleteComponents, mActionCopy );
208 mMenuEdit->insertAction( mActionDeleteComponents, mActionPaste );
209 mMenuEdit->insertSeparator( mActionDeleteComponents );
212 if ( settings.
value( QStringLiteral(
"Processing/Configuration/SHOW_ALGORITHMS_KNOWN_ISSUES" ),
false ).toBool() )
216 mAlgorithmsTree->setFilters( filters );
217 mAlgorithmsTree->setDragDropMode( QTreeWidget::DragOnly );
218 mAlgorithmsTree->setDropIndicatorShown(
true );
220 mAlgorithmsModel =
new QgsModelerToolboxModel(
this );
221 mAlgorithmsTree->setToolboxProxyModel( mAlgorithmsModel );
223 connect( mView, &QgsModelGraphicsView::algorithmDropped,
this, [ = ](
const QString & algorithmId,
const QPointF & pos )
225 addAlgorithm( algorithmId, pos );
227 connect( mAlgorithmsTree, &QgsProcessingToolboxTreeView::doubleClicked,
this, [ = ]()
229 if ( mAlgorithmsTree->selectedAlgorithm() )
230 addAlgorithm( mAlgorithmsTree->selectedAlgorithm()->id(), QPointF() );
232 connect( mInputsTreeWidget, &QgsModelDesignerInputsTreeWidget::doubleClicked,
this, [ = ](
const QModelIndex & )
234 const QString parameterType = mInputsTreeWidget->currentItem()->data( 0, Qt::UserRole ).toString();
235 addInput( parameterType, QPointF() );
238 connect( mView, &QgsModelGraphicsView::inputDropped,
this, &QgsModelDesignerDialog::addInput );
241 QShortcut *ctrlEquals =
new QShortcut( QKeySequence( QStringLiteral(
"Ctrl+=" ) ),
this );
242 connect( ctrlEquals, &QShortcut::activated,
this, &QgsModelDesignerDialog::zoomIn );
245 mUndoDock->setObjectName( QStringLiteral(
"UndoDock" ) );
246 mUndoView =
new QUndoView( mUndoStack,
this );
247 mUndoDock->setWidget( mUndoView );
248 mUndoDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
249 addDockWidget( Qt::DockWidgetArea::LeftDockWidgetArea, mUndoDock );
251 tabifyDockWidget( mUndoDock, mPropertiesDock );
252 tabifyDockWidget( mVariablesDock, mPropertiesDock );
253 mPropertiesDock->raise();
254 tabifyDockWidget( mInputsDock, mAlgorithmsDock );
255 mInputsDock->raise();
261 beginUndoCommand( tr(
"Change Model Variables" ) );
262 mModel->setVariables( mVariablesEditor->variablesInActiveScope() );
266 connect( mNameEdit, &QLineEdit::textChanged,
this, [ = ](
const QString & name )
270 beginUndoCommand( tr(
"Change Model Name" ), NameChanged );
271 mModel->setName( name );
276 connect( mGroupEdit, &QLineEdit::textChanged,
this, [ = ](
const QString & group )
280 beginUndoCommand( tr(
"Change Model Group" ), GroupChanged );
281 mModel->setGroup( group );
288 QToolButton *toolbuttonExportToScript =
new QToolButton();
289 toolbuttonExportToScript->setPopupMode( QToolButton::InstantPopup );
290 toolbuttonExportToScript->addAction( mActionExportAsScriptAlgorithm );
291 toolbuttonExportToScript->setDefaultAction( mActionExportAsScriptAlgorithm );
292 mToolbar->insertWidget( mActionExportImage, toolbuttonExportToScript );
293 connect( mActionExportAsScriptAlgorithm, &QAction::triggered,
this, &QgsModelDesignerDialog::exportAsScriptAlgorithm );
295 mActionShowComments->setChecked( settings.
value( QStringLiteral(
"/Processing/Modeler/ShowComments" ),
true ).toBool() );
296 connect( mActionShowComments, &QAction::toggled,
this, &QgsModelDesignerDialog::toggleComments );
300 mSelectTool->setAction( mActionSelectMoveItem );
302 mView->setTool( mSelectTool );
305 connect( mView, &QgsModelGraphicsView::macroCommandStarted,
this, [ = ](
const QString & text )
307 mIgnoreUndoStackChanges++;
308 mUndoStack->beginMacro( text );
309 mIgnoreUndoStackChanges--;
311 connect( mView, &QgsModelGraphicsView::macroCommandEnded,
this, [ = ]
313 mIgnoreUndoStackChanges++;
314 mUndoStack->endMacro();
315 mIgnoreUndoStackChanges--;
317 connect( mView, &QgsModelGraphicsView::beginCommand,
this, [ = ](
const QString & text )
319 beginUndoCommand( text );
321 connect( mView, &QgsModelGraphicsView::endCommand,
this, [ = ]
325 connect( mView, &QgsModelGraphicsView::deleteSelectedItems,
this, [ = ]
330 connect( mActionAddGroupBox, &QAction::triggered,
this, [ = ]
332 const QPointF viewCenter = mView->mapToScene( mView->viewport()->rect().center() );
333 QgsProcessingModelGroupBox group;
334 group.setPosition( viewCenter );
335 group.setDescription( tr(
"New Group" ) );
337 beginUndoCommand( tr(
"Add Group Box" ) );
338 model()->addGroupBox( group );
345 QgsModelDesignerDialog::~QgsModelDesignerDialog()
347 mIgnoreUndoStackChanges++;
351 void QgsModelDesignerDialog::closeEvent( QCloseEvent *event )
353 if ( checkForUnsavedChanges() )
359 void QgsModelDesignerDialog::beginUndoCommand(
const QString &text,
int id )
361 if ( mBlockUndoCommands || !mUndoStack )
364 if ( mActiveCommand )
367 mActiveCommand = qgis::make_unique< QgsModelUndoCommand >( mModel.get(), text,
id );
370 void QgsModelDesignerDialog::endUndoCommand()
372 if ( mBlockUndoCommands || !mActiveCommand || !mUndoStack )
375 mActiveCommand->saveAfterState();
376 mIgnoreUndoStackChanges++;
377 mUndoStack->push( mActiveCommand.release() );
378 mIgnoreUndoStackChanges--;
382 QgsProcessingModelAlgorithm *QgsModelDesignerDialog::model()
387 void QgsModelDesignerDialog::setModel( QgsProcessingModelAlgorithm *model )
389 mModel.reset( model );
391 mGroupEdit->setText( mModel->group() );
392 mNameEdit->setText( mModel->displayName() );
394 updateVariablesGui();
396 mView->centerOn( 0, 0 );
399 mIgnoreUndoStackChanges++;
401 mIgnoreUndoStackChanges--;
406 void QgsModelDesignerDialog::loadModel(
const QString &path )
408 std::unique_ptr< QgsProcessingModelAlgorithm > alg = qgis::make_unique< QgsProcessingModelAlgorithm >();
409 if ( alg->fromFile( path ) )
412 setModel( alg.release() );
417 QMessageBox::critical(
this, tr(
"Open Model" ), tr(
"The selected model could not be loaded.\n"
418 "See the log for more information." ) );
422 void QgsModelDesignerDialog::setModelScene( QgsModelGraphicsScene *scene )
424 QgsModelGraphicsScene *oldScene = mScene;
427 mScene->setParent(
this );
428 mScene->setChildAlgorithmResults( mChildResults );
429 mScene->setModel( mModel.get() );
430 mScene->setMessageBar( mMessageBar );
432 const QPointF center = mView->mapToScene( mView->viewport()->rect().center() );
433 mView->setModelScene( mScene );
435 mSelectTool->resetCache();
436 mSelectTool->setScene( mScene );
438 connect( mScene, &QgsModelGraphicsScene::rebuildRequired,
this, [ = ]
440 if ( mBlockRepaints )
445 connect( mScene, &QgsModelGraphicsScene::componentAboutToChange,
this, [ = ](
const QString & description,
int id ) { beginUndoCommand( description,
id ); } );
446 connect( mScene, &QgsModelGraphicsScene::componentChanged,
this, [ = ] { endUndoCommand(); } );
448 mView->centerOn( center );
451 oldScene->deleteLater();
454 void QgsModelDesignerDialog::updateVariablesGui()
456 mBlockUndoCommands++;
458 std::unique_ptr< QgsExpressionContextScope > variablesScope = qgis::make_unique< QgsExpressionContextScope >( tr(
"Model Variables" ) );
459 const QVariantMap modelVars = mModel->variables();
460 for (
auto it = modelVars.constBegin(); it != modelVars.constEnd(); ++it )
462 variablesScope->setVariable( it.key(), it.value() );
465 variablesContext.
appendScope( variablesScope.release() );
466 mVariablesEditor->setContext( &variablesContext );
467 mVariablesEditor->setEditableScopeIndex( 0 );
469 mBlockUndoCommands--;
472 void QgsModelDesignerDialog::setDirty(
bool dirty )
478 bool QgsModelDesignerDialog::validateSave()
480 if ( mNameEdit->text().trimmed().isEmpty() )
482 mMessageBar->pushWarning( QString(), tr(
"Please a enter model name before saving" ) );
489 bool QgsModelDesignerDialog::checkForUnsavedChanges()
493 QMessageBox::StandardButton ret = QMessageBox::question(
this, tr(
"Save Model?" ),
494 tr(
"There are unsaved changes in this model. Do you want to keep those?" ),
495 QMessageBox::Save | QMessageBox::Cancel | QMessageBox::Discard, QMessageBox::Cancel );
498 case QMessageBox::Save:
502 case QMessageBox::Discard:
515 void QgsModelDesignerDialog::setLastRunChildAlgorithmResults(
const QVariantMap &results )
517 mChildResults = results;
519 mScene->setChildAlgorithmResults( mChildResults );
522 void QgsModelDesignerDialog::setLastRunChildAlgorithmInputs(
const QVariantMap &inputs )
524 mChildInputs = inputs;
526 mScene->setChildAlgorithmInputs( mChildInputs );
529 void QgsModelDesignerDialog::zoomIn()
531 mView->setTransformationAnchor( QGraphicsView::NoAnchor );
532 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
534 const double factor = settings.
value( QStringLiteral(
"/qgis/zoom_favor" ), 2.0 ).toDouble();
535 mView->scale( factor, factor );
536 mView->centerOn( point );
539 void QgsModelDesignerDialog::zoomOut()
541 mView->setTransformationAnchor( QGraphicsView::NoAnchor );
542 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
544 const double factor = 1.0 / settings.
value( QStringLiteral(
"/qgis/zoom_favor" ), 2.0 ).toDouble();
545 mView->scale( factor, factor );
546 mView->centerOn( point );
549 void QgsModelDesignerDialog::zoomActual()
551 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
552 mView->resetTransform();
553 mView->scale( QgsApplication::desktop()->logicalDpiX() / 96, QgsApplication::desktop()->logicalDpiX() / 96 );
554 mView->centerOn( point );
557 void QgsModelDesignerDialog::zoomFull()
559 QRectF totalRect = mView->scene()->itemsBoundingRect();
560 totalRect.adjust( -10, -10, 10, 10 );
561 mView->fitInView( totalRect, Qt::KeepAspectRatio );
564 void QgsModelDesignerDialog::exportToImage()
566 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as Image" ), tr(
"PNG files (*.png *.PNG)" ) );
567 if ( filename.isEmpty() )
572 repaintModel(
false );
574 QRectF totalRect = mView->scene()->itemsBoundingRect();
575 totalRect.adjust( -10, -10, 10, 10 );
576 const QRectF imageRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
578 QImage img( totalRect.width(), totalRect.height(),
579 QImage::Format_ARGB32_Premultiplied );
580 img.fill( Qt::white );
582 painter.setRenderHint( QPainter::Antialiasing );
583 painter.begin( &img );
584 mView->scene()->render( &painter, imageRect, totalRect );
587 img.save( filename );
589 mMessageBar->pushMessage( QString(), tr(
"Successfully exported model as image to <a href=\"{}\">{}</a>" ).arg( QUrl::fromLocalFile( filename ).toString(), QDir::toNativeSeparators( filename ) ),
Qgis::Success, 5 );
590 repaintModel(
true );
593 void QgsModelDesignerDialog::exportToPdf()
595 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as PDF" ), tr(
"PDF files (*.pdf *.PDF)" ) );
596 if ( filename.isEmpty() )
601 repaintModel(
false );
603 QRectF totalRect = mView->scene()->itemsBoundingRect();
604 totalRect.adjust( -10, -10, 10, 10 );
605 const QRectF printerRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
608 printer.setOutputFormat( QPrinter::PdfFormat );
609 printer.setOutputFileName( filename );
610 printer.setPaperSize( QSizeF( printerRect.width(), printerRect.height() ), QPrinter::DevicePixel );
611 printer.setFullPage(
true );
613 QPainter painter( &printer );
614 mView->scene()->render( &painter, printerRect, totalRect );
617 mMessageBar->pushMessage( QString(), tr(
"Successfully exported model as PDF to <a href=\"{}\">{}</a>" ).arg( QUrl::fromLocalFile( filename ).toString(), QDir::toNativeSeparators( filename ) ),
Qgis::Success, 5 );
618 repaintModel(
true );
621 void QgsModelDesignerDialog::exportToSvg()
623 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as SVG" ), tr(
"SVG files (*.svg *.SVG)" ) );
624 if ( filename.isEmpty() )
629 repaintModel(
false );
631 QRectF totalRect = mView->scene()->itemsBoundingRect();
632 totalRect.adjust( -10, -10, 10, 10 );
633 const QRectF svgRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
636 svg.setFileName( filename );
637 svg.setSize( QSize( totalRect.width(), totalRect.height() ) );
638 svg.setViewBox( svgRect );
639 svg.setTitle( mModel->displayName() );
641 QPainter painter( &svg );
642 mView->scene()->render( &painter, svgRect, totalRect );
645 mMessageBar->pushMessage( QString(), tr(
"Successfully exported model as SVG to <a href=\"{}\">{}</a>" ).arg( QUrl::fromLocalFile( filename ).toString(), QDir::toNativeSeparators( filename ) ),
Qgis::Success, 5 );
646 repaintModel(
true );
649 void QgsModelDesignerDialog::exportAsPython()
651 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as Python Script" ), tr(
"Processing scripts (*.py *.PY)" ) );
652 if ( filename.isEmpty() )
659 QFile outFile( filename );
660 if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
664 QTextStream fout( &outFile );
668 mMessageBar->pushMessage( QString(), tr(
"Successfully exported model as Python script to <a href=\"{}\">{}</a>" ).arg( QUrl::fromLocalFile( filename ).toString(), QDir::toNativeSeparators( filename ) ),
Qgis::Success, 5 );
671 void QgsModelDesignerDialog::toggleComments(
bool show )
675 repaintModel(
true );
678 void QgsModelDesignerDialog::updateWindowTitle()
680 QString title = tr(
"Model Designer" );
681 if ( !mModel->name().isEmpty() )
682 title = QStringLiteral(
"%1 - %2" ).arg( title, mModel->name() );
685 title.prepend(
'*' );
687 setWindowTitle( title );
690 void QgsModelDesignerDialog::deleteSelected()
692 QList< QgsModelComponentGraphicItem * > items = mScene->selectedComponentItems();
696 if ( items.size() == 1 )
698 items.at( 0 )->deleteComponent();
702 std::sort( items.begin(), items.end(), []( QgsModelComponentGraphicItem * p1, QgsModelComponentGraphicItem * p2 )
705 if ( dynamic_cast< QgsModelCommentGraphicItem *>( p1 ) )
707 else if ( dynamic_cast< QgsModelCommentGraphicItem *>( p2 ) )
709 else if ( dynamic_cast< QgsModelGroupBoxGraphicItem *>( p1 ) )
711 else if ( dynamic_cast< QgsModelGroupBoxGraphicItem *>( p2 ) )
713 else if ( dynamic_cast< QgsModelOutputGraphicItem *>( p1 ) )
715 else if ( dynamic_cast< QgsModelOutputGraphicItem *>( p2 ) )
717 else if ( dynamic_cast< QgsModelChildAlgorithmGraphicItem *>( p1 ) )
719 else if ( dynamic_cast< QgsModelChildAlgorithmGraphicItem *>( p2 ) )
725 beginUndoCommand( tr(
"Delete Components" ) );
727 QVariant prevState = mModel->toVariant();
728 mBlockUndoCommands++;
729 mBlockRepaints =
true;
731 while ( !items.empty() )
733 QgsModelComponentGraphicItem *toDelete =
nullptr;
734 for ( QgsModelComponentGraphicItem *item : items )
736 if ( item->canDeleteComponent() )
749 toDelete->deleteComponent();
750 items.removeAll( toDelete );
755 mModel->loadVariant( prevState );
756 QMessageBox::warning(
nullptr, QObject::tr(
"Could not remove components" ),
757 QObject::tr(
"Components depend on the selected items.\n"
758 "Try to remove them before trying deleting these components." ) );
759 mBlockUndoCommands--;
760 mActiveCommand.reset();
764 mBlockUndoCommands--;
768 mBlockRepaints =
false;
772 void QgsModelDesignerDialog::populateZoomToMenu()
775 for (
const QgsProcessingModelGroupBox &box : model()->groupBoxes() )
777 if ( QgsModelComponentGraphicItem *item = mScene->groupBoxItem( box.uuid() ) )
779 QAction *zoomAction =
new QAction( box.description(), mGroupMenu );
780 connect( zoomAction, &QAction::triggered,
this, [ = ]
782 mView->centerOn( item );
784 mGroupMenu->addAction( zoomAction );
789 void QgsModelDesignerDialog::validate()
792 if ( model()->validate( issues ) )
794 mMessageBar->pushSuccess( QString(), tr(
"Model is valid!" ) );
798 QgsMessageBarItem *messageWidget = mMessageBar->createMessage( QString(), tr(
"Model is invalid!" ) );
799 QPushButton *detailsButton =
new QPushButton( tr(
"Details" ) );
800 connect( detailsButton, &QPushButton::clicked, detailsButton, [ = ]
802 QgsMessageViewer *dialog =
new QgsMessageViewer( detailsButton );
803 dialog->setTitle( tr(
"Model is Invalid" ) );
805 QString longMessage = tr(
"<p>This model is not valid:</p>" ) + QStringLiteral(
"<ul>" );
806 for (
const QString &issue : issues )
808 longMessage += QStringLiteral(
"<li>%1</li>" ).arg( issue );
810 longMessage += QStringLiteral(
"</ul>" );
813 dialog->showMessage();
815 messageWidget->layout()->addWidget( detailsButton );
816 mMessageBar->clearWidgets();
821 void QgsModelDesignerDialog::reorderInputs()
823 QgsModelInputReorderDialog dlg(
this );
824 dlg.setModel( mModel.get() );
827 const QStringList inputOrder = dlg.inputOrder();
828 beginUndoCommand( tr(
"Reorder Inputs" ) );
829 mModel->setParameterOrder( inputOrder );
834 bool QgsModelDesignerDialog::isDirty()
const
836 return mHasChanged && mUndoStack->index() != -1;
839 void QgsModelDesignerDialog::fillInputsTree()
842 std::unique_ptr< QTreeWidgetItem > parametersItem = qgis::make_unique< QTreeWidgetItem >();
843 parametersItem->setText( 0, tr(
"Parameters" ) );
847 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
854 std::unique_ptr< QTreeWidgetItem > paramItem = qgis::make_unique< QTreeWidgetItem >();
855 paramItem->setText( 0, param->name() );
856 paramItem->setData( 0, Qt::UserRole, param->id() );
857 paramItem->setIcon( 0, icon );
858 paramItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
859 paramItem->setToolTip( 0, param->description() );
860 parametersItem->addChild( paramItem.release() );
863 mInputsTreeWidget->addTopLevelItem( parametersItem.release() );
864 mInputsTreeWidget->topLevelItem( 0 )->setExpanded(
true );
872 QgsModelChildDependenciesWidget::QgsModelChildDependenciesWidget( QWidget *parent, QgsProcessingModelAlgorithm *model,
const QString &childId )
875 , mChildId( childId )
877 QHBoxLayout *hl =
new QHBoxLayout();
879 hl->setContentsMargins( 0, 0, 0, 0 );
881 mLineEdit =
new QLineEdit();
882 mLineEdit->setEnabled(
false );
883 hl->addWidget( mLineEdit, 1 );
885 mToolButton =
new QToolButton();
886 mToolButton->setText( QString( QChar( 0x2026 ) ) );
887 hl->addWidget( mToolButton );
891 mLineEdit->setText( tr(
"%1 dependencies selected" ).arg( 0 ) );
893 connect( mToolButton, &QToolButton::clicked,
this, &QgsModelChildDependenciesWidget::showDialog );
896 void QgsModelChildDependenciesWidget::setValue(
const QList<QgsProcessingModelChildDependency> &value )
903 void QgsModelChildDependenciesWidget::showDialog()
905 const QList<QgsProcessingModelChildDependency> available = mModel->availableDependenciesForChildAlgorithm( mChildId );
907 QVariantList availableOptions;
908 for (
const QgsProcessingModelChildDependency &dep : available )
909 availableOptions << QVariant::fromValue( dep );
910 QVariantList selectedOptions;
911 for (
const QgsProcessingModelChildDependency &dep : mValue )
912 selectedOptions << QVariant::fromValue( dep );
917 QgsProcessingMultipleSelectionPanelWidget *widget =
new QgsProcessingMultipleSelectionPanelWidget( availableOptions, selectedOptions );
918 widget->setPanelTitle( tr(
"Algorithm Dependencies" ) );
920 widget->setValueFormatter( [ = ](
const QVariant & v ) -> QString
922 const QgsProcessingModelChildDependency dep = v.value< QgsProcessingModelChildDependency >();
924 const QString description = mModel->childAlgorithm( dep.childId ).description();
925 if ( dep.conditionalBranch.isEmpty() )
928 return tr(
"Condition “%1” from algorithm “%2”" ).arg( dep.conditionalBranch, description );
931 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged,
this, [ = ]()
933 QList< QgsProcessingModelChildDependency > res;
934 for (
const QVariant &v : widget->selectedOptions() )
936 res << v.value< QgsProcessingModelChildDependency >();
945 void QgsModelChildDependenciesWidget::updateSummaryText()
947 mLineEdit->setText( tr(
"%1 dependencies selected" ).arg( mValue.count() ) );