An hexadecimal spin box

The fifth chapter of "C++ GUI Programming with Qt 4, Second Edition" is dedicated to custom widget. The first part is an interesting example that shows how to extends a specific widget by subclassing it.

The specific case treated is this: we know the widget QSpinBox and we like it, but we would like to use it to choose an hexadecimal value, and this kind of customization could not be get by setting of properties of the standard widget.

A solution to this problem is subclassing QSpinBox modifying the original behavior as requested.

Subclassing a widget has just one difference from the normal C++ inheritance mechanism, that is the use of the Q_OBJECT macro, a requirement for every QObject subclass:

// ...
#include
// ...

class HexSpinBox : public QSpinBox
{
Q_OBJECT
public:
HexSpinBox(QWidget *parent = 0);

// ...
};

Here we need to add a private custom data member, a validator that we are going to use to ensure our spin box would accept only hexadecimal values:

// ...
class QRegExpValidator;
// ...

class HexSpinBox : public QSpinBox
{
// ...
private:
QRegExpValidator *validator;

};

In the constructor we reset the default range for the spin box, and instantiate the validator:

HexSpinBox::HexSpinBox(QWidget *parent) : QSpinBox(parent)
{
setRange(0, 255);
validator = new QRegExpValidator(QRegExp("[0-9A-Fa-f]{1,8}"), this);
}

The validator would ensure that the user could insert only hexadecimal values. Notice that the number of digit allowed, 1 to 8, does not make perfect sense, when the range is set in the interval [0, 255]. It would made more sense to set it to 1 to 2 and change it accordingly when the range changes.

For instance, that would be the code that should be execute to rearrange the validator when the maximum value changes:

int digits = 1;
while(max /= 16)
++digits;
QString s = QString("[0-9A-Fa-f]{1,%1}").arg(digits);
QRegExp re(s);

validator->setRegExp(re);

To make our class working properly we need to redefine a few protected function:

protected:
QValidator::State validate(QString &text, int &pos) const;
int valueFromText(const QString &text) const;
QString textFromValue(int value) const;

The first one is used by the spin box to check if the character the user is trying to insert is admissible. We check it using our validator:

QValidator::State HexSpinBox::validate(QString &text, int &pos) const
{
return validator->validate(text, pos);
}

The second one is used internally to let the arrows in the spin box to work correctly, converting the increased (or decreased) integer value in its correct representation:

QString HexSpinBox::textFromValue(int value) const
{
return QString::number(value, 16).toUpper();
}

The third one is used by the spin box to convert the string input by the used in the number that is going to be stored internally as the current value:

int HexSpinBox::valueFromText(const QString &text) const
{
return text.toInt(0, 16);
}

Notice that QString::toInt() requires as first parameter a pointer to a bool where the function stores true if the call succeeded. Since we have no use in this check - there is no way to signal to the valueFromText() caller the possible function failure but throwing an exception, that would be something unexpected and, well, a bit to strong as a reaction - we can simply pass a NULL pointer instead.

I suggest you reading "C++ GUI Programming with Qt 4, Second Edition" by Jasmin Blanchette and Mark Summerfield for more details.

No comments:

Post a Comment