Skip to content

Commit

Permalink
Correctly associate layer with dynamic properties
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Sep 4, 2018
1 parent 8082497 commit e76b761
Show file tree
Hide file tree
Showing 7 changed files with 310 additions and 7 deletions.
Expand Up @@ -10,6 +10,32 @@



class QgsProcessingContextGenerator
{
%Docstring

An interface for objects which can create Processing contexts.

.. versionadded:: 3.4
%End

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

virtual QgsProcessingContext *processingContext() = 0;
%Docstring
This method needs to be reimplemented in all classes which implement this interface
and return a Processing context.

Note that ownership of the context is not transferred - it is intended that subclasses
return a pointer to a context which they have already created and own.
%End

virtual ~QgsProcessingContextGenerator();
};

class QgsAbstractProcessingParameterWidgetWrapper : QObject
{
%Docstring
Expand Down Expand Up @@ -107,12 +133,26 @@ current value is associated with.
Returns the current value of the parameter.

.. seealso:: :py:func:`setParameterValue`
%End

void registerProcessingContextGenerator( QgsProcessingContextGenerator *generator );
%Docstring
Register a Processing context generator class that will be used to retrieve
a Processing context for the wrapper when required.
%End

virtual void postInitialize( const QList< QgsAbstractProcessingParameterWidgetWrapper * > &wrappers );
%Docstring
Called after all wrappers have been created within a particular dialog or context,
allowing the wrapper to connect to the wrappers of other, related parameters.
%End

signals:


void widgetValueHasChanged( QgsAbstractProcessingParameterWidgetWrapper *wrapper );
%Docstring
Emitted whenever the parameter value (as defined by the wrapped widget) is changed.
%End

protected:
Expand Down
22 changes: 17 additions & 5 deletions python/plugins/processing/gui/ParametersPanel.py
Expand Up @@ -42,6 +42,7 @@
QgsProcessingParameterFeatureSink,
QgsProcessingParameterVectorDestination,
QgsProject)
from qgis.gui import QgsProcessingContextGenerator

from qgis.PyQt import uic
from qgis.PyQt.QtCore import QCoreApplication, Qt
Expand Down Expand Up @@ -83,6 +84,19 @@ def __init__(self, parent, alg):
self.dependentItems = {}
self.iterateButtons = {}

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)

self.initWidgets()

QgsProject.instance().layerWasAdded.connect(self.layerRegistryChanged)
Expand All @@ -102,8 +116,6 @@ def formatParameterTooltip(self, parameter):
)

def initWidgets(self):
context = createContext()

# If there are advanced parameters — show corresponding groupbox
for param in self.alg.parameterDefinitions():
if param.flags() & QgsProcessingParameterDefinition.FlagAdvanced:
Expand All @@ -121,7 +133,8 @@ def initWidgets(self):
self.wrappers[param.name()] = wrapper
is_python_wrapper = issubclass(wrapper.__class__, WidgetWrapper)
if not is_python_wrapper:
widget = wrapper.createWrappedWidget(context)
widget = wrapper.createWrappedWidget(self.processing_context)
wrapper.registerProcessingContextGenerator(self.context_generator)
else:
widget = wrapper.widget

Expand Down Expand Up @@ -203,7 +216,6 @@ def skipOutputChanged(checkbox, skipped):
wrapper.postInitialize(list(self.wrappers.values()))

def setParameters(self, parameters):
context = createContext()
for param in self.alg.parameterDefinitions():
if param.flags() & QgsProcessingParameterDefinition.FlagHidden:
continue
Expand All @@ -215,7 +227,7 @@ def setParameters(self, parameters):
value = parameters[param.name()]

wrapper = self.wrappers[param.name()]
wrapper.setParameterValue(value, context)
wrapper.setParameterValue(value, self.processing_context)
else:
dest_widget = self.outputWidgets[param.name()]
dest_widget.setValue(parameters[param.name()])
Expand Down
2 changes: 1 addition & 1 deletion python/plugins/processing/gui/wrappers.py
Expand Up @@ -148,13 +148,13 @@ def getExtendedLayerName(layer):


