[processing] Port enum widget wrapper to new API

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
This commit is contained in:
Nyall Dawson 2019-02-21 15:29:11 +10:00
parent 160ca692b7
commit cccf974211
14 changed files with 1371 additions and 4 deletions

View File

@ -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 *
************************************************************************/

View File

@ -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

View File

@ -94,7 +94,6 @@ class CheckValidity(QgisAlgorithm):
self.tr('Method'), self.methods, defaultValue=2))
self.parameterDefinition(self.METHOD).setMetadata({
'widget_wrapper': {
'class': 'processing.gui.wrappers.EnumWidgetWrapper',
'useCheckBoxes': True,
'columns': 3}})

View File

@ -107,7 +107,6 @@ class SpatialJoin(QgisAlgorithm):
allowMultiple=True, defaultValue=[0])
predicate.setMetadata({
'widget_wrapper': {
'class': 'processing.gui.wrappers.EnumWidgetWrapper',
'useCheckBoxes': True,
'columns': 2}})
self.addParameter(predicate)

View File

@ -124,7 +124,6 @@ class SpatialJoinSummary(QgisAlgorithm):
allowMultiple=True, defaultValue=[0])
predicate.setMetadata({
'widget_wrapper': {
'class': 'processing.gui.wrappers.EnumWidgetWrapper',
'useCheckBoxes': True,
'columns': 2}})
self.addParameter(predicate)

View File

@ -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 );

View File

@ -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
@ -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

View File

@ -37,6 +37,7 @@ QgsProcessingGuiRegistry::QgsProcessingGuiRegistry()
addParameterWidgetFactory( new QgsProcessingMatrixWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingFileWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingExpressionWidgetWrapper() );
addParameterWidgetFactory( new QgsProcessingEnumWidgetWrapper() );
}
QgsProcessingGuiRegistry::~QgsProcessingGuiRegistry()

View File

@ -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

View File

@ -0,0 +1,117 @@
/***************************************************************************
qgsprocessingmultipleselectiondialog.h
----------------------------------
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. *
* *
***************************************************************************/
#ifndef QGSPROCESSINGMULTIPLESELECTIONDIALOG_H
#define QGSPROCESSINGMULTIPLESELECTIONDIALOG_H
#include "qgis.h"
#include "qgis_gui.h"
#include "ui_qgsprocessingmultipleselectiondialogbase.h"
#include "qgsprocessingparameters.h"
class QStandardItemModel;
class QToolButton;
class QStandardItem;
///@cond NOT_STABLE
/**
* \ingroup gui
* \brief Dialog for configuration of a matrix (fixed table) parameter.
* \note Not stable API
* \since QGIS 3.6
*/
class GUI_EXPORT QgsProcessingMultipleSelectionDialog : public QDialog, private Ui::QgsProcessingMultipleSelectionDialogBase
{
Q_OBJECT
public:
/**
* Constructor for QgsProcessingMultipleSelectionDialog.
*
* The \a availableOptions list specifies the list of standard known options for the parameter,
* whilst the \a selectedOptions list specifies which options should be initially selected.
*
* The \a selectedOptions list may contain extra options which are not present in \a availableOptions,
* in which case they will be also added as existing options within the dialog.
*/
QgsProcessingMultipleSelectionDialog( const QVariantList &availableOptions = QVariantList(),
const QVariantList &selectedOptions = QVariantList(),
QWidget *parent SIP_TRANSFERTHIS = nullptr, Qt::WindowFlags flags = nullptr );
/**
* Sets a callback function to use when encountering an invalid geometry and
*/
#ifndef SIP_RUN
void setValueFormatter( const std::function< QString( const QVariant & )> &formatter );
#else
void setValueFormatter( SIP_PYCALLABLE );
% 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
#endif
/**
* Returns the ordered list of selected options.
*/
QVariantList selectedOptions() const;
private slots:
void selectAll( bool checked );
void toggleSelection();
private:
std::function< QString( const QVariant & )> mValueFormatter;
QPushButton *mButtonSelectAll = nullptr;
QPushButton *mButtonClearSelection = nullptr;
QPushButton *mButtonToggleSelection = nullptr;
QStandardItemModel *mModel = nullptr;
QList< QStandardItem * > currentItems();
void populateList( const QVariantList &availableOptions, const QVariantList &selectedOptions );
friend class TestProcessingGui;
};
///@endcond
#endif // QGSPROCESSINGMULTIPLESELECTIONDIALOG_H

View File

