Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE][processing] Create widget for configuring advanced per-sour…
…ce options

Includes:
- optional limit on number of features processed from the source
- optional setting to override the default "invalid geometry handling" method
from the global processing options. Allows users to disable invalid geometry
handling on a per-input basis
  • Loading branch information
nyalldawson committed Mar 24, 2020
1 parent 488c8c7 commit c040822
Show file tree
Hide file tree
Showing 5 changed files with 311 additions and 0 deletions.
2 changes: 2 additions & 0 deletions src/gui/CMakeLists.txt
Expand Up @@ -263,6 +263,7 @@ SET(QGIS_GUI_SRCS
processing/qgsprocessingalgorithmconfigurationwidget.cpp
processing/qgsprocessingalgorithmdialogbase.cpp
processing/qgsprocessingconfigurationwidgets.cpp
processing/qgsprocessingfeaturesourceoptionswidget.cpp
processing/qgsprocessingguiregistry.cpp
processing/qgsprocessingmaplayercombobox.cpp
processing/qgsprocessingmatrixparameterdialog.cpp
Expand Down Expand Up @@ -943,6 +944,7 @@ SET(QGIS_GUI_HDRS
processing/qgsprocessingalgorithmconfigurationwidget.h
processing/qgsprocessingalgorithmdialogbase.h
processing/qgsprocessingconfigurationwidgets.h
processing/qgsprocessingfeaturesourceoptionswidget.h
processing/qgsprocessinggui.h
processing/qgsprocessingguiregistry.h
processing/qgsprocessingmaplayercombobox.h
Expand Down
67 changes: 67 additions & 0 deletions src/gui/processing/qgsprocessingfeaturesourceoptionswidget.cpp
@@ -0,0 +1,67 @@
/***************************************************************************
qgsprocessingfeaturesourceoptionswidget.cpp
------------------------------------
Date : March 2020
Copyright : (C) 2020 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 "qgsprocessingfeaturesourceoptionswidget.h"
#include "qgis.h"

///@cond NOT_STABLE

QgsProcessingFeatureSourceOptionsWidget::QgsProcessingFeatureSourceOptionsWidget( QWidget *parent )
: QgsPanelWidget( parent )
{
setupUi( this );

mFeatureLimitSpinBox->setClearValue( 0, tr( "Not set" ) );
mFeatureLimitSpinBox->clear();

mComboInvalidFeatureFiltering->addItem( tr( "Use Default" ) );
mComboInvalidFeatureFiltering->addItem( tr( "Do not Filter (Better Performance)" ), QgsFeatureRequest::GeometryNoCheck );
mComboInvalidFeatureFiltering->addItem( tr( "Skip (Ignore) Features with Invalid Geometries" ), QgsFeatureRequest::GeometrySkipInvalid );
mComboInvalidFeatureFiltering->addItem( tr( "Stop Algorithm Execution When a Geometry is Invalid" ), QgsFeatureRequest::GeometryAbortOnInvalid );

connect( mFeatureLimitSpinBox, qgis::overload<int>::of( &QSpinBox::valueChanged ), this, &QgsPanelWidget::widgetChanged );
connect( mComboInvalidFeatureFiltering, qgis::overload<int>::of( &QComboBox::currentIndexChanged ), this, &QgsPanelWidget::widgetChanged );
}

void QgsProcessingFeatureSourceOptionsWidget::setGeometryCheckMethod( bool isOverridden, QgsFeatureRequest::InvalidGeometryCheck check )
{
if ( !isOverridden )
mComboInvalidFeatureFiltering->setCurrentIndex( mComboInvalidFeatureFiltering->findData( QVariant() ) );
else
mComboInvalidFeatureFiltering->setCurrentIndex( mComboInvalidFeatureFiltering->findData( check ) );
}

void QgsProcessingFeatureSourceOptionsWidget::setFeatureLimit( int limit )
{
mFeatureLimitSpinBox->setValue( limit );
}

QgsFeatureRequest::InvalidGeometryCheck QgsProcessingFeatureSourceOptionsWidget::geometryCheckMethod() const
{
return mComboInvalidFeatureFiltering->currentData().isValid() ? static_cast< QgsFeatureRequest::InvalidGeometryCheck >( mComboInvalidFeatureFiltering->currentData().toInt() ) : QgsFeatureRequest::GeometryAbortOnInvalid;
}

bool QgsProcessingFeatureSourceOptionsWidget::isOverridingInvalidGeometryCheck() const
{
return mComboInvalidFeatureFiltering->currentData().isValid();
}

int QgsProcessingFeatureSourceOptionsWidget::featureLimit() const
{
return mFeatureLimitSpinBox->value() > 0 ? mFeatureLimitSpinBox->value() : -1;
}

///@endcond
87 changes: 87 additions & 0 deletions src/gui/processing/qgsprocessingfeaturesourceoptionswidget.h
@@ -0,0 +1,87 @@
/***************************************************************************
qgsprocessingfeaturesourceoptionswidget.h
----------------------------------
Date : March 2020
Copyright : (C) 2020 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 QGSPROCESSINGFEATURESOURCEOPTIONSWIDGET_H
#define QGSPROCESSINGFEATURESOURCEOPTIONSWIDGET_H

#include "qgis.h"
#include "qgis_gui.h"
#include "qgsfeaturerequest.h"
#include "ui_qgsprocessingfeaturesourceoptionsbase.h"

#define SIP_NO_FILE

///@cond NOT_STABLE

/**
* \ingroup gui
* \brief Widget for configuring advanced settings for a feature source.
* \note Not stable API
* \since QGIS 3.14
*/
class GUI_EXPORT QgsProcessingFeatureSourceOptionsWidget : public QgsPanelWidget, private Ui::QgsProcessingFeatureSourceOptionsBase
{
Q_OBJECT

public:

/**
* Constructor for QgsProcessingFeatureSourceOptionsWidget, with the specified \a parent widget.
*/
QgsProcessingFeatureSourceOptionsWidget( QWidget *parent SIP_TRANSFERTHIS = nullptr );

/**
* Sets the geometry check method to use, and whether the default method is overridden.
*
* \see isOverridingInvalidGeometryCheck()
* \see geometryCheckMethod()
*/
void setGeometryCheckMethod( bool isOverridden, QgsFeatureRequest::InvalidGeometryCheck check );

/**
* Sets the feature \a limit for the source.
*
* \see featureLimit()
*/
void setFeatureLimit( int limit );

/**
* Returns the selected geometry check method. Also check isOverridingInvalidGeometryCheck() to verify
* whether this method should be applied, or the default one used instead.
*
* \see isOverridingInvalidGeometryCheck()
* \see setGeometryCheckMethod()
*/
QgsFeatureRequest::InvalidGeometryCheck geometryCheckMethod() const;

/**
* Returns TRUE if the default geometry check method is being overridden.
* \see geometryCheckMethod()
* \see setGeometryCheckMethod()
*/
bool isOverridingInvalidGeometryCheck() const;

/**
* Returns the feature limit set in the widget, or -1 if no limit is set.
*
* \see setFeatureLimit()
*/
int featureLimit() const;

};

///@endcond

#endif // QGSPROCESSINGFEATURESOURCEOPTIONSWIDGET_H
121 changes: 121 additions & 0 deletions src/ui/processing/qgsprocessingfeaturesourceoptionsbase.ui
@@ -0,0 +1,121 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsProcessingFeatureSourceOptionsBase</class>
<widget class="QgsPanelWidget" name="QgsProcessingFeatureSourceOptionsBase">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>448</width>
<height>197</height>
</rect>
</property>
<property name="windowTitle">
<string>Form</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<property name="spacing">
<number>2</number>
</property>
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item>
<widget class="QgsScrollArea" name="scrollArea">
<property name="frameShape">
<enum>QFrame::NoFrame</enum>
</property>
<property name="widgetResizable">
<bool>true</bool>
</property>
<widget class="QWidget" name="scrollAreaWidgetContents">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>448</width>
<height>197</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout" columnstretch="0,1">
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Invalid feature filtering</string>
</property>
</widget>
</item>
<item row="3" column="0">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
<item row="1" column="1">
<widget class="QComboBox" name="mComboInvalidFeatureFiltering">
<property name="toolTip">
<string>If set, allows the default method for handling features with invalid geometries to be overridden</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QgsSpinBox" name="mFeatureLimitSpinBox">
<property name="toolTip">
<string>If set, limits the maximum number of features which will be processed from this source</string>
</property>
<property name="maximum">
<number>999999999</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Limit features processed</string>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsScrollArea</class>
<extends>QScrollArea</extends>
<header>qgsscrollarea.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsPanelWidget</class>
<extends>QWidget</extends>
<header>qgspanelwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsSpinBox</class>
<extends>QSpinBox</extends>
<header>qgsspinbox.h</header>
</customwidget>
</customwidgets>
<resources/>
<connections/>
</ui>
34 changes: 34 additions & 0 deletions tests/src/gui/testprocessinggui.cpp
Expand Up @@ -76,6 +76,7 @@
#include "qgsdatabasetablecombobox.h"
#include "qgsprocessingoutputdestinationwidget.h"
#include "qgssettings.h"
#include "qgsprocessingfeaturesourceoptionswidget.h"

class TestParamType : public QgsProcessingParameterDefinition
{
Expand Down Expand Up @@ -216,6 +217,7 @@ class TestProcessingGui : public QObject
void testOutputDefinitionWidgetRasterOut();
void testOutputDefinitionWidgetFolder();
void testOutputDefinitionWidgetFileOut();
void testFeatureSourceOptionsWidget();

private:

Expand Down Expand Up @@ -5946,6 +5948,38 @@ void TestProcessingGui::testOutputDefinitionWidgetFileOut()
QCOMPARE( changedSpy3.count(), 3 );
}

void TestProcessingGui::testFeatureSourceOptionsWidget()
{
QgsProcessingFeatureSourceOptionsWidget w;
QSignalSpy spy( &w, &QgsProcessingFeatureSourceOptionsWidget::widgetChanged );

w.setFeatureLimit( 66 );
QCOMPARE( spy.count(), 1 );
QCOMPARE( w.featureLimit(), 66 );
w.setFeatureLimit( 66 );
QCOMPARE( spy.count(), 1 );
w.setFeatureLimit( -1 );
QCOMPARE( spy.count(), 2 );
QCOMPARE( w.featureLimit(), -1 );

w.setGeometryCheckMethod( false, QgsFeatureRequest::GeometrySkipInvalid );
QCOMPARE( spy.count(), 2 );
QVERIFY( !w.isOverridingInvalidGeometryCheck() );
w.setGeometryCheckMethod( true, QgsFeatureRequest::GeometrySkipInvalid );
QCOMPARE( spy.count(), 3 );
QVERIFY( w.isOverridingInvalidGeometryCheck() );
QCOMPARE( w.geometryCheckMethod(), QgsFeatureRequest::GeometrySkipInvalid );
w.setGeometryCheckMethod( true, QgsFeatureRequest::GeometrySkipInvalid );
QCOMPARE( spy.count(), 3 );
w.setGeometryCheckMethod( true, QgsFeatureRequest::GeometryAbortOnInvalid );
QCOMPARE( spy.count(), 4 );
QVERIFY( w.isOverridingInvalidGeometryCheck() );
QCOMPARE( w.geometryCheckMethod(), QgsFeatureRequest::GeometryAbortOnInvalid );
w.setGeometryCheckMethod( false, QgsFeatureRequest::GeometryAbortOnInvalid );
QVERIFY( !w.isOverridingInvalidGeometryCheck() );
QCOMPARE( spy.count(), 5 );
}

void TestProcessingGui::cleanupTempDir()
{
QDir tmpDir = QDir( mTempDir );
Expand Down

0 comments on commit c040822

Please sign in to comment.