45#include <QActionGroup>
48#include <QKeySequence>
53#include <QSvgGenerator>
59#include "moc_qgsmodeldesignerdialog.cpp"
64QgsModelerToolboxModel::QgsModelerToolboxModel( QObject *parent )
69Qt::ItemFlags QgsModelerToolboxModel::flags(
const QModelIndex &index )
const
71 Qt::ItemFlags f = QgsProcessingToolboxProxyModel::flags( index );
72 const QModelIndex sourceIndex = mapToSource( index );
73 if ( toolboxModel()->isAlgorithm( sourceIndex ) || toolboxModel()->isParameter( sourceIndex ) )
75 f = f | Qt::ItemIsDragEnabled;
80Qt::DropActions QgsModelerToolboxModel::supportedDragActions()
const
82 return Qt::CopyAction;
85QgsModelDesignerDialog::QgsModelDesignerDialog( QWidget *parent, Qt::WindowFlags flags )
86 : QMainWindow( parent, flags )
87 , mToolsActionGroup( new QActionGroup( this ) )
95 setAttribute( Qt::WA_DeleteOnClose );
96 setDockOptions( dockOptions() | QMainWindow::GroupedDragging );
97 setWindowFlags( Qt::WindowMinimizeButtonHint | Qt::WindowMaximizeButtonHint | Qt::WindowCloseButtonHint );
101 mModel = std::make_unique<QgsProcessingModelAlgorithm>();
104 mUndoStack =
new QUndoStack(
this );
105 connect( mUndoStack, &QUndoStack::indexChanged,
this, [
this] {
106 if ( mIgnoreUndoStackChanges )
109 mBlockUndoCommands++;
110 updateVariablesGui();
111 mGroupEdit->setText( mModel->group() );
112 mNameEdit->setText( mModel->displayName() );
113 mBlockUndoCommands--;
117 mPropertiesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
118 mInputsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
119 mAlgorithmsDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable );
120 mVariablesDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
122 mToolboxTree->header()->setVisible(
false );
123 mToolboxSearchEdit->setShowSearchIcon(
true );
124 mToolboxSearchEdit->setPlaceholderText( tr(
"Search…" ) );
125 connect( mToolboxSearchEdit, &QgsFilterLineEdit::textChanged, mToolboxTree, &QgsProcessingToolboxTreeView::setFilterString );
127 mInputsTreeWidget->header()->setVisible(
false );
128 mInputsTreeWidget->setAlternatingRowColors(
true );
129 mInputsTreeWidget->setDragDropMode( QTreeWidget::DragOnly );
130 mInputsTreeWidget->setDropIndicatorShown(
true );
132 mNameEdit->setPlaceholderText( tr(
"Enter model name here" ) );
133 mGroupEdit->setPlaceholderText( tr(
"Enter group name here" ) );
136 mMessageBar->setSizePolicy( QSizePolicy::Minimum, QSizePolicy::Fixed );
137 mainLayout->insertWidget( 0, mMessageBar );
139 mView->setAcceptDrops(
true );
142 connect( mActionClose, &QAction::triggered,
this, &QWidget::close );
143 connect( mActionNew, &QAction::triggered,
this, &QgsModelDesignerDialog::newModel );
144 connect( mActionZoomIn, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomIn );
145 connect( mActionZoomOut, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomOut );
146 connect( mActionZoomActual, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomActual );
147 connect( mActionZoomToItems, &QAction::triggered,
this, &QgsModelDesignerDialog::zoomFull );
148 connect( mActionExportImage, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToImage );
149 connect( mActionExportPdf, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToPdf );
150 connect( mActionExportSvg, &QAction::triggered,
this, &QgsModelDesignerDialog::exportToSvg );
151 connect( mActionExportPython, &QAction::triggered,
this, &QgsModelDesignerDialog::exportAsPython );
152 connect( mActionSave, &QAction::triggered,
this, [
this] { saveModel(
false ); } );
153 connect( mActionSaveAs, &QAction::triggered,
this, [
this] { saveModel(
true ); } );
154 connect( mActionDeleteComponents, &QAction::triggered,
this, &QgsModelDesignerDialog::deleteSelected );
155 connect( mActionSnapSelected, &QAction::triggered, mView, &QgsModelGraphicsView::snapSelected );
156 connect( mActionValidate, &QAction::triggered,
this, &QgsModelDesignerDialog::validate );
157 connect( mActionReorderInputs, &QAction::triggered,
this, &QgsModelDesignerDialog::reorderInputs );
158 connect( mActionReorderOutputs, &QAction::triggered,
this, &QgsModelDesignerDialog::reorderOutputs );
159 connect( mActionEditHelp, &QAction::triggered,
this, &QgsModelDesignerDialog::editHelp );
160 connect( mReorderInputsButton, &QPushButton::clicked,
this, &QgsModelDesignerDialog::reorderInputs );
161 connect( mActionRun, &QAction::triggered,
this, [
this] { run(); } );
162 connect( mActionRunSelectedSteps, &QAction::triggered,
this, &QgsModelDesignerDialog::runSelectedSteps );
164 mActionSnappingEnabled->setChecked( settings.
value( QStringLiteral(
"/Processing/Modeler/enableSnapToGrid" ),
false ).toBool() );
165 connect( mActionSnappingEnabled, &QAction::toggled,
this, [
this](
bool enabled ) {
166 mView->snapper()->setSnapToGrid( enabled );
167 QgsSettings().
setValue( QStringLiteral(
"/Processing/Modeler/enableSnapToGrid" ), enabled );
169 mView->snapper()->setSnapToGrid( mActionSnappingEnabled->isChecked() );
171 connect( mActionSelectAll, &QAction::triggered,
this, [
this] {
175 QStringList docksTitle = settings.
value( QStringLiteral(
"ModelDesigner/hiddenDocksTitle" ), QStringList(),
QgsSettings::App ).toStringList();
176 QStringList docksActive = settings.
value( QStringLiteral(
"ModelDesigner/hiddenDocksActive" ), QStringList(),
QgsSettings::App ).toStringList();
177 if ( !docksTitle.isEmpty() )
179 for (
const auto &title : docksTitle )
181 mPanelStatus.insert( title, PanelStatus(
true, docksActive.contains( title ) ) );
184 mActionHidePanels->setChecked( !docksTitle.isEmpty() );
185 connect( mActionHidePanels, &QAction::toggled,
this, &QgsModelDesignerDialog::setPanelVisibility );
187 mUndoAction = mUndoStack->createUndoAction(
this );
189 mUndoAction->setShortcuts( QKeySequence::Undo );
190 mRedoAction = mUndoStack->createRedoAction(
this );
192 mRedoAction->setShortcuts( QKeySequence::Redo );
194 mMenuEdit->insertAction( mActionDeleteComponents, mRedoAction );
195 mMenuEdit->insertAction( mActionDeleteComponents, mUndoAction );
196 mMenuEdit->insertSeparator( mActionDeleteComponents );
197 mToolbar->insertAction( mActionZoomIn, mUndoAction );
198 mToolbar->insertAction( mActionZoomIn, mRedoAction );
199 mToolbar->insertSeparator( mActionZoomIn );
201 mGroupMenu =
new QMenu( tr(
"Zoom To" ),
this );
202 mMenuView->insertMenu( mActionZoomIn, mGroupMenu );
203 connect( mGroupMenu, &QMenu::aboutToShow,
this, &QgsModelDesignerDialog::populateZoomToMenu );
207 mActionCut =
new QAction( tr(
"Cu&t" ),
this );
208 mActionCut->setShortcuts( QKeySequence::Cut );
209 mActionCut->setStatusTip( tr(
"Cut" ) );
211 connect( mActionCut, &QAction::triggered,
this, [
this] {
212 mView->copySelectedItems( QgsModelGraphicsView::ClipboardCut );
215 mActionCopy =
new QAction( tr(
"&Copy" ),
this );
216 mActionCopy->setShortcuts( QKeySequence::Copy );
217 mActionCopy->setStatusTip( tr(
"Copy" ) );
219 connect( mActionCopy, &QAction::triggered,
this, [
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, [
this] {
228 mView->pasteItems( QgsModelGraphicsView::PasteModeCursor );
230 mMenuEdit->insertAction( mActionDeleteComponents, mActionCut );
231 mMenuEdit->insertAction( mActionDeleteComponents, mActionCopy );
232 mMenuEdit->insertAction( mActionDeleteComponents, mActionPaste );
233 mMenuEdit->insertSeparator( mActionDeleteComponents );
235 mAlgorithmsModel =
new QgsModelerToolboxModel(
this );
236 mToolboxTree->setToolboxProxyModel( mAlgorithmsModel );
239 if ( settings.
value( QStringLiteral(
"Processing/Configuration/SHOW_ALGORITHMS_KNOWN_ISSUES" ),
false ).toBool() )
243 mToolboxTree->setFilters( filters );
244 mToolboxTree->setDragDropMode( QTreeWidget::DragOnly );
245 mToolboxTree->setDropIndicatorShown(
true );
247 connect( mView, &QgsModelGraphicsView::algorithmDropped,
this, [
this](
const QString &algorithmId,
const QPointF &pos ) {
248 addAlgorithm( algorithmId, pos );
250 connect( mView, &QgsModelGraphicsView::inputDropped,
this, &QgsModelDesignerDialog::addInput );
252 connect( mToolboxTree, &QgsProcessingToolboxTreeView::doubleClicked,
this, [
this](
const QModelIndex & ) {
253 if ( mToolboxTree->selectedAlgorithm() )
254 addAlgorithm( mToolboxTree->selectedAlgorithm()->id(), QPointF() );
255 if ( mToolboxTree->selectedParameterType() )
256 addInput( mToolboxTree->selectedParameterType()->id(), QPointF() );
259 connect( mInputsTreeWidget, &QgsModelDesignerInputsTreeWidget::doubleClicked,
this, [
this](
const QModelIndex & ) {
260 const QString parameterType = mInputsTreeWidget->currentItem()->data( 0, Qt::UserRole ).toString();
261 addInput( parameterType, QPointF() );
265 QShortcut *ctrlEquals =
new QShortcut( QKeySequence( QStringLiteral(
"Ctrl+=" ) ),
this );
266 connect( ctrlEquals, &QShortcut::activated,
this, &QgsModelDesignerDialog::zoomIn );
269 mUndoDock->setObjectName( QStringLiteral(
"UndoDock" ) );
270 mUndoView =
new QUndoView( mUndoStack,
this );
271 mUndoDock->setWidget( mUndoView );
272 mUndoDock->setFeatures( QDockWidget::DockWidgetFloatable | QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable );
273 addDockWidget( Qt::DockWidgetArea::LeftDockWidgetArea, mUndoDock );
275 tabifyDockWidget( mUndoDock, mPropertiesDock );
276 tabifyDockWidget( mVariablesDock, mPropertiesDock );
277 mPropertiesDock->raise();
278 tabifyDockWidget( mInputsDock, mAlgorithmsDock );
279 mInputsDock->raise();
284 beginUndoCommand( tr(
"Change Model Variables" ) );
285 mModel->setVariables( mVariablesEditor->variablesInActiveScope() );
289 connect( mNameEdit, &QLineEdit::textChanged,
this, [
this](
const QString &name ) {
292 beginUndoCommand( tr(
"Change Model Name" ), NameChanged );
293 mModel->setName( name );
298 connect( mGroupEdit, &QLineEdit::textChanged,
this, [
this](
const QString &group ) {
301 beginUndoCommand( tr(
"Change Model Group" ), GroupChanged );
302 mModel->setGroup( group );
310 QToolButton *toolbuttonExportToScript =
new QToolButton();
311 toolbuttonExportToScript->setPopupMode( QToolButton::InstantPopup );
312 toolbuttonExportToScript->addAction( mActionExportAsScriptAlgorithm );
313 toolbuttonExportToScript->setDefaultAction( mActionExportAsScriptAlgorithm );
314 mToolbar->insertWidget( mActionExportImage, toolbuttonExportToScript );
315 connect( mActionExportAsScriptAlgorithm, &QAction::triggered,
this, &QgsModelDesignerDialog::exportAsScriptAlgorithm );
317 mActionShowComments->setChecked( settings.
value( QStringLiteral(
"/Processing/Modeler/ShowComments" ),
true ).toBool() );
318 connect( mActionShowComments, &QAction::toggled,
this, &QgsModelDesignerDialog::toggleComments );
321 mPanTool->setAction( mActionPan );
323 mToolsActionGroup->addAction( mActionPan );
324 connect( mActionPan, &QAction::triggered, mPanTool, [
this] { mView->setTool( mPanTool ); } );
327 mSelectTool->setAction( mActionSelectMoveItem );
329 mToolsActionGroup->addAction( mActionSelectMoveItem );
330 connect( mActionSelectMoveItem, &QAction::triggered, mSelectTool, [
this] { mView->setTool( mSelectTool ); } );
332 mView->setTool( mSelectTool );
335 connect( mView, &QgsModelGraphicsView::macroCommandStarted,
this, [
this](
const QString &text ) {
336 mIgnoreUndoStackChanges++;
337 mUndoStack->beginMacro( text );
338 mIgnoreUndoStackChanges--;
340 connect( mView, &QgsModelGraphicsView::macroCommandEnded,
this, [
this] {
341 mIgnoreUndoStackChanges++;
342 mUndoStack->endMacro();
343 mIgnoreUndoStackChanges--;
345 connect( mView, &QgsModelGraphicsView::commandBegun,
this, [
this](
const QString &text ) {
346 beginUndoCommand( text );
348 connect( mView, &QgsModelGraphicsView::commandEnded,
this, [
this] {
351 connect( mView, &QgsModelGraphicsView::commandAborted,
this, [
this] {
354 connect( mView, &QgsModelGraphicsView::deleteSelectedItems,
this, [
this] {
358 connect( mActionAddGroupBox, &QAction::triggered,
this, [
this] {
359 const QPointF viewCenter = mView->mapToScene( mView->viewport()->rect().center() );
360 QgsProcessingModelGroupBox group;
361 group.setPosition( viewCenter );
362 group.setDescription( tr(
"New Group" ) );
364 beginUndoCommand( tr(
"Add Group Box" ) );
365 model()->addGroupBox( group );
373 restoreState( settings.
value( QStringLiteral(
"ModelDesigner/state" ), QByteArray(),
QgsSettings::App ).toByteArray() );
376QgsModelDesignerDialog::~QgsModelDesignerDialog()
379 if ( !mPanelStatus.isEmpty() )
381 QStringList docksTitle;
382 QStringList docksActive;
384 for (
const auto &panel : mPanelStatus.toStdMap() )
386 if ( panel.second.isVisible )
387 docksTitle << panel.first;
388 if ( panel.second.isActive )
389 docksActive << panel.first;
403 mIgnoreUndoStackChanges++;
407void QgsModelDesignerDialog::closeEvent( QCloseEvent *event )
409 if ( checkForUnsavedChanges() )
415void QgsModelDesignerDialog::beginUndoCommand(
const QString &text,
int id )
417 if ( mBlockUndoCommands || !mUndoStack )
420 if ( mActiveCommand )
423 mActiveCommand = std::make_unique<QgsModelUndoCommand>( mModel.get(), text,
id );
426void QgsModelDesignerDialog::endUndoCommand()
428 if ( mBlockUndoCommands || !mActiveCommand || !mUndoStack )
431 mActiveCommand->saveAfterState();
432 mIgnoreUndoStackChanges++;
433 mUndoStack->push( mActiveCommand.release() );
434 mIgnoreUndoStackChanges--;
438void QgsModelDesignerDialog::abortUndoCommand()
440 if ( mActiveCommand )
441 mActiveCommand->setObsolete(
true );
444QgsProcessingModelAlgorithm *QgsModelDesignerDialog::model()
449void QgsModelDesignerDialog::setModel( QgsProcessingModelAlgorithm *model )
451 mModel.reset( model );
453 mGroupEdit->setText( mModel->group() );
454 mNameEdit->setText( mModel->displayName() );
455 repaintModel(
true );
456 updateVariablesGui();
458 mView->centerOn( 0, 0 );
461 mIgnoreUndoStackChanges++;
463 mIgnoreUndoStackChanges--;
468void QgsModelDesignerDialog::loadModel(
const QString &path )
470 auto 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->setLastRunResult( mLastResult );
492 mScene->setModel( mModel.get() );
493 mScene->setMessageBar( mMessageBar );
495 mView->setModelScene( mScene );
497 mSelectTool->resetCache();
498 mSelectTool->setScene( mScene );
500 connect( mScene, &QgsModelGraphicsScene::rebuildRequired,
this, [
this] {
501 if ( mBlockRepaints )
506 connect( mScene, &QgsModelGraphicsScene::componentAboutToChange,
this, [
this](
const QString &description,
int id ) { beginUndoCommand( description,
id ); } );
507 connect( mScene, &QgsModelGraphicsScene::componentChanged,
this, [
this] { endUndoCommand(); } );
508 connect( mScene, &QgsModelGraphicsScene::runFromChild,
this, &QgsModelDesignerDialog::runFromChild );
509 connect( mScene, &QgsModelGraphicsScene::runSelected,
this, &QgsModelDesignerDialog::runSelectedSteps );
510 connect( mScene, &QgsModelGraphicsScene::showChildAlgorithmOutputs,
this, &QgsModelDesignerDialog::showChildAlgorithmOutputs );
511 connect( mScene, &QgsModelGraphicsScene::showChildAlgorithmLog,
this, &QgsModelDesignerDialog::showChildAlgorithmLog );
514 oldScene->deleteLater();
517void QgsModelDesignerDialog::activate()
521 setWindowState( windowState() & ~Qt::WindowMinimized );
525void QgsModelDesignerDialog::updateVariablesGui()
527 mBlockUndoCommands++;
529 auto 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?" ), tr(
"There are unsaved changes in this model. Do you want to keep those?" ), QMessageBox::Save | QMessageBox::Cancel | QMessageBox::Discard, QMessageBox::Cancel );
574 case QMessageBox::Save:
575 return saveModel(
false );
577 case QMessageBox::Discard:
592 mLastResult.mergeWith( result );
594 mScene->setLastRunResult( mLastResult );
597void QgsModelDesignerDialog::setModelName(
const QString &name )
599 mNameEdit->setText( name );
602void QgsModelDesignerDialog::zoomIn()
604 mView->setTransformationAnchor( QGraphicsView::NoAnchor );
605 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
607 const double factor = settings.
value( QStringLiteral(
"/qgis/zoom_favor" ), 2.0 ).toDouble();
608 mView->scale( factor, factor );
609 mView->centerOn( point );
612void QgsModelDesignerDialog::zoomOut()
614 mView->setTransformationAnchor( QGraphicsView::NoAnchor );
615 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
617 const double factor = 1.0 / settings.
value( QStringLiteral(
"/qgis/zoom_favor" ), 2.0 ).toDouble();
618 mView->scale( factor, factor );
619 mView->centerOn( point );
622void QgsModelDesignerDialog::zoomActual()
624 QPointF point = mView->mapToScene( QPoint( mView->viewport()->width() / 2.0, mView->viewport()->height() / 2 ) );
625 mView->resetTransform();
626 mView->scale( mScreenHelper->screenDpi() / 96, mScreenHelper->screenDpi() / 96 );
627 mView->centerOn( point );
630void QgsModelDesignerDialog::zoomFull()
632 QRectF totalRect = mView->scene()->itemsBoundingRect();
633 totalRect.adjust( -10, -10, 10, 10 );
634 mView->fitInView( totalRect, Qt::KeepAspectRatio );
637void QgsModelDesignerDialog::newModel()
639 if ( !checkForUnsavedChanges() )
642 auto alg = std::make_unique<QgsProcessingModelAlgorithm>();
644 setModel( alg.release() );
647void QgsModelDesignerDialog::exportToImage()
650 QString lastExportDir = settings.
value( QStringLiteral(
"lastModelDesignerExportDir" ), QDir::homePath(),
QgsSettings::App ).toString();
652 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as Image" ), lastExportDir, tr(
"PNG files (*.png *.PNG)" ) );
656 if ( filename.isEmpty() )
661 const QFileInfo saveFileInfo( filename );
664 repaintModel(
false );
666 QRectF totalRect = mView->scene()->itemsBoundingRect();
667 totalRect.adjust( -10, -10, 10, 10 );
668 const QRectF imageRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
670 QImage img( totalRect.width(), totalRect.height(), QImage::Format_ARGB32_Premultiplied );
671 img.fill( Qt::white );
673 painter.setRenderHint( QPainter::Antialiasing );
674 painter.begin( &img );
675 mView->scene()->render( &painter, imageRect, totalRect );
678 img.save( filename );
680 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 );
681 repaintModel(
true );
684void QgsModelDesignerDialog::exportToPdf()
687 QString lastExportDir = settings.
value( QStringLiteral(
"lastModelDesignerExportDir" ), QDir::homePath(),
QgsSettings::App ).toString();
689 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as PDF" ), lastExportDir, tr(
"PDF files (*.pdf *.PDF)" ) );
693 if ( filename.isEmpty() )
698 const QFileInfo saveFileInfo( filename );
701 repaintModel(
false );
703 QRectF totalRect = mView->scene()->itemsBoundingRect();
704 totalRect.adjust( -10, -10, 10, 10 );
705 const QRectF printerRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
707 QPdfWriter pdfWriter( filename );
709 const double scaleFactor = 96 / 25.4;
711 QPageLayout pageLayout( QPageSize( totalRect.size() / scaleFactor, QPageSize::Millimeter ), QPageLayout::Portrait, QMarginsF( 0, 0, 0, 0 ) );
712 pageLayout.setMode( QPageLayout::FullPageMode );
713 pdfWriter.setPageLayout( pageLayout );
715 QPainter painter( &pdfWriter );
716 mView->scene()->render( &painter, printerRect, totalRect );
719 mMessageBar->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 );
720 repaintModel(
true );
723void QgsModelDesignerDialog::exportToSvg()
726 QString lastExportDir = settings.
value( QStringLiteral(
"lastModelDesignerExportDir" ), QDir::homePath(),
QgsSettings::App ).toString();
728 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as SVG" ), lastExportDir, tr(
"SVG files (*.svg *.SVG)" ) );
732 if ( filename.isEmpty() )
737 const QFileInfo saveFileInfo( filename );
740 repaintModel(
false );
742 QRectF totalRect = mView->scene()->itemsBoundingRect();
743 totalRect.adjust( -10, -10, 10, 10 );
744 const QRectF svgRect = QRectF( 0, 0, totalRect.width(), totalRect.height() );
747 svg.setFileName( filename );
748 svg.setSize( QSize( totalRect.width(), totalRect.height() ) );
749 svg.setViewBox( svgRect );
750 svg.setTitle( mModel->displayName() );
752 QPainter painter( &svg );
753 mView->scene()->render( &painter, svgRect, totalRect );
756 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 );
757 repaintModel(
true );
760void QgsModelDesignerDialog::exportAsPython()
763 QString lastExportDir = settings.
value( QStringLiteral(
"lastModelDesignerExportDir" ), QDir::homePath(),
QgsSettings::App ).toString();
765 QString filename = QFileDialog::getSaveFileName(
this, tr(
"Save Model as Python Script" ), lastExportDir, tr(
"Processing scripts (*.py *.PY)" ) );
769 if ( filename.isEmpty() )
774 const QFileInfo saveFileInfo( filename );
779 QFile outFile( filename );
780 if ( !outFile.open( QIODevice::WriteOnly | QIODevice::Truncate ) )
784 QTextStream fout( &outFile );
788 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 );
791void QgsModelDesignerDialog::toggleComments(
bool show )
795 repaintModel(
true );
798void QgsModelDesignerDialog::updateWindowTitle()
800 QString title = tr(
"Model Designer" );
801 if ( !mModel->name().isEmpty() )
802 title = mModel->group().isEmpty()
803 ? QStringLiteral(
"%1: %2" ).arg( title, mModel->name() )
804 : QStringLiteral(
"%1: %2 - %3" ).arg( title, mModel->group(), mModel->name() );
807 title.prepend(
'*' );
809 setWindowTitle( title );
812void QgsModelDesignerDialog::deleteSelected()
814 QList<QgsModelComponentGraphicItem *> items = mScene->selectedComponentItems();
818 if ( items.size() == 1 )
820 items.at( 0 )->deleteComponent();
824 std::sort( items.begin(), items.end(), []( QgsModelComponentGraphicItem *p1, QgsModelComponentGraphicItem *p2 ) {
829 if ( dynamic_cast<QgsModelCommentGraphicItem *>( p1 ) && dynamic_cast<QgsModelCommentGraphicItem *>( p2 ) )
831 else if ( dynamic_cast<QgsModelCommentGraphicItem *>( p1 ) )
833 else if ( dynamic_cast<QgsModelCommentGraphicItem *>( p2 ) )
836 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p1 ) && dynamic_cast<QgsModelGroupBoxGraphicItem *>( p2 ) )
838 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p1 ) )
840 else if ( dynamic_cast<QgsModelGroupBoxGraphicItem *>( p2 ) )
843 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p1 ) && dynamic_cast<QgsModelOutputGraphicItem *>( p2 ) )
845 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p1 ) )
847 else if ( dynamic_cast<QgsModelOutputGraphicItem *>( p2 ) )
850 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p1 ) && dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p2 ) )
852 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p1 ) )
854 else if ( dynamic_cast<QgsModelChildAlgorithmGraphicItem *>( p2 ) )
861 beginUndoCommand( tr(
"Delete Components" ) );
863 QVariant prevState = mModel->toVariant();
864 mBlockUndoCommands++;
865 mBlockRepaints =
true;
867 while ( !items.empty() )
869 QgsModelComponentGraphicItem *toDelete =
nullptr;
870 for ( QgsModelComponentGraphicItem *item : items )
872 if ( item->canDeleteComponent() )
885 toDelete->deleteComponent();
886 items.removeAll( toDelete );
891 mModel->loadVariant( prevState );
892 QMessageBox::warning(
nullptr, QObject::tr(
"Could not remove components" ), QObject::tr(
"Components depend on the selected items.\n"
893 "Try to remove them before trying deleting these components." ) );
894 mBlockUndoCommands--;
895 mActiveCommand.reset();
899 mBlockUndoCommands--;
903 mBlockRepaints =
false;
907void QgsModelDesignerDialog::populateZoomToMenu()
910 for (
const QgsProcessingModelGroupBox &box : model()->groupBoxes() )
912 if ( QgsModelComponentGraphicItem *item = mScene->groupBoxItem( box.uuid() ) )
914 QAction *zoomAction =
new QAction( box.description(), mGroupMenu );
915 connect( zoomAction, &QAction::triggered,
this, [
this, item] {
916 QRectF groupRect = item->mapToScene( item->boundingRect() ).boundingRect();
917 groupRect.adjust( -10, -10, 10, 10 );
918 mView->fitInView( groupRect, Qt::KeepAspectRatio );
919 mView->centerOn( item );
921 mGroupMenu->addAction( zoomAction );
926void QgsModelDesignerDialog::setPanelVisibility(
bool hidden )
928 const QList<QDockWidget *> docks = findChildren<QDockWidget *>();
929 const QList<QTabBar *> tabBars = findChildren<QTabBar *>();
933 mPanelStatus.clear();
935 for ( QDockWidget *dock : docks )
937 mPanelStatus.insert( dock->windowTitle(), PanelStatus( dock->isVisible(),
false ) );
938 dock->setVisible(
false );
942 for ( QTabBar *tabBar : tabBars )
944 QString currentTabTitle = tabBar->tabText( tabBar->currentIndex() );
945 mPanelStatus[currentTabTitle].isActive =
true;
951 for ( QDockWidget *dock : docks )
953 if ( mPanelStatus.contains( dock->windowTitle() ) )
955 dock->setVisible( mPanelStatus.value( dock->windowTitle() ).isVisible );
960 for ( QTabBar *tabBar : tabBars )
963 for (
int i = 0; i < tabBar->count(); ++i )
965 QString tabTitle = tabBar->tabText( i );
966 if ( mPanelStatus.contains( tabTitle ) && mPanelStatus.value( tabTitle ).isActive )
968 tabBar->setCurrentIndex( i );
972 mPanelStatus.clear();
976void QgsModelDesignerDialog::editHelp()
978 QgsProcessingHelpEditorDialog dialog(
this );
979 dialog.setWindowTitle( tr(
"Edit Model Help" ) );
980 dialog.setAlgorithm( mModel.get() );
983 beginUndoCommand( tr(
"Edit Model Help" ) );
984 mModel->setHelpContent( dialog.helpContent() );
989void QgsModelDesignerDialog::runSelectedSteps()
991 QSet<QString> children;
992 const QList<QgsModelComponentGraphicItem *> items = mScene->selectedComponentItems();
993 for ( QgsModelComponentGraphicItem *item : items )
995 if ( QgsProcessingModelChildAlgorithm *childAlgorithm =
dynamic_cast<QgsProcessingModelChildAlgorithm *
>( item->component() ) )
997 children.insert( childAlgorithm->childId() );
1001 if ( children.isEmpty() )
1003 mMessageBar->pushWarning( QString(), tr(
"No steps are selected" ) );
1010void QgsModelDesignerDialog::runFromChild(
const QString &
id )
1012 QSet<QString> children = mModel->dependentChildAlgorithms(
id );
1013 children.insert(
id );
1017void QgsModelDesignerDialog::run(
const QSet<QString> &childAlgorithmSubset )
1020 const bool isValid = model()->validate( errors );
1023 QMessageBox messageBox;
1024 messageBox.setWindowTitle( tr(
"Model is Invalid" ) );
1025 messageBox.setIcon( QMessageBox::Icon::Warning );
1026 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?" ) );
1027 messageBox.setStandardButtons( QMessageBox::StandardButton::Yes | QMessageBox::StandardButton::Cancel );
1028 messageBox.setDefaultButton( QMessageBox::StandardButton::Cancel );
1030 QString errorString;
1031 for (
const QString &error : std::as_const( errors ) )
1033 QString cleanedError = error;
1034 const thread_local QRegularExpression re( QStringLiteral(
"<[^>]*>" ) );
1035 cleanedError.replace( re, QString() );
1036 errorString += QStringLiteral(
"• %1\n" ).arg( cleanedError );
1039 messageBox.setDetailedText( errorString );
1040 if ( messageBox.exec() == QMessageBox::StandardButton::Cancel )
1044 if ( !childAlgorithmSubset.isEmpty() )
1046 for (
const QString &child : childAlgorithmSubset )
1049 const QSet<QString> requirements = mModel->dependsOnChildAlgorithms( child );
1050 for (
const QString &requirement : requirements )
1052 if ( !mLastResult.executedChildIds().contains( requirement ) )
1054 QMessageBox messageBox;
1055 messageBox.setWindowTitle( tr(
"Run Model" ) );
1056 messageBox.setIcon( QMessageBox::Icon::Warning );
1057 messageBox.setText( tr(
"Prerequisite parts of this model have not yet been run (try running the full model first)." ) );
1058 messageBox.setStandardButtons( QMessageBox::StandardButton::Ok );
1066 std::unique_ptr<QgsProcessingAlgorithmDialogBase> dialog( createExecutionDialog() );
1071 dialog->setParameters( mModel->designerParameterValues() );
1073 connect( dialog.get(), &QgsProcessingAlgorithmDialogBase::algorithmAboutToRun,
this, [
this, &childAlgorithmSubset](
QgsProcessingContext *context ) {
1074 if ( !childAlgorithmSubset.empty() )
1077 auto modelConfig = std::make_unique<QgsProcessingModelInitialRunConfig>();
1078 modelConfig->setChildAlgorithmSubset( childAlgorithmSubset );
1079 modelConfig->setPreviouslyExecutedChildAlgorithms( mLastResult.executedChildIds() );
1080 modelConfig->setInitialChildInputs( mLastResult.rawChildInputs() );
1081 modelConfig->setInitialChildOutputs( mLastResult.rawChildOutputs() );
1085 const QMap<QString, QgsMapLayer *> previousOutputLayers = mLayerStore.temporaryLayerStore()->mapLayers();
1086 auto previousResultStore = std::make_unique<QgsMapLayerStore>();
1087 for ( auto it = previousOutputLayers.constBegin(); it != previousOutputLayers.constEnd(); ++it )
1089 std::unique_ptr<QgsMapLayer> clone( it.value()->clone() );
1090 clone->setId( it.value()->id() );
1091 previousResultStore->addMapLayer( clone.release() );
1093 previousResultStore->moveToThread( nullptr );
1094 modelConfig->setPreviousLayerStore( std::move( previousResultStore ) );
1095 context->setModelInitialRunConfig( std::move( modelConfig ) );
1099 connect( dialog.get(), &QgsProcessingAlgorithmDialogBase::algorithmFinished,
this, [
this, &dialog](
bool,
const QVariantMap & ) {
1100 QgsProcessingContext *context = dialog->processingContext();
1102 setLastRunResult( context->modelResult() );
1104 mModel->setDesignerParameterValues( dialog->createProcessingParameters( QgsProcessingParametersGenerator::Flag::SkipDefaultValueParameters ) );
1107 mLayerStore.temporaryLayerStore()->removeAllMapLayers();
1108 mLayerStore.takeResultsFrom( *context );
1114void QgsModelDesignerDialog::showChildAlgorithmOutputs(
const QString &childId )
1116 const QString childDescription = mModel->childAlgorithm( childId ).description();
1119 const QVariantMap childAlgorithmOutputs = result.
outputs();
1120 if ( childAlgorithmOutputs.isEmpty() )
1122 mMessageBar->pushWarning( QString(), tr(
"No results are available for %1" ).arg( childDescription ) );
1129 mMessageBar->pushCritical( QString(), tr(
"Results cannot be shown for an invalid model component" ) );
1133 const QList<const QgsProcessingParameterDefinition *> outputParams =
algorithm->destinationParameterDefinitions();
1134 if ( outputParams.isEmpty() )
1137 QgsDebugError(
"Cannot show results for algorithms with no outputs" );
1141 bool foundResults =
false;
1144 const QVariant output = childAlgorithmOutputs.value( outputParam->name() );
1145 if ( !output.isValid() )
1148 if ( output.type() == QVariant::String )
1152 QgsDebugMsgLevel( QStringLiteral(
"Loading previous result for %1: %2" ).arg( outputParam->name(), output.toString() ), 2 );
1154 std::unique_ptr<QgsMapLayer> layer( resultLayer->clone() );
1157 if ( outputParams.size() > 1 )
1158 baseName = tr(
"%1 — %2" ).arg( childDescription, outputParam->name() );
1160 baseName = childDescription;
1164 QString name = baseName;
1169 name = tr(
"%1 (%2)" ).arg( baseName ).arg( counter );
1172 layer->setName( name );
1175 foundResults =
true;
1180 QgsDebugError( QStringLiteral(
"Could not load previous result for %1: %2" ).arg( outputParam->name(), output.toString() ) );
1185 if ( !foundResults )
1187 mMessageBar->pushWarning( QString(), tr(
"No results are available for %1" ).arg( childDescription ) );
1192void QgsModelDesignerDialog::showChildAlgorithmLog(
const QString &childId )
1194 const QString childDescription = mModel->childAlgorithm( childId ).description();
1197 if ( result.
htmlLog().isEmpty() )
1199 mMessageBar->pushWarning( QString(), tr(
"No log is available for %1" ).arg( childDescription ) );
1204 m.setWindowTitle( childDescription );
1205 m.setCheckBoxVisible(
false );
1206 m.setMessageAsHtml( result.
htmlLog() );
1210void QgsModelDesignerDialog::validate()
1213 if ( model()->validate( issues ) )
1215 mMessageBar->pushSuccess( QString(), tr(
"Model is valid!" ) );
1220 QPushButton *detailsButton =
new QPushButton( tr(
"Details" ) );
1221 connect( detailsButton, &QPushButton::clicked, detailsButton, [detailsButton, issues] {
1223 dialog->
setTitle( tr(
"Model is Invalid" ) );
1225 QString longMessage = tr(
"<p>This model is not valid:</p>" ) + QStringLiteral(
"<ul>" );
1226 for (
const QString &issue : issues )
1228 longMessage += QStringLiteral(
"<li>%1</li>" ).arg( issue );
1230 longMessage += QLatin1String(
"</ul>" );
1235 messageWidget->layout()->addWidget( detailsButton );
1236 mMessageBar->clearWidgets();
1241void QgsModelDesignerDialog::reorderInputs()
1243 QgsModelInputReorderDialog dlg(
this );
1244 dlg.setModel( mModel.get() );
1247 const QStringList inputOrder = dlg.inputOrder();
1248 beginUndoCommand( tr(
"Reorder Inputs" ) );
1249 mModel->setParameterOrder( inputOrder );
1254void QgsModelDesignerDialog::reorderOutputs()
1256 QgsModelOutputReorderDialog dlg(
this );
1257 dlg.setModel( mModel.get() );
1260 const QStringList outputOrder = dlg.outputOrder();
1261 beginUndoCommand( tr(
"Reorder Outputs" ) );
1262 mModel->setOutputOrder( outputOrder );
1263 mModel->setOutputGroup( dlg.outputGroup() );
1268bool QgsModelDesignerDialog::isDirty()
const
1270 return mHasChanged && mUndoStack->index() != -1;
1273void QgsModelDesignerDialog::fillInputsTree()
1276 auto parametersItem = std::make_unique<QTreeWidgetItem>();
1277 parametersItem->setText( 0, tr(
"Parameters" ) );
1280 return QString::localeAwareCompare( a->name(), b->name() ) < 0;
1287 auto paramItem = std::make_unique<QTreeWidgetItem>();
1288 paramItem->setText( 0, param->name() );
1289 paramItem->setData( 0, Qt::UserRole, param->id() );
1290 paramItem->setIcon( 0, icon );
1291 paramItem->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
1292 paramItem->setToolTip( 0, param->description() );
1293 parametersItem->addChild( paramItem.release() );
1296 mInputsTreeWidget->addTopLevelItem( parametersItem.release() );
1297 mInputsTreeWidget->topLevelItem( 0 )->setExpanded(
true );
1305QgsModelChildDependenciesWidget::QgsModelChildDependenciesWidget( QWidget *parent, QgsProcessingModelAlgorithm *model,
const QString &childId )
1308 , mChildId( childId )
1310 QHBoxLayout *hl =
new QHBoxLayout();
1311 hl->setContentsMargins( 0, 0, 0, 0 );
1313 mLineEdit =
new QLineEdit();
1314 mLineEdit->setEnabled(
false );
1315 hl->addWidget( mLineEdit, 1 );
1317 mToolButton =
new QToolButton();
1318 mToolButton->setText( QString( QChar( 0x2026 ) ) );
1319 hl->addWidget( mToolButton );
1323 mLineEdit->setText( tr(
"%1 dependencies selected" ).arg( 0 ) );
1325 connect( mToolButton, &QToolButton::clicked,
this, &QgsModelChildDependenciesWidget::showDialog );
1328void QgsModelChildDependenciesWidget::setValue(
const QList<QgsProcessingModelChildDependency> &value )
1332 updateSummaryText();
1335void QgsModelChildDependenciesWidget::showDialog()
1337 const QList<QgsProcessingModelChildDependency> available = mModel->availableDependenciesForChildAlgorithm( mChildId );
1339 QVariantList availableOptions;
1340 for (
const QgsProcessingModelChildDependency &dep : available )
1341 availableOptions << QVariant::fromValue( dep );
1342 QVariantList selectedOptions;
1343 for (
const QgsProcessingModelChildDependency &dep : mValue )
1344 selectedOptions << QVariant::fromValue( dep );
1349 QgsProcessingMultipleSelectionPanelWidget *widget =
new QgsProcessingMultipleSelectionPanelWidget( availableOptions, selectedOptions );
1350 widget->setPanelTitle( tr(
"Algorithm Dependencies" ) );
1352 widget->setValueFormatter( [
this](
const QVariant &v ) -> QString {
1353 const QgsProcessingModelChildDependency dep = v.value<QgsProcessingModelChildDependency>();
1355 const QString description = mModel->childAlgorithm( dep.childId ).description();
1356 if ( dep.conditionalBranch.isEmpty() )
1359 return tr(
"Condition “%1” from algorithm “%2”" ).arg( dep.conditionalBranch, description );
1362 connect( widget, &QgsProcessingMultipleSelectionPanelWidget::selectionChanged,
this, [
this, widget]() {
1363 QList<QgsProcessingModelChildDependency> res;
1364 for (
const QVariant &v : widget->selectedOptions() )
1366 res << v.value<QgsProcessingModelChildDependency>();
1375void QgsModelChildDependenciesWidget::updateSummaryText()
1377 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...
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())
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
Abstract base class for processing algorithms.
Contains information about the context in which a processing algorithm is executed.
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.
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.
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.
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)