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
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
also respects the additional properties specific to the parameter type handled by the widget subclass.
%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.
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
};
@ -181,6 +226,14 @@ Returns the color for the comments for the parameter.
void switchToCommentTab();
%Docstring
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
public slots:

View File

@ -127,15 +127,6 @@ an expression context for the widget.
create an expression context when required.
%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:
void expressionChanged( const QString &expression );

View File

@ -40,12 +40,13 @@ from qgis.core import (Qgis,
QgsProcessing,
QgsProject,
QgsProcessingModelParameter,
QgsSettings
QgsSettings,
)
from qgis.gui import (QgsProcessingParameterDefinitionDialog,
QgsProcessingParameterWidgetContext,
QgsModelGraphicsScene,
QgsModelDesignerDialog)
QgsModelDesignerDialog,
QgsProcessingContextGenerator)
from processing.gui.HelpEditionDialog import HelpEditionDialog
from processing.gui.AlgorithmDialog import AlgorithmDialog
from processing.modeler.ModelerParameterDefinitionDialog import ModelerParameterDefinitionDialog
@ -105,6 +106,19 @@ class ModelerDialog(QgsModelDesignerDialog):
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):
alg = self.model()
dlg = HelpEditionDialog(alg)
@ -270,6 +284,7 @@ class ModelerDialog(QgsModelDesignerDialog):
context=context,
widgetContext=widget_context,
algorithm=self.model())
dlg.registerProcessingContextGenerator(self.context_generator)
if dlg.exec_():
new_param = dlg.createParameter()
self.autogenerate_parameter_name(new_param)

View File

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

View File

@ -34,14 +34,35 @@
#include <QTextEdit>
QgsProcessingAbstractParameterDefinitionWidget::QgsProcessingAbstractParameterDefinitionWidget( QgsProcessingContext &,
const QgsProcessingParameterWidgetContext &,
const QgsProcessingParameterWidgetContext &context,
const QgsProcessingParameterDefinition *,
const QgsProcessingAlgorithm *, 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
//
@ -120,6 +141,14 @@ QgsProcessingParameterDefinition *QgsProcessingParameterDefinitionWidget::create
return param.release();
}
void QgsProcessingParameterDefinitionWidget::registerProcessingContextGenerator( QgsProcessingContextGenerator *generator )
{
if ( mDefinitionWidget )
{
mDefinitionWidget->registerProcessingContextGenerator( generator );
}
}
//
// QgsProcessingParameterDefinitionDialog
//
@ -210,6 +239,14 @@ void QgsProcessingParameterDefinitionDialog::switchToCommentTab()
mCommentEdit->selectAll();
}
void QgsProcessingParameterDefinitionDialog::registerProcessingContextGenerator( QgsProcessingContextGenerator *generator )
{
if ( mWidget )
{
mWidget->registerProcessingContextGenerator( generator );
}
}
void QgsProcessingParameterDefinitionDialog::accept()
{
if ( mWidget->mDescriptionLineEdit->text().isEmpty() )

View File

@ -25,8 +25,11 @@
#include "qgis_gui.h"
#include "qgis_sip.h"
#include "qgsprocessingparameters.h"
#include "qgsexpressioncontextgenerator.h"
#include "qgsprocessingwidgetwrapper.h"
class QgsProcessingContextGenerator;
class QgsProcessingParameterWidgetContext;
class QLineEdit;
class QCheckBox;
class QTabWidget;
@ -40,7 +43,7 @@ class QgsColorButton;
* \ingroup gui
* \since QGIS 3.10
*/
class GUI_EXPORT QgsProcessingAbstractParameterDefinitionWidget : public QWidget
class GUI_EXPORT QgsProcessingAbstractParameterDefinitionWidget : public QWidget, public QgsExpressionContextGenerator
{
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.
*/
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;
/**
* 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:
QString mType;
@ -203,6 +251,14 @@ class GUI_EXPORT QgsProcessingParameterDefinitionDialog: public QDialog
*/
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:
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 )
: QgsProcessingAbstractParameterDefinitionWidget( context, widgetContext, definition, algorithm, parent )
{
mModel = widgetContext.model();
QVBoxLayout *vlayout = new QVBoxLayout();
vlayout->setContentsMargins( 0, 0, 0, 0 );
vlayout->addWidget( new QLabel( tr( "Default value" ) ) );
mDefaultLineEdit = new QgsExpressionLineEdit();
if ( mModel )
{
QgsExpressionContext expressionContext = createExpressionContext( context, algorithm );
mDefaultLineEdit->setExpressionContext( expressionContext );
}
mDefaultLineEdit->registerExpressionContextGenerator( this );
if ( const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( definition ) )
mDefaultLineEdit->setExpression( QgsProcessingParameters::parameterAsExpression( expParam, expParam->defaultValueForGui(), context ) );
vlayout->addWidget( mDefaultLineEdit );
@ -1866,14 +1861,13 @@ QgsProcessingExpressionParameterDefinitionWidget::QgsProcessingExpressionParamet
if ( const QgsProcessingParameterExpression *expParam = dynamic_cast<const QgsProcessingParameterExpression *>( definition ) )
initialParent = expParam->parentLayerParameterName();
if ( mModel )
if ( QgsProcessingModelAlgorithm *model = widgetContext.model() )
{
// 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 )
{
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() );
if ( !initialParent.isEmpty() && initialParent == definition->name() )
@ -1881,7 +1875,7 @@ QgsProcessingExpressionParameterDefinitionWidget::QgsProcessingExpressionParamet
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() );
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() );
param->setFlags( flags );
if ( mModel )
{
QStringList extraVars = mModel->variables().keys();
param->setAdditionalExpressionContextVariables( extraVars );
}
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 )
: QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
{

View File

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

View File

@ -132,13 +132,6 @@ class GUI_EXPORT QgsExpressionLineEdit : public QWidget
*/
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:
/**