@ -29,12 +29,16 @@
#include "qgssettings.h"
#include "qgsexpressionlineedit.h"
#include "qgsfieldexpressionwidget.h"
#include "qgsprocessingmultipleselectiondialog.h"
#include <QLabel>
#include <QHBoxLayout>
#include <QCheckBox>
#include <QComboBox>
#include <QLineEdit>
#include <QPlainTextEdit>
#include <QRadioButton>
#include <QButtonGroup>
#include <QMenu>
///@cond PRIVATE
@ -1397,5 +1401,339 @@ QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingExpressionWidgetWrappe
}
//
// QgsProcessingEnumPanelWidget
//
QgsProcessingEnumPanelWidget::QgsProcessingEnumPanelWidget( QWidget *parent, const QgsProcessingParameterEnum *param )
: QWidget( parent )
, mParam( param )
{
QHBoxLayout *hl = new QHBoxLayout();
hl->setMargin( 0 );
hl->setContentsMargins( 0, 0, 0, 0 );
mLineEdit = new QLineEdit();
mLineEdit->setEnabled( false );
hl->addWidget( mLineEdit, 1 );
mToolButton = new QToolButton();
mToolButton->setText( tr( "" ) );
hl->addWidget( mToolButton );
setLayout( hl );
if ( mParam )
{
mLineEdit->setText( tr( "%1 options selected" ).arg( 0 ) );
}
connect( mToolButton, &QToolButton::clicked, this, &QgsProcessingEnumPanelWidget::showDialog );
}
void QgsProcessingEnumPanelWidget::setValue( const QVariant &value )
{
if ( value.isValid() )
mValue = value.type() == QVariant::List ? value.toList() : QVariantList() << value;
else
mValue.clear();
updateSummaryText();
emit changed();
}
void QgsProcessingEnumPanelWidget::showDialog()
{
QVariantList availableOptions;
if ( mParam )
{
availableOptions.reserve( mParam->options().size() );
for ( int i = 0; i < mParam->options().count(); ++i )
availableOptions << i;
}
QgsProcessingMultipleSelectionDialog dlg( availableOptions, mValue, this, nullptr );
const QStringList options = mParam ? mParam->options() : QStringList();
dlg.setValueFormatter( [options]( const QVariant & v ) -> QString
{
const int i = v.toInt();
return options.size() > i ? options.at( i ) : QString();
} );
if ( dlg.exec() )
{
setValue( dlg.selectedOptions() );
}
}
void QgsProcessingEnumPanelWidget::updateSummaryText()
{
if ( mParam )
mLineEdit->setText( tr( "%1 options selected" ).arg( mValue.count() ) );
}
//
// QgsProcessingEnumCheckboxPanelWidget
//
QgsProcessingEnumCheckboxPanelWidget::QgsProcessingEnumCheckboxPanelWidget( QWidget *parent, const QgsProcessingParameterEnum *param, int columns )
: QWidget( parent )
, mParam( param )
, mButtonGroup( new QButtonGroup( this ) )
, mColumns( columns )
{
mButtonGroup->setExclusive( !mParam->allowMultiple() );
QGridLayout *l = new QGridLayout();
l->setContentsMargins( 0, 0, 0, 0 );
l->setMargin( 0 );
int rows = static_cast< int >( std::ceil( mParam->options().count() / static_cast< double >( mColumns ) ) );
for ( int i = 0; i < mParam->options().count(); ++i )
{
QAbstractButton *button = nullptr;
if ( mParam->allowMultiple() )
button = new QCheckBox( mParam->options().at( i ) );
else
button = new QRadioButton( mParam->options().at( i ) );
connect( button, &QAbstractButton::toggled, this, [ = ]
{
if ( !mBlockChangedSignal )
emit changed();
} );
mButtons.insert( i, button );
mButtonGroup->addButton( button, i );
l->addWidget( button, i % rows, i / rows );
}
l->addItem( new QSpacerItem( 0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum ), 0, mColumns );
setLayout( l );
if ( mParam->allowMultiple() )
{
setContextMenuPolicy( Qt::CustomContextMenu );
connect( this, &QWidget::customContextMenuRequested, this, &QgsProcessingEnumCheckboxPanelWidget::showPopupMenu );
}
}
QVariant QgsProcessingEnumCheckboxPanelWidget::value() const
{
if ( mParam->allowMultiple() )
{
QVariantList value;
for ( auto it = mButtons.constBegin(); it != mButtons.constEnd(); ++it )
{
if ( it.value()->isChecked() )
value.append( it.key() );
}
return value;
}
else
{
return mButtonGroup->checkedId() >= 0 ? mButtonGroup->checkedId() : QVariant();
}
}
void QgsProcessingEnumCheckboxPanelWidget::setValue( const QVariant &value )
{
mBlockChangedSignal = true;
if ( mParam->allowMultiple() )
{
QVariantList selected;
if ( value.isValid() )
selected = value.type() == QVariant::List ? value.toList() : QVariantList() << value;
for ( auto it = mButtons.constBegin(); it != mButtons.constEnd(); ++it )
{
it.value()->setChecked( selected.contains( it.key() ) );
}
}
else
{
QVariant v = value;
if ( v.type() == QVariant::List )
v = v.toList().value( 0 );
if ( mButtons.contains( v ) )
mButtons.value( v )->setChecked( true );
}
mBlockChangedSignal = false;
emit changed();
}
void QgsProcessingEnumCheckboxPanelWidget::showPopupMenu()
{
QMenu popupMenu;
QAction *selectAllAction = new QAction( tr( "Select All" ), &popupMenu );
connect( selectAllAction, &QAction::triggered, this, &QgsProcessingEnumCheckboxPanelWidget::selectAll );
QAction *clearAllAction = new QAction( tr( "Clear Selection" ), &popupMenu );
connect( clearAllAction, &QAction::triggered, this, &QgsProcessingEnumCheckboxPanelWidget::deselectAll );
popupMenu.addAction( selectAllAction );
popupMenu.addAction( clearAllAction );
popupMenu.exec( QCursor::pos() );
}
void QgsProcessingEnumCheckboxPanelWidget::selectAll()
{
mBlockChangedSignal = true;
for ( auto it = mButtons.constBegin(); it != mButtons.constEnd(); ++it )
it.value()->setChecked( true );
mBlockChangedSignal = false;
emit changed();
}
void QgsProcessingEnumCheckboxPanelWidget::deselectAll()
{
mBlockChangedSignal = true;
for ( auto it = mButtons.constBegin(); it != mButtons.constEnd(); ++it )
it.value()->setChecked( false );
mBlockChangedSignal = false;
emit changed();
}
//
// QgsProcessingEnumWidgetWrapper
//
QgsProcessingEnumWidgetWrapper::QgsProcessingEnumWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type, QWidget *parent )
: QgsAbstractProcessingParameterWidgetWrapper( parameter, type, parent )
{
}
QWidget *QgsProcessingEnumWidgetWrapper::createWidget()
{
const QgsProcessingParameterEnum *expParam = dynamic_cast< const QgsProcessingParameterEnum *>( parameterDefinition() );
switch ( type() )
{
case QgsProcessingGui::Standard:
{
// checkbox panel only for use outside in standard gui!
if ( expParam->metadata().value( QStringLiteral( "widget_wrapper" ) ).toMap().value( QStringLiteral( "useCheckBoxes" ), false ).toBool() )
{
const int columns = expParam->metadata().value( QStringLiteral( "widget_wrapper" ) ).toMap().value( QStringLiteral( "columns" ), 2 ).toInt();
mCheckboxPanel = new QgsProcessingEnumCheckboxPanelWidget( nullptr, expParam, columns );
mCheckboxPanel->setToolTip( parameterDefinition()->toolTip() );
connect( mCheckboxPanel, &QgsProcessingEnumCheckboxPanelWidget::changed, this, [ = ]
{
emit widgetValueHasChanged( this );
} );
return mCheckboxPanel;
}
}
FALLTHROUGH
case QgsProcessingGui::Modeler:
case QgsProcessingGui::Batch:
{
if ( expParam->allowMultiple() )
{
mPanel = new QgsProcessingEnumPanelWidget( nullptr, expParam );
mPanel->setToolTip( parameterDefinition()->toolTip() );
connect( mPanel, &QgsProcessingEnumPanelWidget::changed, this, [ = ]
{
emit widgetValueHasChanged( this );
} );
return mPanel;
}
else
{
mComboBox = new QComboBox();
if ( expParam->flags() & QgsProcessingParameterDefinition::FlagOptional )
mComboBox->addItem( tr( "[Not selected]" ), QVariant() );
const QStringList options = expParam->options();
for ( int i = 0; i < options.count(); ++i )
mComboBox->addItem( options.at( i ), i );
mComboBox->setToolTip( parameterDefinition()->toolTip() );
connect( mComboBox, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, [ = ]( int )
{
emit widgetValueHasChanged( this );
} );
return mComboBox;
}
};
}
return nullptr;
}
void QgsProcessingEnumWidgetWrapper::setWidgetValue( const QVariant &value, QgsProcessingContext &context )
{
if ( mComboBox )
{
if ( !value.isValid() )
mComboBox->setCurrentIndex( mComboBox->findData( QVariant() ) );
else
{
const int v = QgsProcessingParameters::parameterAsEnum( parameterDefinition(), value, context );
mComboBox->setCurrentIndex( mComboBox->findData( v ) );
}
}
else if ( mPanel || mCheckboxPanel )
{
QVariantList opts;
if ( value.isValid() )
{
const QList< int > v = QgsProcessingParameters::parameterAsEnums( parameterDefinition(), value, context );
opts.reserve( v.size() );
for ( int i : v )
opts << i;
}
if ( mPanel )
mPanel->setValue( opts );
else if ( mCheckboxPanel )
mCheckboxPanel->setValue( opts );
}
}
QVariant QgsProcessingEnumWidgetWrapper::widgetValue() const
{
if ( mComboBox )
return mComboBox->currentData();
else if ( mPanel )
return mPanel->value();
else if ( mCheckboxPanel )
return mCheckboxPanel->value();
else
return QVariant();
}
QStringList QgsProcessingEnumWidgetWrapper::compatibleParameterTypes() const
{
return QStringList()
<< QgsProcessingParameterEnum::typeName()
<< QgsProcessingParameterString::typeName()
<< QgsProcessingParameterNumber::typeName();
}
QStringList QgsProcessingEnumWidgetWrapper::compatibleOutputTypes() const
{
return QStringList()
<< QgsProcessingOutputString::typeName()
<< QgsProcessingOutputNumber::typeName();
}
QList<int> QgsProcessingEnumWidgetWrapper::compatibleDataTypes() const
{
return QList<int>();
}
QString QgsProcessingEnumWidgetWrapper::modelerExpressionFormatString() const
{
return tr( "selected option index (starting from 0), array of indices, or comma separated string of options (e.g. '1,3')" );
}
QString QgsProcessingEnumWidgetWrapper::parameterType() const
{
return QgsProcessingParameterEnum::typeName();
}
QgsAbstractProcessingParameterWidgetWrapper *QgsProcessingEnumWidgetWrapper::createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type )
{
return new QgsProcessingEnumWidgetWrapper( parameter, type );
}
///@endcond PRIVATE

