Skip to content

Commit

Permalink
[processing] Initial work on porting base GUI classes to c++
Browse files Browse the repository at this point in the history
- Add abstract base class for Processing widget wrappers to c++
- Add wrapper factory interface to c++
- Make QgsProcessingGuiRegistry also register widget wrapper
factories, and be responsible for creation of new c++
processing widget wrapper instances
- Start on private c++ implementation of boolean widget wrapper,
including unit tests
  • Loading branch information
nyalldawson committed Sep 4, 2018
1 parent bb91170 commit 644ef6a
Show file tree
Hide file tree
Showing 21 changed files with 1,293 additions and 189 deletions.
Expand Up @@ -10,13 +10,15 @@




class QgsProcessingGuiRegistry
{
%Docstring
The QgsProcessingGuiRegistry is a home for widgets for processing
configuration widgets.

QgsProcessingGuiRegistry is not usually directly created, but rather accessed through
:py:func:`QgsGui.processingGuiRegistry()`

.. versionadded:: 3.2
%End

Expand Down Expand Up @@ -55,6 +57,41 @@ next to parameter widgets. Most algorithms do not have a configuration widget
and in this case, None will be returned.

.. versionadded:: 3.2
%End

bool addParameterWidgetFactory( QgsProcessingParameterWidgetFactoryInterface *factory /Transfer/ );
%Docstring
Adds a parameter widget ``factory`` to the registry. Ownership of ``factory`` is transferred to the registry.

Returns true if the factory was successfully added, or false if the factory could not be added. Each
factory must return a unique value for QgsProcessingParameterWidgetFactoryInterface.parameterType(),
and attempting to add a new factory with a duplicate type will result in failure.

.. seealso:: :py:func:`removeParameterWidgetFactory`

.. seealso:: :py:func:`createParameterWidgetWrapper`

.. versionadded:: 3.4
%End

void removeParameterWidgetFactory( QgsProcessingParameterWidgetFactoryInterface *factory );
%Docstring
Removes a parameter widget ``factory`` from the registry. The factory will be deleted.

.. seealso:: :py:func:`addParameterWidgetFactory`

.. versionadded:: 3.4
%End

QgsAbstractProcessingParameterWidgetWrapper *createParameterWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsAbstractProcessingParameterWidgetWrapper::WidgetType type ) /Factory/;
%Docstring
Creates a new parameter widget wrapper for the given ``parameter``. The ``type`` argument
dictates the type of dialog the wrapper should be created for. The caller takes ownership
of the returned wrapper.

If no factory is registered which handles the given ``parameter``, a None will be returned.

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

};
Expand Down
194 changes: 194 additions & 0 deletions python/gui/auto_generated/processing/qgsprocessingwidgetwrapper.sip.in
@@ -0,0 +1,194 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/processing/qgsprocessingwidgetwrapper.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/






class QgsAbstractProcessingParameterWidgetWrapper : QObject
{
%Docstring

A widget wrapper for Processing parameter value widgets.

Widget wrappers are used to create widgets for individual Processing parameters, and
handle retrieving and setting the current value for those parameters.

Widget wrappers can be created for different dialog types, allowing different
appearance and behavior of widgets depending on whether they are being created for
use in a standard algorithm dialog, a batch processing dialog, or a modeler dialog.

Individual widget wrappers are not usually created directly, instead they are
constructed through the central registry, via calls to
QgsGui.processingGuiRegistry()->createParameterWidgetWrapper().

.. versionadded:: 3.4
%End

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

enum WidgetType
{
Standard,
Batch,
Modeler,
};

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

WidgetType type() const;
%Docstring
Returns the dialog type for which widgets and labels will be created by this wrapper.
%End

QWidget *createWrappedWidget( const QgsProcessingContext &context ) /Factory/;
%Docstring
Creates and return a new wrapped widget which allows customization of the parameter's value.

The caller takes ownership of the returned widget. The wrapped widget can be retrieved at a later
stage by calling wrappedWidget().

The supplied ``context`` is used while initializing the widget to the parameter's default value.

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

QLabel *createWrappedLabel() /Factory/;
%Docstring
Creates and returns a new label to accompany widgets created by the wrapper.

The caller takes ownership of the returned label. Some parameter type and dialog type
combinations will return None for this method. If None is returned, then no
label should be shown for the parameter's widget (i.e. the label is embedded inside the
widget itself).

