Automatically handle adding data defined button for dynamic properties

Also exposes this capability to modeler - so that model algorithms
can use data defined parameters within their child algorithms.

TODO:
- tests
- setting the associated vector layer
This commit is contained in:
Nyall Dawson 2018-08-31 16:36:18 +10:00
parent a717b85e2e
commit 70c1680754
10 changed files with 109 additions and 32 deletions

View File

@ -92,22 +92,21 @@ Returns the current wrapped label, if any.
Returns the parameter definition associated with this wrapper. Returns the parameter definition associated with this wrapper.
%End %End
void setParameterValue( const QVariant &value, const QgsProcessingContext &context );
virtual void setWidgetValue( const QVariant &value, const QgsProcessingContext &context ) = 0;
%Docstring %Docstring
Sets the current ``value`` for the parameter. Sets the current ``value`` for the parameter.
The ``context`` argument is used to specify the wider Processing context which the The ``context`` argument is used to specify the wider Processing context which the
current value is associated with. current value is associated with.
.. seealso:: :py:func:`value` .. seealso:: :py:func:`parameterValue`
%End %End
virtual QVariant value() const = 0; QVariant parameterValue() const;
%Docstring %Docstring
Returns the current value of the parameter. Returns the current value of the parameter.
.. seealso:: :py:func:`setWidgetValue` .. seealso:: :py:func:`setParameterValue`
%End %End
virtual void postInitialize( const QList< QgsAbstractProcessingParameterWidgetWrapper * > &wrappers ); virtual void postInitialize( const QList< QgsAbstractProcessingParameterWidgetWrapper * > &wrappers );
@ -137,6 +136,23 @@ label should be shown for the parameter's widget (i.e. the label is embedded ins
widget itself). widget itself).
.. seealso:: :py:func:`createWidget` .. seealso:: :py:func:`createWidget`
%End
virtual void setWidgetValue( const QVariant &value, const QgsProcessingContext &context ) = 0;
%Docstring
Sets the current ``value`` for the parameter to show in the widget.
The ``context`` argument is used to specify the wider Processing context which the
current value is associated with.
.. seealso:: :py:func:`widgetValue`
%End
virtual QVariant widgetValue() const = 0;
%Docstring
Returns the current value of the parameter.
.. seealso:: :py:func:`setWidgetValue`
%End %End
}; };

View File

@ -110,7 +110,7 @@ class AlgorithmDialog(QgsProcessingAlgorithmDialogBase):
if widget is None: if widget is None:
continue continue
value = wrapper.value() value = wrapper.parameterValue()
parameters[param.name()] = value parameters[param.name()] = value
if not param.checkValueIsAcceptable(value): if not param.checkValueIsAcceptable(value):

View File

@ -219,7 +219,7 @@ class ParametersPanel(BASE, WIDGET):
value = parameters[param.name()] value = parameters[param.name()]
wrapper = self.wrappers[param.name()] wrapper = self.wrappers[param.name()]
wrapper.setWidgetValue(value, context) wrapper.setParameterValue(value, context)
else: else:
dest_widget = self.outputWidgets[param.name()] dest_widget = self.outputWidgets[param.name()]
dest_widget.setValue(parameters[param.name()]) dest_widget.setValue(parameters[param.name()])

View File

@ -202,6 +202,12 @@ class WidgetWrapper(QgsAbstractProcessingParameterWidgetWrapper):
def setValue(self, value): def setValue(self, value):
pass pass
def value(self):
return None
def widgetValue(self):
return self.value()
def setWidgetValue(self, value, context): def setWidgetValue(self, value, context):
self.setValue(value) self.setValue(value)

View File

@ -145,7 +145,7 @@ QgsProcessingModelChildParameterSource QgsProcessingModelerParameterWidget::valu
switch ( mStackedWidget->currentIndex() ) switch ( mStackedWidget->currentIndex() )
{ {
case 0: case 0:
return QgsProcessingModelChildParameterSource::fromStaticValue( mStaticWidgetWrapper->value() ); return QgsProcessingModelChildParameterSource::fromStaticValue( mStaticWidgetWrapper->parameterValue() );
case 1: case 1:
return QgsProcessingModelChildParameterSource::fromExpression( mExpressionWidget->expression() ); return QgsProcessingModelChildParameterSource::fromExpression( mExpressionWidget->expression() );
@ -238,7 +238,7 @@ void QgsProcessingModelerParameterWidget::setSourceType( QgsProcessingModelChild
void QgsProcessingModelerParameterWidget::updateUi() void QgsProcessingModelerParameterWidget::updateUi()
{ {
mStaticWidgetWrapper->setWidgetValue( mStaticValue, mContext ); mStaticWidgetWrapper->setParameterValue( mStaticValue, mContext );
mExpressionWidget->setExpression( mExpression ); mExpressionWidget->setExpression( mExpression );

View File

@ -19,8 +19,9 @@
#include "qgsprocessingwidgetwrapper.h" #include "qgsprocessingwidgetwrapper.h"
#include "qgsprocessingparameters.h" #include "qgsprocessingparameters.h"
#include "qgsprocessingmodelerwidget.h" #include "qgsprocessingmodelerwidget.h"
#include "qgspropertyoverridebutton.h"
#include <QLabel> #include <QLabel>
#include <QHBoxLayout>
QgsAbstractProcessingParameterWidgetWrapper::QgsAbstractProcessingParameterWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QObject *parent ) QgsAbstractProcessingParameterWidgetWrapper::QgsAbstractProcessingParameterWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QObject *parent )
: QObject( parent ) : QObject( parent )
@ -40,9 +41,25 @@ QWidget *QgsAbstractProcessingParameterWidgetWrapper::createWrappedWidget( const
return mWidget; return mWidget;
mWidget = createWidget(); mWidget = createWidget();
QWidget *wrappedWidget = mWidget;
if ( mType != QgsProcessingGui::Batch && mParameterDefinition->isDynamic() )
{
QHBoxLayout *hLayout = new QHBoxLayout();
hLayout->setMargin( 0 );
hLayout->setContentsMargins( 0, 0, 0, 0 );
hLayout->addWidget( mWidget, 1 );
mPropertyButton = new QgsPropertyOverrideButton();
hLayout->addWidget( mPropertyButton );
mPropertyButton->init( 0, QgsProperty(), mParameterDefinition->dynamicPropertyDefinition() );
mPropertyButton->registerEnabledWidget( mWidget, false );
wrappedWidget = new QWidget();
wrappedWidget->setLayout( hLayout );
}
setWidgetValue( mParameterDefinition->defaultValue(), context ); setWidgetValue( mParameterDefinition->defaultValue(), context );
return mWidget; return wrappedWidget;
} }
QLabel *QgsAbstractProcessingParameterWidgetWrapper::createWrappedLabel() QLabel *QgsAbstractProcessingParameterWidgetWrapper::createWrappedLabel()
@ -69,6 +86,26 @@ const QgsProcessingParameterDefinition *QgsAbstractProcessingParameterWidgetWrap
return mParameterDefinition; return mParameterDefinition;
} }
void QgsAbstractProcessingParameterWidgetWrapper::setParameterValue( const QVariant &value, const QgsProcessingContext &context )
{
if ( mPropertyButton && value.canConvert< QgsProperty >() )
{
mPropertyButton->setToProperty( value.value< QgsProperty >() );
}
else
{
setWidgetValue( value, context );
}
}
QVariant QgsAbstractProcessingParameterWidgetWrapper::parameterValue() const
{
if ( mPropertyButton && mPropertyButton->isActive() )
return mPropertyButton->toProperty();
else
return widgetValue();
}
QLabel *QgsAbstractProcessingParameterWidgetWrapper::createLabel() QLabel *QgsAbstractProcessingParameterWidgetWrapper::createLabel()
{ {
switch ( mType ) switch ( mType )

View File

@ -30,6 +30,7 @@ class QgsProcessingContext;
class QgsProcessingModelerParameterWidget; class QgsProcessingModelerParameterWidget;
class QgsProcessingModelAlgorithm; class QgsProcessingModelAlgorithm;
class QLabel; class QLabel;
class QgsPropertyOverrideButton;
/** /**
* \class QgsAbstractProcessingParameterWidgetWrapper * \class QgsAbstractProcessingParameterWidgetWrapper
@ -111,24 +112,22 @@ class GUI_EXPORT QgsAbstractProcessingParameterWidgetWrapper : public QObject
*/ */
const QgsProcessingParameterDefinition *parameterDefinition() const; const QgsProcessingParameterDefinition *parameterDefinition() const;
// TODO QGIS 4.0 - rename to setValue, avoid conflict with Python WidgetWrapper method
/** /**
* Sets the current \a value for the parameter. * Sets the current \a value for the parameter.
* *
* The \a context argument is used to specify the wider Processing context which the * The \a context argument is used to specify the wider Processing context which the
* current value is associated with. * current value is associated with.
* *
* \see value() * \see parameterValue()
*/ */
virtual void setWidgetValue( const QVariant &value, const QgsProcessingContext &context ) = 0; void setParameterValue( const QVariant &value, const QgsProcessingContext &context );
/** /**
* Returns the current value of the parameter. * Returns the current value of the parameter.
* *
* \see setWidgetValue() * \see setParameterValue()
*/ */
virtual QVariant value() const = 0; QVariant parameterValue() const;
/** /**
* Called after all wrappers have been created within a particular dialog or context, * Called after all wrappers have been created within a particular dialog or context,
@ -159,12 +158,30 @@ class GUI_EXPORT QgsAbstractProcessingParameterWidgetWrapper : public QObject
*/ */
virtual QLabel *createLabel() SIP_FACTORY; virtual QLabel *createLabel() SIP_FACTORY;
/**
* Sets the current \a value for the parameter to show in the widget.
*
* The \a context argument is used to specify the wider Processing context which the
* current value is associated with.
*
* \see widgetValue()
*/
virtual void setWidgetValue( const QVariant &value, const QgsProcessingContext &context ) = 0;
/**
* Returns the current value of the parameter.
*
* \see setWidgetValue()
*/
virtual QVariant widgetValue() const = 0;
private: private:
QgsProcessingGui::WidgetType mType = QgsProcessingGui::Standard; QgsProcessingGui::WidgetType mType = QgsProcessingGui::Standard;
const QgsProcessingParameterDefinition *mParameterDefinition = nullptr; const QgsProcessingParameterDefinition *mParameterDefinition = nullptr;
QPointer< QWidget > mWidget; QPointer< QWidget > mWidget;
QPointer< QgsPropertyOverrideButton > mPropertyButton;
QPointer< QLabel > mLabel; QPointer< QLabel > mLabel;
}; };

View File

@ -92,7 +92,7 @@ void QgsProcessingBooleanWidgetWrapper::setWidgetValue( const QVariant &value, c
} }
} }
QVariant QgsProcessingBooleanWidgetWrapper::value() const QVariant QgsProcessingBooleanWidgetWrapper::widgetValue() const
{ {
switch ( type() ) switch ( type() )
{ {

View File

@ -41,12 +41,11 @@ class GUI_EXPORT QgsProcessingBooleanWidgetWrapper : public QgsAbstractProcessin
// QgsProcessingParameterWidgetWrapper interface // QgsProcessingParameterWidgetWrapper interface
QWidget *createWidget() override SIP_FACTORY; QWidget *createWidget() override SIP_FACTORY;
QLabel *createLabel() override SIP_FACTORY; QLabel *createLabel() override SIP_FACTORY;
protected:
void setWidgetValue( const QVariant &value, const QgsProcessingContext &context ) override; void setWidgetValue( const QVariant &value, const QgsProcessingContext &context ) override;
QVariant value() const override; QVariant widgetValue() const override;
protected:
protected:
QStringList compatibleParameterTypes() const override; QStringList compatibleParameterTypes() const override;
@ -58,6 +57,8 @@ class GUI_EXPORT QgsProcessingBooleanWidgetWrapper : public QgsAbstractProcessin
QCheckBox *mCheckBox = nullptr; QCheckBox *mCheckBox = nullptr;
QComboBox *mComboBox = nullptr; QComboBox *mComboBox = nullptr;
friend class TestProcessingGui;
}; };
///@endcond PRIVATE ///@endcond PRIVATE

View File

@ -79,7 +79,7 @@ class TestWidgetWrapper : public QgsAbstractProcessingParameterWidgetWrapper
{ {
} }
QVariant value() const override QVariant widgetValue() const override
{ {
return QVariant(); return QVariant();
} }
@ -286,12 +286,12 @@ void TestProcessingGui::testWrapperGeneral()
param = TestParamType( QStringLiteral( "boolean" ), QStringLiteral( "bool" ), true ); param = TestParamType( QStringLiteral( "boolean" ), QStringLiteral( "bool" ), true );
QgsProcessingBooleanWidgetWrapper trueDefault( &param ); QgsProcessingBooleanWidgetWrapper trueDefault( &param );
w = trueDefault.createWrappedWidget( context ); w = trueDefault.createWrappedWidget( context );
QVERIFY( trueDefault.value().toBool() ); QVERIFY( trueDefault.widgetValue().toBool() );
delete w; delete w;
param = TestParamType( QStringLiteral( "boolean" ), QStringLiteral( "bool" ), false ); param = TestParamType( QStringLiteral( "boolean" ), QStringLiteral( "bool" ), false );
QgsProcessingBooleanWidgetWrapper falseDefault( &param ); QgsProcessingBooleanWidgetWrapper falseDefault( &param );
w = falseDefault.createWrappedWidget( context ); w = falseDefault.createWrappedWidget( context );
QVERIFY( !falseDefault.value().toBool() ); QVERIFY( !falseDefault.widgetValue().toBool() );
delete w; delete w;
} }
@ -406,10 +406,10 @@ void TestProcessingGui::testBooleanWrapper()
QgsProcessingContext context; QgsProcessingContext context;
QWidget *w = wrapper.createWrappedWidget( context ); QWidget *w = wrapper.createWrappedWidget( context );
wrapper.setWidgetValue( true, context ); wrapper.setWidgetValue( true, context );
QVERIFY( wrapper.value().toBool() ); QVERIFY( wrapper.widgetValue().toBool() );
QVERIFY( static_cast< QCheckBox * >( wrapper.wrappedWidget() )->isChecked() ); QVERIFY( static_cast< QCheckBox * >( wrapper.wrappedWidget() )->isChecked() );
wrapper.setWidgetValue( false, context ); wrapper.setWidgetValue( false, context );
QVERIFY( !wrapper.value().toBool() ); QVERIFY( !wrapper.widgetValue().toBool() );
QVERIFY( !static_cast< QCheckBox * >( wrapper.wrappedWidget() )->isChecked() ); QVERIFY( !static_cast< QCheckBox * >( wrapper.wrappedWidget() )->isChecked() );
// should be no label in standard mode // should be no label in standard mode
@ -422,10 +422,10 @@ void TestProcessingGui::testBooleanWrapper()
w = wrapperB.createWrappedWidget( context ); w = wrapperB.createWrappedWidget( context );
wrapperB.setWidgetValue( true, context ); wrapperB.setWidgetValue( true, context );
QVERIFY( wrapperB.value().toBool() ); QVERIFY( wrapperB.widgetValue().toBool() );
QVERIFY( static_cast< QComboBox * >( wrapperB.wrappedWidget() )->currentData().toBool() ); QVERIFY( static_cast< QComboBox * >( wrapperB.wrappedWidget() )->currentData().toBool() );
wrapperB.setWidgetValue( false, context ); wrapperB.setWidgetValue( false, context );
QVERIFY( !wrapperB.value().toBool() ); QVERIFY( !wrapperB.widgetValue().toBool() );
QVERIFY( !static_cast< QComboBox * >( wrapperB.wrappedWidget() )->currentData().toBool() ); QVERIFY( !static_cast< QComboBox * >( wrapperB.wrappedWidget() )->currentData().toBool() );
// should be no label in batch mode // should be no label in batch mode
@ -437,10 +437,10 @@ void TestProcessingGui::testBooleanWrapper()
w = wrapperM.createWrappedWidget( context ); w = wrapperM.createWrappedWidget( context );
wrapperM.setWidgetValue( true, context ); wrapperM.setWidgetValue( true, context );
QVERIFY( wrapperM.value().toBool() ); QVERIFY( wrapperM.widgetValue().toBool() );
QVERIFY( static_cast< QComboBox * >( wrapperM.wrappedWidget() )->currentData().toBool() ); QVERIFY( static_cast< QComboBox * >( wrapperM.wrappedWidget() )->currentData().toBool() );
wrapperM.setWidgetValue( false, context ); wrapperM.setWidgetValue( false, context );
QVERIFY( !wrapperM.value().toBool() ); QVERIFY( !wrapperM.widgetValue().toBool() );
QVERIFY( !static_cast< QComboBox * >( wrapperM.wrappedWidget() )->currentData().toBool() ); QVERIFY( !static_cast< QComboBox * >( wrapperM.wrappedWidget() )->currentData().toBool() );
// should be a label in modeler mode // should be a label in modeler mode