Cleaner approach -- create expression contexts in modeler parameter

definition dialogs using expression context and processing context
generators
This commit is contained in:
Nyall Dawson 2021-01-14 14:03:47 +10:00
parent 5a9e8c6290
commit 814b65ba1c
9 changed files with 190 additions and 76 deletions

View File

@ -12,7 +12,8 @@
class QgsProcessingAbstractParameterDefinitionWidget : QWidget
class QgsProcessingAbstractParameterDefinitionWidget : QWidget, QgsExpressionContextGenerator
{ {
%Docstring %Docstring
Abstract base class for widgets which allow users to specify the properties of a Abstract base class for widgets which allow users to specify the properties of a
@ -53,6 +54,42 @@ Common properties for parameters, including the ``name``, ``description``, and p
method. Subclass implementations must use these properties when crafting a parameter definition which method. Subclass implementations must use these properties when crafting a parameter definition which
also respects the additional properties specific to the parameter type handled by the widget subclass. also respects the additional properties specific to the parameter type handled by the widget subclass.
%End %End
virtual void setWidgetContext( const QgsProcessingParameterWidgetContext &context );
%Docstring
Sets the ``context`` in which the Processing definition widget is shown, e.g., the
parent model algorithm, a linked map canvas, and other relevant information which allows the widget
to fine-tune its behavior.
Subclasses should take care to call the base class method when reimplementing this method.
.. seealso:: :py:func:`widgetContext`
.. versionadded:: 3.18
%End
const QgsProcessingParameterWidgetContext &widgetContext() const;
%Docstring
Returns the context in which the Processing definition widget is shown, e.g., the
parent model algorithm, a linked map canvas, and other relevant information which allows the widget
to fine-tune its behavior.
.. seealso:: :py:func:`setWidgetContext`
.. versionadded:: 3.18
%End
void registerProcessingContextGenerator( QgsProcessingContextGenerator *generator );
%Docstring
Registers a Processing context ``generator`` class that will be used to retrieve
a Processing context for the widget when required.
.. versionadded:: 3.18
%End
virtual QgsExpressionContext createExpressionContext() const;
}; };
@ -96,6 +133,14 @@ associated with the parameter.
Returns a new instance of a parameter definition, using the current settings defined in the dialog. Returns a new instance of a parameter definition, using the current settings defined in the dialog.
The ``name`` parameter specifies the name for the newly created parameter. The ``name`` parameter specifies the name for the newly created parameter.
%End
void registerProcessingContextGenerator( QgsProcessingContextGenerator *generator );
%Docstring
Registers a Processing context ``generator`` class that will be used to retrieve
a Processing context for the widget when required.
.. versionadded:: 3.18
%End %End
}; };
@ -181,6 +226,14 @@ Returns the color for the comments for the parameter.
void switchToCommentTab(); void switchToCommentTab();
%Docstring %Docstring
Switches the dialog to the comments tab. Switches the dialog to the comments tab.
%End
void registerProcessingContextGenerator( QgsProcessingContextGenerator *generator );
%Docstring
Registers a Processing context ``generator`` class that will be used to retrieve
a Processing context for the widget when required.
.. versionadded:: 3.18
%End %End
public slots: public slots:

View File

@ -127,15 +127,6 @@ an expression context for the widget.
create an expression context when required. create an expression context when required.
%End %End
void setExpressionContext( QgsExpressionContext &context );
%Docstring
Set a default expression context. Will be used if no QgsEcpressionContextGenerator or layer is set.
:param context: The QgsExpressionContext to use.
.. versionadded:: 3.18
%End
signals: signals:
void expressionChanged( const QString &expression ); void expressionChanged( const QString &expression );

View File

