Skip to content

Commit

Permalink
[feature] Add "View Output Layers" option for model child algorithms
Browse files Browse the repository at this point in the history
When editing a model through the designer (and after having run
that model), you can now right click any child step in the model
and select "View Output Layers". This will add the output layers
from that step as new layers in the current QGIS project.

This action is available for ALL child algorithms in the model,
even if the model is not configured to use the outputs from those
children as model outputs.

This is designed as a helpful debugging action. If a user's model
fails (or gives unexpected results), they can then trace through
the model and view the outputs for suspected problematic steps.
It avoids the need to add temporary outputs to a model and re-run
to test.

Additionally, this action is always available after running the model,
EVEN if the model itself failed (eg because of a misconfigured
step later in the model).

Sponsored by City of Canning
  • Loading branch information
nyalldawson committed Apr 18, 2024
1 parent 58f7658 commit 6b1b89d
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 3 deletions.
Expand Up @@ -404,6 +404,15 @@ Sets the results obtained for this child algorithm for the last model execution
void setInputs( const QVariantMap &inputs );
%Docstring
Sets the inputs used for this child algorithm for the last model execution through the dialog.
%End

signals:

void showPreviousResults();
%Docstring
Emitted when the user opts to view previous results from this child algorithm.

.. versionadded:: 3.38
%End

protected:
Expand Down
Expand Up @@ -177,6 +177,13 @@ Emitted whenever a component of the model is changed.
%Docstring
Emitted whenever the selected item changes.
If ``None``, no item is selected.
%End

void showPreviousResults( const QString &childId );
%Docstring
Emitted when the user opts to view previous results from the child algorithm with matching ID.

.. versionadded:: 3.38
%End

protected:
Expand Down
Expand Up @@ -404,6 +404,15 @@ Sets the results obtained for this child algorithm for the last model execution
void setInputs( const QVariantMap &inputs );
%Docstring
Sets the inputs used for this child algorithm for the last model execution through the dialog.
%End

signals:

void showPreviousResults();
%Docstring
Emitted when the user opts to view previous results from this child algorithm.

.. versionadded:: 3.38
%End

protected:
Expand Down
Expand Up @@ -177,6 +177,13 @@ Emitted whenever a component of the model is changed.
%Docstring
Emitted whenever the selected item changes.
If ``None``, no item is selected.
%End

void showPreviousResults( const QString &childId );
%Docstring
Emitted when the user opts to view previous results from the child algorithm with matching ID.

.. versionadded:: 3.38
%End

protected:
Expand Down
15 changes: 15 additions & 0 deletions src/gui/processing/models/qgsmodelcomponentgraphicitem.cpp
Expand Up @@ -890,6 +890,21 @@ void QgsModelChildAlgorithmGraphicItem::contextMenuEvent( QGraphicsSceneContextM
QAction *deactivateAction = popupmenu->addAction( QObject::tr( "Deactivate" ) );
connect( deactivateAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::deactivateAlgorithm );
}

// only show the "View Output Layers" action for algorithms which create layers
if ( const QgsProcessingAlgorithm *algorithm = child->algorithm() )
{
const QList< const QgsProcessingParameterDefinition * > outputParams = algorithm->destinationParameterDefinitions();
if ( !outputParams.isEmpty() )
{
popupmenu->addSeparator();
QAction *viewOutputLayersAction = popupmenu->addAction( QObject::tr( "View Output Layers" ) );
viewOutputLayersAction->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "mActionShowSelectedLayers.svg" ) ) );
connect( viewOutputLayersAction, &QAction::triggered, this, &QgsModelChildAlgorithmGraphicItem::showPreviousResults );
if ( mResults.empty() )
viewOutputLayersAction->setEnabled( false );
}
}
}

popupmenu->exec( event->screenPos() );
Expand Down
9 changes: 9 additions & 0 deletions src/gui/processing/models/qgsmodelcomponentgraphicitem.h
Expand Up @@ -470,6 +470,15 @@ class GUI_EXPORT QgsModelChildAlgorithmGraphicItem : public QgsModelComponentGra
*/
void setInputs( const QVariantMap &inputs );

signals:

/**
* Emitted when the user opts to view previous results from this child algorithm.
*
* \since QGIS 3.38
*/
void showPreviousResults();

protected:

QColor fillColor( State state ) const override;
Expand Down
79 changes: 79 additions & 0 deletions src/gui/processing/models/qgsmodeldesignerdialog.cpp
Expand Up @@ -39,6 +39,7 @@
#include "qgsscreenhelper.h"
#include "qgsmessagelog.h"
#include "qgsprocessingalgorithmdialogbase.h"
#include "qgsproject.h"
#include <QShortcut>
#include <QKeySequence>
#include <QFileDialog>
Expand Down Expand Up @@ -511,6 +512,7 @@ void QgsModelDesignerDialog::setModelScene( QgsModelGraphicsScene *scene )
} );
connect( mScene, &QgsModelGraphicsScene::componentAboutToChange, this, [ = ]( const QString & description, int id ) { beginUndoCommand( description, id ); } );
connect( mScene, &QgsModelGraphicsScene::componentChanged, this, [ = ] { endUndoCommand(); } );
connect( mScene, &QgsModelGraphicsScene::showPreviousResults, this, &QgsModelDesignerDialog::showPreviousResults );