View File

@ -21,11 +21,14 @@
#define SIP_NO_FILE
#include "qgsprocessingwidgetwrapper.h"
#include <QAbstractButton>
class QCheckBox;
class QComboBox;
class QLineEdit;
class QPlainTextEdit;
class QToolButton;
class QButtonGroup;
class QgsProjectionSelectionWidget;
class QgsSpinBox;
class QgsDoubleSpinBox;
@ -34,6 +37,7 @@ class QgsProcessingMatrixParameterPanel;
class QgsFileWidget;
class QgsFieldExpressionWidget;
class QgsExpressionLineEdit;
class QgsProcessingParameterEnum;
///@cond PRIVATE
@ -401,6 +405,107 @@ class GUI_EXPORT QgsProcessingExpressionWidgetWrapper : public QgsAbstractProces
friend class TestProcessingGui;
};
class GUI_EXPORT QgsProcessingEnumCheckboxPanelWidget : public QWidget
{
Q_OBJECT
public:
QgsProcessingEnumCheckboxPanelWidget( QWidget *parent = nullptr, const QgsProcessingParameterEnum *param = nullptr, int columns = 2 );
QVariant value() const;
void setValue( const QVariant &value );
signals:
void changed();
private slots:
void showPopupMenu();
void selectAll();
void deselectAll();
private:
const QgsProcessingParameterEnum *mParam = nullptr;
QMap< QVariant, QAbstractButton * > mButtons;
QButtonGroup *mButtonGroup = nullptr;
int mColumns = 2;
bool mBlockChangedSignal = false;
friend class TestProcessingGui;
};
class GUI_EXPORT QgsProcessingEnumPanelWidget : public QWidget
{
Q_OBJECT
public:
QgsProcessingEnumPanelWidget( QWidget *parent = nullptr, const QgsProcessingParameterEnum *param = nullptr );
QVariant value() const { return mValue; }
void setValue( const QVariant &value );
signals:
void changed();
private slots:
void showDialog();
private:
void updateSummaryText();
const QgsProcessingParameterEnum *mParam = nullptr;
QLineEdit *mLineEdit = nullptr;
QToolButton *mToolButton = nullptr;
QVariantList mValue;
friend class TestProcessingGui;
};
class GUI_EXPORT QgsProcessingEnumWidgetWrapper : public QgsAbstractProcessingParameterWidgetWrapper, public QgsProcessingParameterWidgetFactoryInterface
{
Q_OBJECT
public:
QgsProcessingEnumWidgetWrapper( const QgsProcessingParameterDefinition *parameter = nullptr,
QgsProcessingGui::WidgetType type = QgsProcessingGui::Standard, QWidget *parent = nullptr );
// QgsProcessingParameterWidgetFactoryInterface
QString parameterType() const override;
QgsAbstractProcessingParameterWidgetWrapper *createWidgetWrapper( const QgsProcessingParameterDefinition *parameter, QgsProcessingGui::WidgetType type ) override;
// QgsProcessingParameterWidgetWrapper interface
QWidget *createWidget() override SIP_FACTORY;
protected:
void setWidgetValue( const QVariant &value, QgsProcessingContext &context ) override;
QVariant widgetValue() const override;
QStringList compatibleParameterTypes() const override;
QStringList compatibleOutputTypes() const override;
QList< int > compatibleDataTypes() const override;
QString modelerExpressionFormatString() const override;
private:
QComboBox *mComboBox = nullptr;
QgsProcessingEnumPanelWidget *mPanel = nullptr;
QgsProcessingEnumCheckboxPanelWidget *mCheckboxPanel = nullptr;
friend class TestProcessingGui;
};
///@endcond PRIVATE
#endif // QGSPROCESSINGWIDGETWRAPPERIMPL_H

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsProcessingMultipleSelectionDialogBase</class>
<widget class="QDialog" name="QgsProcessingMultipleSelectionDialogBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>380</width>
<height>320</height>
</rect>
</property>
<property name="windowTitle">
<string>Multiple selection</string>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<property name="spacing">
<number>6</number>
</property>
<property name="leftMargin">
<number>9</number>
</property>
<property name="topMargin">
<number>9</number>
</property>
<property name="rightMargin">
<number>9</number>
</property>
<property name="bottomMargin">
<number>9</number>
</property>
<item>
<widget class="QListView" name="mSelectionList">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
<property name="alternatingRowColors">
<bool>true</bool>
</property>
</widget>
</item>
<item>
<widget class="QDialogButtonBox" name="mButtonBox">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="standardButtons">
<set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections>
<connection>
<sender>mButtonBox</sender>
<signal>accepted()</signal>
<receiver>QgsProcessingMultipleSelectionDialogBase</receiver>
<slot>accept()</slot>
<hints>
<hint type="sourcelabel">
<x>248</x>
<y>254</y>
</hint>
<hint type="destinationlabel">
<x>157</x>
<y>274</y>
</hint>
</hints>
</connection>
<connection>
<sender>mButtonBox</sender>
<signal>rejected()</signal>
<receiver>QgsProcessingMultipleSelectionDialogBase</receiver>
<slot>reject()</slot>
<hints>
<hint type="sourcelabel">
<x>316</x>
<y>260</y>
</hint>
<hint type="destinationlabel">
<x>286</x>
<y>274</y>
</hint>
</hints>
</connection>
</connections>
</ui>

