Giving the User Unobtrusive Feedback During Data Entry with Qt

Credit: Alex Martelli

Problem

You want to validate the contents of a line-entry widget on the fly while the user is entering data and give unobtrusive feedback about whether the current contents are valid, incomplete, or invalid.

Solution

Changing the widget’s background color to different shades is an excellent way to provide unobtrusive feedback during data entry. As usual, it’s best to package the solution as a reusable widget:

from qt import *

class ValidatingLineEdit(QLineEdit):

    colors = Qt.red, Qt.yellow, Qt.green 

    def _ _init_ _(self, validate, parent=None, name=None):
        QLineEdit._ _init_ _(self, parent, name)
        self.validate = validate
        self.connect(self, SIGNAL("textChanged(const QString &)"), self.changed)
        self.color = None
        self.changed('')

    def changed(self, newText):
        colorIndex = self.validate(unicode(newText))
        if colorIndex is None: return
        color = self.colors[colorIndex].light(196)
        if color != self.color:
            self.setPaletteBackgroundColor(color)
            self.color = color

The function passed as the validate argument must accept a Unicode string and return either None, meaning no color change, or an index into the widget’s colors attribute. By default, 0 indicates red (an incorrect entry), 1 indicates yellow (an incomplete entry), and 2 indicates green (an entry that is already acceptable).

Discussion

When the user is entering data in a line-entry field, it can be helpful to validate the field’s contents on the fly, at every change, and give unobtrusive feedback about whether the current content is valid, incomplete, or invalid. One way to do this is by setting the field’s background color accordingly (using light pastel shades, not strong contrast colors, so the feedback is unobtrusive).

Qt has a reputation for being cranky about color control, but Qt 3 now supplies the setPaletteBackgroundColor method on all widgets, which is effective for our specific purpose. This recipe packages a LineEdit widget with the minimal amount of infrastructure to ensure that the background color is changed appropriately, based on a validation function that you pass when you instantiate the widget.

Here is a simple validation function, suitable for instantiating a ValidatingLineEdit widget. As an example criterion, this function assumes that a valid entry is one containing 4, 5, or 6 digits, and no character that is not a digit:

def validate(text):
    if not text: return 1              # empty -> "incomplete"
    if not text.isdigit(  ): return 0    # nondigits -> "invalid"
    if len(text) < 4: return 1         # too short -> "incomplete"
    if len(text) > 6: return 0         # too long -> "invalid"
    return 2                           # otherwise -> "acceptable"

Note that you can also customize the widget’s colors attribute by assigning to it a tuple of QColor instances of your choice at any time. The validation function must always return either None, meaning no color change, or a valid index into the widget’s current colors attribute.

If content-validation takes a long time, you should delay validating the field and wait until the user is done with it. Often, a good time for relatively lengthy validation is when the entry widget loses focus, although it may be simplest (but maybe not as effective, ergonomically) to validate all fields only when the user clicks an OK button (the latter strategy is surely preferable when complex validation criteria depend on the contents of several widgets).

This widget’s architecture is simpler, and a bit less flexible, than the usual, recommended Qt approach. To be Qt-canonical, you should emit signals and expose slots, leaving it up to containers and applications to connect them appropriately. This is an excellent approach, and a flexible one, but simplicity also has its appeal. You should be aware of the vast potential of the signals/slots approach, but—unless you’re writing widgets for mass distribution—you can wait to architect this approach into a specific customized widget until you need it in your application.

See Also

Information about Qt is available at http://www.trolltech.com; PyQt is available and described at http://www.riverbankcomputing.co.uk/pyqt/index.php.

Get Python Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.