@ -40,12 +40,13 @@ from qgis.core import (Qgis,
QgsProcessing, QgsProcessing,
QgsProject, QgsProject,
QgsProcessingModelParameter, QgsProcessingModelParameter,
QgsSettings QgsSettings,
) )
from qgis.gui import (QgsProcessingParameterDefinitionDialog, from qgis.gui import (QgsProcessingParameterDefinitionDialog,
QgsProcessingParameterWidgetContext, QgsProcessingParameterWidgetContext,
QgsModelGraphicsScene, QgsModelGraphicsScene,
QgsModelDesignerDialog) QgsModelDesignerDialog,
QgsProcessingContextGenerator)
from processing.gui.HelpEditionDialog import HelpEditionDialog from processing.gui.HelpEditionDialog import HelpEditionDialog
from processing.gui.AlgorithmDialog import AlgorithmDialog from processing.gui.AlgorithmDialog import AlgorithmDialog
from processing.modeler.ModelerParameterDefinitionDialog import ModelerParameterDefinitionDialog from processing.modeler.ModelerParameterDefinitionDialog import ModelerParameterDefinitionDialog
@ -105,6 +106,19 @@ class ModelerDialog(QgsModelDesignerDialog):
self.view().centerOn(0, 0) self.view().centerOn(0, 0)
self.processing_context = createContext()
class ContextGenerator(QgsProcessingContextGenerator):
def __init__(self, context):
super().__init__()
self.processing_context = context
def processingContext(self):
return self.processing_context
self.context_generator = ContextGenerator(self.processing_context)
def editHelp(self): def editHelp(self):
alg = self.model() alg = self.model()
dlg = HelpEditionDialog(alg) dlg = HelpEditionDialog(alg)
@ -270,6 +284,7 @@ class ModelerDialog(QgsModelDesignerDialog):
context=context, context=context,
widgetContext=widget_context, widgetContext=widget_context,
algorithm=self.model()) algorithm=self.model())
dlg.registerProcessingContextGenerator(self.context_generator)
if dlg.exec_(): if dlg.exec_():
new_param = dlg.createParameter() new_param = dlg.createParameter()
self.autogenerate_parameter_name(new_param) self.autogenerate_parameter_name(new_param)

View File

@ -31,7 +31,8 @@ from qgis.gui import (
QgsProcessingParameterWidgetContext, QgsProcessingParameterWidgetContext,
QgsModelParameterGraphicItem, QgsModelParameterGraphicItem,
QgsModelChildAlgorithmGraphicItem, QgsModelChildAlgorithmGraphicItem,
QgsModelOutputGraphicItem QgsModelOutputGraphicItem,
QgsProcessingContextGenerator
) )
from processing.modeler.ModelerParameterDefinitionDialog import ModelerParameterDefinitionDialog from processing.modeler.ModelerParameterDefinitionDialog import ModelerParameterDefinitionDialog
from processing.modeler.ModelerParametersDialog import ModelerParametersDialog from processing.modeler.ModelerParametersDialog import ModelerParametersDialog
@ -50,6 +51,19 @@ class ModelerInputGraphicItem(QgsModelParameterGraphicItem):
def __init__(self, element, model): def __init__(self, element, model):
super().__init__(element, model, None) super().__init__(element, model, None)
self.processing_context = createContext()
class ContextGenerator(QgsProcessingContextGenerator):
def __init__(self, context):
super().__init__()
self.processing_context = context
def processingContext(self):
return self.processing_context
self.context_generator = ContextGenerator(self.processing_context)
def create_widget_context(self): def create_widget_context(self):
""" """
Returns a new widget context for use in the model editor Returns a new widget context for use in the model editor
@ -91,6 +105,8 @@ class ModelerInputGraphicItem(QgsModelParameterGraphicItem):
algorithm=self.model()) algorithm=self.model())
dlg.setComments(comment) dlg.setComments(comment)
dlg.setCommentColor(comment_color) dlg.setCommentColor(comment_color)
dlg.registerProcessingContextGenerator(self.context_generator)
if edit_comment: if edit_comment:
dlg.switchToCommentTab() dlg.switchToCommentTab()

View File