The wrapped label can be retrieved at a later stage by calling wrappedLabel().

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

QWidget *wrappedWidget();
%Docstring
Returns the current wrapped widget, if any.

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

QLabel *wrappedLabel();
%Docstring
Returns the current wrapped label, if any.

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

const QgsProcessingParameterDefinition *parameterDefinition() const;
%Docstring
Returns the parameter definition associated with this wrapper.
%End

virtual void setWidgetValue( const QVariant &value, const QgsProcessingContext &context ) = 0;
%Docstring
Sets the current ``value`` for the parameter.

The ``context`` argument is used to specify the wider Processing context which the
current value is associated with.

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

virtual QVariant value() const = 0;
%Docstring
Returns the current value of the parameter.

.. seealso:: :py:func:`setWidgetValue`
%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

protected:

virtual QWidget *createWidget() = 0 /Factory/;
%Docstring
Creates a new widget which allows customization of the parameter's value.

The caller takes ownership of the returned widget.

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

virtual QLabel *createLabel() /Factory/;
%Docstring
Creates a new label to accompany widgets created by the wrapper.

The caller takes ownership of the returned label. Some parameter type and dialog type
combinations will return None for this method. If None is returned, then no
label should be shown for the parameter's widget (i.e. the label is embedded inside the
widget itself).

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

};


class QgsProcessingParameterWidgetFactoryInterface
{
%Docstring

An interface for Processing widget wrapper factories.

Widget wrapper factories allow creation of QgsAbstractProcessingParameterWidgetWrapper objects.
They are centrally managed by :py:class:`QgsProcessingGuiRegistry`. Usually, individual factories
are not directly utilized, rather the QgsGui.processingGuiRegistry()->createParameterWidgetWrapper()
method is used to create widget wrappers.

.. versionadded:: 3.4
%End

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

virtual ~QgsProcessingParameterWidgetFactoryInterface();

virtual QString parameterType() const = 0;
%Docstring
Returns the type string for the parameter type the factory is associated with.
%End

virtual QgsAbstractProcessingParameterWidgetWrapper *createWidgetWrapper( const QgsProcessingParameterDefinition *parameter,
QgsAbstractProcessingParameterWidgetWrapper::WidgetType type ) = 0 /Factory/;
%Docstring
Creates a new widget wrapper for the specified ``parameter`` definition.

The ``type`` argument indicates the dialog type to create a wrapper for.
%End

};

/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/processing/qgsprocessingwidgetwrapper.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/
1 change: 1 addition & 0 deletions python/gui/gui_auto.sip
Expand Up @@ -319,4 +319,5 @@
%Include auto_generated/processing/qgsprocessingrecentalgorithmlog.sip
%Include auto_generated/processing/qgsprocessingtoolboxmodel.sip
%Include auto_generated/processing/qgsprocessingtoolboxtreeview.sip
%Include auto_generated/processing/qgsprocessingwidgetwrapper.sip
%Include auto_generated/qgsadvanceddigitizingcanvasitem.sip
6 changes: 3 additions & 3 deletions python/plugins/processing/algs/gdal/ui/RasterOptionsWidget.py
Expand Up @@ -43,12 +43,12 @@ def createWidget(self):
options = [(self.dialog.resolveValueDescription(s), s) for s in strings]
for desc, val in options:
widget.addItem(desc, val)
widget.setEditText(self.param.defaultValue() or '')
widget.setEditText(self.parameterDefinition().defaultValue() or '')
return widget
elif self.dialogType == DIALOG_BATCH:
widget = QLineEdit()
if self.param.defaultValue():
widget.setText(self.param.defaultValue())
if self.parameterDefinition().defaultValue():
widget.setText(self.parameterDefinition().defaultValue())
return widget
else:
return QgsRasterFormatSaveOptionsWidget()
Expand Down
Expand Up @@ -513,7 +513,7 @@ def updatePanelEnabledState():

