Qt basics: Widgets

Qt Support for Input Masks and Validators

By Jeff Tranter

In this blog post, part of a series on Qt basics, I'd like to explore Qt's support for input masks and validators. In this first part we'll look at the support from widgets. In a future installment we will see how these features are supported from QML.

Overview

A general principle of good UX design is to prevent users from making errors in data input. Rather than reporting invalid input that a user has entered after the fact, a better practice it to design an interface that prevents the user from entering invalid data in the first place. While this is not always possible, the use of checkboxes, radio buttons, and combo boxes are common ways to restrict the number and combination of options that a user can enter.

When a user can enter arbitrary text, controlling and validating the data is more challenging. Qt's input masks and validators are two ways in which the programmer can control the data entered by the user and to check it for validity. When programming with widgets, both are available when using a QLineEdit widget.

Input Masks

Lets look first at input masks. The QLineEdit [1] widget supports a property called inputMask. This is a string that defines the valid characters that can be entered into the widget. The input mask can be set using the method setInputMask() and returned using inputMask(). By default, a QLineEdit has no input mask and the user can enter any characters. Setting the input mask to an empty string is equivalent to having no input mask.

The mask consists of a series of characters that define the valid data that the user can enter. It uses a unique format to indicate the character classes that can be entered. The table below (taken from the Qt documentation) lists the supported input mask characters:

Character Meaning
A ASCII alphabetic character required. A-Z, a-z.
a ASCII alphabetic character permitted but not required.
N ASCII alphanumeric character required. A-Z, a-z, 0-9.
n ASCII alphanumeric character permitted but not required.
X Any character required.
x Any character permitted but not required.
9 ASCII digit required. 0-9.
0 ASCII digit permitted but not required.
S ASCII digit required. 1-9.
d ASCII digit permitted but not required (1-9).
# ASCII digit or plus/minus sign permitted but not required.
H Hexadecimal character required. A-F, a-f, 0-9.
h Hexadecimal character permitted but not required.
B Binary character required. 0-1.
b Binary character permitted but not required.
> All following alphabetic characters are uppercased.
< All following alphabetic characters are lowercased.
! Switch off case conversion.
[ ] { } Reserved.

The mask consists of a string of mask characters and separators, optionally followed by a semicolon and the character used for blanks. The blank characters are always removed from the text after editing. You can use "\" to escape the special characters listed above to use them as separators. When an input mask is set, any characters that do not match the mask are ignored on input and not displayed in the line edit. This allows meeting the goal of ensuring only valid user input can be entered.

Here are a few simple examples of input mask strings:

A four digit hexadecimal number, e.g. "FE39": "HHHH".

A six character US ZIP code, e.g. "90210": "99999".

A Canadian postal code, e.g "K2L 1S2": "A9A 9A9".

There are a few other things to note about input masks. The maxLength property of the QLineEdit will be set by the input mask, if one is defined. The QLineEdit widget emits the signals editingFinished() and returnPressed() when the Return or Enter key is pressed or the line edit loses focus. These signals will only be emitted if the input matches the input mask, when one is defined. A method hasAcceptableInput() returns a boolean value indicating if the input satisfies the inputMask (and the validator, to be covered later). You can use this method validate input. For example, you might enable a "Next" button in a wizard type UI or a "Commit" button in a dialog only when the input was acceptable.

Input Mask Example

I wrote a simple example program [6] that illustrates the use of an input mask. It provides a line edit for input text and another line edit to enter an input mask. As characters are entered, the status bar indicates whether the input is considered acceptable. It also indicates in the status bar when the editingFinished() and returnPressed() signals are emitted. A Quit button is provided to exit and a Clear button sets the line edits to empty strings. A Help button is provided to display a dialog showing the mask string characters.

Here are some screen shots:

Input mask example 1
Input mask example 2

You can run the application and experiment with different input text and input mask values, to better understand how this feature works. I encourage you to download and run the program and examine its source code. The code should work with any version of Qt 5. Most of the logic is in the file mainwindow.cpp, which is less than 100 lines long, and you should find it self-explanatory.

Validators

An input mask is only adequate for simple validation of characters. To go beyond this, Qt provides the QValidator class [2].

QValidator is an abstract base class that defines a common interface for text validators that is used by several classes that inherit from QValidator. You can also subclass QValidator to create your own custom validators.

