42#include <QKeySequence>
45#include <QSvgGenerator>
53#include <QActionGroup>
58QgsModelerToolboxModel::QgsModelerToolboxModel( QObject *parent )
64Qt::ItemFlags QgsModelerToolboxModel::flags(
const QModelIndex &index )
const
66 Qt::ItemFlags f = QgsProcessingToolboxProxyModel::flags( index );
67 const QModelIndex sourceIndex = mapToSource( index );
68 if ( toolboxModel()->isAlgorithm( sourceIndex ) )
70 f = f | Qt::ItemIsDragEnabled;
75Qt::DropActions QgsModelerToolboxModel::supportedDragActions()
const
77 return Qt::CopyAction;
82QgsModelDesignerDialog::QgsModelDesignerDialog( QWidget *parent, Qt::WindowFlags flags )
83 : QMainWindow( parent, flags )
84 , mToolsActionGroup( new QActionGroup( this ) )
90 setAttribute( Qt::WA_DeleteOnClose );
91 setDockOptions( dockOptions() | QMainWindow::GroupedDragging );
92 setWindowFlags( Qt::WindowMinimizeButtonHint |
93 Qt::WindowMaximizeButtonHint |
94 Qt::WindowCloseButtonHint );
98 mModel = std::make_unique< QgsProcessingModelAlgorithm >();
101 mUndoStack =
new QUndoStack(
this );
102 connect( mUndoStack, &QUndoStack::indexChanged,
this, [ = ]
104 if ( mIgnoreUndoStackChanges )
107 mBlockUndoCommands++;
108 updateVariablesGui();
109 mGroupEdit->setText( mModel->group() );
110 mNameEdit->setText( mModel->displayName() );
111 mBlockUndoCommands--;
115 mPropertiesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
116 mInputsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
117 mAlgorithmsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
118 mVariablesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
120 mAlgorithmsTree->header()->setVisible(
false );
121 mAlgorithmSearchEdit->setShowSearchIcon(
true );
122 mAlgorithmSearchEdit->setPlaceholderText( tr(
"Search…" ) );
123 connect( mAlgorithmSearchEdit, &QgsFilterLineEdit::textChanged, mAlgorithmsTree, &QgsProcessingToolboxTreeView::setFilterString );
125 mInputsTreeWidget->header()->setVisible(
false );
126 mInputsTreeWidget->setAlternatingRowColors(
true );
127 mInputsTreeWidget->setDragDropMode( QTreeWidget::DragOnly );
128 mInputsTreeWidget->setDropIndicatorShown(
true );
130 mNameEdit->setPlaceholderText( tr(
"Enter model name here" ) );
131 mGroupEdit->setPlaceholderText( tr(
"Enter group name here" ) );
134 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
135 mainLayout->insertWidget( 0, mMessageBar );
137 mView->setAcceptDrops(
true );
140 connect( mActionClose, &QAction::triggered,
this, &QWidget::close );
141 connect( mActionNew, &QAction::triggered,
this, &QgsModelDesignerDialog::newModel );
142 connect( mActionZoomIn, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomIn );
143 connect( mActionZoomOut, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomOut );
144 connect( mActionZoomActual, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomActual );
145 connect( mActionZoomToItems, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomFull );
146 connect( mActionExportImage, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToImage );
147 connect( mActionExportPdf, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToPdf );
148 connect( mActionExportSvg, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToSvg );
149 connect( mActionExportPython, &QAction::triggered,
this, &QgsModelDesignerDialog::exportAsPython );
150 connect( mActionSave, &QAction::triggered,
this, [ = ] { saveModel(
false ); } );
151 connect( mActionSaveAs, &QAction::triggered,
this, [ = ] { saveModel(
true ); } );
152 connect( mActionDeleteComponents, &QAction::triggered,
this, &QgsModelDesignerDialog::deleteSelected );
153 connect( mActionSnapSelected, &QAction::triggered, mView, &QgsModelGraphicsView::snapSelected );
154 connect( mActionValidate, &QAction::triggered,
this, &QgsModelDesignerDialog::validate );
155 connect( mActionReorderInputs, &QAction::triggered,
this, &QgsModelDesignerDialog::reorderInputs );
156 connect( mActionReorderOutputs, &QAction::triggered,
this, &QgsModelDesignerDialog::reorderOutputs );
157 connect( mActionEditHelp, &QAction::triggered,
this, &QgsModelDesignerDialog::editHelp );
158 connect( mReorderInputsButton, &QPushButton::clicked,
this, &QgsModelDesignerDialog::reorderInputs );
160 mActionSnappingEnabled->setChecked( settings.
value( QStringLiteral(
"/Processing/Modeler/enableSnapToGrid" ),
false ).toBool() );
161 connect( mActionSnappingEnabled, &QAction::toggled,
this, [ = ](
bool enabled )
163 mView->snapper()->setSnapToGrid( enabled );
164 QgsSettings().
setValue( QStringLiteral(
"/Processing/Modeler/enableSnapToGrid" ), enabled );
166 mView->snapper()->setSnapToGrid( mActionSnappingEnabled->isChecked() );
168 connect( mActionSelectAll, &QAction::triggered,
this, [ = ]
173 QStringList docksTitle = settings.
value( QStringLiteral(
"ModelDesigner/hiddenDocksTitle" ), QStringList(),
QgsSettings::App ).toStringList();
174 QStringList docksActive = settings.
value( QStringLiteral(
"ModelDesigner/hiddenDocksActive" ), QStringList(),
QgsSettings::App ).toStringList();
175 if ( !docksTitle.isEmpty() )
177 for (
const auto &title : docksTitle )
179 mPanelStatus.insert( title, PanelStatus(
true, docksActive.contains( title ) ) );
182 mActionHidePanels->setChecked( !docksTitle.isEmpty() );
183 connect( mActionHidePanels, &QAction::toggled,
this, &QgsModelDesignerDialog::setPanelVisibility );
185 mUndoAction = mUndoStack->createUndoAction(
this );
187 mUndoAction->setShortcuts( QKeySequence::Undo );
188 mRedoAction = mUndoStack->createRedoAction(
this );
190 mRedoAction->setShortcuts( QKeySequence::Redo );
192 mMenuEdit->insertAction( mActionDeleteComponents, mRedoAction );
193 mMenuEdit->insertAction( mActionDeleteComponents, mUndoAction );
194 mMenuEdit->insertSeparator( mActionDeleteComponents );
195 mToolbar->insertAction( mActionZoomIn, mUndoAction );
196 mToolbar->insertAction( mActionZoomIn, mRedoAction );
197 mToolbar->insertSeparator( mActionZoomIn );
199 mGroupMenu =
new QMenu( tr(
"Zoom To" ),
this );
200 mMenuView->insertMenu( mActionZoomIn, mGroupMenu );
201 connect( mGroupMenu, &QMenu::aboutToShow,
this, &QgsModelDesignerDialog::populateZoomToMenu );
205 mActionCut =
new QAction( tr(
"Cu&t" ),
this );
206 mActionCut->setShortcuts( QKeySequence::Cut );
207 mActionCut->setStatusTip( tr(
"Cut" ) );
209 connect( mActionCut, &QAction::triggered,
this, [ = ]
211 mView->copySelectedItems( QgsModelGraphicsView::ClipboardCut );
214 mActionCopy =
new QAction( tr(
"&Copy" ),
this );
215 mActionCopy->setShortcuts( QKeySequence::Copy );
216 mActionCopy->setStatusTip( tr(
"Copy" ) );
218 connect( mActionCopy, &QAction::triggered,
this, [ = ]
220 mView->copySelectedItems( QgsModelGraphicsView::ClipboardCopy );
223 mActionPaste =
new QAction( tr(
"&Paste" ),
this );
224 mActionPaste->setShortcuts( QKeySequence::Paste );
225 mActionPaste->setStatusTip( tr(
"Paste" ) );
227 connect( mActionPaste, &QAction::triggered,
this, [ = ]
229 mView->pasteItems( QgsModelGraphicsView::PasteModeCursor );
231 mMenuEdit->insertAction( mActionDeleteComponents, mActionCut );
232 mMenuEdit->insertAction( mActionDeleteComponents, mActionCopy );
233 mMenuEdit->insertAction( mActionDeleteComponents, mActionPaste );
234 mMenuEdit->insertSeparator( mActionDeleteComponents );
236 mAlgorithmsModel =
new QgsModelerToolboxModel(
this );
237 mAlgorithmsTree->setToolboxProxyModel( mAlgorithmsModel );
240 if ( settings.
value( QStringLiteral(
"Processing/Configuration/SHOW_ALGORITHMS_KNOWN_ISSUES" ),
false ).toBool() )
244 mAlgorithmsTree->setFilters( filters );
245 mAlgorithmsTree->setDragDropMode( QTreeWidget::DragOnly );
246 mAlgorithmsTree->setDropIndicatorShown(
true );
248 connect( mView, &QgsModelGraphicsView::algorithmDropped,
this, [ = ](
const QString & algorithmId,
const QPointF & pos )
250 addAlgorithm( algorithmId, pos );
252 connect( mAlgorithmsTree, &QgsProcessingToolboxTreeView::doubleClicked,
this, [ = ]()
254 if ( mAlgorithmsTree->selectedAlgorithm() )
255 addAlgorithm( mAlgorithmsTree->selectedAlgorithm()->id(), QPointF() );
257 connect( mInputsTreeWidget, &QgsModelDesignerInputsTreeWidget::doubleClicked,
this, [ = ](
const QModelIndex & )
259 const QString parameterType = mInputsTreeWidget->currentItem()->data( 0, Qt::UserRole ).toString();
260 addInput( parameterType, QPointF() );
263 connect( mView, &QgsModelGraphicsView::inputDropped,
this, &QgsModelDesignerDialog::addInput );
266 QShortcut *ctrlEquals =
new QShortcut( QKeySequence( QStringLiteral(
"Ctrl+=" ) ),
this );
267 connect( ctrlEquals, &QShortcut::activated,
this, &QgsModelDesignerDialog::zoomIn );
270 mUndoDock->setObjectName( QStringLiteral(
"UndoDock" ) );
271 mUndoView =
new QUndoView( mUndoStack,
this );
272 mUndoDock->setWidget( mUndoView );
273 mUndoDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
274 addDockWidget( Qt::DockWidgetArea::LeftDockWidgetArea, mUndoDock );
276 tabifyDockWidget( mUndoDock, mPropertiesDock );
277 tabifyDockWidget( mVariablesDock, mPropertiesDock );
278 mPropertiesDock->raise();
279 tabifyDockWidget( mInputsDock, mAlgorithmsDock );
280 mInputsDock->raise();
286 beginUndoCommand( tr(
"Change Model Variables" ) );
287 mModel->setVariables( mVariablesEditor->variablesInActiveScope() );
291 connect( mNameEdit, &QLineEdit::textChanged,
this, [ = ](
const QString & name )
295 beginUndoCommand( tr(
"Change Model Name" ), NameChanged );
296 mModel->setName( name );
301 connect( mGroupEdit, &QLineEdit::textChanged,
this, [ = ](
const QString & group )
305 beginUndoCommand( tr(
"Change Model Group" ), GroupChanged );
306 mModel->setGroup( group );
313 QToolButton *toolbuttonExportToScript =
new QToolButton();
314 toolbuttonExportToScript->setPopupMode( QToolButton::InstantPopup );
315 toolbuttonExportToScript->addAction( mActionExportAsScriptAlgorithm );
316 toolbuttonExportToScript->setDefaultAction( mActionExportAsScriptAlgorithm );
317 mToolbar->insertWidget( mActionExportImage, toolbuttonExportToScript );
318 connect( mActionExportAsScriptAlgorithm, &QAction::triggered,
this, &QgsModelDesignerDialog::exportAsScriptAlgorithm );
320 mActionShowComments->setChecked( settings.
value( QStringLiteral(
"/Processing/Modeler/ShowComments" ),
true ).toBool() );
321 connect( mActionShowComments, &QAction::toggled,
this, &QgsModelDesignerDialog::toggleComments );
324 mPanTool->setAction( mActionPan );
326 mToolsActionGroup->addAction( mActionPan );
327 connect( mActionPan, &QAction::triggered, mPanTool, [ = ] { mView->setTool( mPanTool ); } );
330 mSelectTool->setAction( mActionSelectMoveItem );
332 mToolsActionGroup->addAction( mActionSelectMoveItem );
333 connect( mActionSelectMoveItem, &QAction::triggered, mSelectTool, [ = ] { mView->setTool( mSelectTool ); } );
335 mView->setTool( mSelectTool );
338 connect( mView, &QgsModelGraphicsView::macroCommandStarted,
this, [ = ](
const QString & text )
340 mIgnoreUndoStackChanges++;
341 mUndoStack->beginMacro( text );
342 mIgnoreUndoStackChanges--;
344 connect( mView, &QgsModelGraphicsView::macroCommandEnded,
this, [ = ]
346 mIgnoreUndoStackChanges++;
347 mUndoStack->endMacro();
348 mIgnoreUndoStackChanges--;
350 connect( mView, &QgsModelGraphicsView::beginCommand,
this, [ = ](
const QString & text )
352 beginUndoCommand( text );
354 connect( mView, &QgsModelGraphicsView::endCommand,
this, [ = ]
358 connect( mView, &QgsModelGraphicsView::deleteSelectedItems,
this, [ = ]
363 connect( mActionAddGroupBox, &QAction::triggered,
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( QStringLiteral(
"ModelDesigner/state" ), QByteArray(),
QgsSettings::App ).toByteArray() );
382QgsModelDesignerDialog::~QgsModelDesignerDialog()
385 if ( !mPanelStatus.isEmpty() )
387 QStringList docksTitle;
388 QStringList docksActive;
390 for (
const auto &panel : mPanelStatus.toStdMap() )
392 if ( panel.second.isVisible )
393 docksTitle << panel.first;
394 if ( panel.second.isActive )
395 docksActive << panel.first;
409 mIgnoreUndoStackChanges++;
413void QgsModelDesignerDialog::closeEvent( QCloseEvent *event )
415 if ( checkForUnsavedChanges() )
421void QgsModelDesignerDialog::beginUndoCommand(
const QString &text,
int id )
423 if ( mBlockUndoCommands || !mUndoStack )
426 if ( mActiveCommand )
429 mActiveCommand = std::make_unique< QgsModelUndoCommand >( mModel.get(), text,
id );
432void QgsModelDesignerDialog::endUndoCommand()
434 if ( mBlockUndoCommands || !mActiveCommand || !mUndoStack )
437 mActiveCommand->saveAfterState();
438 mIgnoreUndoStackChanges++;
439 mUndoStack->push( mActiveCommand.release() );
440 mIgnoreUndoStackChanges--;
444QgsProcessingModelAlgorithm *QgsModelDesignerDialog::model()
449void QgsModelDesignerDialog::setModel( QgsProcessingModelAlgorithm *model )
451 mModel.reset( model );
453 mGroupEdit->setText( mModel->group() );
454 mNameEdit->setText( mModel->displayName() );
456 updateVariablesGui();
458 mView->centerOn( 0, 0 );
461 mIgnoreUndoStackChanges++;
463 mIgnoreUndoStackChanges--;
468void QgsModelDesignerDialog::loadModel(
const QString &path )
470 std::unique_ptr< QgsProcessingModelAlgorithm > alg = std::make_unique< QgsProcessingModelAlgorithm >();
471 if ( alg->fromFile( path ) )
474 alg->setSourceFilePath( path );
475 setModel( alg.release() );
480 QMessageBox::critical(
this, tr(
"Open Model" ), tr(
"The selected model could not be loaded.\n"
481 "See the log for more information." ) );
485void QgsModelDesignerDialog::setModelScene( QgsModelGraphicsScene *scene )
487 QgsModelGraphicsScene *oldScene = mScene;
490 mScene->setParent(
this );
491 mScene->setChildAlgorithmResults( mChildResults );
492 mScene->setModel( mModel.get() );
493 mScene->setMessageBar( mMessageBar );
495 const QPointF center = mView->mapToScene( mView->viewport()->rect().center() );
496 mView->setModelScene( mScene );
498 mSelectTool->resetCache();
499 mSelectTool->setScene( mScene );
501 connect( mScene, &QgsModelGraphicsScene::rebuildRequired,
this, [ = ]
503 if ( mBlockRepaints )
508 connect( mScene, &QgsModelGraphicsScene::componentAboutToChange,
this, [ = ](
const QString & description,
int id ) { beginUndoCommand( description,
id ); } );
509 connect( mScene, &QgsModelGraphicsScene::componentChanged,
this, [ = ] { endUndoCommand(); } );
511 mView->centerOn( center );
514 oldScene->deleteLater();
517void QgsModelDesignerDialog::activate()
521 setWindowState( windowState() & ~Qt::WindowMinimized );
525void QgsModelDesignerDialog::updateVariablesGui()
527 mBlockUndoCommands++;
529 std::unique_ptr< QgsExpressionContextScope > variablesScope = std::make_unique< QgsExpressionContextScope >( tr(
"Model Variables" ) );
530 const QVariantMap modelVars = mModel->variables();
531 for (
auto it = modelVars.constBegin(); it != modelVars.constEnd(); ++it )
533 variablesScope->setVariable( it.key(), it.value() );
536 variablesContext.
appendScope( variablesScope.release() );
537 mVariablesEditor->setContext( &variablesContext );
538 mVariablesEditor->setEditableScopeIndex( 0 );
540 mBlockUndoCommands--;
543void QgsModelDesignerDialog::setDirty(
bool dirty )
549bool QgsModelDesignerDialog::validateSave( SaveAction action )
553 case QgsModelDesignerDialog::SaveAction::SaveAsFile:
555 case QgsModelDesignerDialog::SaveAction::SaveInProject:
556 if ( mNameEdit->text().trimmed().isEmpty() )
558 mMessageBar->pushWarning( QString(), tr(
"Please enter a model name before saving" ) );
567bool QgsModelDesignerDialog::checkForUnsavedChanges()
571 QMessageBox::StandardButton ret = QMessageBox::question(
this, tr(
"Save Model?" ),
572 tr(
"There are unsaved changes in this model. Do you want to keep those?" ),
573 QMessageBox::Save | QMessageBox::Cancel | QMessageBox::Discard, QMessageBox::Cancel );
576 case QMessageBox::Save:
577 return saveModel(
false );
579 case QMessageBox::Discard:
592void QgsModelDesignerDialog::setLastRunChildAlgorithmResults(
const QVariantMap &results )
594 mChildResults = results;
596 mScene->setChildAlgorithmResults( mChildResults );
599void QgsModelDesignerDialog::setLastRunChildAlgorithmInputs(
const QVariantMap &inputs )
601 mChildInputs = inputs;
603 mScene->setChildAlgorithmInputs( mChildInputs );
606void QgsModelDesignerDialog::setModelName(
const QString &name )
608 mNameEdit->setText( name );
611void QgsModelDesignerDialog::zoomIn()
613 mView->setTransformationAnchor( QGraphicsView::NoAnchor );
614 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
616 const double factor = settings.
value( QStringLiteral(
"/qgis/zoom_favor" ), 2.0 ).toDouble();
617 mView->scale( factor, factor );
618 mView->centerOn( point );
621void QgsModelDesignerDialog::zoomOut()
623 mView->setTransformationAnchor( QGraphicsView::NoAnchor );
624 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
626 const double factor = 1.0 / settings.
value( QStringLiteral(
"/qgis/zoom_favor" ), 2.0 ).toDouble();
627 mView->scale( factor, factor );
628 mView->centerOn( point );
631void QgsModelDesignerDialog::zoomActual()
633 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
634 mView->resetTransform();
635 mView->scale( mScreenHelper->screenDpi() / 96, mScreenHelper->screenDpi() / 96 );
636 mView->centerOn( point );
639void QgsModelDesignerDialog::zoomFull()
641 QRectF totalRect = mView->scene()->itemsBoundingRect();
642 totalRect.adjust( -10, -10, 10, 10 );
643 mView->fitInView( totalRect, Qt::KeepAspectRatio );
646void QgsModelDesignerDialog::newModel()
648 if ( !checkForUnsavedChanges() )
651 std::unique_ptr< QgsProcessingModelAlgorithm > alg = std::make_unique< QgsProcessingModelAlgorithm >();
653 setModel( alg.release() );
656void QgsModelDesignerDialog::exportToImage()
659 QString lastExportDir = settings.
value( QStringLiteral(
"lastModelDesignerExportDir" ), QDir::homePath(),
QgsSettings::App ).toString();
661 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as Image" ),
663 tr(
"PNG files (*.png *.PNG)" ) );
667 if ( filename.isEmpty() )
672 const QFileInfo saveFileInfo( filename );
675 repaintModel(
false );
677 QRectF totalRect = mView->scene()->itemsBoundingRect();
678 totalRect.adjust( -10, -10, 10, 10 );
679 const QRectF imageRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
681 QImage img( totalRect.width(), totalRect.height(),
682 QImage::Format_ARGB32_Premultiplied );
683 img.fill( Qt::white );
685 painter.setRenderHint( QPainter::Antialiasing );
686 painter.begin( &img );
687 mView->scene()->render( &painter, imageRect, totalRect );
690 img.save( filename );
692 mMessageBar->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 );
693 repaintModel(
true );
696void QgsModelDesignerDialog::exportToPdf()
699 QString lastExportDir = settings.
value( QStringLiteral(
"lastModelDesignerExportDir" ), QDir::homePath(),
QgsSettings::App ).toString();
701 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as PDF" ),
703 tr(
"PDF files (*.pdf *.PDF)" ) );
707 if ( filename.isEmpty() )
712 const QFileInfo saveFileInfo( filename );
715 repaintModel(
false );
717 QRectF totalRect = mView->scene()->itemsBoundingRect();
718 totalRect.adjust( -10, -10, 10, 10 );
719 const QRectF printerRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
722 printer.setOutputFormat( QPrinter::PdfFormat );
723 printer.setOutputFileName( filename );
725 const double scaleFactor = 96 / 25.4;
727 QPageLayout pageLayout( QPageSize( totalRect.size() / scaleFactor, QPageSize::Millimeter ),
728 QPageLayout::Portrait,
729 QMarginsF( 0, 0, 0, 0 ) );
730 pageLayout.setMode( QPageLayout::FullPageMode );
731 printer.setPageLayout( pageLayout );
733 printer.setFullPage(
true );
735 QPainter painter( &printer );
736 mView->scene()->render( &painter, printerRect, totalRect );
739 mMessageBar->pushMessage( QString(), tr(
"Successfully exported model as PDF to <a href=\"%1\">%2</a>" ).arg( QUrl::fromLocalFile( filename ).toString(),
741 repaintModel(
true );
744void QgsModelDesignerDialog::exportToSvg()
747 QString lastExportDir = settings.
value( QStringLiteral(
"lastModelDesignerExportDir" ), QDir::homePath(),
QgsSettings::App ).toString();
749 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as SVG" ),
751 tr(
"SVG files (*.svg *.SVG)" ) );
755 if ( filename.isEmpty() )
760 const QFileInfo saveFileInfo( filename );
763 repaintModel(
false );
765 QRectF totalRect = mView->scene()->itemsBoundingRect();
766 totalRect.adjust( -10, -10, 10, 10 );
767 const QRectF svgRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
770 svg.setFileName( filename );
771 svg.setSize( QSize( totalRect.width(), totalRect.height() ) );
772 svg.setViewBox( svgRect );
773 svg.setTitle( mModel->displayName() );
775 QPainter painter( &svg );
776 mView->scene()->render( &painter, svgRect, totalRect );
779 mMessageBar->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 );
780 repaintModel(
true );
783void QgsModelDesignerDialog::exportAsPython()
786 QString lastExportDir = settings.
value( QStringLiteral(
"lastModelDesignerExportDir" ), QDir::homePath(),
QgsSettings::App ).toString();
788 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as Python Script" ),
790 tr(
"Processing scripts (*.py *.PY)" ) );
794 if ( filename.isEmpty() )
799 const QFileInfo saveFileInfo( filename );
804 QFile outFile( filename );
805 if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
809 QTextStream fout( &outFile );
813 mMessageBar->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 );
816void QgsModelDesignerDialog::toggleComments(
bool show )
820 repaintModel(
true );
823void QgsModelDesignerDialog::updateWindowTitle()
825 QString title = tr(
"Model Designer" );
826 if ( !mModel->name().isEmpty() )
827 title = QStringLiteral(
"%1 - %2" ).arg( title, mModel->name() );
830 title.prepend(
'*' );
832 setWindowTitle( title );
835void QgsModelDesignerDialog::deleteSelected()
837 QList< QgsModelComponentGraphicItem * > items = mScene->selectedComponentItems();
841 if ( items.size() == 1 )
843 items.at( 0 )->deleteComponent();
847 std::sort( items.begin(), items.end(), []( QgsModelComponentGraphicItem * p1, QgsModelComponentGraphicItem * p2 )
850 if ( dynamic_cast< QgsModelCommentGraphicItem *>( p1 ) )
852 else if ( dynamic_cast< QgsModelCommentGraphicItem *>( p2 ) )
854 else if ( dynamic_cast< QgsModelGroupBoxGraphicItem *>( p1 ) )
856 else if ( dynamic_cast< QgsModelGroupBoxGraphicItem *>( p2 ) )
858 else if ( dynamic_cast< QgsModelOutputGraphicItem *>( p1 ) )
860 else if ( dynamic_cast< QgsModelOutputGraphicItem *>( p2 ) )
862 else if ( dynamic_cast< QgsModelChildAlgorithmGraphicItem *>( p1 ) )
864 else if ( dynamic_cast< QgsModelChildAlgorithmGraphicItem *>( p2 ) )
870 beginUndoCommand( tr(
"Delete Components" ) );
872 QVariant prevState = mModel->toVariant();
873 mBlockUndoCommands++;
874 mBlockRepaints =
true;
876 while ( !items.empty() )
878 QgsModelComponentGraphicItem *toDelete =
nullptr;
879 for ( QgsModelComponentGraphicItem *item : items )
881 if ( item->canDeleteComponent() )
894 toDelete->deleteComponent();
895 items.removeAll( toDelete );
900 mModel->loadVariant( prevState );
901 QMessageBox::warning(
nullptr, QObject::tr(
"Could not remove components" ),
902 QObject::tr(
"Components depend on the selected items.\n"
903 "Try to remove them before trying deleting these components." ) );
904 mBlockUndoCommands--;
905 mActiveCommand.reset();
909 mBlockUndoCommands--;
913 mBlockRepaints =
false;
917void QgsModelDesignerDialog::populateZoomToMenu()
920 for (
const QgsProcessingModelGroupBox &box : model()->groupBoxes() )
922 if ( QgsModelComponentGraphicItem *item = mScene->groupBoxItem( box.uuid() ) )
924 QAction *zoomAction =
new QAction( box.description(), mGroupMenu );
925 connect( zoomAction, &QAction::triggered,
this, [ = ]
927 QRectF groupRect = item->mapToScene( item->boundingRect() ).boundingRect();
928 groupRect.adjust( -10, -10, 10, 10 );
929 mView->fitInView( groupRect, Qt::KeepAspectRatio );
930 mView->centerOn( item );
932 mGroupMenu->addAction( zoomAction );
937void QgsModelDesignerDialog::setPanelVisibility(
bool hidden )
939 const QList<QDockWidget *> docks = findChildren<QDockWidget *>();
940 const QList<QTabBar *> tabBars = findChildren<QTabBar *>();
944 mPanelStatus.clear();
946 for ( QDockWidget *dock : docks )
948 mPanelStatus.insert( dock->windowTitle(), PanelStatus( dock->isVisible(),
false ) );
949 dock->setVisible(
false );
953 for ( QTabBar *tabBar : tabBars )
955 QString currentTabTitle = tabBar->tabText( tabBar->currentIndex() );
956 mPanelStatus[ currentTabTitle ].isActive =
true;
962 for ( QDockWidget *dock : docks )
964 if ( mPanelStatus.contains( dock->windowTitle() ) )
966 dock->setVisible( mPanelStatus.value( dock->windowTitle() ).isVisible );
971 for ( QTabBar *tabBar : tabBars )
974 for (
int i = 0; i < tabBar->count(); ++i )
976 QString tabTitle = tabBar->tabText( i );
977 if ( mPanelStatus.contains( tabTitle ) && mPanelStatus.value( tabTitle ).isActive )
979 tabBar->setCurrentIndex( i );
983 mPanelStatus.clear();
987void QgsModelDesignerDialog::editHelp()
989 QgsProcessingHelpEditorDialog dialog(
this );
990 dialog.setWindowTitle( tr(
"Edit Model Help" ) );
991 dialog.setAlgorithm( mModel.get() );
994 beginUndoCommand( tr(
"Edit Model Help" ) );
995 mModel->setHelpContent( dialog.helpContent() );
1000void QgsModelDesignerDialog::validate()
1003 if ( model()->validate( issues ) )
1005 mMessageBar->pushSuccess( QString(), tr(
"Model is valid!" ) );
1010 QPushButton *detailsButton =
new QPushButton( tr(
"Details" ) );
1011 connect( detailsButton, &QPushButton::clicked, detailsButton, [ = ]
1014 dialog->
setTitle( tr(
"Model is Invalid" ) );
1016 QString longMessage = tr(
"<p>This model is not valid:</p>" ) + QStringLiteral(
"<ul>" );
1017 for (
const QString &issue : issues )
1019 longMessage += QStringLiteral(
"<li>%1</li>" ).arg( issue );
1021 longMessage += QLatin1String(
"</ul>" );
1026 messageWidget->layout()->addWidget( detailsButton );
1027 mMessageBar->clearWidgets();
1032void QgsModelDesignerDialog::reorderInputs()
1034 QgsModelInputReorderDialog dlg(
this );
1035 dlg.setModel( mModel.get() );
1038 const QStringList inputOrder = dlg.inputOrder();
1039 beginUndoCommand( tr(
"Reorder Inputs" ) );
1040 mModel->setParameterOrder( inputOrder );
1045void QgsModelDesignerDialog::reorderOutputs()
1047 QgsModelOutputReorderDialog dlg(
this );
1048 dlg.setModel( mModel.get() );
1051 const QStringList outputOrder = dlg.outputOrder();
1052 beginUndoCommand( tr(
"Reorder Outputs" ) );
1053 mModel->setOutputOrder( outputOrder );
1054 mModel->setOutputGroup( dlg.outputGroup() );
1059bool QgsModelDesignerDialog::isDirty()
const
1061 return mHasChanged && mUndoStack->index() != -1;
1064void QgsModelDesignerDialog::fillInputsTree()
1067 std::unique_ptr< QTreeWidgetItem > parametersItem = std::make_unique< QTreeWidgetItem >();
1068 parametersItem->setText( 0, tr(
"Parameters" ) );
1072 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
1079 std::unique_ptr< QTreeWidgetItem > paramItem = std::make_unique< QTreeWidgetItem >();
1080 paramItem->setText( 0, param->name() );
1081 paramItem->setData( 0, Qt::UserRole, param->id() );
1082 paramItem->setIcon( 0, icon );
1083 paramItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1084 paramItem->setToolTip( 0, param->description() );
1085 parametersItem->addChild( paramItem.release() );
1088 mInputsTreeWidget->addTopLevelItem( parametersItem.release() );
1089 mInputsTreeWidget->topLevelItem( 0 )->setExpanded(
true );
1097QgsModelChildDependenciesWidget::QgsModelChildDependenciesWidget( QWidget *parent, QgsProcessingModelAlgorithm *model,
const QString &childId )
1100 , mChildId( childId )
1102 QHBoxLayout *hl =
new QHBoxLayout();
1103 hl->setContentsMargins( 0, 0, 0, 0 );
1105 mLineEdit =
new QLineEdit();
1106 mLineEdit->setEnabled(
false );
1107 hl->addWidget( mLineEdit, 1 );
1109 mToolButton =
new QToolButton();
1110 mToolButton->setText( QString( QChar( 0x2026 ) ) );
1111 hl->addWidget( mToolButton );
1115 mLineEdit->setText( tr(
"%1 dependencies selected" ).arg( 0 ) );
1117 connect( mToolButton, &QToolButton::clicked,
this, &QgsModelChildDependenciesWidget::showDialog );
1120void QgsModelChildDependenciesWidget::setValue(
const QList<QgsProcessingModelChildDependency> &value )
1124 updateSummaryText();
1127void QgsModelChildDependenciesWidget::showDialog()
1129 const QList<QgsProcessingModelChildDependency> available = mModel->availableDependenciesForChildAlgorithm( mChildId );
1131 QVariantList availableOptions;
1132 for (
const QgsProcessingModelChildDependency &dep : available )
1133 availableOptions << QVariant::fromValue( dep );
1134 QVariantList selectedOptions;
1135 for (
const QgsProcessingModelChildDependency &dep : mValue )
1136 selectedOptions << QVariant::fromValue( dep );
1141 QgsProcessingMultipleSelectionPanelWidget *widget =
new QgsProcessingMultipleSelectionPanelWidget( availableOptions, selectedOptions );
1142 widget->setPanelTitle( tr(
"Algorithm Dependencies" ) );
1144 widget->setValueFormatter( [ = ](
const QVariant & v ) -> QString
1146 const QgsProcessingModelChildDependency dep = v.value< QgsProcessingModelChildDependency >();
1148 const QString description = mModel->childAlgorithm( dep.childId ).description();
1149 if ( dep.conditionalBranch.isEmpty() )
1152 return tr(
"Condition “%1” from algorithm “%2”" ).arg( dep.conditionalBranch, description );
1155 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged,
this, [ = ]()
1157 QList< QgsProcessingModelChildDependency > res;
1158 for (
const QVariant &v : widget->selectedOptions() )
1160 res << v.value< QgsProcessingModelChildDependency >();
1169void QgsModelChildDependenciesWidget::updateSummaryText()
1171 mLineEdit->setText( tr(
"%n dependencies selected",
nullptr, mValue.count() ) );
@ Warning
Warning message.
@ Critical
Critical/error message.
@ Success
Used for reporting a successful operation.
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...
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)
Adds a message to the log instance (and creates it if necessary).
A generic message view for displaying QGIS messages.
void setTitle(const QString &title) override
Sets title for the messages.
void setMessage(const QString &message, MessageType msgType) override
Sets message, it won't be displayed until.
void showMessage(bool blocking=true) override
display the message to the user and deletes itself
Makes metadata of processing parameters available.
@ ExposeToModeler
Is this parameter available in the modeler. Is set to on by default.
QList< QgsProcessingParameterType * > parameterTypes() const
Returns a list with all known parameter types.
@ PythonQgsProcessingAlgorithmSubclass
Full Python QgsProcessingAlgorithm subclass.
A utility class for dynamic handling of changes to screen properties.
This class is a composition of two QSettings instances:
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.