def postInitialize(self, wrappers):
for wrapper in wrappers:
if wrapper.param.name() == self.param.parentLayerParameter():
if wrapper.param.name() == self.parameterDefinition().parentLayerParameter():
if wrapper.value():
self.setLayer(wrapper.value())
wrapper.widgetValueHasChanged.connect(self.parentLayerChanged)
Expand Down
6 changes: 3 additions & 3 deletions python/plugins/processing/algs/qgis/ui/HeatmapWidgets.py
Expand Up @@ -181,13 +181,13 @@ def postInitialize(self, wrappers):
return

for wrapper in wrappers:
if wrapper.param.name() == self.param.parent_layer:
if wrapper.parameterDefinition().name() == self.param.parent_layer:
self.setSource(wrapper.value())
wrapper.widgetValueHasChanged.connect(self.parentLayerChanged)
elif wrapper.param.name() == self.param.radius_param:
elif wrapper.parameterDefinition().name() == self.param.radius_param:
self.setRadius(wrapper.value())
wrapper.widgetValueHasChanged.connect(self.radiusChanged)
elif wrapper.param.name() == self.param.radius_field_param:
elif wrapper.parameterDefinition().name() == self.param.radius_field_param:
self.setSource(wrapper.value())
wrapper.widgetValueHasChanged.connect(self.radiusFieldChanged)

Expand Down
Expand Up @@ -223,30 +223,30 @@ def refresh(self):
for lyr in layers:
for n in range(lyr.bandCount()):
options[lyr.name()] = '{:s}@{:d}'.format(lyr.name(), n + 1)
self.widget.setList(options)
self.widget().setList(options)

def setValue(self, value):
if self.dialogType == DIALOG_STANDARD:
pass # TODO
elif self.dialogType == DIALOG_BATCH:
return self.widget.setText(value)
return self.widget().setText(value)
else:
self.widget.setValue(value)
self.widget().setValue(value)

def value(self):
if self.dialogType in DIALOG_STANDARD:
return self.widget.value()
return self.widget().value()
elif self.dialogType == DIALOG_BATCH:
return self.widget.text()
return self.widget().text()
else:
return self.widget.value()
return self.widget().value()


class LayersListWidgetWrapper(WidgetWrapper):

def createWidget(self):
if self.dialogType == DIALOG_BATCH:
widget = BatchInputSelectionPanel(self.param, self.row, self.col, self.dialog)
widget = BatchInputSelectionPanel(self.parameterDefinition(), self.row, self.col, self.dialog)
widget.valueChanged.connect(lambda: self.widgetValueHasChanged.emit(self))
return widget
else:
Expand All @@ -272,7 +272,7 @@ def value(self):
return self.widget.getText()
else:
options = self._getOptions()
values = [options[i] for i in self.widget.selectedoptions]
if len(values) == 0 and not self.param.flags() & QgsProcessingParameterDefinition.FlagOptional:
values = [options[i] for i in self.widget().selectedoptions]
if len(values) == 0 and not self.parameterDefinition().flags() & QgsProcessingParameterDefinition.FlagOptional:
raise InvalidParameterValue()
return values
23 changes: 16 additions & 7 deletions python/plugins/processing/gui/AlgorithmDialog.py
Expand Up @@ -98,14 +98,23 @@ def getParameterValues(self):
if param.flags() & QgsProcessingParameterDefinition.FlagHidden:
continue
if not param.isDestination():
wrapper = self.mainWidget().wrappers[param.name()]
value = None
if wrapper.widget is not None:
value = wrapper.value()
parameters[param.name()] = value
try:
wrapper = self.mainWidget().wrappers[param.name()]
except KeyError:
continue

try:
widget = wrapper.wrappedWidget()
except AttributeError:
widget = wrapper.widget
if widget is None:
continue

value = wrapper.value()
parameters[param.name()] = value

if not param.checkValueIsAcceptable(value):
raise AlgorithmDialogBase.InvalidParameterValue(param, wrapper.widget)
if not param.checkValueIsAcceptable(value):
raise AlgorithmDialogBase.InvalidParameterValue(param, widget)
else:
dest_project = None
if not param.flags() & QgsProcessingParameterDefinition.FlagHidden and \
Expand Down

0 comments on commit 644ef6a

Please sign in to comment.