View File

@ -24,6 +24,7 @@
#include <QLineEdit>
#include <QPushButton>
#include <QPlainTextEdit>
#include <QStandardItemModel>
#include "qgstest.h"
#include "qgsgui.h"
@ -34,6 +35,7 @@
#include "qgsprocessingwidgetwrapper.h"
#include "qgsprocessingwidgetwrapperimpl.h"
#include "qgsprocessingmodelerparameterwidget.h"
#include "qgsprocessingparameters.h"
#include "qgsnativealgorithms.h"
#include "processing/models/qgsprocessingmodelalgorithm.h"
#include "qgsxmlutils.h"
@ -49,6 +51,7 @@
#include "qgsfilewidget.h"
#include "qgsexpressionlineedit.h"
#include "qgsfieldexpressionwidget.h"
#include "qgsprocessingmultipleselectiondialog.h"
class TestParamType : public QgsProcessingParameterDefinition
{
@ -163,6 +166,10 @@ class TestProcessingGui : public QObject
void testMatrixDialog();
void testMatrixWrapper();
void testExpressionWrapper();
void testMultipleSelectionDialog();
void testEnumSelectionPanel();
void testEnumCheckboxPanel();
void testEnumWrapper();
private:
@ -1951,6 +1958,476 @@ void TestProcessingGui::testExpressionWrapper()
testWrapper( QgsProcessingGui::Modeler );
}
void TestProcessingGui::testMultipleSelectionDialog()
{
QVariantList availableOptions;
QVariantList selectedOptions;
std::unique_ptr< QgsProcessingMultipleSelectionDialog > dlg = qgis::make_unique< QgsProcessingMultipleSelectionDialog >( availableOptions, selectedOptions );
QVERIFY( dlg->selectedOptions().isEmpty() );
QCOMPARE( dlg->mModel->rowCount(), 0 );
std::unique_ptr< QgsVectorLayer > vl = qgis::make_unique< QgsVectorLayer >( QStringLiteral( "LineString" ), QStringLiteral( "x" ), QStringLiteral( "memory" ) );
availableOptions << QVariant( "aa" ) << QVariant( 15 ) << QVariant::fromValue( vl.get() );
dlg = qgis::make_unique< QgsProcessingMultipleSelectionDialog >( availableOptions, selectedOptions );
QVERIFY( dlg->selectedOptions().isEmpty() );
QCOMPARE( dlg->mModel->rowCount(), 3 );
dlg->selectAll( true );
QCOMPARE( dlg->selectedOptions(), availableOptions );
dlg->mModel->item( 1 )->setCheckState( Qt::Unchecked );
QCOMPARE( dlg->selectedOptions(), QVariantList() << "aa" << QVariant::fromValue( vl.get() ) );
// reorder rows
QList<QStandardItem *> itemList = dlg->mModel->takeRow( 2 );
dlg->mModel->insertRow( 0, itemList );
QCOMPARE( dlg->selectedOptions(), QVariantList() << QVariant::fromValue( vl.get() ) << "aa" );
// additional options
availableOptions.clear();
selectedOptions << QVariant( "bb" ) << QVariant( 6.6 );
dlg = qgis::make_unique< QgsProcessingMultipleSelectionDialog >( availableOptions, selectedOptions );
QCOMPARE( dlg->mModel->rowCount(), 2 );
QCOMPARE( dlg->selectedOptions(), selectedOptions );
dlg->mModel->item( 1 )->setCheckState( Qt::Unchecked );
QCOMPARE( dlg->selectedOptions(), QVariantList() << "bb" );
// mix of standard and additional options
availableOptions << QVariant( 6.6 ) << QVariant( "aa" );
dlg = qgis::make_unique< QgsProcessingMultipleSelectionDialog >( availableOptions, selectedOptions );
QCOMPARE( dlg->mModel->rowCount(), 3 );
QCOMPARE( dlg->selectedOptions(), selectedOptions ); // order must be maintained!
dlg->mModel->item( 2 )->setCheckState( Qt::Checked );
QCOMPARE( dlg->selectedOptions(), QVariantList() << "bb" << QVariant( 6.6 ) << QVariant( "aa" ) );
// selection buttons
selectedOptions.clear();
availableOptions = QVariantList() << QVariant( "a" ) << QVariant( "b" ) << QVariant( "c" );
dlg = qgis::make_unique< QgsProcessingMultipleSelectionDialog >( availableOptions, selectedOptions );
QVERIFY( dlg->selectedOptions().isEmpty() );
dlg->mSelectionList->selectionModel()->select( dlg->mModel->index( 1, 0 ), QItemSelectionModel::ClearAndSelect );
// without a multi-selection, select all/toggle options should affect all items
dlg->selectAll( true );
QCOMPARE( dlg->selectedOptions(), availableOptions );
dlg->selectAll( false );
QVERIFY( dlg->selectedOptions().isEmpty() );
dlg->toggleSelection();
QCOMPARE( dlg->selectedOptions(), availableOptions );
dlg->toggleSelection();
QVERIFY( dlg->selectedOptions().isEmpty() );
// with multi-selection, actions should only affected selected rows
dlg->mSelectionList->selectionModel()->select( dlg->mModel->index( 2, 0 ), QItemSelectionModel::Select );
dlg->selectAll( true );
QCOMPARE( dlg->selectedOptions(), QVariantList() << "b" << "c" );
dlg->selectAll( false );
QVERIFY( dlg->selectedOptions().isEmpty() );
dlg->selectAll( true );
QCOMPARE( dlg->selectedOptions(), QVariantList() << "b" << "c" );
dlg->mModel->item( 0 )->setCheckState( Qt::Checked );
dlg->selectAll( false );
QCOMPARE( dlg->selectedOptions(), QVariantList() << "a" );
dlg->toggleSelection();
QCOMPARE( dlg->selectedOptions(), QVariantList() << "a" << "b" << "c" );
dlg->toggleSelection();
QCOMPARE( dlg->selectedOptions(), QVariantList() << "a" );
// text format
availableOptions = QVariantList() << QVariant( "a" ) << 6 << 6.2;
dlg = qgis::make_unique< QgsProcessingMultipleSelectionDialog >( availableOptions, selectedOptions );
QCOMPARE( dlg->mModel->item( 0 )->text(), QStringLiteral( "a" ) );
QCOMPARE( dlg->mModel->item( 1 )->text(), QStringLiteral( "6" ) );
QCOMPARE( dlg->mModel->item( 2 )->text(), QStringLiteral( "6.2" ) );
dlg->setValueFormatter( []( const QVariant & v )-> QString
{
return v.toString() + '_';
} );
QCOMPARE( dlg->mModel->item( 0 )->text(), QStringLiteral( "a_" ) );
QCOMPARE( dlg->mModel->item( 1 )->text(), QStringLiteral( "6_" ) );
QCOMPARE( dlg->mModel->item( 2 )->text(), QStringLiteral( "6.2_" ) );
}
void TestProcessingGui::testEnumSelectionPanel()
{
QgsProcessingParameterEnum enumParam( QString(), QString(), QStringList() << QStringLiteral( "a" ) << QStringLiteral( "b" ) << QStringLiteral( "c" ), true );
QgsProcessingEnumPanelWidget w( nullptr, &enumParam );
QSignalSpy spy( &w, &QgsProcessingEnumPanelWidget::changed );
QCOMPARE( w.mLineEdit->text(), QStringLiteral( "0 options selected" ) );
w.setValue( 1 );
QCOMPARE( spy.count(), 1 );
QCOMPARE( w.value().toList(), QVariantList() << 1 );
QCOMPARE( w.mLineEdit->text(), QStringLiteral( "1 options selected" ) );
w.setValue( QVariantList() << 2 << 0 );
QCOMPARE( spy.count(), 2 );
QCOMPARE( w.value().toList(), QVariantList() << 2 << 0 );
QCOMPARE( w.mLineEdit->text(), QStringLiteral( "2 options selected" ) );
w.setValue( QVariant() );
QCOMPARE( spy.count(), 3 );
QCOMPARE( w.value().toList(), QVariantList() );
QCOMPARE( w.mLineEdit->text(), QStringLiteral( "0 options selected" ) );
}
void TestProcessingGui::testEnumCheckboxPanel()
{
//single value
QgsProcessingParameterEnum param( QStringLiteral( "enum" ), QStringLiteral( "enum" ), QStringList() << QStringLiteral( "a" ) << QStringLiteral( "b" ) << QStringLiteral( "c" ), false );
QgsProcessingEnumCheckboxPanelWidget panel( nullptr, &param );
QSignalSpy spy( &panel, &QgsProcessingEnumCheckboxPanelWidget::changed );
QCOMPARE( panel.value(), QVariant() );
panel.setValue( 2 );
QCOMPARE( spy.count(), 1 );
QCOMPARE( panel.value().toInt(), 2 );
QVERIFY( !panel.mButtons[ 0 ]->isChecked() );
QVERIFY( !panel.mButtons[ 1 ]->isChecked() );
QVERIFY( panel.mButtons[ 2 ]->isChecked() );
panel.setValue( 0 );
QCOMPARE( spy.count(), 2 );
QCOMPARE( panel.value().toInt(), 0 );
QVERIFY( panel.mButtons[ 0 ]->isChecked() );
QVERIFY( !panel.mButtons[ 1 ]->isChecked() );
QVERIFY( !panel.mButtons[ 2 ]->isChecked() );
panel.mButtons[1]->setChecked( true );
QCOMPARE( spy.count(), 4 );
QCOMPARE( panel.value().toInt(), 1 );
panel.setValue( QVariantList() << 2 );
QCOMPARE( spy.count(), 5 );
QCOMPARE( panel.value().toInt(), 2 );
QVERIFY( !panel.mButtons[ 0 ]->isChecked() );
QVERIFY( !panel.mButtons[ 1 ]->isChecked() );
QVERIFY( panel.mButtons[ 2 ]->isChecked() );
// multiple value
QgsProcessingParameterEnum param2( QStringLiteral( "enum" ), QStringLiteral( "enum" ), QStringList() << QStringLiteral( "a" ) << QStringLiteral( "b" ) << QStringLiteral( "c" ), true );
QgsProcessingEnumCheckboxPanelWidget panel2( nullptr, &param2 );
QSignalSpy spy2( &panel2, &QgsProcessingEnumCheckboxPanelWidget::changed );
QCOMPARE( panel2.value().toList(), QVariantList() );
panel2.setValue( 2 );
QCOMPARE( spy2.count(), 1 );
QCOMPARE( panel2.value().toList(), QVariantList() << 2 );
QVERIFY( !panel2.mButtons[ 0 ]->isChecked() );
QVERIFY( !panel2.mButtons[ 1 ]->isChecked() );
QVERIFY( panel2.mButtons[ 2 ]->isChecked() );
panel2.setValue( QVariantList() << 0 << 1 );
QCOMPARE( spy2.count(), 2 );
QCOMPARE( panel2.value().toList(), QVariantList() << 0 << 1 );
QVERIFY( panel2.mButtons[ 0 ]->isChecked() );
QVERIFY( panel2.mButtons[ 1 ]->isChecked() );
QVERIFY( !panel2.mButtons[ 2 ]->isChecked() );
panel2.mButtons[0]->setChecked( false );
QCOMPARE( spy2.count(), 3 );
QCOMPARE( panel2.value().toList(), QVariantList() << 1 );
panel2.mButtons[2]->setChecked( true );
QCOMPARE( spy2.count(), 4 );
QCOMPARE( panel2.value().toList(), QVariantList() << 1 << 2 );
panel2.deselectAll();
QCOMPARE( spy2.count(), 5 );
QCOMPARE( panel2.value().toList(), QVariantList() );
panel2.selectAll();
QCOMPARE( spy2.count(), 6 );
QCOMPARE( panel2.value().toList(), QVariantList() << 0 << 1 << 2 );
// multiple value optional
QgsProcessingParameterEnum param3( QStringLiteral( "enum" ), QStringLiteral( "enum" ), QStringList() << QStringLiteral( "a" ) << QStringLiteral( "b" ) << QStringLiteral( "c" ), true, QVariant(), true );
QgsProcessingEnumCheckboxPanelWidget panel3( nullptr, &param3 );
QSignalSpy spy3( &panel3, &QgsProcessingEnumCheckboxPanelWidget::changed );
QCOMPARE( panel3.value().toList(), QVariantList() );
panel3.setValue( 2 );
QCOMPARE( spy3.count(), 1 );
QCOMPARE( panel3.value().toList(), QVariantList() << 2 );
QVERIFY( !panel3.mButtons[ 0 ]->isChecked() );
QVERIFY( !panel3.mButtons[ 1 ]->isChecked() );
QVERIFY( panel3.mButtons[ 2 ]->isChecked() );
panel3.setValue( QVariantList() << 0 << 1 );
QCOMPARE( spy3.count(), 2 );
QCOMPARE( panel3.value().toList(), QVariantList() << 0 << 1 );
QVERIFY( panel3.mButtons[ 0 ]->isChecked() );
QVERIFY( panel3.mButtons[ 1 ]->isChecked() );
QVERIFY( !panel3.mButtons[ 2 ]->isChecked() );
panel3.mButtons[0]->setChecked( false );
QCOMPARE( spy3.count(), 3 );
QCOMPARE( panel3.value().toList(), QVariantList() << 1 );
panel3.mButtons[2]->setChecked( true );
QCOMPARE( spy3.count(), 4 );
QCOMPARE( panel3.value().toList(), QVariantList() << 1 << 2 );
panel3.deselectAll();
QCOMPARE( spy3.count(), 5 );
QCOMPARE( panel3.value().toList(), QVariantList() );
panel3.selectAll();
QCOMPARE( spy3.count(), 6 );
QCOMPARE( panel3.value().toList(), QVariantList() << 0 << 1 << 2 );
panel3.setValue( QVariantList() );
QCOMPARE( panel3.value().toList(), QVariantList() );
QVERIFY( !panel3.mButtons[ 0 ]->isChecked() );
QVERIFY( !panel3.mButtons[ 1 ]->isChecked() );
QVERIFY( !panel3.mButtons[ 2 ]->isChecked() );
QCOMPARE( spy3.count(), 7 );
panel3.selectAll();
QCOMPARE( spy3.count(), 8 );
panel3.setValue( QVariant() );
QCOMPARE( panel3.value().toList(), QVariantList() );
QVERIFY( !panel3.mButtons[ 0 ]->isChecked() );
QVERIFY( !panel3.mButtons[ 1 ]->isChecked() );
QVERIFY( !panel3.mButtons[ 2 ]->isChecked() );
QCOMPARE( spy3.count(), 9 );
}
void TestProcessingGui::testEnumWrapper()
{
auto testWrapper = []( QgsProcessingGui::WidgetType type, bool checkboxStyle = false )
{
// non optional, single value
QgsProcessingParameterEnum param( QStringLiteral( "enum" ), QStringLiteral( "enum" ), QStringList() << QStringLiteral( "a" ) << QStringLiteral( "b" ) << QStringLiteral( "c" ), false );
QVariantMap metadata;
QVariantMap wrapperMetadata;
wrapperMetadata.insert( QStringLiteral( "useCheckBoxes" ), true );
metadata.insert( QStringLiteral( "widget_wrapper" ), wrapperMetadata );
if ( checkboxStyle )
param.setMetadata( metadata );
QgsProcessingEnumWidgetWrapper wrapper( &param, type );
QgsProcessingContext context;
QWidget *w = wrapper.createWrappedWidget( context );
QSignalSpy spy( &wrapper, &QgsProcessingEnumWidgetWrapper::widgetValueHasChanged );
wrapper.setWidgetValue( 1, context );
QCOMPARE( spy.count(), 1 );
QCOMPARE( wrapper.widgetValue().toInt(), 1 );
if ( !checkboxStyle )
{
QCOMPARE( static_cast< QComboBox * >( wrapper.wrappedWidget() )->currentIndex(), 1 );
QCOMPARE( static_cast< QComboBox * >( wrapper.wrappedWidget() )->currentText(), QStringLiteral( "b" ) );
}
else
{
QCOMPARE( static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper.wrappedWidget() )->value().toInt(), 1 );
}
wrapper.setWidgetValue( 0, context );
QCOMPARE( spy.count(), 2 );
QCOMPARE( wrapper.widgetValue().toInt(), 0 );
if ( !checkboxStyle )
{
QCOMPARE( static_cast< QComboBox * >( wrapper.wrappedWidget() )->currentIndex(), 0 );
QCOMPARE( static_cast< QComboBox * >( wrapper.wrappedWidget() )->currentText(), QStringLiteral( "a" ) );
}
else
{
QCOMPARE( static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper.wrappedWidget() )->value().toInt(), 0 );
}
QLabel *l = wrapper.createWrappedLabel();
if ( wrapper.type() != QgsProcessingGui::Batch )
{
QVERIFY( l );
QCOMPARE( l->text(), QStringLiteral( "enum" ) );
QCOMPARE( l->toolTip(), param.toolTip() );
delete l;
}
else
{
QVERIFY( !l );
}
// check signal
if ( !checkboxStyle )
static_cast< QComboBox * >( wrapper.wrappedWidget() )->setCurrentIndex( 2 );
else
static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper.wrappedWidget() )->setValue( 2 );
QCOMPARE( spy.count(), 3 );
delete w;
// optional
QgsProcessingParameterEnum param2( QStringLiteral( "enum" ), QStringLiteral( "enum" ), QStringList() << QStringLiteral( "a" ) << QStringLiteral( "b" ) << QStringLiteral( "c" ), false, QVariant(), true );
if ( checkboxStyle )
param2.setMetadata( metadata );
QgsProcessingEnumWidgetWrapper wrapper2( &param2, type );
w = wrapper2.createWrappedWidget( context );
QSignalSpy spy2( &wrapper2, &QgsProcessingEnumWidgetWrapper::widgetValueHasChanged );
wrapper2.setWidgetValue( 1, context );
QCOMPARE( spy2.count(), 1 );
QCOMPARE( wrapper2.widgetValue().toInt(), 1 );
if ( !checkboxStyle )
{
QCOMPARE( static_cast< QComboBox * >( wrapper2.wrappedWidget() )->currentIndex(), 2 );
QCOMPARE( static_cast< QComboBox * >( wrapper2.wrappedWidget() )->currentText(), QStringLiteral( "b" ) );
}
else
{
QCOMPARE( static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper2.wrappedWidget() )->value().toInt(), 1 );
}
wrapper2.setWidgetValue( 0, context );
QCOMPARE( spy2.count(), 2 );
QCOMPARE( wrapper2.widgetValue().toInt(), 0 );
if ( !checkboxStyle )
{
QCOMPARE( static_cast< QComboBox * >( wrapper2.wrappedWidget() )->currentIndex(), 1 );
QCOMPARE( static_cast< QComboBox * >( wrapper2.wrappedWidget() )->currentText(), QStringLiteral( "a" ) );
}
else
{
QCOMPARE( static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper2.wrappedWidget() )->value().toInt(), 0 );
}
wrapper2.setWidgetValue( QVariant(), context );
QCOMPARE( spy2.count(), 3 );
if ( !checkboxStyle )
{
QVERIFY( !wrapper2.widgetValue().isValid() );
QCOMPARE( static_cast< QComboBox * >( wrapper2.wrappedWidget() )->currentIndex(), 0 );
QCOMPARE( static_cast< QComboBox * >( wrapper2.wrappedWidget() )->currentText(), QStringLiteral( "[Not selected]" ) );
}
// check signal
if ( !checkboxStyle )
static_cast< QComboBox * >( wrapper2.wrappedWidget() )->setCurrentIndex( 2 );
else
static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper2.wrappedWidget() )->setValue( 1 );
QCOMPARE( spy2.count(), 4 );
delete w;
// allow multiple
QgsProcessingParameterEnum param3( QStringLiteral( "enum" ), QStringLiteral( "enum" ), QStringList() << QStringLiteral( "a" ) << QStringLiteral( "b" ) << QStringLiteral( "c" ), true, QVariant(), false );
if ( checkboxStyle )
param3.setMetadata( metadata );
QgsProcessingEnumWidgetWrapper wrapper3( &param3, type );
w = wrapper3.createWrappedWidget( context );
QSignalSpy spy3( &wrapper3, &QgsProcessingEnumWidgetWrapper::widgetValueHasChanged );
wrapper3.setWidgetValue( 1, context );
QCOMPARE( spy3.count(), 1 );
QCOMPARE( wrapper3.widgetValue().toList(), QVariantList() << 1 );
if ( !checkboxStyle )
QCOMPARE( static_cast< QgsProcessingEnumPanelWidget * >( wrapper3.wrappedWidget() )->value().toList(), QVariantList() << 1 );
else
QCOMPARE( static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper3.wrappedWidget() )->value().toList(), QVariantList() << 1 );
wrapper3.setWidgetValue( 0, context );
QCOMPARE( spy3.count(), 2 );
QCOMPARE( wrapper3.widgetValue().toList(), QVariantList() << 0 );
if ( !checkboxStyle )
QCOMPARE( static_cast< QgsProcessingEnumPanelWidget * >( wrapper3.wrappedWidget() )->value().toList(), QVariantList() << 0 );
else
QCOMPARE( static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper3.wrappedWidget() )->value().toList(), QVariantList() << 0 );
wrapper3.setWidgetValue( QVariantList() << 2 << 1, context );
QCOMPARE( spy3.count(), 3 );
if ( !checkboxStyle )
{
QCOMPARE( wrapper3.widgetValue().toList(), QVariantList() << 2 << 1 );
QCOMPARE( static_cast< QgsProcessingEnumPanelWidget * >( wrapper3.wrappedWidget() )->value().toList(), QVariantList() << 2 << 1 );
}
else
{
// checkbox style isn't ordered
QCOMPARE( wrapper3.widgetValue().toList(), QVariantList() << 1 << 2 );
QCOMPARE( static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper3.wrappedWidget() )->value().toList(), QVariantList() << 1 << 2 );
}
// check signal
if ( !checkboxStyle )
static_cast< QgsProcessingEnumPanelWidget * >( wrapper3.wrappedWidget() )->setValue( QVariantList() << 0 << 1 );
else
static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper3.wrappedWidget() )->setValue( QVariantList() << 0 << 1 );
QCOMPARE( spy3.count(), 4 );
delete w;
// allow multiple, optional
QgsProcessingParameterEnum param4( QStringLiteral( "enum" ), QStringLiteral( "enum" ), QStringList() << QStringLiteral( "a" ) << QStringLiteral( "b" ) << QStringLiteral( "c" ), true, QVariant(), false );
if ( checkboxStyle )
param4.setMetadata( metadata );
QgsProcessingEnumWidgetWrapper wrapper4( &param4, type );
w = wrapper4.createWrappedWidget( context );
QSignalSpy spy4( &wrapper4, &QgsProcessingEnumWidgetWrapper::widgetValueHasChanged );
wrapper4.setWidgetValue( 1, context );
QCOMPARE( spy4.count(), 1 );
QCOMPARE( wrapper4.widgetValue().toList(), QVariantList() << 1 );
if ( !checkboxStyle )
QCOMPARE( static_cast< QgsProcessingEnumPanelWidget * >( wrapper4.wrappedWidget() )->value().toList(), QVariantList() << 1 );
else
QCOMPARE( static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper4.wrappedWidget() )->value().toList(), QVariantList() << 1 );
wrapper4.setWidgetValue( 0, context );
QCOMPARE( spy4.count(), 2 );
QCOMPARE( wrapper4.widgetValue().toList(), QVariantList() << 0 );
if ( !checkboxStyle )
QCOMPARE( static_cast< QgsProcessingEnumPanelWidget * >( wrapper4.wrappedWidget() )->value().toList(), QVariantList() << 0 );
else
QCOMPARE( static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper4.wrappedWidget() )->value().toList(), QVariantList() << 0 );
wrapper4.setWidgetValue( QVariantList() << 2 << 1, context );
QCOMPARE( spy4.count(), 3 );
if ( !checkboxStyle )
{
QCOMPARE( wrapper4.widgetValue().toList(), QVariantList() << 2 << 1 );
QCOMPARE( static_cast< QgsProcessingEnumPanelWidget * >( wrapper4.wrappedWidget() )->value().toList(), QVariantList() << 2 << 1 );
}
else
{
// checkbox style isn't ordered
QCOMPARE( wrapper4.widgetValue().toList(), QVariantList() << 1 << 2 );
QCOMPARE( static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper4.wrappedWidget() )->value().toList(), QVariantList() << 1 << 2 );
}
wrapper4.setWidgetValue( QVariantList(), context );
QCOMPARE( spy4.count(), 4 );
QCOMPARE( wrapper4.widgetValue().toList(), QVariantList() );
if ( !checkboxStyle )
QCOMPARE( static_cast< QgsProcessingEnumPanelWidget * >( wrapper4.wrappedWidget() )->value().toList(), QVariantList() );
else
QCOMPARE( static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper4.wrappedWidget() )->value().toList(), QVariantList() );
wrapper4.setWidgetValue( QVariant(), context );
QCOMPARE( spy4.count(), 5 );
QCOMPARE( wrapper4.widgetValue().toList(), QVariantList() );
if ( !checkboxStyle )
QCOMPARE( static_cast< QgsProcessingEnumPanelWidget * >( wrapper4.wrappedWidget() )->value().toList(), QVariantList() );
else
QCOMPARE( static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper4.wrappedWidget() )->value().toList(), QVariantList() );
// check signal
if ( !checkboxStyle )
{
static_cast< QgsProcessingEnumPanelWidget * >( wrapper4.wrappedWidget() )->setValue( QVariantList() << 0 << 1 );
QCOMPARE( spy4.count(), 6 );
static_cast< QgsProcessingEnumPanelWidget * >( wrapper4.wrappedWidget() )->setValue( QVariant() );
QCOMPARE( spy4.count(), 7 );
}
else
{
static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper4.wrappedWidget() )->setValue( QVariantList() << 0 << 1 );
QCOMPARE( spy4.count(), 6 );
static_cast< QgsProcessingEnumCheckboxPanelWidget * >( wrapper4.wrappedWidget() )->setValue( QVariant() );
QCOMPARE( spy4.count(), 7 );
}
delete w;
};
// standard wrapper
testWrapper( QgsProcessingGui::Standard );
// batch wrapper
testWrapper( QgsProcessingGui::Batch );
// modeler wrapper
testWrapper( QgsProcessingGui::Modeler );
// checkbox style (not for batch or model mode!)
testWrapper( QgsProcessingGui::Standard, true );
}
void TestProcessingGui::cleanupTempDir()
{
QDir tmpDir = QDir( mTempDir );