class WidgetWrapper(QgsAbstractProcessingParameterWidgetWrapper):
widgetValueHasChanged = pyqtSignal(object)

NOT_SET_OPTION = '~~~~!!!!NOT SET!!!!~~~~~~~'

def __init__(self, param, dialog, row=0, col=0, **kwargs):
self.dialogType = dialogTypes.get(dialog.__class__.__name__, QgsProcessingGui.Standard)
super().__init__(param, self.dialogType)

self.param = param
self.dialog = dialog
self.row = row
Expand Down
80 changes: 79 additions & 1 deletion src/gui/processing/qgsprocessingwidgetwrapper.cpp
Expand Up @@ -94,6 +94,9 @@ void QgsAbstractProcessingParameterWidgetWrapper::setParameterValue( const QVari
}
else
{
if ( mPropertyButton )
mPropertyButton->setToProperty( QgsProperty() );

setWidgetValue( value, context );
}
}
Expand All @@ -106,6 +109,11 @@ QVariant QgsAbstractProcessingParameterWidgetWrapper::parameterValue() const
return widgetValue();
}

void QgsAbstractProcessingParameterWidgetWrapper::registerProcessingContextGenerator( QgsProcessingContextGenerator *generator )
{
mProcessingContextGenerator = generator;
}

QLabel *QgsAbstractProcessingParameterWidgetWrapper::createLabel()
{
switch ( mType )
Expand All @@ -124,9 +132,79 @@ QLabel *QgsAbstractProcessingParameterWidgetWrapper::createLabel()
return nullptr;
}

void QgsAbstractProcessingParameterWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> & )
void QgsAbstractProcessingParameterWidgetWrapper::postInitialize( const QList<QgsAbstractProcessingParameterWidgetWrapper *> &wrappers )
{
switch ( mType )
{
case QgsProcessingGui::Batch:
case QgsProcessingGui::Standard:
{
if ( parameterDefinition()->isDynamic() )
{
for ( const QgsAbstractProcessingParameterWidgetWrapper *wrapper : wrappers )
{
if ( wrapper->parameterDefinition()->name() == parameterDefinition()->dynamicLayerParameterName() )
{
setDynamicParentLayerParameter( wrapper->parameterValue() );
connect( wrapper, &QgsAbstractProcessingParameterWidgetWrapper::widgetValueHasChanged, this, &QgsAbstractProcessingParameterWidgetWrapper::parentLayerChanged );
break;
}
}
}
break;
}

case QgsProcessingGui::Modeler:
break;
}
}

void QgsAbstractProcessingParameterWidgetWrapper::parentLayerChanged( QgsAbstractProcessingParameterWidgetWrapper *wrapper )
{
if ( wrapper )
{
setDynamicParentLayerParameter( wrapper->parameterValue() );
}
}

