Skip to content

Commit

Permalink
[processing] Port enum widget wrapper to new API
Browse files Browse the repository at this point in the history
Fixes:
- enum parameters set to "allow multiple" only allow a single
value selection when used in modeler
- optional enum parameters cannot be set to no value when
used outside of modeler

Fixes #20406
  • Loading branch information
nyalldawson committed Mar 8, 2019
1 parent 160ca69 commit cccf974
Show file tree
Hide file tree
Showing 14 changed files with 1,371 additions and 4 deletions.
@@ -0,0 +1,88 @@
/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/processing/qgsprocessingmultipleselectiondialog.h *
* *
* Do not edit manually ! Edit header and run scripts/sipify.pl again *
************************************************************************/






class QgsProcessingMultipleSelectionDialog : QDialog
{
%Docstring
Dialog for configuration of a matrix (fixed table) parameter.

.. note::

Not stable API

.. versionadded:: 3.6
%End

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

QgsProcessingMultipleSelectionDialog( const QVariantList &availableOptions = QVariantList(),
const QVariantList &selectedOptions = QVariantList(),
QWidget *parent /TransferThis/ = 0, Qt::WindowFlags flags = 0 );
%Docstring
Constructor for QgsProcessingMultipleSelectionDialog.

The ``availableOptions`` list specifies the list of standard known options for the parameter,
whilst the ``selectedOptions`` list specifies which options should be initially selected.

The ``selectedOptions`` list may contain extra options which are not present in ``availableOptions``,
in which case they will be also added as existing options within the dialog.
%End


void setValueFormatter( SIP_PYCALLABLE );
%Docstring
Sets a callback function to use when encountering an invalid geometry and
%End
%MethodCode

Py_BEGIN_ALLOW_THREADS

sipCpp->setValueFormatter( [a0]( const QVariant &v )->QString
{
QString res;
SIP_BLOCK_THREADS
PyObject *s = sipCallMethod( NULL, a0, "D", &v, sipType_QVariant, NULL );
int state;
int sipIsError = 0;
QString *t1 = reinterpret_cast<QString *>( sipConvertToType( s, sipType_QString, 0, SIP_NOT_NONE, &state, &sipIsError ) );
if ( sipIsError == 0 )
{
res = QString( *t1 );
}
sipReleaseType( t1, sipType_QString, state );
SIP_UNBLOCK_THREADS
return res;
} );

Py_END_ALLOW_THREADS
%End


QVariantList selectedOptions() const;
%Docstring
Returns the ordered list of selected options.
%End

};


