Validating user input in PyQt4 using QValidator

This post demonstrates how to use the QValidator class to validate text input in PyQt4. Text validation can help the user to understand what information is required when entering data into a form. For example, preventing a user from entering letters into a text field that expects a number makes it clearer what is needed. In addition to this the colour of the text field can be changed to provide the user with a visual feedback, e.g. setting the text colour to red when an invalid input has been entered.

Basic use of QValidator with QLineEdit

To use a QValidator, create an instance of it then apply it to a QLineEdit using the setValidator method. The example below uses a QDoubleValidator to ensure that only text representing a valid double (i.e. a real number) can be input.

lineedit = QtGui.QLineEdit(self)
validator = QtGui.QDoubleValidator()
lineedit.setValidator(validator)

Multiple widgets can share a single QValidator instance. If you have multiple QLineEdit widgets that all need to accept a double you can point them all at the same validator.

Types of validator include QDoubleValidator (for doubles), QIntValidator (for integers) and QRegExpValidator (for regular expressions, which covers almost everything else).

Changing the background colour to provide feedback

To give the user some visual feedback the background colour of a QLineEdit can be changed to reflect the current validation state. The state must be either Acceptable, Intermediate or Invalid -- see the QValidator documentation for a full description. The screenshots below show a QLineEdit with a QDoubleValidator in a intermediate state (yellow) and an acceptable state (green). The only way a QLineEdit can normally be in an invalid state is if the text is changed programatically using setText or if the validator is set after the text has been entered.

The following method identifies the sender of the signal that called it, and validates it's content using the assigned validator. The background colour of the widget is then set using the setStyleSheet method.

def check_state(self, *args, **kwargs):
    sender = self.sender()
    validator = sender.validator()
    state = validator.validate(sender.text(), 0)[0]
    if state == QtGui.QValidator.Acceptable:
        color = '#c4df9b' # green
    elif state == QtGui.QValidator.Intermediate:
        color = '#fff79a' # yellow
    else:
        color = '#f6989d' # red
    sender.setStyleSheet('QLineEdit { background-color: %s }' % color)

To get this method to run every time the text changes, connect it to the QLineEdit's textChanged signal. So that the inital state of the widget is validated the textChanged signal is also emitted manually once.

lineedit.textChanged.connect(self.check_state)
lineedit.textChanged.emit(lineedit.text())

Evaluating fields before submitting a form

When the user submits a form a small function can be run to check that each input is valid. If there is an invalid field, instead of continuing with the process (resulting in an error later), the GUI can direct the user to the invalid field. The specifics of this will depend on the complexity of the form being validated, but it could be as simple as calling the setFocus() method of a QLineEdit to place the user's cursor in the offending field.

Regular expressions

The QRegExpValidator validator allows you to validate input using a regular expression. The Qt documentation for QRegExpValidator is quite clear, but it's nice to have a Python example. The QLineEdit in the following example will accept any integer between 0 and 255 inclusive.

lineedit = QtGui.QLineEdit(self)
regexp = QtCore.QRegExp('^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$')
validator = QtGui.QRegExpValidator(regexp)
lineedit.setValidator(validator)

Regular expressions are almost a language in themselves. The Python documentation includes an introduction to regular expressions.

Some people, when confronted with a problem, think “I know, I'll use regular expressions.”

Now they have two problems.

-- Jamie Zawinski, alt.religion.emacs