@ -34,14 +34,35 @@
#include <QTextEdit> #include <QTextEdit>
QgsProcessingAbstractParameterDefinitionWidget::QgsProcessingAbstractParameterDefinitionWidget( QgsProcessingContext &, QgsProcessingAbstractParameterDefinitionWidget::QgsProcessingAbstractParameterDefinitionWidget( QgsProcessingContext &,
const QgsProcessingParameterWidgetContext &, const QgsProcessingParameterWidgetContext &context,
const QgsProcessingParameterDefinition *, const QgsProcessingParameterDefinition *,
const QgsProcessingAlgorithm *, QWidget *parent ) const QgsProcessingAlgorithm *, QWidget *parent )
: QWidget( parent ) : QWidget( parent )
, mWidgetContext( context )
{ {
} }
void QgsProcessingAbstractParameterDefinitionWidget::setWidgetContext( const QgsProcessingParameterWidgetContext &context )
{
mWidgetContext = context;
}
const QgsProcessingParameterWidgetContext &QgsProcessingAbstractParameterDefinitionWidget::widgetContext() const
{
return mWidgetContext;
}
void QgsProcessingAbstractParameterDefinitionWidget::registerProcessingContextGenerator( QgsProcessingContextGenerator *generator )
{
mContextGenerator = generator;
}
QgsExpressionContext QgsProcessingAbstractParameterDefinitionWidget::createExpressionContext() const
{
return QgsProcessingGuiUtils::createExpressionContext( mContextGenerator, mWidgetContext, nullptr, nullptr );
}
// //
// QgsProcessingParameterDefinitionWidget // QgsProcessingParameterDefinitionWidget
// //
@ -120,6 +141,14 @@ QgsProcessingParameterDefinition *QgsProcessingParameterDefinitionWidget::create
return param.release(); return param.release();
} }
void QgsProcessingParameterDefinitionWidget::registerProcessingContextGenerator( QgsProcessingContextGenerator *generator )
{
if ( mDefinitionWidget )
{
mDefinitionWidget->registerProcessingContextGenerator( generator );
}
}
// //
// QgsProcessingParameterDefinitionDialog // QgsProcessingParameterDefinitionDialog
// //
@ -210,6 +239,14 @@ void QgsProcessingParameterDefinitionDialog::switchToCommentTab()
mCommentEdit->selectAll(); mCommentEdit->selectAll();
} }
void QgsProcessingParameterDefinitionDialog::registerProcessingContextGenerator( QgsProcessingContextGenerator *generator )
{
if ( mWidget )
{
mWidget->registerProcessingContextGenerator( generator );
}
}
void QgsProcessingParameterDefinitionDialog::accept() void QgsProcessingParameterDefinitionDialog::accept()
{ {
if ( mWidget->mDescriptionLineEdit->text().isEmpty() ) if ( mWidget->mDescriptionLineEdit->text().isEmpty() )

View File

@ -25,8 +25,11 @@
#include "qgis_gui.h" #include "qgis_gui.h"
#include "qgis_sip.h" #include "qgis_sip.h"
#include "qgsprocessingparameters.h" #include "qgsprocessingparameters.h"
#include "qgsexpressioncontextgenerator.h"
#include "qgsprocessingwidgetwrapper.h"
class QgsProcessingContextGenerator;
class QgsProcessingParameterWidgetContext;
class QLineEdit; class QLineEdit;
class QCheckBox; class QCheckBox;
class QTabWidget; class QTabWidget;
@ -40,7 +43,7 @@ class QgsColorButton;
* \ingroup gui * \ingroup gui
* \since QGIS 3.10 * \since QGIS 3.10
*/ */
class GUI_EXPORT QgsProcessingAbstractParameterDefinitionWidget : public QWidget class GUI_EXPORT QgsProcessingAbstractParameterDefinitionWidget : public QWidget, public QgsExpressionContextGenerator
{ {
Q_OBJECT Q_OBJECT
@ -73,6 +76,43 @@ class GUI_EXPORT QgsProcessingAbstractParameterDefinitionWidget : public QWidget
* also respects the additional properties specific to the parameter type handled by the widget subclass. * also respects the additional properties specific to the parameter type handled by the widget subclass.
*/ */
virtual QgsProcessingParameterDefinition *createParameter( const QString &name, const QString &description, QgsProcessingParameterDefinition::Flags flags ) const = 0 SIP_FACTORY; virtual QgsProcessingParameterDefinition *createParameter( const QString &name, const QString &description, QgsProcessingParameterDefinition::Flags flags ) const = 0 SIP_FACTORY;
/**
* Sets the \a context in which the Processing definition widget is shown, e.g., the
* parent model algorithm, a linked map canvas, and other relevant information which allows the widget
* to fine-tune its behavior.
*
* Subclasses should take care to call the base class method when reimplementing this method.
*
* \see widgetContext()
* \since QGIS 3.18
*/
virtual void setWidgetContext( const QgsProcessingParameterWidgetContext &context );
/**
* Returns the context in which the Processing definition widget is shown, e.g., the
* parent model algorithm, a linked map canvas, and other relevant information which allows the widget
* to fine-tune its behavior.
*
* \see setWidgetContext()
* \since QGIS 3.18
*/
const QgsProcessingParameterWidgetContext &widgetContext() const;
/**
* Registers a Processing context \a generator class that will be used to retrieve
* a Processing context for the widget when required.
*
* \since QGIS 3.18
*/
void registerProcessingContextGenerator( QgsProcessingContextGenerator *generator );
QgsExpressionContext createExpressionContext() const override;
private:
QgsProcessingContextGenerator *mContextGenerator = nullptr;
QgsProcessingParameterWidgetContext mWidgetContext;
}; };
@ -117,6 +157,14 @@ class GUI_EXPORT QgsProcessingParameterDefinitionWidget: public QWidget
*/ */
QgsProcessingParameterDefinition *createParameter( const QString &name = QString() ) const SIP_FACTORY; QgsProcessingParameterDefinition *createParameter( const QString &name = QString() ) const SIP_FACTORY;
/**
* Registers a Processing context \a generator class that will be used to retrieve
* a Processing context for the widget when required.
*
* \since QGIS 3.18
*/
void registerProcessingContextGenerator( QgsProcessingContextGenerator *generator );
private: private:
QString mType; QString mType;
@ -203,6 +251,14 @@ class GUI_EXPORT QgsProcessingParameterDefinitionDialog: public QDialog
*/ */
void switchToCommentTab(); void switchToCommentTab();
/**
* Registers a Processing context \a generator class that will be used to retrieve
* a Processing context for the widget when required.
*
* \since QGIS 3.18
*/
void registerProcessingContextGenerator( QgsProcessingContextGenerator *generator );
public slots: public slots:
void accept() override; void accept() override;

View File

@ -1841,18 +1841,13 @@ QgsProcessingAbstractParameterDefinitionWidget *QgsProcessingFileWidgetWrapper::
QgsProcessingExpressionParameterDefinitionWidget::QgsProcessingExpressionParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent ) QgsProcessingExpressionParameterDefinitionWidget::QgsProcessingExpressionParameterDefinitionWidget( QgsProcessingContext &context, const QgsProcessingParameterWidgetContext &widgetContext, const QgsProcessingParameterDefinition *definition, const QgsProcessingAlgorithm *algorithm, QWidget *parent )
: QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent ) : QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
{ {
mModel = widgetContext.model();
QVBoxLayout *vlayout = new QVBoxLayout(); QVBoxLayout *vlayout = new QVBoxLayout();
vlayout->setContentsMargins( 0, 0, 0, 0 ); vlayout->setContentsMargins( 0, 0, 0, 0 );
vlayout->addWidget( new QLabel( tr( "Default value" ) ) ); vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
mDefaultLineEdit = new QgsExpressionLineEdit(); mDefaultLineEdit = new QgsExpressionLineEdit();
if ( mModel ) mDefaultLineEdit->registerExpressionContextGenerator( this );
{
QgsExpressionContext expressionContext = createExpressionContext( context, algorithm );
mDefaultLineEdit->setExpressionContext( expressionContext );
}
if ( const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( definition ) ) if ( const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( definition ) )
mDefaultLineEdit->setExpression( QgsProcessingParameters::parameterAsExpression( expParam, expParam->defaultValueForGui(), context ) ); mDefaultLineEdit->setExpression( QgsProcessingParameters::parameterAsExpression( expParam, expParam->defaultValueForGui(), context ) );
vlayout->addWidget( mDefaultLineEdit ); vlayout->addWidget( mDefaultLineEdit );
@ -1866,14 +1861,13 @@ QgsProcessingExpressionParameterDefinitionWidget::QgsProcessingExpressionParamet
if ( const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( definition ) ) if ( const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( definition ) )
initialParent = expParam->parentLayerParameterName(); initialParent = expParam->parentLayerParameterName();
if ( QgsProcessingModelAlgorithm *model = widgetContext.model() )
if ( mModel )
{ {
// populate combo box with other model input choices // populate combo box with other model input choices
const QMap<QString, QgsProcessingModelParameter> components = mModel->parameterComponents(); const QMap<QString, QgsProcessingModelParameter> components = model->parameterComponents();
for ( auto it = components.constBegin(); it != components.constEnd(); ++it ) for ( auto it = components.constBegin(); it != components.constEnd(); ++it )
{ {
if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast< const QgsProcessingParameterFeatureSource * >( mModel->parameterDefinition( it.value().parameterName() ) ) ) if ( const QgsProcessingParameterFeatureSource *definition = dynamic_cast< const QgsProcessingParameterFeatureSource * >( model->parameterDefinition( it.value().parameterName() ) ) )
{ {
mParentLayerComboBox-> addItem( definition->description(), definition->name() ); mParentLayerComboBox-> addItem( definition->description(), definition->name() );
if ( !initialParent.isEmpty() && initialParent == definition->name() ) if ( !initialParent.isEmpty() && initialParent == definition->name() )
@ -1881,7 +1875,7 @@ QgsProcessingExpressionParameterDefinitionWidget::QgsProcessingExpressionParamet
mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 ); mParentLayerComboBox->setCurrentIndex( mParentLayerComboBox->count() - 1 );
} }
} }
else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast< const QgsProcessingParameterVectorLayer * >( mModel->parameterDefinition( it.value().parameterName() ) ) ) else if ( const QgsProcessingParameterVectorLayer *definition = dynamic_cast< const QgsProcessingParameterVectorLayer * >( model->parameterDefinition( it.value().parameterName() ) ) )
{ {
mParentLayerComboBox-> addItem( definition->description(), definition->name() ); mParentLayerComboBox-> addItem( definition->description(), definition->name() );
if ( !initialParent.isEmpty() && initialParent == definition->name() ) if ( !initialParent.isEmpty() && initialParent == definition->name() )
@ -1907,46 +1901,9 @@ QgsProcessingParameterDefinition *QgsProcessingExpressionParameterDefinitionWidg
{ {
auto param = qgis::make_unique< QgsProcessingParameterExpression >( name, description, mDefaultLineEdit->expression(), mParentLayerComboBox->currentData().toString() ); auto param = qgis::make_unique< QgsProcessingParameterExpression >( name, description, mDefaultLineEdit->expression(), mParentLayerComboBox->currentData().toString() );
param->setFlags( flags ); param->setFlags( flags );
if ( mModel )
{
QStringList extraVars = mModel->variables().keys();
param->setAdditionalExpressionContextVariables( extraVars );
}
return param.release(); return param.release();
} }
QgsExpressionContext QgsProcessingExpressionParameterDefinitionWidget::createExpressionContext( QgsProcessingContext &context, const QgsProcessingAlgorithm *algorithm ) const
{
QgsExpressionContext finalContext = context.expressionContext();
QString childId;
QgsExpressionContextScope *algScope = QgsExpressionContextUtils::processingAlgorithmScope( algorithm, QVariantMap(), context );
finalContext.appendScope( algScope );
QgsExpressionContextScope *modelScope = QgsExpressionContextUtils::processingModelAlgorithmScope( mModel, QVariantMap(), context );
finalContext << modelScope;
const QgsProcessingAlgorithm *childAlg = nullptr;
if ( mModel->childAlgorithms().contains( childId ) )
childAlg = mModel->childAlgorithm( childId ).algorithm();
QgsExpressionContextScope *algorithmScope = QgsExpressionContextUtils::processingAlgorithmScope( childAlg, QVariantMap(), context );
finalContext << algorithmScope;
QgsExpressionContextScope *childScope = mModel->createExpressionContextScopeForChildAlgorithm( childId, context, QVariantMap(), QVariantMap() );
finalContext << childScope;
QStringList highlightedVariables = childScope->variableNames();
QStringList highlightedFunctions = childScope->functionNames();
highlightedVariables += algorithmScope->variableNames();
highlightedVariables += algScope->variableNames();
highlightedVariables += mModel->variables().keys();
highlightedFunctions += algScope->functionNames();
highlightedFunctions += algorithmScope->functionNames();
finalContext.setHighlightedVariables( highlightedVariables );
finalContext.setHighlightedFunctions( highlightedFunctions );
return finalContext;
}
QgsProcessingExpressionWidgetWrapper::QgsProcessingExpressionWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent ) QgsProcessingExpressionWidgetWrapper::QgsProcessingExpressionWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent )
: QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent ) : QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
{ {

View File

@ -66,8 +66,6 @@ class QgsProcessingMapLayerComboBox;
class QgsRasterBandComboBox; class QgsRasterBandComboBox;
class QgsProcessingLayerOutputDestinationWidget; class QgsProcessingLayerOutputDestinationWidget;
class QgsCheckableComboBox; class QgsCheckableComboBox;
class QgsProcessingModelAlgorithm;
class QgsExpressionContext;
///@cond PRIVATE ///@cond PRIVATE
@ -635,13 +633,11 @@ class GUI_EXPORT QgsProcessingExpressionParameterDefinitionWidget : public QgsPr
const QgsProcessingParameterDefinition *definition = nullptr, const QgsProcessingParameterDefinition *definition = nullptr,
const QgsProcessingAlgorithm *algorithm = nullptr, QWidget *parent SIP_TRANSFERTHIS = nullptr ); const QgsProcessingAlgorithm *algorithm = nullptr, QWidget *parent SIP_TRANSFERTHIS = nullptr );
QgsProcessingParameterDefinition *createParameter( const QString &name, const QString &description, QgsProcessingParameterDefinition::Flags flags ) const override; QgsProcessingParameterDefinition *createParameter( const QString &name, const QString &description, QgsProcessingParameterDefinition::Flags flags ) const override;
QgsExpressionContext createExpressionContext( QgsProcessingContext &context, const QgsProcessingAlgorithm *algorithm ) const;
private: private:
QComboBox *mParentLayerComboBox = nullptr; QComboBox *mParentLayerComboBox = nullptr;
QgsExpressionLineEdit *mDefaultLineEdit = nullptr; QgsExpressionLineEdit *mDefaultLineEdit = nullptr;
QgsProcessingModelAlgorithm *mModel = nullptr;
}; };

View File

@ -132,13 +132,6 @@ class GUI_EXPORT QgsExpressionLineEdit : public QWidget
*/ */
void registerExpressionContextGenerator( const QgsExpressionContextGenerator *generator ); void registerExpressionContextGenerator( const QgsExpressionContextGenerator *generator );
/**
* Set a default expression context. Will be used if no QgsEcpressionContextGenerator or layer is set.
* \param context The QgsExpressionContext to use.
* \since QGIS 3.18
*/
void setExpressionContext( QgsExpressionContext &context ) { mExpressionContext = context; }
signals: signals:
/** /**