Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Validation behaviour too confusing for end user
Forum Updated to NodeBB v4.3 + New Features

Validation behaviour too confusing for end user

Scheduled Pinned Locked Moved Solved General and Desktop
18 Posts 3 Posters 10.7k Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • VRoninV VRonin

    @JNBarchan said in Validation behaviour too confusing for end user:

    I can only guess that the widget lets him type intermediate entry, and then it is up to me to go recheck for "truly" Acceptable

    You can easily do this on editingFinished() signal. This also applies to the default behaviour of QRegularExpressionValidator if you don't type anything after the decimal separator


    let's try this:
    (again, sorry for C++ but I have no idea how you would subclass in Python)

    class LenientRegExpValidator : public QRegularExpressionValidator{
    Q_OBJECT
    Q_DISABLE_COPY(LenientRegExpValidator)
    public:
    LenientRegExpValidator(QObject* parent = Q_NULLPTR) : QRegularExpressionValidator(parent){}
    LenientRegExpValidator(const QRegularExpression &re, QObject *parent = Q_NULLPTR) : QRegularExpressionValidator(re,parent){}
    QValidator::State validate(QString &input, int &pos) const Q_DECL_OVERRIDE{
    const auto regExpr = regularExpression();
    if(regExpr.pattern().isEmpty())
    return Acceptable;
    const auto fullMatch = regExpr.match(input);
    if (fullMatch.hasMatch())
    return QValidator::Acceptable;
    const auto partialMatch = regExpr.globalMatch(input, 0,QRegularExpression::PartialPreferFirstMatch);
    if(partialMatch.hasNext())
    return QValidator::Intermediate;
    return QValidator::Invalid;
    }
    };
    
    JonBJ Offline
    JonBJ Offline
    JonB
    wrote on last edited by
    #9

    @VRonin

    You can easily do this on editingFinished() signal.

    But that's the bit I don't get! What do I do in the editingFinished handler? It's not like I can return false and then Qt would refuse leaving editing of the widget (which is exactly what I am used to in systems where a validator is evaluated on attempt to move out of editing instead of each character is typed).

    On top of that, the docs state:

    Note that if there is a validator() or inputMask() set on the line edit and enter/return is pressed, the editingFinished() signal will only be emitted if the input follows the inputMask() and the validator() returns QValidator::Acceptable.

    So if I'm still returning Intermediate for what they've typed, that says I won't get the editingFinished signal anyway. Not that I see that matters, since I don't get what I would do in it even if I did receive it....

    aha_1980A VRoninV 2 Replies Last reply
    0
    • JonBJ JonB

      @VRonin

      You can easily do this on editingFinished() signal.

      But that's the bit I don't get! What do I do in the editingFinished handler? It's not like I can return false and then Qt would refuse leaving editing of the widget (which is exactly what I am used to in systems where a validator is evaluated on attempt to move out of editing instead of each character is typed).

      On top of that, the docs state:

      Note that if there is a validator() or inputMask() set on the line edit and enter/return is pressed, the editingFinished() signal will only be emitted if the input follows the inputMask() and the validator() returns QValidator::Acceptable.

      So if I'm still returning Intermediate for what they've typed, that says I won't get the editingFinished signal anyway. Not that I see that matters, since I don't get what I would do in it even if I did receive it....

      aha_1980A Offline
      aha_1980A Offline
      aha_1980
      Lifetime Qt Champion
      wrote on last edited by
      #10

      @JNBarchan said in Validation behaviour too confusing for end user:

      @VRonin

      You can easily do this on editingFinished() signal.

      But that's the bit I don't get! What do I do in the editingFinished handler? It's not like I can return false and then Qt would refuse leaving editing of the widget (which is exactly what I am used to in systems where a validator is evaluated on attempt to move out of editing instead of each character is typed).

      No, the QValidator works different. it forbids entering chars that lead to non-acceptable cases. for your example, it forbids entering a second . when there's already one. this is very convenient once you get used to.

      On top of that, the docs state:

      Note that if there is a validator() or inputMask() set on the line edit and enter/return is pressed, the editingFinished() signal will only be emitted if the input follows the inputMask() and the validator() returns QValidator::Acceptable.

      So if I'm still returning Intermediate for what they've typed, that says I won't get the editingFinished signal anyway. Not that I see that matters, since I don't get what I would do in it even if I did receive it....

      in editingFinished you could enable an Ok button, for example. I dont know why you would disallow moving out of the edit? you can always get focus out when the user switches to another window.

      Qt has to stay free or it will die.

      1 Reply Last reply
      0
      • JonBJ JonB

        @VRonin

        You can easily do this on editingFinished() signal.

        But that's the bit I don't get! What do I do in the editingFinished handler? It's not like I can return false and then Qt would refuse leaving editing of the widget (which is exactly what I am used to in systems where a validator is evaluated on attempt to move out of editing instead of each character is typed).

        On top of that, the docs state:

        Note that if there is a validator() or inputMask() set on the line edit and enter/return is pressed, the editingFinished() signal will only be emitted if the input follows the inputMask() and the validator() returns QValidator::Acceptable.

        So if I'm still returning Intermediate for what they've typed, that says I won't get the editingFinished signal anyway. Not that I see that matters, since I don't get what I would do in it even if I did receive it....

        VRoninV Offline
        VRoninV Offline
        VRonin
        wrote on last edited by
        #11

        @JNBarchan said in Validation behaviour too confusing for end user:

        But that's the bit I don't get! What do I do in the editingFinished handler?

        What I normally do is set the line edit background to red and maybe show a red QLabel to warn about the invalid output

        that says I won't get the editingFinished signal anyway

        Good spot. You can use textedited then

        "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
        ~Napoleon Bonaparte

        On a crusade to banish setIndexWidget() from the holy land of Qt

        JonBJ 1 Reply Last reply
        0
        • VRoninV VRonin

          @JNBarchan said in Validation behaviour too confusing for end user:

          But that's the bit I don't get! What do I do in the editingFinished handler?

          What I normally do is set the line edit background to red and maybe show a red QLabel to warn about the invalid output

          that says I won't get the editingFinished signal anyway

          Good spot. You can use textedited then

          JonBJ Offline
          JonBJ Offline
          JonB
          wrote on last edited by JonB
          #12

          Thank you all for your input.

          I have sat and thought about this carefully for a while. I am coming round to the conclusion that I cannot meet what I would desire.

          I have no problem with the behaviours of QValidator::Acceptable & QValidator::Invalid. But I do not see how the behaviour of QValidator::Intermediate helps me/meets my requirements. (Please note: I do understand how it works, that's not the issue.)

          Consider my example of a "decimal number": \d+(\.\d{1,2})?

          • If user types a letter, it immediately returns Invalid and prevents the character being placed in the widget. Perfect, since that character can never be acceptable.

          • What should I return if user deletes the single leading digit, in preparation for typing in a new one?

          1. At present it returns Invalid, preventing the user from deleting. I have said I do not like this behaviour. So, I shall be changing over to Intermediate... but how/when?

          2. If I return Intermediate instead of Invalid in all cases, this is simple. However, that allows the useless typing of a letter. It allows everything through, requiring me to check up more on completion.

          3. I start to think of only returning Intermediate instead of Invalid in certain cases, of the kind outlines in @VRonin's last posted code example. That seems possible, till I consider what code I would need. I would require something like a regular expression capturing the difference between "what is potentially heading in the right direction" as opposed to "is simply unacceptable" (e..g typing a letter). @VRonin has suggested QRegularExpression::PartialPreferFirstMatch, but from what I can see in the documentation (not tested) of "partial matches" these only allow for an "incomplete match which could be satisfied by appending further characters". That will not help with the kind of situation I am thinking of, where the user perform edits "in the start/middle of the string" to get to what he wants. I would have to think out a regular expression or code for every situation I can imagine as my definition of "intermediate" to achieve this, a non-trivial task.

          I can see that Qt's Intermediate may work in this sense assuming the user types linearly from left to right --- which is what it seems to be designed for --- but not in my sense. I also realise upon careful reflection how difficult it is to express just what should be Intermediate versus Invalid.

          You people may be familiar & happy with how Qt validators work, but I (and my potential users) may not be attuned to its way of working. At least in my case of entering a "decimal number".

          In light of the above I think I am left with "2.5" choices:

          1. Make all Invalids return Intermediate instead (coding too hard to distinguish specific cases). Then I probably need to do color-marking of "intermediate-value" widgets (how would I even do this from within existing class derived from QValidator, I don't see a method to get at the widget which is being validated?). Then I definitely would need to do "final" validation on, say, Dialog "OK" or whatever, but there are 50 dialogs with say an average of 5 widgets to validate on each one, there's no central place for me to track them all down to alter code? This is why I'm thinking I'm not going to be able to use Intemediate.

          2. a. Put up with exactly the current regular expression & behaviour. The user who attempted to use the widget and needed to delete the first digit to change it to another got completely stuck and had no idea what was wrong/what he needed to do :( But hey ho, you guys seem to like the behaviour :)
            b. Ask my stakeholder if I may change the validator to \d?(\.\d{1,2})? This is my preferred solution. In return for allowing the leading-delete, it will allow through .23, but provided the code can accept this it may be the simplest to resolve just this situation...

          Those are my thoughts! I hope you're all fascinated :)

          1 Reply Last reply
          1
          • VRoninV Offline
            VRoninV Offline
            VRonin
            wrote on last edited by
            #13

            I'm afraid there's not a generic way. In your specific case, probably a QDoubleValidator instead of a regexp one might work better.
            For other cases you'd probably need to return intermediate always and reimplement QValidator::fixup to delete (or do something with it) the line if the input is invalid

            "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
            ~Napoleon Bonaparte

            On a crusade to banish setIndexWidget() from the holy land of Qt

            JonBJ 1 Reply Last reply
            0
            • VRoninV VRonin

              I'm afraid there's not a generic way. In your specific case, probably a QDoubleValidator instead of a regexp one might work better.
              For other cases you'd probably need to return intermediate always and reimplement QValidator::fixup to delete (or do something with it) the line if the input is invalid

              JonBJ Offline
              JonBJ Offline
              JonB
              wrote on last edited by JonB
              #14

              @VRonin
              Yup, thanks for your confirmation & patience.

              I might have a look at QDoubleValidator, in case it internally handles my case better. Just to complicate things, I know it's not your area, but because I am Python/PyQt what I actually need to validate is that it satisfies an internal PyQt type named Decimal, which is not the same as double, and isn't even documented as to what format it parses...! :(

              One final (honest!) question, inspired by something I mentioned above and your earlier comment:

              What I normally do is set the line edit background to red and maybe show a red QLabel to warn about the invalid output

              How (what code approach do you take) to achieve this in Qt? So far as I can see, one uses QLineEdit::setValidator() to set the validator, so a line edit can see its validator, but QValidator does not have a member to reference the QLineEdit it has been called from? And I suspect there cannot be one, as you could associate the same QValidator object with multiple QLineEdits, if you chose to do so. So when my overridden QValidator::validate() wants to return Intermediate, how can I have code there to know which widget to affect?

              In the validator model I am accustomed to from another language/library, there is a one-to-one relationship between a control and its validator, so you can access one from the other. But I think that's not the case in Qt, so how do you manage it? I can only see creating a "lookup" table for each dialog so that I know the widget from the validator (assuming I stick to one-to-one), or maybe you sub-class QValidator and add a member for the associated QLineEdit, and that's going to be real messy for me....

              VRoninV 1 Reply Last reply
              0
              • JonBJ JonB

                @VRonin
                Yup, thanks for your confirmation & patience.

                I might have a look at QDoubleValidator, in case it internally handles my case better. Just to complicate things, I know it's not your area, but because I am Python/PyQt what I actually need to validate is that it satisfies an internal PyQt type named Decimal, which is not the same as double, and isn't even documented as to what format it parses...! :(

                One final (honest!) question, inspired by something I mentioned above and your earlier comment:

                What I normally do is set the line edit background to red and maybe show a red QLabel to warn about the invalid output

                How (what code approach do you take) to achieve this in Qt? So far as I can see, one uses QLineEdit::setValidator() to set the validator, so a line edit can see its validator, but QValidator does not have a member to reference the QLineEdit it has been called from? And I suspect there cannot be one, as you could associate the same QValidator object with multiple QLineEdits, if you chose to do so. So when my overridden QValidator::validate() wants to return Intermediate, how can I have code there to know which widget to affect?

                In the validator model I am accustomed to from another language/library, there is a one-to-one relationship between a control and its validator, so you can access one from the other. But I think that's not the case in Qt, so how do you manage it? I can only see creating a "lookup" table for each dialog so that I know the widget from the validator (assuming I stick to one-to-one), or maybe you sub-class QValidator and add a member for the associated QLineEdit, and that's going to be real messy for me....

                VRoninV Offline
                VRoninV Offline
                VRonin
                wrote on last edited by
                #15

                @JNBarchan said in Validation behaviour too confusing for end user:

                I might have a look at QDoubleValidator, in case it internally handles my case better.

                QDoubleValidator basically just uses QLocale::toDouble so should be more lenient.

                How (what code approach do you take) to achieve this in Qt?

                I normally change it to red when the user tries to submit a form

                "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                ~Napoleon Bonaparte

                On a crusade to banish setIndexWidget() from the holy land of Qt

                JonBJ 2 Replies Last reply
                0
                • VRoninV VRonin

                  @JNBarchan said in Validation behaviour too confusing for end user:

                  I might have a look at QDoubleValidator, in case it internally handles my case better.

                  QDoubleValidator basically just uses QLocale::toDouble so should be more lenient.

                  How (what code approach do you take) to achieve this in Qt?

                  I normally change it to red when the user tries to submit a form

                  JonBJ Offline
                  JonBJ Offline
                  JonB
                  wrote on last edited by JonB
                  #16

                  @VRonin said in Validation behaviour too confusing for end user:

                  How (what code approach do you take) to achieve this in Qt?

                  I normally change it to red when the user tries to submit a form

                  Sorry, this is not what I was asking. Ahh! Do you mean, you don't turn it red during QValidator::validate(), so don't access the QLineEdit from there; instead during QDialog:onOK() (or whatever it is, accept()) you enumerate each QLineEdit in the dialog and call its QLineEdit::hasAcceptableInput()? So you never try to map from validator to widget?

                  VRoninV 1 Reply Last reply
                  0
                  • JonBJ JonB

                    @VRonin said in Validation behaviour too confusing for end user:

                    How (what code approach do you take) to achieve this in Qt?

                    I normally change it to red when the user tries to submit a form

                    Sorry, this is not what I was asking. Ahh! Do you mean, you don't turn it red during QValidator::validate(), so don't access the QLineEdit from there; instead during QDialog:onOK() (or whatever it is, accept()) you enumerate each QLineEdit in the dialog and call its QLineEdit::hasAcceptableInput()? So you never try to map from validator to widget?

                    VRoninV Offline
                    VRoninV Offline
                    VRonin
                    wrote on last edited by
                    #17

                    @JNBarchan said in Validation behaviour too confusing for end user:

                    ou enumerate each QLineEdit in the dialog and call its QLineEdit::hasAcceptableInput()? So you never try to map from validator to widget?

                    Correct. The validator's job is not to take care of how an item is displayed. That's either the job of the linedit or its parent (either via connecting the textEdited() signal, using fixup() to clear invalid input and then using editingFinished() or uppon submit of form (e.g. when you press the Ok button at the end of a dialog)

                    "La mort n'est rien, mais vivre vaincu et sans gloire, c'est mourir tous les jours"
                    ~Napoleon Bonaparte

                    On a crusade to banish setIndexWidget() from the holy land of Qt

                    1 Reply Last reply
                    2
                    • VRoninV VRonin

                      @JNBarchan said in Validation behaviour too confusing for end user:

                      I might have a look at QDoubleValidator, in case it internally handles my case better.

                      QDoubleValidator basically just uses QLocale::toDouble so should be more lenient.

                      How (what code approach do you take) to achieve this in Qt?

                      I normally change it to red when the user tries to submit a form

                      JonBJ Offline
                      JonBJ Offline
                      JonB
                      wrote on last edited by
                      #18

                      @VRonin said in Validation behaviour too confusing for end user:

                      @JNBarchan said in Validation behaviour too confusing for end user:

                      I might have a look at QDoubleValidator, in case it internally handles my case better.

                      QDoubleValidator basically just uses QLocale::toDouble so should be more lenient.

                      Changed over to QDoubleValidator. Whether it implements via a different regular expression from the one I inherited or implements validation with dedicated code, either way it gives me/my user a more pleasant editing experience, allowing the original issue of being able to delete a lone leading digit. It also of course "feels" better.

                      So thank you, that will do nicely here after all this discussion (though that was worthwhile so that I now understand how Qt validators work).

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved