Skip to content
Permalink
Browse files

[processing] Fix and improve how in place algorithm parameter widget

handles the input layer by creating a new QgsProcessingHiddenWidgetWrapper
wrapper subclass

Fixes #37424
  • Loading branch information
nyalldawson committed Jul 9, 2020
1 parent a98f914 commit 1219f553787870d3ed285e8a30907f580af37de0
@@ -588,6 +588,53 @@ and should give helpful text to users to indicate the expected results from the
This is purely a text format and no expression validation is made against it.
%End

};

class QgsProcessingHiddenWidgetWrapper: QgsAbstractProcessingParameterWidgetWrapper
{
%Docstring

An widget wrapper for hidden widgets.

The hidden widget wrapper allows for creation of a widget wrapper which does not provide
a graphical widget, yet still implements the :py:class:`QgsAbstractProcessingParameterWidgetWrapper`
interface.

.. versionadded:: 3.14
%End

%TypeHeaderCode
#include "qgsprocessingwidgetwrapper.h"
%End
public:

QgsProcessingHiddenWidgetWrapper( const QgsProcessingParameterDefinition *parameter = 0,
QgsProcessingGui::WidgetType type = QgsProcessingGui::Standard,
QObject *parent /TransferThis/ = 0 );
%Docstring
Constructor for QgsProcessingHiddenWidgetWrapper, for the specified
``parameter`` definition and dialog ``type``.
%End

virtual void setWidgetValue( const QVariant &value, QgsProcessingContext &context );

virtual QVariant widgetValue() const;


virtual const QgsVectorLayer *linkedVectorLayer() const;


void setLinkedVectorLayer( const QgsVectorLayer *layer );
%Docstring
Sets the vector layer linked to the wrapper.
%End

protected:
virtual QWidget *createWidget();

virtual QLabel *createLabel();


};

/************************************************************************
@@ -72,6 +72,8 @@ def __init__(self, alg, in_place=False, parent=None):
self.buttonBox().addButton(self.runAsBatchButton,
QDialogButtonBox.ResetRole) # reset role to ensure left alignment
else:
self.mainWidget().setParameters({'INPUT': self.active_layer})

self.runAsBatchButton = None
has_selection = self.active_layer and (self.active_layer.selectedFeatureCount() > 0)
self.buttonBox().button(QDialogButtonBox.Ok).setText(
@@ -84,8 +86,7 @@ def __init__(self, alg, in_place=False, parent=None):
self.updateRunButtonVisibility()

def getParametersPanel(self, alg, parent):
panel = ParametersPanel(parent, alg, self.in_place)
panel.active_layer = self.active_layer
panel = ParametersPanel(parent, alg, self.in_place, self.active_layer)
return panel

def runAsBatch(self):
@@ -35,7 +35,8 @@
QgsProcessingParametersWidget,
QgsGui,
QgsProcessingGui,
QgsProcessingParametersGenerator)
QgsProcessingParametersGenerator,
QgsProcessingHiddenWidgetWrapper)
from qgis.utils import iface

from processing.gui.wrappers import WidgetWrapperFactory, WidgetWrapper
@@ -45,10 +46,10 @@

class ParametersPanel(QgsProcessingParametersWidget):

def __init__(self, parent, alg, in_place=False):
def __init__(self, parent, alg, in_place=False, active_layer=None):
super().__init__(alg, parent)
self.in_place = in_place
self.active_layer = None
self.active_layer = active_layer

self.wrappers = {}

@@ -101,6 +102,15 @@ def initWidgets(self):
if param.isDestination():
continue
else:
if self.in_place and param.name() in ('INPUT', 'OUTPUT'):
# don't show the input/output parameter widgets in in-place mode
# we still need to CREATE them, because other wrappers may need to interact
# with them (e.g. those parameters which need the input layer for field
# selections/crs properties/etc)
self.wrappers[param.name()] = QgsProcessingHiddenWidgetWrapper(param, QgsProcessingGui.Standard, self)
self.wrappers[param.name()].setLinkedVectorLayer(self.active_layer)
continue

wrapper = WidgetWrapperFactory.create_wrapper(param, self.parent())
wrapper.setWidgetContext(widget_context)
wrapper.registerProcessingContextGenerator(self.context_generator)
@@ -119,13 +129,6 @@ def initWidgets(self):
else:
widget = wrapper.widget

if self.in_place and param.name() in ('INPUT', 'OUTPUT'):
# don't show the input/output parameter widgets in in-place mode
# we still need to CREATE them, because other wrappers may need to interact
# with them (e.g. those parameters which need the input layer for field
# selections/crs properties/etc)
continue

if widget is not None:
if is_python_wrapper:
widget.setToolTip(param.toolTip())
@@ -200,11 +203,6 @@ def createProcessingParameters(self):
if param.flags() & QgsProcessingParameterDefinition.FlagHidden:
continue
if not param.isDestination():

if self.in_place and param.name() == 'INPUT':
parameters[param.name()] = self.active_layer
continue

try:
wrapper = self.wrappers[param.name()]
except KeyError:
@@ -219,7 +217,7 @@ def createProcessingParameters(self):
else:
widget = wrapper.wrappedWidget()

if widget is None:
if not isinstance(wrapper, QgsProcessingHiddenWidgetWrapper) and widget is None:
continue

value = wrapper.parameterValue()
@@ -435,3 +435,44 @@ QgsExpressionContext QgsProcessingGuiUtils::createExpressionContext( QgsProcessi
return c;
}
///@endcond

QgsProcessingHiddenWidgetWrapper::QgsProcessingHiddenWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QObject *parent )
: QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
{

}

void QgsProcessingHiddenWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext & )
{
if ( mValue == value )
return;

mValue = value;
emit widgetValueHasChanged( this );
}

QVariant QgsProcessingHiddenWidgetWrapper::widgetValue() const
{
return mValue;
}

const QgsVectorLayer *QgsProcessingHiddenWidgetWrapper::linkedVectorLayer() const
{
return mLayer;
}

void QgsProcessingHiddenWidgetWrapper::setLinkedVectorLayer( const QgsVectorLayer *layer )
{
mLayer = layer;
}

QWidget *QgsProcessingHiddenWidgetWrapper::createWidget()
{
return nullptr;

}

QLabel *QgsProcessingHiddenWidgetWrapper::createLabel()
{
return nullptr;
}
@@ -646,4 +646,49 @@ class GUI_EXPORT QgsProcessingParameterWidgetFactoryInterface

};

/**
* \class QgsProcessingHiddenWidgetWrapper
*
* An widget wrapper for hidden widgets.
*
* The hidden widget wrapper allows for creation of a widget wrapper which does not provide
* a graphical widget, yet still implements the QgsAbstractProcessingParameterWidgetWrapper
* interface.
*
* \ingroup gui
* \since QGIS 3.14
*/
class GUI_EXPORT QgsProcessingHiddenWidgetWrapper: public QgsAbstractProcessingParameterWidgetWrapper
{
public:

/**
* Constructor for QgsProcessingHiddenWidgetWrapper, for the specified
* \a parameter definition and dialog \a type.
*/
QgsProcessingHiddenWidgetWrapper( const QgsProcessingParameterDefinition *parameter = nullptr,
QgsProcessingGui::WidgetType type = QgsProcessingGui::Standard,
QObject *parent SIP_TRANSFERTHIS = nullptr );

void setWidgetValue( const QVariant &value, QgsProcessingContext &context ) override;
QVariant widgetValue() const override;

const QgsVectorLayer *linkedVectorLayer() const override;

/**
* Sets the vector layer linked to the wrapper.
*/
void setLinkedVectorLayer( const QgsVectorLayer *layer );

protected:
QWidget *createWidget() override;
QLabel *createLabel() override;

private:

QVariant mValue;
QPointer < const QgsVectorLayer > mLayer;

};

#endif // QGSPROCESSINGWIDGETWRAPPER_H
@@ -189,6 +189,7 @@ class TestProcessingGui : public QObject
void testWrapperGeneral();
void testWrapperDynamic();
void testModelerWrapper();
void testHiddenWrapper();
void testBooleanWrapper();
void testStringWrapper();
void testFileWrapper();
@@ -784,6 +785,33 @@ void TestProcessingGui::testModelerWrapper()

}

void TestProcessingGui::testHiddenWrapper()
{
TestParamType param( QStringLiteral( "boolean" ), QStringLiteral( "bool" ) );

QgsProcessingHiddenWidgetWrapper wrapper( &param );
QSignalSpy spy( &wrapper, &QgsProcessingHiddenWidgetWrapper::widgetValueHasChanged );

QgsProcessingContext context;
wrapper.setWidgetValue( 1, context );
QCOMPARE( spy.count(), 1 );
QCOMPARE( wrapper.widgetValue().toInt(), 1 );
wrapper.setWidgetValue( 1, context );
QCOMPARE( spy.count(), 1 );
QCOMPARE( wrapper.widgetValue().toInt(), 1 );
wrapper.setWidgetValue( 2, context );
QCOMPARE( spy.count(), 2 );
QCOMPARE( wrapper.widgetValue().toInt(), 2 );

QVERIFY( !wrapper.createWrappedWidget( context ) );
QVERIFY( !wrapper.createWrappedLabel() );

std::unique_ptr< QgsVectorLayer > vl = qgis::make_unique< QgsVectorLayer >( QStringLiteral( "Polygon?crs=epsg:3111&field=pk:int" ), QStringLiteral( "vl" ), QStringLiteral( "memory" ) );
QVERIFY( !wrapper.linkedVectorLayer() );
wrapper.setLinkedVectorLayer( vl.get() );
QCOMPARE( wrapper.linkedVectorLayer(), vl.get() );
}

void TestProcessingGui::testBooleanWrapper()
{
TestParamType param( QStringLiteral( "boolean" ), QStringLiteral( "bool" ) );

0 comments on commit 1219f55

Please sign in to comment.
You can’t perform that action at this time.