14.5.  Subclassing QValidator

[ fromfile: validation.xml id: home-made-valid ]

When the requirements for validating user input go beyond simple numeric range-checking or validation with a regular expression, you can define your own validator class by deriving from QValidator. For the next example, we define a palindrome as a string that reads the same backward or forward, ignoring case, white space, and punctuation. Unfortunately, it is not possible for a single regular expression to determine whether strings of arbitrary size are palindromes. Example 14.9 shows Palindate, a QValidator that can used to verify if a given string is a palindrome.

Example 14.9. src/validate/palindrome/palindate.h

#include <QValidator>
#include <QString>

class Palindate : public QValidator {
   Q_OBJECT
public:
   explicit Palindate(QObject* parent = 0);
   QValidator::State validate(QString& input, int&) const;
};

<include src="src/validate/palindrome/palindate.h" href="src/validate/palindrome/palindate.h" id="palindateh" allfiles="1" mode="cpp"/>


The QValidator class has one required override, validate(), defined in Example 14.10. It makes a lowercase copy, inpStr, of the given string, and removes all white space and punctuation from it. It then compares inpStr with the string revStr, which contains the same characters but in reverse order.

Example 14.10. src/validate/palindrome/palindate.cpp

[ . . . . ]
QValidator::State Palindate::validate(QString& str, int& ) const {
   QString inpStr(str.toLower());
   QString skipchars("-_!,;. \t");
   foreach(QChar ch, skipchars) 
      inpStr = inpStr.remove(ch);       1
   QString revStr;                      2
   for(int i=inpStr.length(); i > 0; --i) 
      revStr.append(inpStr[i-1]);
   if(inpStr == revStr) 
      return Acceptable;
   else
      return Intermediate;
}

1

You could do this faster with a regex.

2

Surprising there is no reverse() function.

<include src="src/validate/palindrome/palindate.cpp" href="src/validate/palindrome/palindate.cpp" id="palindatecpp" mode="cpp"/>


Example 14.11 defines a widget that contains a QLineEdit to test the validator.

Example 14.11. src/validate/palindrome/palindromeform.h

[ . . . . ]
class PalindromeForm : public QWidget {
   Q_OBJECT
public:
   PalindromeForm(QWidget* parent=0);
   QString getPalindrome();
public slots:
   void showResult();
   void again();
private:
   Palindate* m_Palindate;
   QLineEdit* m_LineEdit;
   QLabel* m_Result;
   QString m_InputString;
   void setupForm();
};
[ . . . . ]

<include src="src/validate/palindrome/palindromeform.h" href="src/validate/palindrome/palindromeform.h" id="pformh" mode="cpp"/>


In Example 14.12, the widgets are set up, and a Palindate validator is set on the QLineEdit.

Example 14.12. src/validate/palindrome/palindromeform.cpp

[ . . . . ]
PalindromeForm::PalindromeForm(QWidget* parent) : QWidget(parent),
  m_Palindate(new Palindate),
  m_LineEdit(new QLineEdit),
  m_Result(new QLabel) {
    setupForm();
}

void PalindromeForm::setupForm() {
   setWindowTitle("Palindrome Checker");
   m_LineEdit->setValidator(m_Palindate);
   connect(m_LineEdit, SIGNAL(returnPressed()),
           this, SLOT(showResult()));
[ . . . . ]
}
void PalindromeForm::showResult() {
   QString str = m_LineEdit->text();
   int pos(0);
   if(m_Palindate->validate(str,pos) == QValidator::Acceptable) {
      m_InputString = str;
      m_Result->setText("Valid Palindrome!");
   }
   else {
      m_InputString = "";
      m_Result->setText("Not a Palindrome!");
   }
}
[ . . . . ]

<include src="src/validate/palindrome/palindromeform.cpp" href="src/validate/palindrome/palindromeform.cpp" id="pformcpp" mode="cpp"/>


Figure 14.5 shows a screenshot of the running application.

Figure 14.5.  Palindrome Checker

Palindrome Checker