Search code examples
c++qtvalidationqt5qspinbox

How to implement a validator for a spin box to check if (n mod k == 0)?


I have a QSpinBox that takes ints, with a certain minimum and maximum values set. I need to set a filter on the spin box to allow only numbers divisible by a certain other integer number. This seems to be the right task for a QIntValidator. But I don't understand:

  • when to return Intermediate and when Acceptable, given that the number may have 2 to 4 digits, and until the number is input fully by the user, no decision can be made

  • how to handle setting the minimum and maximum values: currently, I set them using setMinimumValue() and setMaximumValue() respectively; what does installing a validator change in this regard?

The number can be both negative and positive.


Solution

  • QSpinBox has its own implementation of validate method inherited from its base class QAbstractSpinBox. If you want to customize the behaviour of this method, for example by using QIntValidator, you need to subclass QSpinBox and reimplement the validate method accordingly in your subclass.

    The logics you require - "allow only numbers divisible by a certain other integer number" - is beyond the range of QIntValidator's capabilities. All it can do is to say whether the given string can be converted to integer within a specified range - or whether the given string can be extended to become convertable to such an integer (Intermediate state).

    I think you could use QIntValidator as a preprocessor inside your QSpinBox subclass' validate method implementation: you call QIntValidator::validate first and if it returns the Invalid state, so does your method. But if it returns Intermediate or Acceptable, you need to proceed on your own:

    1. Take the input string and try to convert it into an integer - probably QString::toInt should suffice.
    2. Encode your own logics which looks at the received integer and determines whether it is divisible by a certain other integer. If yes, your method returns Acceptable state. If not, you need to somehow figure out whether this integer can be "extended" to become acceptable. For example, you say the number can be 2 to 4 characters long - that means that at least a subset of 1, 2 and 3-digits integers could be extended to become acceptable. If the processed integer belongs to such a subset, your method should return Intermediate, otherwise it should return Invalid.

    About min/max values: QSpinBox's setMinimum and setMaximum methods define the range which QSpinBox::validate uses to check the input. If you follow my suggestion of using QIntValidator in the beginning of validate method of your QSpinBox subclass, you should set these minimum and maximum values to QIntValidator's bottom and top properties respectively:

    class MySpinBox: public QSpinBox
    {
        <...>
    };
    
    QValidator::State MySpinBox::validate(QString &text, int &pos) const
    {
        QIntValidator validator;
        validator.setBottom(minimum());
        validator.setTop(maximum());
        QValidator::State state = validator.validate(text, pos);
        if (state == QValidator::Invalid) {
            return state;
        }
    
        <...> // Otherwise proceed with your own logics
    }