/************************************************************************
* This file has been generated automatically from *
* *
* src/gui/processing/qgsprocessingmultipleselectiondialog.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 @@ -327,6 +327,7 @@
%Include auto_generated/processing/qgsprocessingalgorithmconfigurationwidget.sip
%Include auto_generated/processing/qgsprocessingalgorithmdialogbase.sip
%Include auto_generated/processing/qgsprocessingmodelerparameterwidget.sip
%Include auto_generated/processing/qgsprocessingmultipleselectiondialog.sip
%Include auto_generated/processing/qgsprocessingrecentalgorithmlog.sip
%Include auto_generated/processing/qgsprocessingtoolboxmodel.sip
%Include auto_generated/processing/qgsprocessingtoolboxtreeview.sip
Expand Down
1 change: 0 additions & 1 deletion python/plugins/processing/algs/qgis/CheckValidity.py
Expand Up @@ -94,7 +94,6 @@ def initAlgorithm(self, config=None):
self.tr('Method'), self.methods, defaultValue=2))
self.parameterDefinition(self.METHOD).setMetadata({
'widget_wrapper': {
'class': 'processing.gui.wrappers.EnumWidgetWrapper',
'useCheckBoxes': True,
'columns': 3}})

Expand Down
1 change: 0 additions & 1 deletion python/plugins/processing/algs/qgis/SpatialJoin.py
Expand Up @@ -107,7 +107,6 @@ def initAlgorithm(self, config=None):
allowMultiple=True, defaultValue=[0])
predicate.setMetadata({
'widget_wrapper': {
'class': 'processing.gui.wrappers.EnumWidgetWrapper',
'useCheckBoxes': True,
'columns': 2}})
self.addParameter(predicate)
Expand Down
1 change: 0 additions & 1 deletion python/plugins/processing/algs/qgis/SpatialJoinSummary.py
Expand Up @@ -124,7 +124,6 @@ def initAlgorithm(self, config=None):
allowMultiple=True, defaultValue=[0])
predicate.setMetadata({
'widget_wrapper': {
'class': 'processing.gui.wrappers.EnumWidgetWrapper',
'useCheckBoxes': True,
'columns': 2}})
self.addParameter(predicate)
Expand Down
1 change: 0 additions & 1 deletion src/analysis/processing/qgsalgorithmextractbylocation.cpp
Expand Up @@ -29,7 +29,6 @@ void QgsLocationBasedAlgorithm::addPredicateParameter()

QVariantMap predicateMetadata;
QVariantMap widgetMetadata;
widgetMetadata.insert( QStringLiteral( "class" ), QStringLiteral( "processing.gui.wrappers.EnumWidgetWrapper" ) );
widgetMetadata.insert( QStringLiteral( "useCheckBoxes" ), true );
widgetMetadata.insert( QStringLiteral( "columns" ), 2 );
predicateMetadata.insert( QStringLiteral( "widget_wrapper" ), widgetMetadata );
Expand Down
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -201,6 +201,7 @@ SET(QGIS_GUI_SRCS
processing/qgsprocessingguiregistry.cpp
processing/qgsprocessingmatrixparameterdialog.cpp
processing/qgsprocessingmodelerparameterwidget.cpp
processing/qgsprocessingmultipleselectiondialog.cpp
processing/qgsprocessingrecentalgorithmlog.cpp
processing/qgsprocessingtoolboxmodel.cpp
processing/qgsprocessingtoolboxtreeview.cpp
Expand Down Expand Up @@ -744,6 +745,7 @@ SET(QGIS_GUI_MOC_HDRS
processing/qgsprocessingconfigurationwidgets.h
processing/qgsprocessingmatrixparameterdialog.h
processing/qgsprocessingmodelerparameterwidget.h
processing/qgsprocessingmultipleselectiondialog.h
processing/qgsprocessingrecentalgorithmlog.h
processing/qgsprocessingtoolboxmodel.h
processing/qgsprocessingtoolboxtreeview.h
Expand Down
1 change: 1 addition & 0 deletions src/gui/processing/qgsprocessingguiregistry.cpp
Expand Up @@ -37,6 +37,7 @@ QgsProcessingGuiRegistry::QgsProcessingGuiRegistry()
addParameterWidgetFactory( new QgsProcessingMatrixWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingFileWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingExpressionWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingEnumWidgetWrapper() );
}

QgsProcessingGuiRegistry::~QgsProcessingGuiRegistry()
Expand Down
153 changes: 153 additions & 0 deletions src/gui/processing/qgsprocessingmultipleselectiondialog.cpp
@@ -0,0 +1,153 @@
/***************************************************************************
qgsprocessingmultipleselectiondialog.cpp
------------------------------------
Date : February 2019
Copyright : (C) 2019 Nyall Dawson
Email : nyall dot dawson at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#include "qgsprocessingmultipleselectiondialog.h"
#include "qgsgui.h"
#include <QStandardItemModel>
#include <QStandardItem>
#include <QPushButton>
#include <QLineEdit>
#include <QToolButton>

///@cond NOT_STABLE

QgsProcessingMultipleSelectionDialog::QgsProcessingMultipleSelectionDialog( const QVariantList &availableOptions,
const QVariantList &selectedOptions,
QWidget *parent, Qt::WindowFlags flags )
: QDialog( parent, flags )
, mValueFormatter( []( const QVariant & v )->QString { return v.toString(); } )
{
setupUi( this );

QgsGui::enableAutoGeometryRestore( this );

mSelectionList->setSelectionBehavior( QAbstractItemView::SelectRows );
mSelectionList->setSelectionMode( QAbstractItemView::ExtendedSelection );
mSelectionList->setDragDropMode( QAbstractItemView::InternalMove );

mButtonSelectAll = new QPushButton( tr( "Select All" ) );
mButtonBox->addButton( mButtonSelectAll, QDialogButtonBox::ActionRole );

mButtonClearSelection = new QPushButton( tr( "Clear Selection" ) );
mButtonBox->addButton( mButtonClearSelection, QDialogButtonBox::ActionRole );

mButtonToggleSelection = new QPushButton( tr( "Toggle Selection" ) );
mButtonBox->addButton( mButtonToggleSelection, QDialogButtonBox::ActionRole );

connect( mButtonSelectAll, &QPushButton::clicked, this, [ = ] { selectAll( true ); } );
connect( mButtonClearSelection, &QPushButton::clicked, this, [ = ] { selectAll( false ); } );
connect( mButtonToggleSelection, &QPushButton::clicked, this, &QgsProcessingMultipleSelectionDialog::toggleSelection );

populateList( availableOptions, selectedOptions );
}

void QgsProcessingMultipleSelectionDialog::setValueFormatter( const std::function<QString( const QVariant & )> &formatter )
{
mValueFormatter = formatter;
// update item text using new formatter
for ( int i = 0; i < mModel->rowCount(); ++i )
{
mModel->item( i )->setText( mValueFormatter( mModel->item( i )->data( Qt::UserRole ) ) );
}
}

QVariantList QgsProcessingMultipleSelectionDialog::selectedOptions() const
{
QVariantList options;
options.reserve( mModel->rowCount() );
for ( int i = 0; i < mModel->rowCount(); ++i )
{
if ( mModel->item( i )->checkState() == Qt::Checked )
options << mModel->item( i )->data( Qt::UserRole );
}
return options;
}

void QgsProcessingMultipleSelectionDialog::selectAll( const bool checked )
{
const QList<QStandardItem *> items = currentItems();
for ( QStandardItem *item : items )
{
item->setCheckState( checked ? Qt::Checked : Qt::Unchecked );
}
}

void QgsProcessingMultipleSelectionDialog::toggleSelection()
{
const QList<QStandardItem *> items = currentItems();
for ( QStandardItem *item : items )
{
item->setCheckState( item->checkState() == Qt::Unchecked ? Qt::Checked : Qt::Unchecked );
}
}

QList<QStandardItem *> QgsProcessingMultipleSelectionDialog::currentItems()
{
QList<QStandardItem *> items;
const QModelIndexList selection = mSelectionList->selectionModel()->selectedIndexes();
if ( selection.size() > 1 )
{
items.reserve( selection.size() );
for ( const QModelIndex &index : selection )
{
items << mModel->itemFromIndex( index );
}
}
else
{
items.reserve( mModel->rowCount() );
for ( int i = 0; i < mModel->rowCount(); ++i )
{
items << mModel->item( i );
}
}
return items;
}

void QgsProcessingMultipleSelectionDialog::populateList( const QVariantList &availableOptions, const QVariantList &selectedOptions )
{
mModel = new QStandardItemModel( this );

QVariantList remainingOptions = availableOptions;

// we add selected options first, keeping the existing order of options
for ( const QVariant &option : selectedOptions )
{
// if isinstance(t, QgsProcessingModelChildParameterSource):
// item = QStandardItem(t.staticValue())
// else:
std::unique_ptr< QStandardItem > item = qgis::make_unique< QStandardItem >( mValueFormatter( option ) );
item->setData( option, Qt::UserRole );
item->setCheckState( Qt::Checked );
item->setCheckable( true );
item->setDropEnabled( false );
mModel->appendRow( item.release() );
remainingOptions.removeAll( option );
}

for ( const QVariant &option : qgis::as_const( remainingOptions ) )
{
std::unique_ptr< QStandardItem > item = qgis::make_unique< QStandardItem >( mValueFormatter( option ) );
item->setData( option, Qt::UserRole );
item->setCheckState( Qt::Unchecked );
item->setCheckable( true );
item->setDropEnabled( false );
mModel->appendRow( item.release() );
}

mSelectionList->setModel( mModel );
}

///@endcond

0 comments on commit cccf974

Please sign in to comment.