diff --git a/python/gui/auto_generated/qgsextentwidget.sip.in b/python/gui/auto_generated/qgsextentwidget.sip.in index 682293ac5af..efac681b32b 100644 --- a/python/gui/auto_generated/qgsextentwidget.sip.in +++ b/python/gui/auto_generated/qgsextentwidget.sip.in @@ -158,6 +158,17 @@ Returns the name of the extent layer. Returns ``True`` if the widget is in a valid state, i.e. has an extent set. %End + void setNullValueAllowed( bool allowed, const QString ¬SetText = QString() ); +%Docstring +Sets whether the widget can be set to a "not set" (null) state. + +The specified ``notSetText`` will be used for showing null values. + +.. note:: + + This mode only applies to widgets in the condensed state! +%End + public slots: void setOutputExtentFromOriginal(); @@ -191,6 +202,11 @@ Sets a fixed aspect ratio to be used when dragging extent onto the canvas. To unset a fixed aspect ratio, set the width and height to zero. :param ratio: aspect ratio's width and height +%End + + void clear(); +%Docstring +Clears the widget, setting it to a null value. %End signals: diff --git a/src/gui/qgsextentwidget.cpp b/src/gui/qgsextentwidget.cpp index 16ac4738e6e..841d5277547 100644 --- a/src/gui/qgsextentwidget.cpp +++ b/src/gui/qgsextentwidget.cpp @@ -39,6 +39,7 @@ QgsExtentWidget::QgsExtentWidget( QWidget *parent, WidgetStyle style ) mCondensedRe = QRegularExpression( QStringLiteral( "\\s*([\\d\\.]+)\\s*,\\s*([\\d\\.]+)\\s*,\\s*([\\d\\.]+)\\s*,\\s*([\\d\\.]+)\\s*(\\[.*?\\])" ) ); mCondensedLineEdit->setValidator( new QRegularExpressionValidator( mCondensedRe, this ) ); mCondensedLineEdit->setShowClearButton( false ); + connect( mCondensedLineEdit, &QgsFilterLineEdit::cleared, this, &QgsExtentWidget::clear ); connect( mCondensedLineEdit, &QLineEdit::textEdited, this, &QgsExtentWidget::setOutputExtentFromCondensedLineEdit ); @@ -232,15 +233,36 @@ void QgsExtentWidget::setOutputExtentFromLineEdit() void QgsExtentWidget::setOutputExtentFromCondensedLineEdit() { const QString text = mCondensedLineEdit->text(); - const QRegularExpressionMatch match = mCondensedRe.match( text ); - if ( match.hasMatch() ) + if ( text.isEmpty() ) { - whileBlocking( mXMinLineEdit )->setText( match.captured( 1 ) ); - whileBlocking( mXMaxLineEdit )->setText( match.captured( 2 ) ); - whileBlocking( mYMinLineEdit )->setText( match.captured( 3 ) ); - whileBlocking( mYMaxLineEdit )->setText( match.captured( 4 ) ); - emit extentChanged( outputExtent() ); + clear(); } + else + { + const QRegularExpressionMatch match = mCondensedRe.match( text ); + if ( match.hasMatch() ) + { + whileBlocking( mXMinLineEdit )->setText( match.captured( 1 ) ); + whileBlocking( mXMaxLineEdit )->setText( match.captured( 2 ) ); + whileBlocking( mYMinLineEdit )->setText( match.captured( 3 ) ); + whileBlocking( mYMaxLineEdit )->setText( match.captured( 4 ) ); + emit extentChanged( outputExtent() ); + } + } +} + +void QgsExtentWidget::clear() +{ + bool prevWasNull = mIsValid; + + whileBlocking( mXMinLineEdit )->clear(); + whileBlocking( mXMaxLineEdit )->clear(); + whileBlocking( mYMinLineEdit )->clear(); + whileBlocking( mYMaxLineEdit )->clear(); + setValid( false ); + + if ( prevWasNull ) + emit extentChanged( outputExtent() ); } QString QgsExtentWidget::extentLayerName() const @@ -253,6 +275,12 @@ bool QgsExtentWidget::isValid() const return mIsValid; } +void QgsExtentWidget::setNullValueAllowed( bool allowed, const QString ¬SetText ) +{ + mCondensedLineEdit->setShowClearButton( allowed ); + mCondensedLineEdit->setNullValue( notSetText ); +} + void QgsExtentWidget::setValid( bool valid ) { if ( valid == mIsValid ) diff --git a/src/gui/qgsextentwidget.h b/src/gui/qgsextentwidget.h index 765e570579a..bdb52dd0308 100644 --- a/src/gui/qgsextentwidget.h +++ b/src/gui/qgsextentwidget.h @@ -166,6 +166,15 @@ class GUI_EXPORT QgsExtentWidget : public QWidget, private Ui::QgsExtentGroupBox */ bool isValid() const; + /** + * Sets whether the widget can be set to a "not set" (null) state. + * + * The specified \a notSetText will be used for showing null values. + * + * \note This mode only applies to widgets in the condensed state! + */ + void setNullValueAllowed( bool allowed, const QString ¬SetText = QString() ); + public slots: /** @@ -200,6 +209,11 @@ class GUI_EXPORT QgsExtentWidget : public QWidget, private Ui::QgsExtentGroupBox */ void setRatio( QSize ratio ) { mRatio = ratio; } + /** + * Clears the widget, setting it to a null value. + */ + void clear(); + signals: /** diff --git a/tests/src/python/test_qgsextentwidget.py b/tests/src/python/test_qgsextentwidget.py index 9dec81a1ccc..ca29245fa2f 100644 --- a/tests/src/python/test_qgsextentwidget.py +++ b/tests/src/python/test_qgsextentwidget.py @@ -158,6 +158,34 @@ class TestQgsExtentWidget(unittest.TestCase): # just test this by restricting the test to 4 decimals self.assertEqual(w.outputExtent().toString(4), QgsRectangle(1, 2, 3, 4).toString(4)) + def testClear(self): + w = QgsExtentWidget() + w.setNullValueAllowed(True, 'test') + valid_spy = QSignalSpy(w.validationChanged) + changed_spy = QSignalSpy(w.extentChanged) + self.assertFalse(w.isValid()) + w.setOriginalExtent(QgsRectangle(1, 2, 3, 4), QgsCoordinateReferenceSystem('epsg:3111')) + w.setCurrentExtent(QgsRectangle(11, 12, 13, 14), QgsCoordinateReferenceSystem('epsg:3113')) + w.setOutputExtentFromOriginal() + self.assertEqual(len(valid_spy), 1) + self.assertEqual(len(changed_spy), 1) + self.assertTrue(w.isValid()) + + w.clear() + self.assertEqual(len(valid_spy), 2) + self.assertEqual(len(changed_spy), 2) + self.assertFalse(w.isValid()) + self.assertTrue(w.outputExtent().isNull()) + w.clear() + self.assertEqual(len(valid_spy), 2) + self.assertEqual(len(changed_spy), 2) + self.assertTrue(w.outputExtent().isNull()) + w.setOutputExtentFromOriginal() + self.assertEqual(len(valid_spy), 3) + self.assertEqual(len(changed_spy), 3) + self.assertTrue(w.isValid()) + self.assertEqual(w.outputExtent(), QgsRectangle(1, 2, 3, 4)) + if __name__ == '__main__': unittest.main()