The interface defines a state which can be returned as a property. The status of a validated string at a given time can be one of Invalid, Intermediate or Acceptable.

An Invalid string is one that is clearly invalid based on the validator's criteria, and an Acceptable string is one that is valid as a final result (the same as for an input mask). The Intermediate state is used to indicate a string which is incomplete but valid as a partial input that is in the process of being entered by the user.

It also defines a method fixup() which accepts a string and attempts to modify it to be valid according to the validator's rules.

Qt provides four subclasses that implement specific validators to cover a number of common validation use cases.

QIntValidator [3] validates a string that should contain an integer within a given range. It provides properties to set the range of lower and upper numeric values.

Similarly, QDoubleValidator [4] validates a string representing a floating point number. As well as the lower and upper ranges of valid values, it supports specifying the number of decimal places as well as whether the string should use standard floating point notation (e.g "1000") or scientific notation (e.g. "1E3"). The validator takes into account the current locale when interpreting the valid characters for digits and the decimal symbol.

The QRegularExpressionValidator [5] class checks a string against a regular expression. It has a property of type QRegularExpression to store the regular expression to match against. This provides a very general method for checking the validity of a string.

Finally, QRegExpValidator is a similar class to the previous one, but uses a regular expression of type QRegExp. In Qt 5, the QRegularExpression class was introduced as an improvement over the QRegExp class used in earlier Qt releases. While QRegExp is still supported for backward compatibility, it is recommended to use QRegularExpression in new code.

To use a QValidator with a QLineEdit, you create a suitable QValidator-derived object and then call setValidator() on the line edit. The line edit will now validate the string using the validator, much as it does when using an input mask. The input will be constrained to the relevant characters and the acceptableInput() method will return if the input is acceptable as a final result.

The editingFinished() and returnPressed() signals will only be emitted if the input is acceptable to the validator.

The validator() method will return the validator set for the line edit. You can clear the validator by passing zero or a null pointer to setValidator().

As well as QLineEdit, the only other standard Qt widget that supports a validator is QComboBox, and it uses it in a similar manner to the line edit.

Validators Example

I also wrote an example program [6] to illustrate the use of validators. It provides a user interface with a QLineEdit for input text. You can select whether to use a validator for type integer, double, or regular expression. Given the type, you can specify the validator parameters such as the range of values for the numeric validators or the regular expression string. As you enter text into the line edit, the status bar indicates of the input is acceptable to the validator.

Some screen shots are shown below:

Validator example 1

Validator example 2

Validator example 3

The program is useful for understanding the behaviour of the validators, especially for testing regular expression strings. I would encourage you to download the source code [6] build and run it, and examine how it was implemented. The code should be self-explanatory. It requires Qt 5.7.0 or later, as I used a feature (qOverload) that was introduced in that version of Qt.

If you want to extend the example, I can suggest a few enhancements you could try making as an exercise:

  • Add support for using a QRegExpValidator.
  • Change the code to use a QComboBox instead of a QLineEdit.
  • Add a comob box that allows setting the locale, and see how the numeric validators take the locale into affect.
  • Provide a combo box with some predefined regular expressions for specific values (e.g. IP address, hardware MAC address, email address, lottery number).
  • Implement your own custom validator (as a sub-class of QValidator) with some special behavior that you specify.

Combining Input Masks and Validators

You may have been wondering if you can use both an input mask and a validator at the same time. In fact, you can. Doing so requires some care. I would recommend you experiment and carefully test your code to ensure that you get the behavior that you want. I extended my previous example to add support for an input mask. You can try the code [6] and experiment with it yourself.

Here's a screen shot:

Masks and validators example

Summary

The Qt source includes two examples of using input masks and validators. You can find them under a Qt install under examples/widgets/widgets/lineedits and examples/widgets/widgets/validators. Input masks and validators are a useful feature of Qt widgets. With only a little additional programming effort, you can leverage them to improve the usability and error checking of your application's user interface.

When using QML, similar facilities are provided. We'll look at this in a future blog post.

References

  1. http://doc.qt.io/qt-5/qlineedit.html
  2. http://doc.qt.io/qt-5/qvalidator.html
  3. http://doc.qt.io/qt-5/qintvalidator.html
  4. http://doc.qt.io/qt-5/qdoublevalidator.html
  5. http://doc.qt.io/qt-5/qregularexpressionvalidator.html
  6. https://github.com/tranter/blogs/tree/master/MasksValidators