2014年3月25日火曜日

Qt validator 入力フォームの値をチェックする

QValidatorで入力値をチェックしてみた

GUIツールキットのQtでGUIを作っています.
QLineEditで入力フォームを作ったときに,入力された値が適切であるかどうかをQtのQValidatorを使って確認するサンプルを作成してみました.

入力値をチェックする機能を自作していたのですが,QtにはQValidatorという便利な機能があることを初めて知りました.コレを使うとスッキリと入力値がチェック出来そうです.

まずQtデザイナを使って次のようなウィンドウを作成しました.
3つのQLineEditを持つウィンドウです.それぞれ次のような役割とします.

ラベルobjectName概要
int form intLineEdit20から500までの整数を入力するフォーム
double form doubleLineEdit浮動小数を入力するフォーム
original form myLineEdit偶数の整数を入力するフォーム

QValidatorは抽象クラスであるため,これを実装したサブクラスを使用します.QtではQIntValidator, QDoubleValidatorとQRegExpValidatorが用意されているみたいですね.
  • QIntValidator 入力値がint型であるかを確認する
  • QDoubleValidator 入力値がdouble型であるかを確認する
  • QRegExpValidator 入力値が指定した正規表現の範囲にあるかを確認する
整数ならQIntValidator, 小数ならQDoubleValidator, 使用できない記号などが含まれていないか確認する場合はQRegExpValidatorと言ったところでしょうか.これ以外にもQValidatorを実装した独自に作成したクラスでもいいですね.

加えて,QIntValidatorとQDoubleValidatorは入力できる数値の範囲も指定できるようです.
// 範囲指定の例: 20から500まで
QIntValidator validator = new QIntValidator(20, 500, this);
ここではintLineEditにQIntValidator, doubleLineEditにQDoubleValidator, myLineEditに独自作成したQEvenIntValidatorを設定します.

// mainwindow.h
class MainWindow: public QMainWindow {
    Q_OBJECT
public:

 /* 省略 */

public slots:
    void intEditTextChanged(const QString& text);
    void doubleEditTextChanged(const QString& text);
    void myEditTextChanged(const QString& text);
private:
    Ui::MainWindow *ui;

    // QValidator群
    QIntValidator *intValidator;
    QDoubleValidator *doubleValidator;
    QEvenIntValidator* evenValidator;

    /**
    * チェックした状態に応じてフォームの背景色を変更します.
    * @param state 状態
    * @param *sender 入力元フォームへのポインタ
    */
    void changeColorBy(QValidator::State state, QLineEdit* sender);
};

これに対してコンストラクタでQValidatorを設定します.setValidator()メソッドで設定するvalidatorのインスタンスを引数に与えます.QIntValidatorとQDoubleValidatorはコンストラクタに入力を受け付ける最小値と最大値を指定できるようです.ここではQIntValidatorに20から500までの数値のみを受け付けるように指定しています.

// mainwindow.cpp
/**
* コンストラクタ
*/
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent), ui(new Ui::MainWindow) {

    /* 省略 */

    // テキスト変更時のシグナルとスロットを接続する
    connect(ui->intLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(intEditTextChanged(const QString&)));
    connect(ui->doubleLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(doubleEditTextChanged(const QString&)));
    connect(ui->myLineEdit, SIGNAL(textChanged(const QString&)), this, SLOT(myEditTextChanged(const QString&)));

    // QIntValidatorをintLineEditに設定する.
    intValidator = new QIntValidator(20, 500, this);
    ui->intLineEdit->setValidator(intValidator);

    // QDoubleValidatorをdoubleLineEditに設定する.
    doubleValidator = new QDoubleValidator(this);
    ui->doubleLineEdit->setValidator(doubleValidator);

    // 独自に作成したQEventIntValidatorをmyLineEditに設定する.
    evenValidator = new QEvenIntValidator(this);
    ui->myLineEdit->setValidator(evenValidator);
}

そして,各入力フォームのテキスト変更スロットで入力値の確認を行う部分をコーディングします.QValidatorのvalidate()メソッドで入力値が適切かどうかを取得します.validate()はQValitor::Stateの列挙対を返します.それぞれ
  • Acceptable
  • Intermediate
  • Invalid
です.それぞれ入力値が○,△,×と言ったところでしょうか.数値のように判定が容易な物はAcceptableとInvalidときれいに判定できますが,文字列などの場合は判定に曖昧さが残るため,中間のIntrermediateを使うみたいですね.


QLineEditのvalidator()メソッドで設定しておいたQValidatorへのポインタが取得できます.
ここでは入力値が適切であるかを確認し,その結果に応じてフォームの背景色を変えるようにしました.
// mainwindow.cpp
void MainWindow::intEditTextChanged(const QString& text) {
    QString str = text;
    int position = 0;
    QIntValidator *validator = (QIntValidator*)ui->intLineEdit->validator();
    QValidator::State state = validator->validate(str, position);
    changeColorBy(state, ui->intLineEdit);
}

void MainWindow::doubleEditTextChanged(const QString &text) {
    QString str = text;
    int position = 0;
    QDoubleValidator* validator = (QDoubleValidator*)ui->doubleLineEdit->validator();
    QValidator::State state = ui->doubleLineEdit->validator()->validate(str, position);
    changeColorBy(state, ui->doubleLineEdit);
}

void MainWindow::myEditTextChanged(const QString &text) {
    QString str = text;
    int position = 0;
    QEvenIntValidator* validator = (QEvenIntValidator*)ui->myLineEdit->validator();
    QValidator::State state = validator->validate(str, position);
    changeColorBy(state, ui->myLineEdit);
}

changeColorBy()でフォームの背景色を次のように変えるようにしました.

// mainwindow.cpp
void MainWindow::changeColorBy(QValidator::State state, QLineEdit *sender) {
    switch(state) {
    case QValidator::Acceptable:
        sender->setStyleSheet("background: white");
        break;
    case QValidator::Intermediate:
        sender->setStyleSheet("background: yellow");
        break;
    case QValidator::Invalid:
        sender->setStyleSheet("background: pink");
        break;
    }
}

独自作成したQEvenIntValidatorは偶数の整数のみを受け付けるクラスです.QIntValidatorを継承しました.
//qevenintvalidator.h
class QEvenIntValidator : public QIntValidator
{
    Q_OBJECT
public:
    explicit QEvenIntValidator(QObject *parent = 0);
    QValidator::State validate(QString& text, int pos);
};
// qevenintvalidator.cpp
QEvenIntValidator::QEvenIntValidator(QObject *parent) :
    QIntValidator(parent) {
}

QValidator::State QEvenIntValidator::validate(QString &text, int pos) {
    QValidator::State state = QIntValidator::validate(text, pos);
    if(state != QValidator::Invalid) {
        int num = text.toInt();
        state = (num % 2 == 0 ? QValidator::Acceptable : QValidator::Intermediate);
    }
    return state;
}

実際作成してみたら,全てのValidatorが数値を扱うための物であることが原因なのか,フォームに数字以外の文字を入力できなくなってしまいました.ま,いっかww.

サンプルコード

ここで作成したサンプルコードはgithubにアップロードしました.
https://github.com/hiroyky/qt_validator_sample

参考

http://qt-project.org/doc/qt-4.8/qvalidator.html
http://qt-project.org/doc/qt-4.8/qintvalidator.html
http://qt-project.org/doc/qt-4.8/qlineedit.html