diff --git a/python/gui/qgsfilterlineedit.sip b/python/gui/qgsfilterlineedit.sip index 8a1e53e03c7..870921c2aff 100644 --- a/python/gui/qgsfilterlineedit.sip +++ b/python/gui/qgsfilterlineedit.sip @@ -1,46 +1,84 @@ - -/** LineEdit with builtin clear button - */ +/** \class QgsFilterLineEdit + * \ingroup gui + * QLineEdit subclass with built in support for clearing the widget's value and + * handling custom null value representations. + * + * When using QgsFilterLineEdit the value(), setValue() and clearValue() methods should be used + * instead of QLineEdit's text(), setText() and clear() methods, and the valueChanged() + * signal should be used instead of textChanged(). + **/ class QgsFilterLineEdit : QLineEdit { %TypeHeaderCode #include %End public: + + /** Constructor for QgsFilterLineEdit. + * @param parent parent widget + * @param nullValue string for representing null values + */ QgsFilterLineEdit( QWidget* parent /TransferThis/ = 0, const QString& nullValue = QString::null ); + /** Sets the string representation for null values in the widget. This does not + * affect the values returned for null values by value(), rather it only affects + * the text that is shown to users when the widget's value is null. + * @param nullValue string to show when widget's value is null + * @see nullValue() + */ void setNullValue( const QString& nullValue ); + /** Returns the string used for representating null values in the widget. + * @see setNullValue() + * @see isNull() + */ QString nullValue() const; /** - * Sets the current text with NULL support + * Sets the current text for the widget with support for handling null values. * - * @param value The text to set. If a Null string is provided, the text will match the nullValue. + * @param value The text to set. If a null string is provided, the text shown in the + * widget will be set to the current nullValue(). + * @see value() */ void setValue( const QString& value ); /** - * Returns the text of this edit with NULL support + * Returns the text of this edit with support for handling null values. If the text + * in the widget matches the current nullValue() then the returned value will be + * a null string. * - * @return Current text (Null string if it matches the nullValue property ) + * @return Current text (or null string if it matches the nullValue() property ) + * @see setValue() */ QString value() const; /** - * Determine if the current text represents Null. + * Determine if the current text represents null. * - * @return True if the value is Null. + * @return True if the widget's value is null. + * @see nullValue() */ bool isNull() const; + public slots: + + /** Clears the widget and resets it to the null value. + * @see nullValue() + */ + virtual void clearValue(); + signals: + + /** Emitted when the widget is cleared + * @see clearValue() + */ void cleared(); /** - * Same as textChanged(const QString& ) but with support for Null values. + * Same as textChanged() but with support for null values. * - * @param value The current text or Null string if it matches the nullValue property. + * @param value The current text or null string if it matches the nullValue() property. */ void valueChanged( const QString& value ); diff --git a/src/gui/qgsfilterlineedit.cpp b/src/gui/qgsfilterlineedit.cpp index 8c61f119e79..5a6fc8306c6 100644 --- a/src/gui/qgsfilterlineedit.cpp +++ b/src/gui/qgsfilterlineedit.cpp @@ -51,8 +51,7 @@ void QgsFilterLineEdit::mousePressEvent( QMouseEvent* e ) if ( shouldShowClear() && clearRect().contains( e->pos() ) ) { - clear(); - emit cleared(); + clearValue(); } } @@ -86,10 +85,11 @@ void QgsFilterLineEdit::focusInEvent( QFocusEvent* e ) } } -void QgsFilterLineEdit::clear() +void QgsFilterLineEdit::clearValue() { setText( mNullValue ); setModified( true ); + emit cleared(); } void QgsFilterLineEdit::paintEvent( QPaintEvent* e ) diff --git a/src/gui/qgsfilterlineedit.h b/src/gui/qgsfilterlineedit.h index 762d6c1ca80..6cbf19b09fa 100644 --- a/src/gui/qgsfilterlineedit.h +++ b/src/gui/qgsfilterlineedit.h @@ -22,49 +22,88 @@ class QToolButton; -/** \ingroup gui - * Lineedit with builtin clear button +/** \class QgsFilterLineEdit + * \ingroup gui + * QLineEdit subclass with built in support for clearing the widget's value and + * handling custom null value representations. + * + * When using QgsFilterLineEdit the value(), setValue() and clearValue() methods should be used + * instead of QLineEdit's text(), setText() and clear() methods, and the valueChanged() + * signal should be used instead of textChanged(). **/ class GUI_EXPORT QgsFilterLineEdit : public QLineEdit { Q_OBJECT Q_PROPERTY( QString nullValue READ nullValue WRITE setNullValue ) + Q_PROPERTY( QString value READ value WRITE setValue NOTIFY valueChanged ) public: + + /** Constructor for QgsFilterLineEdit. + * @param parent parent widget + * @param nullValue string for representing null values + */ QgsFilterLineEdit( QWidget* parent = nullptr, const QString& nullValue = QString::null ); + /** Sets the string representation for null values in the widget. This does not + * affect the values returned for null values by value(), rather it only affects + * the text that is shown to users when the widget's value is null. + * @param nullValue string to show when widget's value is null + * @see nullValue() + */ void setNullValue( const QString& nullValue ) { mNullValue = nullValue; } + /** Returns the string used for representating null values in the widget. + * @see setNullValue() + * @see isNull() + */ QString nullValue() const { return mNullValue; } /** - * Sets the current text with NULL support + * Sets the current text for the widget with support for handling null values. * - * @param value The text to set. If a Null string is provided, the text will match the nullValue. + * @param value The text to set. If a null string is provided, the text shown in the + * widget will be set to the current nullValue(). + * @see value() */ void setValue( const QString& value ) { setText( value.isNull() ? mNullValue : value ); } /** - * Returns the text of this edit with NULL support + * Returns the text of this edit with support for handling null values. If the text + * in the widget matches the current nullValue() then the returned value will be + * a null string. * - * @return Current text (Null string if it matches the nullValue property ) + * @return Current text (or null string if it matches the nullValue() property ) + * @see setValue() */ QString value() const { return isNull() ? QString::null : text(); } /** - * Determine if the current text represents Null. + * Determine if the current text represents null. * - * @return True if the value is Null. + * @return True if the widget's value is null. + * @see nullValue() */ inline bool isNull() const { return text() == mNullValue; } + public slots: + + /** Clears the widget and resets it to the null value. + * @see nullValue() + */ + virtual void clearValue(); + signals: + + /** Emitted when the widget is cleared + * @see clearValue() + */ void cleared(); /** - * Same as textChanged(const QString& ) but with support for Null values. + * Same as textChanged() but with support for null values. * - * @param value The current text or Null string if it matches the nullValue property. + * @param value The current text or null string if it matches the nullValue() property. */ void valueChanged( const QString& value ); @@ -76,7 +115,6 @@ class GUI_EXPORT QgsFilterLineEdit : public QLineEdit void leaveEvent( QEvent* e ) override; private slots: - void clear(); void onTextChanged( const QString &text ); private: diff --git a/tests/src/python/CMakeLists.txt b/tests/src/python/CMakeLists.txt index 7ab831c25f5..3d750bd6909 100644 --- a/tests/src/python/CMakeLists.txt +++ b/tests/src/python/CMakeLists.txt @@ -46,6 +46,7 @@ ADD_PYTHON_TEST(PyQgsFeature test_qgsfeature.py) ADD_PYTHON_TEST(PyQgsProject test_qgsproject.py) ADD_PYTHON_TEST(PyQgsFeatureIterator test_qgsfeatureiterator.py) ADD_PYTHON_TEST(PyQgsField test_qgsfield.py) +ADD_PYTHON_TEST(PyQgsFilterLineEdit test_qgsfilterlineedit.py) ADD_PYTHON_TEST(PyQgsFontUtils test_qgsfontutils.py) ADD_PYTHON_TEST(PyQgsGeometryAvoidIntersections test_qgsgeometry_avoid_intersections.py) ADD_PYTHON_TEST(PyQgsGeometryGeneratorSymbolLayer test_qgsgeometrygeneratorsymbollayer.py) diff --git a/tests/src/python/test_qgsfilterlineedit.py b/tests/src/python/test_qgsfilterlineedit.py new file mode 100644 index 00000000000..2046cfb510f --- /dev/null +++ b/tests/src/python/test_qgsfilterlineedit.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +"""QGIS Unit tests for QgsFilterLineEdit + +.. note:: 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. +""" +__author__ = 'Nyall Dawson' +__date__ = '20/08/2016' +__copyright__ = 'Copyright 2016, The QGIS Project' +# This will get replaced with a git SHA1 when you do a git archive +__revision__ = '$Format:%H$' + +import qgis # NOQA + +from qgis.gui import QgsFilterLineEdit + +try: + from qgis.PyQt.QtTest import QSignalSpy + use_signal_spy = True +except: + use_signal_spy = False + +from qgis.testing import start_app, unittest + +start_app() + + +class TestQgsFilterLineEdit(unittest.TestCase): + + def testGettersSetters(self): + """ test widget getters/setters """ + w = qgis.gui.QgsFilterLineEdit() + + w.setNullValue('null') + self.assertEqual(w.nullValue(), 'null') + w.setValue('value') + self.assertEqual(w.value(), 'value') + self.assertEqual(w.text(), 'value') + + def testNullValueHandling(self): + """ test widget handling of null values """ + w = qgis.gui.QgsFilterLineEdit() + + # start with no null value + w.setValue(None) + self.assertTrue(w.isNull()) + w.setValue('a') + self.assertEqual(w.text(), 'a') + self.assertFalse(w.isNull()) + + # set a null value + w.setNullValue('null') + self.assertEqual(w.value(), 'a') + self.assertEqual(w.text(), 'a') + self.assertFalse(w.isNull()) + + w.setValue(None) + self.assertTrue(w.isNull()) + self.assertFalse(w.value()) + self.assertEqual(w.text(), 'null') + + w.setValue('null') + self.assertEqual(w.text(), 'null') + # ND: I don't think this following logic is correct - should be a distinction between + # the widget's representation of null and the actual value. Ie isNull() + # should be false and value() should return 'null' + # in other words - if you break this test to match my desired behaviour, feel free to remove it! + self.assertTrue(w.isNull()) + self.assertFalse(w.value()) + + def testClear(self): + """ test clearing widget """ + w = qgis.gui.QgsFilterLineEdit() + + w.setValue('abc') + w.clearValue() + self.assertTrue(w.isNull()) + self.assertFalse(w.value()) + w.clearValue() + self.assertTrue(w.isNull()) + self.assertFalse(w.value()) + + w.setNullValue('def') + w.setValue('abc') + self.assertFalse(w.isNull()) + w.clearValue() + self.assertEqual(w.text(), 'def') + self.assertTrue(w.isNull()) + self.assertFalse(w.value()) + + @unittest.skipIf(not use_signal_spy, "No QSignalSpy available") + def test_ChangedSignals(self): + """ test that signals are correctly emitted when clearing""" + + w = qgis.gui.QgsFilterLineEdit() + + cleared_spy = QSignalSpy(w.cleared) + w.setValue('1') + w.clearValue() + + self.assertEqual(len(cleared_spy), 1) + + +if __name__ == '__main__': + unittest.main()