mView->centerOn( center );

Expand Down Expand Up @@ -1048,6 +1050,83 @@ void QgsModelDesignerDialog::run()
dialog->exec();
}

void QgsModelDesignerDialog::showPreviousResults( const QString &childId )
{
const QString childDescription = mModel->childAlgorithm( childId ).description();

const QVariantMap childAlgorithmResults = mChildResults.value( childId ).toMap();
if ( childAlgorithmResults.isEmpty() )
{
mMessageBar->pushWarning( QString(), tr( "No results are available for %1" ).arg( childDescription ) );
return;
}

const QgsProcessingAlgorithm *algorithm = mModel->childAlgorithm( childId ).algorithm();
if ( !algorithm )
{
mMessageBar->pushCritical( QString(), tr( "Results cannot be shown for an invalid model component" ) );
return;
}

const QList< const QgsProcessingParameterDefinition * > outputParams = algorithm->destinationParameterDefinitions();
if ( outputParams.isEmpty() )
{
// this situation should not arise in normal use, we don't show the action in this case
QgsDebugError( "Cannot show results for algorithms with no outputs" );
return;
}

bool foundResults = false;
for ( const QgsProcessingParameterDefinition *outputParam : outputParams )
{
const QVariant output = childAlgorithmResults.value( outputParam->name() );
if ( !output.isValid() )
continue;

if ( output.type() == QVariant::String )
{
if ( QgsMapLayer *resultLayer = QgsProcessingUtils::mapLayerFromString( output.toString(), mLayerStore ) )
{
QgsDebugMsgLevel( QStringLiteral( "Loading previous result for %1: %2" ).arg( outputParam->name(), output.toString() ), 2 );

std::unique_ptr< QgsMapLayer > layer( resultLayer->clone() );

QString baseName;
if ( outputParams.size() > 1 )
baseName = tr( "%1 — %2" ).arg( childDescription, outputParam->name() );
else
baseName = childDescription;

// make name unique, so that's it's easy to see which is the most recent result.
// (this helps when running the model multiple times.)
QString name = baseName;
int counter = 1;
while ( !QgsProject::instance()->mapLayersByName( name ).empty() )
{
counter += 1;
name = tr( "%1 (%2)" ).arg( baseName ).arg( counter );
}

layer->setName( name );

QgsProject::instance()->addMapLayer( layer.release() );
foundResults = true;
}
else
{
// should not happen in normal operation
QgsDebugError( QStringLiteral( "Could not load previous result for %1: %2" ).arg( outputParam->name(), output.toString() ) );
}
}
}

if ( !foundResults )
{
mMessageBar->pushWarning( QString(), tr( "No results are available for %1" ).arg( childDescription ) );
return;
}
}

void QgsModelDesignerDialog::validate()
{
QStringList issues;
Expand Down
1 change: 1 addition & 0 deletions src/gui/processing/models/qgsmodeldesignerdialog.h
Expand Up @@ -190,6 +190,7 @@ class GUI_EXPORT QgsModelDesignerDialog : public QMainWindow, public Ui::QgsMode
void setPanelVisibility( bool hidden );
void editHelp();
void run();
void showPreviousResults( const QString &childId );

private:

Expand Down
12 changes: 9 additions & 3 deletions src/gui/processing/models/qgsmodelgraphicsscene.cpp
Expand Up @@ -138,12 +138,18 @@ void QgsModelGraphicsScene::createItems( QgsProcessingModelAlgorithm *model, Qgs
QgsModelChildAlgorithmGraphicItem *item = createChildAlgGraphicItem( model, it.value().clone() );
addItem( item );
item->setPos( it.value().position().x(), it.value().position().y() );
item->setResults( mChildResults.value( it.value().childId() ).toMap() );
item->setInputs( mChildInputs.value( it.value().childId() ).toMap() );
mChildAlgorithmItems.insert( it.value().childId(), item );

const QString childId = it.value().childId();
item->setResults( mChildResults.value( childId ).toMap() );
item->setInputs( mChildInputs.value( childId ).toMap() );
mChildAlgorithmItems.insert( childId, item );
connect( item, &QgsModelComponentGraphicItem::requestModelRepaint, this, &QgsModelGraphicsScene::rebuildRequired );
connect( item, &QgsModelComponentGraphicItem::changed, this, &QgsModelGraphicsScene::componentChanged );
connect( item, &QgsModelComponentGraphicItem::aboutToChange, this, &QgsModelGraphicsScene::componentAboutToChange );
connect( item, &QgsModelChildAlgorithmGraphicItem::showPreviousResults, this, [this, childId]
{
emit showPreviousResults( childId );
} );

addCommentItemForComponent( model, it.value(), item );
}
Expand Down
7 changes: 7 additions & 0 deletions src/gui/processing/models/qgsmodelgraphicsscene.h
Expand Up @@ -192,6 +192,13 @@ class GUI_EXPORT QgsModelGraphicsScene : public QGraphicsScene
*/
void selectedItemChanged( QgsModelComponentGraphicItem *selected );

/**
* Emitted when the user opts to view previous results from the child algorithm with matching ID.
*
* \since QGIS 3.38
*/
void showPreviousResults( const QString &childId );

protected:

/**
Expand Down

0 comments on commit 6b1b89d

Please sign in to comment.