mirror of
https://github.com/qgis/QGIS.git
synced 2025-11-22 00:14:55 -05:00
[processing] Allow configuration of order of outputs created by a model
This adds a new "Reorder Model Outputs" action to the model designer menu (to accompany the existing "Reorder Model Inputs" action). Selecting this option allows model creators to set a specific order which the outputs from their model must use when loading the results into a project. This gives the model creator a means of ensuring that layers are logically ordered, eg placing a vector layer over a raster layer and a point layer over a polygon layer. Optionally, the model creator can also set a "Group name" for the outputs. If this is specified then all outputs from the model will be placed into a (newly created if necessary) layer tree group with that name. Sponsored by the QGIS Germany User Group
This commit is contained in:
parent
f1c0923fc1
commit
f27195c165
@ -340,6 +340,45 @@ model :py:func:`~QgsProcessingModelAlgorithm.parameterComponents`.
|
||||
.. seealso:: :py:func:`orderedParameters`
|
||||
|
||||
.. versionadded:: 3.14
|
||||
%End
|
||||
|
||||
QList< QgsProcessingModelOutput > orderedOutputs() const;
|
||||
%Docstring
|
||||
Returns an ordered list of outputs for the model.
|
||||
|
||||
.. seealso:: :py:func:`setOutputOrder`
|
||||
|
||||
.. versionadded:: 3.32
|
||||
%End
|
||||
|
||||
void setOutputOrder( const QStringList &order );
|
||||
%Docstring
|
||||
Sets the ``order`` for sorting outputs for the model.
|
||||
|
||||
The ``order`` list should consist of "output child algorithm id:output name" formatted strings corresponding to existing
|
||||
model outputs.
|
||||
|
||||
.. seealso:: :py:func:`orderedOutputs`
|
||||
|
||||
.. versionadded:: 3.32
|
||||
%End
|
||||
|
||||
QString outputGroup() const;
|
||||
%Docstring
|
||||
Returns the destination layer tree group name for outputs created by the model.
|
||||
|
||||
.. seealso:: :py:func:`setOutputGroup`
|
||||
|
||||
.. versionadded:: 3.32
|
||||
%End
|
||||
|
||||
void setOutputGroup( const QString &group );
|
||||
%Docstring
|
||||
Sets the destination layer tree ``group`` name for outputs created by the model.
|
||||
|
||||
.. seealso:: :py:func:`outputGroup`
|
||||
|
||||
.. versionadded:: 3.32
|
||||
%End
|
||||
|
||||
void updateDestinationParameters();
|
||||
|
||||
@ -478,10 +478,10 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
|
||||
|
||||
// look through child alg's outputs to determine whether any of these should be copied
|
||||
// to the final model outputs
|
||||
QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
|
||||
QMap<QString, QgsProcessingModelOutput>::const_iterator outputIt = outputs.constBegin();
|
||||
for ( ; outputIt != outputs.constEnd(); ++outputIt )
|
||||
const QMap<QString, QgsProcessingModelOutput> outputs = child.modelOutputs();
|
||||
for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
|
||||
{
|
||||
const int outputSortKey = mOutputOrder.indexOf( QStringLiteral( "%1:%2" ).arg( childId, outputIt->childOutputName() ) );
|
||||
switch ( mInternalVersion )
|
||||
{
|
||||
case QgsProcessingModelAlgorithm::InternalVersion::Version1:
|
||||
@ -494,6 +494,14 @@ QVariantMap QgsProcessingModelAlgorithm::processAlgorithm( const QVariantMap &pa
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if ( !results.value( outputIt->childOutputName() ).toString().isEmpty() )
|
||||
{
|
||||
QgsProcessingContext::LayerDetails &details = context.layerToLoadOnCompletionDetails( results.value( outputIt->childOutputName() ).toString() );
|
||||
details.groupName = mOutputGroup;
|
||||
if ( outputSortKey > 0 )
|
||||
details.layerSortKey = outputSortKey;
|
||||
}
|
||||
}
|
||||
|
||||
executed.insert( childId );
|
||||
@ -1373,6 +1381,62 @@ void QgsProcessingModelAlgorithm::setParameterOrder( const QStringList &order )
|
||||
mParameterOrder = order;
|
||||
}
|
||||
|
||||
QList<QgsProcessingModelOutput> QgsProcessingModelAlgorithm::orderedOutputs() const
|
||||
{
|
||||
QList< QgsProcessingModelOutput > res;
|
||||
QSet< QString > found;
|
||||
|
||||
for ( const QString &output : mOutputOrder )
|
||||
{
|
||||
bool foundOutput = false;
|
||||
for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
|
||||
{
|
||||
const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
|
||||
for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
|
||||
{
|
||||
if ( output == QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) )
|
||||
{
|
||||
res << outputIt.value();
|
||||
foundOutput = true;
|
||||
found.insert( QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) );
|
||||
}
|
||||
}
|
||||
if ( foundOutput )
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// add any missing ones to end of list
|
||||
for ( auto it = mChildAlgorithms.constBegin(); it != mChildAlgorithms.constEnd(); ++it )
|
||||
{
|
||||
const QMap<QString, QgsProcessingModelOutput> outputs = it.value().modelOutputs();
|
||||
for ( auto outputIt = outputs.constBegin(); outputIt != outputs.constEnd(); ++outputIt )
|
||||
{
|
||||
if ( !found.contains( QStringLiteral( "%1:%2" ).arg( outputIt->childId(), outputIt->childOutputName() ) ) )
|
||||
{
|
||||
res << outputIt.value();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void QgsProcessingModelAlgorithm::setOutputOrder( const QStringList &order )
|
||||
{
|
||||
mOutputOrder = order;
|
||||
}
|
||||
|
||||
QString QgsProcessingModelAlgorithm::outputGroup() const
|
||||
{
|
||||
return mOutputGroup;
|
||||
}
|
||||
|
||||
void QgsProcessingModelAlgorithm::setOutputGroup( const QString &group )
|
||||
{
|
||||
mOutputGroup = group;
|
||||
}
|
||||
|
||||
void QgsProcessingModelAlgorithm::updateDestinationParameters()
|
||||
{
|
||||
//delete existing destination parameters
|
||||
@ -1517,6 +1581,8 @@ QVariant QgsProcessingModelAlgorithm::toVariant() const
|
||||
map.insert( QStringLiteral( "designerParameterValues" ), mDesignerParameterValues );
|
||||
|
||||
map.insert( QStringLiteral( "parameterOrder" ), mParameterOrder );
|
||||
map.insert( QStringLiteral( "outputOrder" ), mOutputOrder );
|
||||
map.insert( QStringLiteral( "outputGroup" ), mOutputGroup );
|
||||
|
||||
return map;
|
||||
}
|
||||
@ -1536,6 +1602,8 @@ bool QgsProcessingModelAlgorithm::loadVariant( const QVariant &model )
|
||||
mDesignerParameterValues = map.value( QStringLiteral( "designerParameterValues" ) ).toMap();
|
||||
|
||||
mParameterOrder = map.value( QStringLiteral( "parameterOrder" ) ).toStringList();
|
||||
mOutputOrder = map.value( QStringLiteral( "outputOrder" ) ).toStringList();
|
||||
mOutputGroup = map.value( QStringLiteral( "outputGroup" ) ).toString();
|
||||
|
||||
mChildAlgorithms.clear();
|
||||
QVariantMap childMap = map.value( QStringLiteral( "children" ) ).toMap();
|
||||
|
||||
@ -297,6 +297,41 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
|
||||
*/
|
||||
void setParameterOrder( const QStringList &order );
|
||||
|
||||
/**
|
||||
* Returns an ordered list of outputs for the model.
|
||||
*
|
||||
* \see setOutputOrder()
|
||||
* \since QGIS 3.32
|
||||
*/
|
||||
QList< QgsProcessingModelOutput > orderedOutputs() const;
|
||||
|
||||
/**
|
||||
* Sets the \a order for sorting outputs for the model.
|
||||
*
|
||||
* The \a order list should consist of "output child algorithm id:output name" formatted strings corresponding to existing
|
||||
* model outputs.
|
||||
*
|
||||
* \see orderedOutputs()
|
||||
* \since QGIS 3.32
|
||||
*/
|
||||
void setOutputOrder( const QStringList &order );
|
||||
|
||||
/**
|
||||
* Returns the destination layer tree group name for outputs created by the model.
|
||||
*
|
||||
* \see setOutputGroup()
|
||||
* \since QGIS 3.32
|
||||
*/
|
||||
QString outputGroup() const;
|
||||
|
||||
/**
|
||||
* Sets the destination layer tree \a group name for outputs created by the model.
|
||||
*
|
||||
* \see outputGroup()
|
||||
* \since QGIS 3.32
|
||||
*/
|
||||
void setOutputGroup( const QString &group );
|
||||
|
||||
/**
|
||||
* Updates the model's parameter definitions to include all relevant destination
|
||||
* parameters as required by child algorithm ModelOutputs.
|
||||
@ -585,6 +620,8 @@ class CORE_EXPORT QgsProcessingModelAlgorithm : public QgsProcessingAlgorithm
|
||||
QMap< QString, QgsProcessingModelGroupBox > mGroupBoxes;
|
||||
|
||||
QStringList mParameterOrder;
|
||||
QStringList mOutputOrder;
|
||||
QString mOutputGroup;
|
||||
|
||||
void dependsOnChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends ) const;
|
||||
void dependentChildAlgorithmsRecursive( const QString &childId, QSet<QString> &depends, const QString &branch ) const;
|
||||
|
||||
@ -402,6 +402,7 @@ set(QGIS_GUI_SRCS
|
||||
processing/models/qgsmodelgraphicsview.cpp
|
||||
processing/models/qgsmodelgroupboxdefinitionwidget.cpp
|
||||
processing/models/qgsmodelinputreorderwidget.cpp
|
||||
processing/models/qgsmodeloutputreorderwidget.cpp
|
||||
processing/models/qgsmodelsnapper.cpp
|
||||
processing/models/qgsmodelundocommand.cpp
|
||||
processing/models/qgsmodelviewmouseevent.cpp
|
||||
@ -1325,6 +1326,7 @@ set(QGIS_GUI_HDRS
|
||||
processing/models/qgsmodelgraphicsview.h
|
||||
processing/models/qgsmodelgroupboxdefinitionwidget.h
|
||||
processing/models/qgsmodelinputreorderwidget.h
|
||||
processing/models/qgsmodeloutputreorderwidget.h
|
||||
processing/models/qgsmodelsnapper.h
|
||||
processing/models/qgsmodelundocommand.h
|
||||
processing/models/qgsmodelviewmouseevent.h
|
||||
|
||||
@ -30,6 +30,7 @@
|
||||
#include "qgsmodelcomponentgraphicitem.h"
|
||||
#include "processing/models/qgsprocessingmodelgroupbox.h"
|
||||
#include "processing/models/qgsmodelinputreorderwidget.h"
|
||||
#include "processing/models/qgsmodeloutputreorderwidget.h"
|
||||
#include "qgsmessageviewer.h"
|
||||
#include "qgsmessagebaritem.h"
|
||||
#include "qgspanelwidget.h"
|
||||
@ -152,6 +153,7 @@ QgsModelDesignerDialog::QgsModelDesignerDialog( QWidget *parent, Qt::WindowFlags
|
||||
connect( mActionSnapSelected, &QAction::triggered, mView, &QgsModelGraphicsView::snapSelected );
|
||||
connect( mActionValidate, &QAction::triggered, this, &QgsModelDesignerDialog::validate );
|
||||
connect( mActionReorderInputs, &QAction::triggered, this, &QgsModelDesignerDialog::reorderInputs );
|
||||
connect( mActionReorderOutputs, &QAction::triggered, this, &QgsModelDesignerDialog::reorderOutputs );
|
||||
connect( mActionEditHelp, &QAction::triggered, this, &QgsModelDesignerDialog::editHelp );
|
||||
connect( mReorderInputsButton, &QPushButton::clicked, this, &QgsModelDesignerDialog::reorderInputs );
|
||||
|
||||
@ -1028,6 +1030,20 @@ void QgsModelDesignerDialog::reorderInputs()
|
||||
}
|
||||
}
|
||||
|
||||
void QgsModelDesignerDialog::reorderOutputs()
|
||||
{
|
||||
QgsModelOutputReorderDialog dlg( this );
|
||||
dlg.setModel( mModel.get() );
|
||||
if ( dlg.exec() )
|
||||
{
|
||||
const QStringList outputOrder = dlg.outputOrder();
|
||||
beginUndoCommand( tr( "Reorder Outputs" ) );
|
||||
mModel->setOutputOrder( outputOrder );
|
||||
mModel->setOutputGroup( dlg.outputGroup() );
|
||||
endUndoCommand();
|
||||
}
|
||||
}
|
||||
|
||||
bool QgsModelDesignerDialog::isDirty() const
|
||||
{
|
||||
return mHasChanged && mUndoStack->index() != -1;
|
||||
|
||||
@ -183,6 +183,7 @@ class GUI_EXPORT QgsModelDesignerDialog : public QMainWindow, public Ui::QgsMode
|
||||
void populateZoomToMenu();
|
||||
void validate();
|
||||
void reorderInputs();
|
||||
void reorderOutputs();
|
||||
void setPanelVisibility( bool hidden );
|
||||
void editHelp();
|
||||
|
||||
|
||||
123
src/gui/processing/models/qgsmodeloutputreorderwidget.cpp
Normal file
123
src/gui/processing/models/qgsmodeloutputreorderwidget.cpp
Normal file
@ -0,0 +1,123 @@
|
||||
/***************************************************************************
|
||||
qgsmodeloutputreorderwidget.cpp
|
||||
------------------------------------
|
||||
Date : April 2023
|
||||
Copyright : (C) 2023 Nyall Dawson
|
||||
Email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#include "qgsmodeloutputreorderwidget.h"
|
||||
#include "qgsgui.h"
|
||||
#include "qgsprocessingmodelalgorithm.h"
|
||||
#include <QDialogButtonBox>
|
||||
#include <QStandardItemModel>
|
||||
///@cond NOT_STABLE
|
||||
|
||||
QgsModelOutputReorderWidget::QgsModelOutputReorderWidget( QWidget *parent )
|
||||
: QWidget( parent )
|
||||
{
|
||||
setupUi( this );
|
||||
|
||||
mItemModel = new QStandardItemModel( 0, 1, this );
|
||||
mOutputsList->setModel( mItemModel );
|
||||
|
||||
mOutputsList->setDropIndicatorShown( true );
|
||||
mOutputsList->setDragDropOverwriteMode( false );
|
||||
mOutputsList->setDragEnabled( true );
|
||||
mOutputsList->setDragDropMode( QAbstractItemView::InternalMove );
|
||||
|
||||
connect( mButtonUp, &QPushButton::clicked, this, [ = ]
|
||||
{
|
||||
int currentRow = mOutputsList->currentIndex().row();
|
||||
if ( currentRow == 0 )
|
||||
return;
|
||||
|
||||
mItemModel->insertRow( currentRow - 1, mItemModel->takeRow( currentRow ) );
|
||||
mOutputsList->setCurrentIndex( mItemModel->index( currentRow - 1, 0 ) );
|
||||
} );
|
||||
|
||||
connect( mButtonDown, &QPushButton::clicked, this, [ = ]
|
||||
{
|
||||
int currentRow = mOutputsList->currentIndex().row();
|
||||
if ( currentRow == mItemModel->rowCount() - 1 )
|
||||
return;
|
||||
|
||||
mItemModel->insertRow( currentRow + 1, mItemModel->takeRow( currentRow ) );
|
||||
mOutputsList->setCurrentIndex( mItemModel->index( currentRow + 1, 0 ) );
|
||||
} );
|
||||
|
||||
}
|
||||
|
||||
void QgsModelOutputReorderWidget::setModel( QgsProcessingModelAlgorithm *model )
|
||||
{
|
||||
mModel = model;
|
||||
mOutputs = mModel->orderedOutputs();
|
||||
mItemModel->clear();
|
||||
for ( const QgsProcessingModelOutput &output : std::as_const( mOutputs ) )
|
||||
{
|
||||
QStandardItem *item = new QStandardItem( output.name() );
|
||||
item->setData( QStringLiteral( "%1:%2" ).arg( output.childId(), output.childOutputName() ), Qt::UserRole + 1 );
|
||||
item->setFlags( Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsDragEnabled );
|
||||
// we show the outputs list reversed in the gui, because we want the "higher" outputs to be at the top of the list
|
||||
mItemModel->insertRow( 0, item );
|
||||
}
|
||||
|
||||
mPlaceInGroupCheck->setChecked( !model->outputGroup().isEmpty() );
|
||||
mGroupNameEdit->setText( model->outputGroup() );
|
||||
}
|
||||
|
||||
QStringList QgsModelOutputReorderWidget::outputOrder() const
|
||||
{
|
||||
QStringList order;
|
||||
order.reserve( mItemModel->rowCount( ) );
|
||||
// we show the outputs list reversed in the gui, because we want the "higher" outputs to be at the top of the list
|
||||
for ( int row = mItemModel->rowCount() - 1; row >= 0; --row )
|
||||
{
|
||||
order << mItemModel->data( mItemModel->index( row, 0 ), Qt::UserRole + 1 ).toString();
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
QString QgsModelOutputReorderWidget::outputGroup() const
|
||||
{
|
||||
return mPlaceInGroupCheck->isChecked() ? mGroupNameEdit->text() : QString();
|
||||
}
|
||||
|
||||
|
||||
QgsModelOutputReorderDialog::QgsModelOutputReorderDialog( QWidget *parent )
|
||||
: QDialog( parent )
|
||||
{
|
||||
setWindowTitle( tr( "Reorder Model Outputs" ) );
|
||||
mWidget = new QgsModelOutputReorderWidget();
|
||||
QVBoxLayout *vl = new QVBoxLayout();
|
||||
vl->addWidget( mWidget, 1 );
|
||||
QDialogButtonBox *buttonBox = new QDialogButtonBox( QDialogButtonBox::Ok | QDialogButtonBox::Cancel );
|
||||
connect( buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept );
|
||||
connect( buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject );
|
||||
vl->addWidget( buttonBox );
|
||||
setLayout( vl );
|
||||
}
|
||||
|
||||
void QgsModelOutputReorderDialog::setModel( QgsProcessingModelAlgorithm *model )
|
||||
{
|
||||
mWidget->setModel( model );
|
||||
}
|
||||
|
||||
QStringList QgsModelOutputReorderDialog::outputOrder() const
|
||||
{
|
||||
return mWidget->outputOrder();
|
||||
}
|
||||
|
||||
QString QgsModelOutputReorderDialog::outputGroup() const
|
||||
{
|
||||
return mWidget->outputGroup();
|
||||
}
|
||||
|
||||
///@endcond
|
||||
111
src/gui/processing/models/qgsmodeloutputreorderwidget.h
Normal file
111
src/gui/processing/models/qgsmodeloutputreorderwidget.h
Normal file
@ -0,0 +1,111 @@
|
||||
/***************************************************************************
|
||||
qgsmodeloutputreorderwidget.h
|
||||
----------------------------------
|
||||
Date : April 2023
|
||||
Copyright : (C) 2023 Nyall Dawson
|
||||
Email : nyall dot dawson at gmail dot com
|
||||
***************************************************************************
|
||||
* *
|
||||
* This program is free software; you can redistribute it and/or modify *
|
||||
* it under the terms of the GNU General Public License as published by *
|
||||
* the Free Software Foundation; either version 2 of the License, or *
|
||||
* (at your option) any later version. *
|
||||
* *
|
||||
***************************************************************************/
|
||||
|
||||
#ifndef QGSMODELOUTPUTREORDERWIDGET_H
|
||||
#define QGSMODELOUTPUTREORDERWIDGET_H
|
||||
|
||||
#define SIP_NO_FILE
|
||||
|
||||
#include "qgis.h"
|
||||
#include "qgis_gui.h"
|
||||
#include "ui_qgsmodeloutputreorderwidgetbase.h"
|
||||
#include "qgsprocessingmodeloutput.h"
|
||||
#include <QDialog>
|
||||
|
||||
class QStandardItemModel;
|
||||
class QgsProcessingModelAlgorithm;
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
/**
|
||||
* A widget for reordering outputs for Processing models.
|
||||
* \ingroup gui
|
||||
* \note Not stable API
|
||||
* \since QGIS 3.32
|
||||
*/
|
||||
class GUI_EXPORT QgsModelOutputReorderWidget : public QWidget, private Ui::QgsModelOutputReorderWidgetBase
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsModelOutputReorderWidget.
|
||||
*/
|
||||
QgsModelOutputReorderWidget( QWidget *parent = nullptr );
|
||||
|
||||
/**
|
||||
* Sets the source \a model from which to obtain the list of outputs.
|
||||
*/
|
||||
void setModel( QgsProcessingModelAlgorithm *model );
|
||||
|
||||
/**
|
||||
* Returns the ordered list of outputs.
|
||||
*/
|
||||
QStringList outputOrder() const;
|
||||
|
||||
/**
|
||||
* Returns the destination group name for outputs.
|
||||
*/
|
||||
QString outputGroup() const;
|
||||
|
||||
private:
|
||||
|
||||
QgsProcessingModelAlgorithm *mModel;
|
||||
QList< QgsProcessingModelOutput > mOutputs;
|
||||
QStandardItemModel *mItemModel = nullptr;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A dialog for reordering outputs for Processing models.
|
||||
* \ingroup gui
|
||||
* \note Not stable API
|
||||
* \since QGIS 3.32
|
||||
*/
|
||||
class GUI_EXPORT QgsModelOutputReorderDialog : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
* Constructor for QgsModelOutputReorderDialog.
|
||||
*/
|
||||
QgsModelOutputReorderDialog( QWidget *parent = nullptr );
|
||||
|
||||
/**
|
||||
* Sets the source \a model from which to obtain the list of outputs.
|
||||
*/
|
||||
void setModel( QgsProcessingModelAlgorithm *model );
|
||||
|
||||
/**
|
||||
* Returns the ordered list of outputs (by name).
|
||||
*/
|
||||
QStringList outputOrder() const;
|
||||
|
||||
/**
|
||||
* Returns the destination group name for outputs.
|
||||
*/
|
||||
QString outputGroup() const;
|
||||
|
||||
private:
|
||||
|
||||
QgsModelOutputReorderWidget *mWidget = nullptr;
|
||||
};
|
||||
|
||||
///@endcond
|
||||
|
||||
#endif // QGSMODELOUTPUTREORDERWIDGET_H
|
||||
@ -41,7 +41,7 @@
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>786</width>
|
||||
<height>24</height>
|
||||
<height>23</height>
|
||||
</rect>
|
||||
</property>
|
||||
<widget class="QMenu" name="menu_Model">
|
||||
@ -61,6 +61,7 @@
|
||||
<addaction name="mActionValidate"/>
|
||||
<addaction name="mActionRun"/>
|
||||
<addaction name="mActionReorderInputs"/>
|
||||
<addaction name="mActionReorderOutputs"/>
|
||||
<addaction name="separator"/>
|
||||
<addaction name="mActionNew"/>
|
||||
<addaction name="mActionOpen"/>
|
||||
@ -756,6 +757,11 @@
|
||||
<string>New Model…</string>
|
||||
</property>
|
||||
</action>
|
||||
<action name="mActionReorderOutputs">
|
||||
<property name="text">
|
||||
<string>Reorder Model Outputs...</string>
|
||||
</property>
|
||||
</action>
|
||||
</widget>
|
||||
<customwidgets>
|
||||
<customwidget>
|
||||
|
||||
123
src/ui/processing/qgsmodeloutputreorderwidgetbase.ui
Normal file
123
src/ui/processing/qgsmodeloutputreorderwidgetbase.ui
Normal file
@ -0,0 +1,123 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>QgsModelOutputReorderWidgetBase</class>
|
||||
<widget class="QWidget" name="QgsModelOutputReorderWidgetBase">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>356</width>
|
||||
<height>253</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string notr="true">Form</string>
|
||||
</property>
|
||||
<layout class="QGridLayout" name="gridLayout_2">
|
||||
<property name="leftMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="topMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="rightMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<property name="bottomMargin">
|
||||
<number>0</number>
|
||||
</property>
|
||||
<item row="1" column="0">
|
||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||
<item>
|
||||
<widget class="QListView" name="mOutputsList">
|
||||
<property name="editTriggers">
|
||||
<set>QAbstractItemView::NoEditTriggers</set>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="pushBtnBox_2">
|
||||
<property name="spacing">
|
||||
<number>4</number>
|
||||
</property>
|
||||
<item>
|
||||
<widget class="QPushButton" name="mButtonUp">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Move up</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../images/images.qrc">
|
||||
<normaloff>:/images/themes/default/mActionArrowUp.svg</normaloff>:/images/themes/default/mActionArrowUp.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QPushButton" name="mButtonDown">
|
||||
<property name="maximumSize">
|
||||
<size>
|
||||
<width>50</width>
|
||||
<height>16777215</height>
|
||||
</size>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Move down</string>
|
||||
</property>
|
||||
<property name="icon">
|
||||
<iconset resource="../../../images/images.qrc">
|
||||
<normaloff>:/images/themes/default/mActionArrowDown.svg</normaloff>:/images/themes/default/mActionArrowDown.svg</iconset>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>0</width>
|
||||
<height>0</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item row="2" column="0">
|
||||
<widget class="QGroupBox" name="mPlaceInGroupCheck">
|
||||
<property name="title">
|
||||
<string>Place outputs in a group</string>
|
||||
</property>
|
||||
<property name="checkable">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="text">
|
||||
<string>Group name</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QLineEdit" name="mGroupNameEdit"/>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources>
|
||||
<include location="../../../images/images.qrc"/>
|
||||
<include location="../../../images/images.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
@ -101,6 +101,7 @@ class TestQgsProcessingModelAlgorithm: public QObject
|
||||
void modelAcceptableValues();
|
||||
void modelValidate();
|
||||
void modelInputs();
|
||||
void modelOutputs();
|
||||
void modelDependencies();
|
||||
void modelSource();
|
||||
void modelNameMatchesFileName();
|
||||
@ -2089,6 +2090,159 @@ void TestQgsProcessingModelAlgorithm::modelInputs()
|
||||
QCOMPARE( m2.parameterDefinitions().at( 2 )->name(), QStringLiteral( "string" ) );
|
||||
}
|
||||
|
||||
void TestQgsProcessingModelAlgorithm::modelOutputs()
|
||||
{
|
||||
QgsProcessingModelAlgorithm m;
|
||||
QVERIFY( m.orderedOutputs().isEmpty() );
|
||||
QVERIFY( m.outputGroup().isEmpty() );
|
||||
m.setOutputGroup( QStringLiteral( "output group" ) );
|
||||
QCOMPARE( m.outputGroup(), QStringLiteral( "output group" ) );
|
||||
|
||||
const QgsProcessingModelParameter sourceParam( "INPUT" );
|
||||
m.addModelParameter( new QgsProcessingParameterFeatureSource( "INPUT" ), sourceParam );
|
||||
|
||||
QgsProcessingModelChildAlgorithm algc1;
|
||||
algc1.setChildId( QStringLiteral( "cx1" ) );
|
||||
algc1.setAlgorithmId( "native:buffer" );
|
||||
algc1.addParameterSources( "INPUT", { QgsProcessingModelChildParameterSource::fromModelParameter( "INPUT" ) } );
|
||||
|
||||
m.addChildAlgorithm( algc1 );
|
||||
QVERIFY( m.orderedOutputs().isEmpty() );
|
||||
|
||||
QgsProcessingModelChildAlgorithm algc2;
|
||||
algc2.setChildId( QStringLiteral( "cx2" ) );
|
||||
algc2.setAlgorithmId( "native:buffer" );
|
||||
algc2.addParameterSources( "INPUT", { QgsProcessingModelChildParameterSource::fromModelParameter( "INPUT" ) } );
|
||||
|
||||
QMap<QString, QgsProcessingModelOutput> outputs;
|
||||
QgsProcessingModelOutput out1;
|
||||
out1.setChildOutputName( QStringLiteral( "OUTPUT" ) );
|
||||
outputs.insert( QStringLiteral( "a" ), out1 );
|
||||
algc2.setModelOutputs( outputs );
|
||||
|
||||
m.addChildAlgorithm( algc2 );
|
||||
|
||||
QCOMPARE( m.orderedOutputs().size(), 1 );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).childId(), QStringLiteral( "cx2" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).name(), QStringLiteral( "a" ) );
|
||||
|
||||
QgsProcessingModelChildAlgorithm algc3;
|
||||
algc3.setChildId( QStringLiteral( "cx3" ) );
|
||||
algc3.setAlgorithmId( "native:buffer" );
|
||||
algc3.addParameterSources( "INPUT", { QgsProcessingModelChildParameterSource::fromModelParameter( "INPUT" ) } );
|
||||
|
||||
outputs.clear();
|
||||
QgsProcessingModelOutput out2;
|
||||
out2.setChildOutputName( QStringLiteral( "OUTPUT" ) );
|
||||
outputs.insert( QStringLiteral( "b" ), out2 );
|
||||
algc3.setModelOutputs( outputs );
|
||||
|
||||
m.addChildAlgorithm( algc3 );
|
||||
|
||||
QCOMPARE( m.orderedOutputs().size(), 2 );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).childId(), QStringLiteral( "cx2" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).name(), QStringLiteral( "a" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 1 ).childId(), QStringLiteral( "cx3" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 1 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 1 ).name(), QStringLiteral( "b" ) );
|
||||
|
||||
QgsProcessingModelChildAlgorithm algc4;
|
||||
algc4.setChildId( QStringLiteral( "cx4" ) );
|
||||
algc4.setAlgorithmId( "native:buffer" );
|
||||
algc4.addParameterSources( "INPUT", { QgsProcessingModelChildParameterSource::fromModelParameter( "INPUT" ) } );
|
||||
|
||||
outputs.clear();
|
||||
QgsProcessingModelOutput out3;
|
||||
out3.setChildOutputName( QStringLiteral( "OUTPUT" ) );
|
||||
outputs.insert( QStringLiteral( "c" ), out2 );
|
||||
algc4.setModelOutputs( outputs );
|
||||
|
||||
m.addChildAlgorithm( algc4 );
|
||||
|
||||
QCOMPARE( m.orderedOutputs().size(), 3 );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).childId(), QStringLiteral( "cx2" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).name(), QStringLiteral( "a" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 1 ).childId(), QStringLiteral( "cx3" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 1 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 1 ).name(), QStringLiteral( "b" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 2 ).childId(), QStringLiteral( "cx4" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 2 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 2 ).name(), QStringLiteral( "c" ) );
|
||||
|
||||
// set specific output order (incomplete, and with some non-matching values)
|
||||
m.setOutputOrder( { QStringLiteral( "cx3:OUTPUT" ), QStringLiteral( "cx2:OUTPUT" ), QStringLiteral( "cx1:OUTPUT" ) } );
|
||||
QCOMPARE( m.orderedOutputs().size(), 3 );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).childId(), QStringLiteral( "cx3" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).name(), QStringLiteral( "b" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 1 ).childId(), QStringLiteral( "cx2" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 1 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 1 ).name(), QStringLiteral( "a" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 2 ).childId(), QStringLiteral( "cx4" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 2 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 2 ).name(), QStringLiteral( "c" ) );
|
||||
|
||||
// set specific output order, complete
|
||||
m.setOutputOrder( { QStringLiteral( "cx3:OUTPUT" ), QStringLiteral( "cx4:OUTPUT" ), QStringLiteral( "cx2:OUTPUT" ) } );
|
||||
QCOMPARE( m.orderedOutputs().size(), 3 );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).childId(), QStringLiteral( "cx3" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 0 ).name(), QStringLiteral( "b" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 1 ).childId(), QStringLiteral( "cx4" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 1 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 1 ).name(), QStringLiteral( "c" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 2 ).childId(), QStringLiteral( "cx2" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 2 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m.orderedOutputs().at( 2 ).name(), QStringLiteral( "a" ) );
|
||||
|
||||
// save/restore
|
||||
QgsProcessingModelAlgorithm m2;
|
||||
m2.loadVariant( m.toVariant() );
|
||||
QCOMPARE( m2.outputGroup(), QStringLiteral( "output group" ) );
|
||||
QCOMPARE( m2.orderedOutputs().size(), 3 );
|
||||
QCOMPARE( m2.orderedOutputs().at( 0 ).childId(), QStringLiteral( "cx3" ) );
|
||||
QCOMPARE( m2.orderedOutputs().at( 0 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m2.orderedOutputs().at( 0 ).name(), QStringLiteral( "b" ) );
|
||||
QCOMPARE( m2.orderedOutputs().at( 1 ).childId(), QStringLiteral( "cx4" ) );
|
||||
QCOMPARE( m2.orderedOutputs().at( 1 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m2.orderedOutputs().at( 1 ).name(), QStringLiteral( "c" ) );
|
||||
QCOMPARE( m2.orderedOutputs().at( 2 ).childId(), QStringLiteral( "cx2" ) );
|
||||
QCOMPARE( m2.orderedOutputs().at( 2 ).childOutputName(), QStringLiteral( "OUTPUT" ) );
|
||||
QCOMPARE( m2.orderedOutputs().at( 2 ).name(), QStringLiteral( "a" ) );
|
||||
|
||||
// also run and check context details
|
||||
QgsProcessingContext context;
|
||||
QgsProcessingFeedback feedback;
|
||||
QVariantMap params;
|
||||
QgsVectorLayer *layer3111 = new QgsVectorLayer( "Point?crs=epsg:3111", "v1", "memory" );
|
||||
QgsProject p;
|
||||
p.addMapLayer( layer3111 );
|
||||
context.setProject( &p );
|
||||
params.insert( QStringLiteral( "INPUT" ), QStringLiteral( "v1" ) );
|
||||
params.insert( QStringLiteral( "cx2:a" ), QgsProcessing::TEMPORARY_OUTPUT );
|
||||
params.insert( QStringLiteral( "cx3:b" ), QgsProcessing::TEMPORARY_OUTPUT );
|
||||
params.insert( QStringLiteral( "cx4:c" ), QgsProcessing::TEMPORARY_OUTPUT );
|
||||
|
||||
QVariantMap results = m.run( params, context, &feedback );
|
||||
const QString destA = results.value( QStringLiteral( "cx2:a" ) ).toString();
|
||||
QVERIFY( !destA.isEmpty() );
|
||||
QCOMPARE( context.layerToLoadOnCompletionDetails( destA ).groupName, QStringLiteral( "output group" ) );
|
||||
QCOMPARE( context.layerToLoadOnCompletionDetails( destA ).layerSortKey, 2 );
|
||||
|
||||
const QString destB = results.value( QStringLiteral( "cx3:b" ) ).toString();
|
||||
QVERIFY( !destB.isEmpty() );
|
||||
QCOMPARE( context.layerToLoadOnCompletionDetails( destB ).groupName, QStringLiteral( "output group" ) );
|
||||
QCOMPARE( context.layerToLoadOnCompletionDetails( destB ).layerSortKey, 0 );
|
||||
|
||||
const QString destC = results.value( QStringLiteral( "cx4:c" ) ).toString();
|
||||
QVERIFY( !destC.isEmpty() );
|
||||
QCOMPARE( context.layerToLoadOnCompletionDetails( destC ).groupName, QStringLiteral( "output group" ) );
|
||||
QCOMPARE( context.layerToLoadOnCompletionDetails( destC ).layerSortKey, 1 );
|
||||
}
|
||||
|
||||
void TestQgsProcessingModelAlgorithm::modelDependencies()
|
||||
{
|
||||
const QgsProcessingModelChildDependency dep( QStringLiteral( "childId" ), QStringLiteral( "branch" ) );
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user