mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-17 00:04:02 -04:00
[processing] Port expression widget wrapper to new API
Fixes confusing expression parameter definitions in modeler child algorithms (there's no direction as to what the widget should be) and ensures that the correct expression context is revealed to the widget when in all modes.
This commit is contained in:
parent
6f6d562ec8
commit
2729af65c3
@ -36,6 +36,7 @@ QgsProcessingGuiRegistry::QgsProcessingGuiRegistry()
|
||||
addParameterWidgetFactory( new QgsProcessingAuthConfigWidgetWrapper() );
|
||||
addParameterWidgetFactory( new QgsProcessingMatrixWidgetWrapper() );
|
||||
addParameterWidgetFactory( new QgsProcessingFileWidgetWrapper() );
|
||||
addParameterWidgetFactory( new QgsProcessingExpressionWidgetWrapper() );
|
||||
}
|
||||
|
||||
QgsProcessingGuiRegistry::~QgsProcessingGuiRegistry()
|
||||
|
@ -27,6 +27,8 @@
|
||||
#include "qgsapplication.h"
|
||||
#include "qgsfilewidget.h"
|
||||
#include "qgssettings.h"
|
||||
#include "qgsexpressionlineedit.h"
|
||||
#include "qgsfieldexpressionwidget.h"
|
||||
#include <QLabel>
|
||||
#include <QHBoxLayout>
|
||||
#include <QCheckBox>
|
||||
@ -1210,5 +1212,190 @@ QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingFileWidgetWrapper::cre
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//
|
||||
// QgsProcessingExpressionWidgetWrapper
|
||||
//
|
||||
|
||||
QgsProcessingExpressionWidgetWrapper::QgsProcessingExpressionWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent )
|
||||
: QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
QWidget *QgsProcessingExpressionWidgetWrapper::createWidget()
|
||||
{
|
||||
const QgsProcessingParameterExpression *expParam = dynamic_cast< const QgsProcessingParameterExpression *>( parameterDefinition() );
|
||||
switch ( type() )
|
||||
{
|
||||
case QgsProcessingGui::Standard:
|
||||
case QgsProcessingGui::Modeler:
|
||||
case QgsProcessingGui::Batch:
|
||||
{
|
||||
if ( expParam->parentLayerParameterName().isEmpty() )
|
||||
{
|
||||
mExpLineEdit = new QgsExpressionLineEdit();
|
||||
mExpLineEdit->setToolTip( parameterDefinition()->toolTip() );
|
||||
mExpLineEdit->setExpressionDialogTitle( parameterDefinition()->description() );
|
||||
mExpLineEdit->registerExpressionContextGenerator( this );
|
||||
connect( mExpLineEdit, &QgsExpressionLineEdit::expressionChanged, this, [ = ]( const QString & )
|
||||
{
|
||||
emit widgetValueHasChanged( this );
|
||||
} );
|
||||
return mExpLineEdit;
|
||||
}
|
||||
else
|
||||
{
|
||||
mFieldExpWidget = new QgsFieldExpressionWidget();
|
||||
mFieldExpWidget->setToolTip( parameterDefinition()->toolTip() );
|
||||
mFieldExpWidget->setExpressionDialogTitle( parameterDefinition()->description() );
|
||||
mFieldExpWidget->registerExpressionContextGenerator( this );
|
||||
connect( mFieldExpWidget, static_cast < void ( QgsFieldExpressionWidget::* )( const QString & ) >( &QgsFieldExpressionWidget::fieldChanged ), this, [ = ]( const QString & )
|
||||
{
|
||||
emit widgetValueHasChanged( this );
|
||||
} );
|
||||
return mFieldExpWidget;
|
||||
}
|
||||
};
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void QgsProcessingExpressionWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
|
||||
{
|
||||
QgsAbstractProcessingParameterWidgetWrapper::postInitialize( wrappers );
|
||||
switch ( type() )
|
||||
{
|
||||
case QgsProcessingGui::Standard:
|
||||
case QgsProcessingGui::Batch:
|
||||
{
|
||||
for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
|
||||
{
|
||||
if ( wrapper->parameterDefinition()->name() == static_cast< const QgsProcessingParameterExpression * >( parameterDefinition() )->parentLayerParameterName() )
|
||||
{
|
||||
setParentLayerWrapperValue( wrapper );
|
||||
connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, [ = ]
|
||||
{
|
||||
setParentLayerWrapperValue( wrapper );
|
||||
} );
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case QgsProcessingGui::Modeler:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void QgsProcessingExpressionWidgetWrapper::setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper )
|
||||
{
|
||||
// evaluate value to layer
|
||||
QgsProcessingContext *context = nullptr;
|
||||
std::unique_ptr< QgsProcessingContext > tmpContext;
|
||||
if ( mProcessingContextGenerator )
|
||||
context = mProcessingContextGenerator->processingContext();
|
||||
|
||||
if ( !context )
|
||||
{
|
||||
tmpContext = qgis::make_unique< QgsProcessingContext >();
|
||||
context = tmpContext.get();
|
||||
}
|
||||
|
||||
QgsVectorLayer *layer = QgsProcessingParameters::parameterAsVectorLayer( parentWrapper->parameterDefinition(), parentWrapper->parameterValue(), *context );
|
||||
if ( !layer )
|
||||
{
|
||||
if ( mFieldExpWidget )
|
||||
mFieldExpWidget->setLayer( nullptr );
|
||||
else if ( mExpLineEdit )
|
||||
mExpLineEdit->setLayer( nullptr );
|
||||
return;
|
||||
}
|
||||
|
||||
// need to grab ownership of layer if required - otherwise layer may be deleted when context
|
||||
// goes out of scope
|
||||
std::unique_ptr< QgsMapLayer > ownedLayer( context->takeResultLayer( layer->id() ) );
|
||||
if ( ownedLayer && ownedLayer->type() == QgsMapLayer::VectorLayer )
|
||||
{
|
||||
mParentLayer.reset( qobject_cast< QgsVectorLayer * >( ownedLayer.release() ) );
|
||||
layer = mParentLayer.get();
|
||||
}
|
||||
else
|
||||
{
|
||||
// don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
|
||||
}
|
||||
|
||||
if ( mFieldExpWidget )
|
||||
mFieldExpWidget->setLayer( layer );
|
||||
else if ( mExpLineEdit )
|
||||
mExpLineEdit->setLayer( layer );
|
||||
}
|
||||
|
||||
void QgsProcessingExpressionWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
|
||||
{
|
||||
const QString v = QgsProcessingParameters::parameterAsString( parameterDefinition(), value, context );
|
||||
if ( mFieldExpWidget )
|
||||
mFieldExpWidget->setExpression( v );
|
||||
else if ( mExpLineEdit )
|
||||
mExpLineEdit->setExpression( v );
|
||||
}
|
||||
|
||||
QVariant QgsProcessingExpressionWidgetWrapper::widgetValue() const
|
||||
{
|
||||
if ( mFieldExpWidget )
|
||||
return mFieldExpWidget->expression();
|
||||
else if ( mExpLineEdit )
|
||||
return mExpLineEdit->expression();
|
||||
else
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
QStringList QgsProcessingExpressionWidgetWrapper::compatibleParameterTypes() const
|
||||
{
|
||||
return QStringList()
|
||||
<< QgsProcessingParameterExpression::typeName()
|
||||
<< QgsProcessingParameterString::typeName()
|
||||
<< QgsProcessingParameterNumber::typeName()
|
||||
<< QgsProcessingParameterDistance::typeName();
|
||||
}
|
||||
|
||||
QStringList QgsProcessingExpressionWidgetWrapper::compatibleOutputTypes() const
|
||||
{
|
||||
return QStringList()
|
||||
<< QgsProcessingOutputString::typeName()
|
||||
<< QgsProcessingOutputNumber::typeName();
|
||||
}
|
||||
|
||||
QList<int> QgsProcessingExpressionWidgetWrapper::compatibleDataTypes() const
|
||||
{
|
||||
return QList< int >();
|
||||
}
|
||||
|
||||
QString QgsProcessingExpressionWidgetWrapper::modelerExpressionFormatString() const
|
||||
{
|
||||
return tr( "string representation of an expression" );
|
||||
}
|
||||
|
||||
const QgsVectorLayer *QgsProcessingExpressionWidgetWrapper::linkedVectorLayer() const
|
||||
{
|
||||
if ( mFieldExpWidget && mFieldExpWidget->layer() )
|
||||
return mFieldExpWidget->layer();
|
||||
|
||||
return QgsAbstractProcessingParameterWidgetWrapper::linkedVectorLayer();
|
||||
}
|
||||
|
||||
QString QgsProcessingExpressionWidgetWrapper::parameterType() const
|
||||
{
|
||||
return QgsProcessingParameterExpression::typeName();
|
||||
}
|
||||
|
||||
QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingExpressionWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type )
|
||||
{
|
||||
return new QgsProcessingExpressionWidgetWrapper( parameter, type );
|
||||
}
|
||||
|
||||
|
||||
///@endcond PRIVATE
|
||||
|
||||
|
@ -32,6 +32,8 @@ class QgsDoubleSpinBox;
|
||||
class QgsAuthConfigSelect;
|
||||
class QgsProcessingMatrixParameterPanel;
|
||||
class QgsFileWidget;
|
||||
class QgsFieldExpressionWidget;
|
||||
class QgsExpressionLineEdit;
|
||||
|
||||
///@cond PRIVATE
|
||||
|
||||
@ -360,6 +362,45 @@ class GUI_EXPORT QgsProcessingFileWidgetWrapper : public QgsAbstractProcessingPa
|
||||
friend class TestProcessingGui;
|
||||
};
|
||||
|
||||
class GUI_EXPORT QgsProcessingExpressionWidgetWrapper : public QgsAbstractProcessingParameterWidgetWrapper, public QgsProcessingParameterWidgetFactoryInterface
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
QgsProcessingExpressionWidgetWrapper( const QgsProcessingParameterDefinition *parameter = nullptr,
|
||||
QgsProcessingGui::WidgetType type = QgsProcessingGui::Standard, QWidget *parent = nullptr );
|
||||
|
||||
// QgsProcessingParameterWidgetFactoryInterface
|
||||
QString parameterType() const override;
|
||||
QgsAbstractProcessingParameterWidgetWrapper *createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type ) override;
|
||||
|
||||
// QgsProcessingParameterWidgetWrapper interface
|
||||
QWidget *createWidget() override SIP_FACTORY;
|
||||
void postInitialize( const QList< QgsAbstractProcessingParameterWidgetWrapper * > &wrappers ) override;
|
||||
public slots:
|
||||
void setParentLayerWrapperValue( const QgsAbstractProcessingParameterWidgetWrapper *parentWrapper );
|
||||
protected:
|
||||
|
||||
void setWidgetValue( const QVariant &value, QgsProcessingContext &context ) override;
|
||||
QVariant widgetValue() const override;
|
||||
|
||||
QStringList compatibleParameterTypes() const override;
|
||||
|
||||
QStringList compatibleOutputTypes() const override;
|
||||
|
||||
QList< int > compatibleDataTypes() const override;
|
||||
QString modelerExpressionFormatString() const override;
|
||||
const QgsVectorLayer *linkedVectorLayer() const override;
|
||||
private:
|
||||
|
||||
QgsFieldExpressionWidget *mFieldExpWidget = nullptr;
|
||||
QgsExpressionLineEdit *mExpLineEdit = nullptr;
|
||||
std::unique_ptr< QgsVectorLayer > mParentLayer;
|
||||
|
||||
friend class TestProcessingGui;
|
||||
};
|
||||
|
||||
///@endcond PRIVATE
|
||||
|
||||
#endif // QGSPROCESSINGWIDGETWRAPPERIMPL_H
|
||||
|
@ -47,6 +47,8 @@
|
||||
#include "qgsprocessingmatrixparameterdialog.h"
|
||||
#include "models/qgsprocessingmodelalgorithm.h"
|
||||
#include "qgsfilewidget.h"
|
||||
#include "qgsexpressionlineedit.h"
|
||||
#include "qgsfieldexpressionwidget.h"
|
||||
|
||||
class TestParamType : public QgsProcessingParameterDefinition
|
||||
{
|
||||
@ -160,6 +162,7 @@ class TestProcessingGui : public QObject
|
||||
void testRangeWrapper();
|
||||
void testMatrixDialog();
|
||||
void testMatrixWrapper();
|
||||
void testExpressionWrapper();
|
||||
|
||||
private:
|
||||
|
||||
@ -1836,6 +1839,118 @@ void TestProcessingGui::testMatrixWrapper()
|
||||
testWrapper( QgsProcessingGui::Modeler );
|
||||
}
|
||||
|
||||
void TestProcessingGui::testExpressionWrapper()
|
||||
{
|
||||
const QgsProcessingAlgorithm *centroidAlg = QgsApplication::processingRegistry()->algorithmById( QStringLiteral( "native:centroids" ) );
|
||||
const QgsProcessingParameterDefinition *layerDef = centroidAlg->parameterDefinition( QStringLiteral( "INPUT" ) );
|
||||
|
||||
auto testWrapper = [layerDef]( QgsProcessingGui::WidgetType type )
|
||||
{
|
||||
QgsProcessingParameterExpression param( QStringLiteral( "expression" ), QStringLiteral( "expression" ) );
|
||||
|
||||
QgsProcessingExpressionWidgetWrapper wrapper( ¶m, type );
|
||||
|
||||
QgsProcessingContext context;
|
||||
QWidget *w = wrapper.createWrappedWidget( context );
|
||||
|
||||
QSignalSpy spy( &wrapper, &QgsProcessingExpressionWidgetWrapper::widgetValueHasChanged );
|
||||
wrapper.setWidgetValue( QStringLiteral( "1+2" ), context );
|
||||
QCOMPARE( spy.count(), 1 );
|
||||
QCOMPARE( wrapper.widgetValue().toString(), QStringLiteral( "1+2" ) );
|
||||
QCOMPARE( static_cast< QgsExpressionLineEdit * >( wrapper.wrappedWidget() )->expression(), QStringLiteral( "1+2" ) );
|
||||
wrapper.setWidgetValue( QString(), context );
|
||||
QCOMPARE( spy.count(), 2 );
|
||||
QVERIFY( wrapper.widgetValue().toString().isEmpty() );
|
||||
QVERIFY( static_cast< QgsExpressionLineEdit * >( wrapper.wrappedWidget() )->expression().isEmpty() );
|
||||
|
||||
QLabel *l = wrapper.createWrappedLabel();
|
||||
if ( wrapper.type() != QgsProcessingGui::Batch )
|
||||
{
|
||||
QVERIFY( l );
|
||||
QCOMPARE( l->text(), QStringLiteral( "expression" ) );
|
||||
QCOMPARE( l->toolTip(), param.toolTip() );
|
||||
delete l;
|
||||
}
|
||||
else
|
||||
{
|
||||
QVERIFY( !l );
|
||||
}
|
||||
|
||||
// check signal
|
||||
static_cast< QgsExpressionLineEdit * >( wrapper.wrappedWidget() )->setExpression( QStringLiteral( "3+4" ) );
|
||||
QCOMPARE( spy.count(), 3 );
|
||||
|
||||
delete w;
|
||||
|
||||
// with layer
|
||||
param.setParentLayerParameterName( QStringLiteral( "other" ) );
|
||||
QgsProcessingExpressionWidgetWrapper wrapper2( ¶m, type );
|
||||
w = wrapper2.createWrappedWidget( context );
|
||||
|
||||
QSignalSpy spy2( &wrapper2, &QgsProcessingExpressionWidgetWrapper::widgetValueHasChanged );
|
||||
wrapper2.setWidgetValue( QStringLiteral( "11+12" ), context );
|
||||
QCOMPARE( spy2.count(), 1 );
|
||||
QCOMPARE( wrapper2.widgetValue().toString(), QStringLiteral( "11+12" ) );
|
||||
QCOMPARE( static_cast< QgsFieldExpressionWidget * >( wrapper2.wrappedWidget() )->expression(), QStringLiteral( "11+12" ) );
|
||||
|
||||
wrapper2.setWidgetValue( QString(), context );
|
||||
QCOMPARE( spy2.count(), 2 );
|
||||
QVERIFY( wrapper2.widgetValue().toString().isEmpty() );
|
||||
QVERIFY( static_cast< QgsFieldExpressionWidget * >( wrapper2.wrappedWidget() )->expression().isEmpty() );
|
||||
|
||||
static_cast< QgsFieldExpressionWidget * >( wrapper2.wrappedWidget() )->setExpression( QStringLiteral( "3+4" ) );
|
||||
QCOMPARE( spy2.count(), 3 );
|
||||
|
||||
TestLayerWrapper layerWrapper( layerDef );
|
||||
QgsProject p;
|
||||
QgsVectorLayer *vl = new QgsVectorLayer( QStringLiteral( "LineString" ), QStringLiteral( "x" ), QStringLiteral( "memory" ) );
|
||||
p.addMapLayer( vl );
|
||||
|
||||
QVERIFY( !wrapper2.mFieldExpWidget->layer() );
|
||||
layerWrapper.setWidgetValue( QVariant::fromValue( vl ), context );
|
||||
wrapper2.setParentLayerWrapperValue( &layerWrapper );
|
||||
QCOMPARE( wrapper2.mFieldExpWidget->layer(), vl );
|
||||
|
||||
// should not be owned by wrapper
|
||||
QVERIFY( !wrapper2.mParentLayer.get() );
|
||||
layerWrapper.setWidgetValue( QVariant(), context );
|
||||
wrapper2.setParentLayerWrapperValue( &layerWrapper );
|
||||
QVERIFY( !wrapper2.mFieldExpWidget->layer() );
|
||||
|
||||
layerWrapper.setWidgetValue( vl->id(), context );
|
||||
wrapper2.setParentLayerWrapperValue( &layerWrapper );
|
||||
QVERIFY( !wrapper2.mFieldExpWidget->layer() );
|
||||
QVERIFY( !wrapper2.mParentLayer.get() );
|
||||
|
||||
// with project layer
|
||||
context.setProject( &p );
|
||||
TestProcessingContextGenerator generator( context );
|
||||
wrapper2.registerProcessingContextGenerator( &generator );
|
||||
|
||||
layerWrapper.setWidgetValue( vl->id(), context );
|
||||
wrapper2.setParentLayerWrapperValue( &layerWrapper );
|
||||
QCOMPARE( wrapper2.mFieldExpWidget->layer(), vl );
|
||||
QVERIFY( !wrapper2.mParentLayer.get() );
|
||||
|
||||
// non-project layer
|
||||
QString pointFileName = TEST_DATA_DIR + QStringLiteral( "/points.shp" );
|
||||
layerWrapper.setWidgetValue( pointFileName, context );
|
||||
wrapper2.setParentLayerWrapperValue( &layerWrapper );
|
||||
QCOMPARE( wrapper2.mFieldExpWidget->layer()->publicSource(), pointFileName );
|
||||
// must be owned by wrapper, or layer may be deleted while still required by wrapper
|
||||
QCOMPARE( wrapper2.mParentLayer->publicSource(), pointFileName );
|
||||
};
|
||||
|
||||
// standard wrapper
|
||||
testWrapper( QgsProcessingGui::Standard );
|
||||
|
||||
// batch wrapper
|
||||
testWrapper( QgsProcessingGui::Batch );
|
||||
|
||||
// modeler wrapper
|
||||
testWrapper( QgsProcessingGui::Modeler );
|
||||
}
|
||||
|
||||
void TestProcessingGui::cleanupTempDir()
|
||||
{
|
||||
QDir tmpDir = QDir( mTempDir );
|
||||
|
Loading…
x
Reference in New Issue
Block a user