Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Add unit tests for QgsFilterLineEdit, improve docs, add clearValue slot
(cherry-picked from edcc247)
  • Loading branch information
nyalldawson committed Sep 8, 2016
1 parent c022818 commit 109c003
Show file tree
Hide file tree
Showing 5 changed files with 209 additions and 25 deletions.
60 changes: 49 additions & 11 deletions 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 <qgsfilterlineedit.h>
%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 );

Expand Down
6 changes: 3 additions & 3 deletions src/gui/qgsfilterlineedit.cpp
Expand Up @@ -51,8 +51,7 @@ void QgsFilterLineEdit::mousePressEvent( QMouseEvent* e )

if ( shouldShowClear() && clearRect().contains( e->pos() ) )
{
clear();
emit cleared();
clearValue();
}
}

Expand Down Expand Up @@ -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 )
Expand Down
60 changes: 49 additions & 11 deletions src/gui/qgsfilterlineedit.h
Expand Up @@ -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 );

Expand All @@ -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:
Expand Down
1 change: 1 addition & 0 deletions tests/src/python/CMakeLists.txt
Expand Up @@ -47,6 +47,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(PyQgsGeometryGeneratorSymbolLayerV2 test_qgsgeometrygeneratorsymbollayerv2.py)
Expand Down
107 changes: 107 additions & 0 deletions 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()

0 comments on commit 109c003

Please sign in to comment.