void QgsAbstractProcessingParameterWidgetWrapper::setDynamicParentLayerParameter( const QVariant &value )
{
if ( mPropertyButton )
{
// 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( parameterDefinition(), value, *context );
if ( !layer )
{
mPropertyButton->setVectorLayer( 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 )
{
mDynamicLayer.reset( qobject_cast< QgsVectorLayer * >( ownedLayer.release() ) );
layer = mDynamicLayer.get();
}
else
{
// don't need ownership of this layer - it wasn't owned by context (so e.g. is owned by the project)
}

mPropertyButton->setVectorLayer( layer );
}
}

QgsProcessingModelerParameterWidget *QgsProcessingParameterWidgetFactoryInterface::createModelerWidgetWrapper( QgsProcessingModelAlgorithm *model, const QString &childId, const QgsProcessingParameterDefinition *parameter, const QgsProcessingContext &context )
Expand Down
53 changes: 53 additions & 0 deletions src/gui/processing/qgsprocessingwidgetwrapper.h
Expand Up @@ -21,16 +21,43 @@
#include <QObject>
#include <QWidget>
#include <QPointer>
#include <memory>
#include "qgis_gui.h"
#include "qgis_sip.h"
#include "qgsprocessinggui.h"
#include "qgsvectorlayer.h"

class QgsProcessingParameterDefinition;
class QgsProcessingContext;
class QgsProcessingModelerParameterWidget;
class QgsProcessingModelAlgorithm;
class QLabel;
class QgsPropertyOverrideButton;
class QgsVectorLayer;

/**
* \class QgsProcessingContextGenerator
*
* An interface for objects which can create Processing contexts.
*
* \ingroup gui
* \since QGIS 3.4
*/
class GUI_EXPORT QgsProcessingContextGenerator
{
public:

/**
* This method needs to be reimplemented in all classes which implement this interface
* and return a Processing context.
*
* Note that ownership of the context is not transferred - it is intended that subclasses
* return a pointer to a context which they have already created and own.
*/
virtual QgsProcessingContext *processingContext() = 0;

virtual ~QgsProcessingContextGenerator() = default;
};

/**
* \class QgsAbstractProcessingParameterWidgetWrapper
Expand Down Expand Up @@ -129,12 +156,28 @@ class GUI_EXPORT QgsAbstractProcessingParameterWidgetWrapper : public QObject
*/
QVariant parameterValue() const;

/**
* Register a Processing context generator class that will be used to retrieve
* a Processing context for the wrapper when required.
*/
void registerProcessingContextGenerator( QgsProcessingContextGenerator *generator );

/**
* Called after all wrappers have been created within a particular dialog or context,
* allowing the wrapper to connect to the wrappers of other, related parameters.
*/
virtual void postInitialize( const QList< QgsAbstractProcessingParameterWidgetWrapper * > &wrappers );

signals:

// TODO QGIS 4.0 - remove wrapper parameter - this is kept for compatibility with 3.x API,
// yet can easily be retrieved by checking the sender()

/**
* Emitted whenever the parameter value (as defined by the wrapped widget) is changed.
*/
void widgetValueHasChanged( QgsAbstractProcessingParameterWidgetWrapper *wrapper );

protected:

/**
Expand Down Expand Up @@ -175,14 +218,24 @@ class GUI_EXPORT QgsAbstractProcessingParameterWidgetWrapper : public QObject
*/
virtual QVariant widgetValue() const = 0;

private slots:

void parentLayerChanged( QgsAbstractProcessingParameterWidgetWrapper *wrapper );

private:

QgsProcessingGui::WidgetType mType = QgsProcessingGui::Standard;
const QgsProcessingParameterDefinition *mParameterDefinition = nullptr;
QgsProcessingContextGenerator *mProcessingContextGenerator = nullptr;

void setDynamicParentLayerParameter( const QVariant &value );

QPointer< QWidget > mWidget;
QPointer< QgsPropertyOverrideButton > mPropertyButton;
QPointer< QLabel > mLabel;
std::unique_ptr< QgsVectorLayer > mDynamicLayer;

friend class TestProcessingGui;

};

Expand Down
11 changes: 11 additions & 0 deletions src/gui/processing/qgsprocessingwidgetwrapperimpl.cpp
Expand Up @@ -46,6 +46,11 @@ QWidget *QgsProcessingBooleanWidgetWrapper::createWidget()

mCheckBox = new QCheckBox( description );
mCheckBox->setToolTip( parameterDefinition()->toolTip() );

connect( mCheckBox, &QCheckBox::toggled, this, [ = ]
{
emit widgetValueHasChanged( this );
} );
return mCheckBox;
};

Expand All @@ -56,6 +61,12 @@ QWidget *QgsProcessingBooleanWidgetWrapper::createWidget()
mComboBox->addItem( tr( "Yes" ), true );
mComboBox->addItem( tr( "No" ), false );
mComboBox->setToolTip( parameterDefinition()->toolTip() );

connect( mComboBox, qgis::overload< int>::of( &QComboBox::currentIndexChanged ), this, [ = ]
{
emit widgetValueHasChanged( this );
} );

return mComboBox;
}
}
Expand Down

0 comments on commit e76b761

Please sign in to comment.