2014年3月26日水曜日

QValidatorとboost::functionを使って関数型言語みたいに入力値のチェックを行うようにしてみた.

QValidatorとboost::functionの組み合わせ

はじめに

GUIツールキットのQtでGUIを作成したとき,QLineEditなどの入力フォームからの入力値が適切であるかどうかを判定する必要があります.このとき,QValidatorの機構を用いると入力値が適切であるかのチェックが簡単になります.これは前回「Qt validator 入力フォームの値をチェックする」で実装しました.

今回はboost::functionを利用して,チェック用の関数を個別に指定できるバリデートクラスを作成してみました.

関数を引数にとって判定を行うクラス

int型を対象としたQLambdaIntValidateクラスとdouble型を対象としたQLambdaDoubleValidateクラスの2つを書いてみました.この2つはint型を扱うのか,double型を扱うのかの違い意外に差異はありません.それぞれQIntValidator及びQDoubleValidatorを継承しています.
(関数を受け取るためクラスのためラムダ式のラムダから命名したのですがもっと名前ないかな~~ww)

QLambdaIntValidator

#include <qintvalidator>
#include <qdoublevalidator>
#ifndef Q_MOC_RUN
#include <boost function.hpp>
#endif

class QLambdaIntValidator: public QIntValidator {
    Q_OBJECT
    using QIntValidator::validate;
public:
    explicit QLambdaIntValidator(QObject* parent = 0);
    explicit QLambdaIntValidator(int min, int max, QObject* parent = 0);
    
    /**
     * @param function 確認用関数
     */
    explicit QLambdaIntValidator(boost::function<bool (int)> function, QObject* parent = 0);
    
    /**
     * @param function 確認用関数
     */
    explicit QLambdaIntValidator(boost::function<bool (int)> function, int min, int max, QObject* parent = 0);
    
    /**
     * int型のチェックに加え,チェックに用いる関数を設定します.
     * @param funciton チェックに用いる関数
     */
    void setValidateFunction(boost::function<bool (int)> function);
    
    /**
     * 指定した関数がfalseを返した場合,どのステートを返すかを指定します.
     * @param state false時に用いるステート
     */
    void setStateWhenFalse(QValidator::State state);
    
    /**
     * 入力値をチェックします.
     * @param text
     * @param pos
     */
    virtual QValidator::State validate(QString& text, int& pos) const;
    
private:
    boost::function<bool (int)> validateFunc;
    QValidator::State falseState;
};
プライベートメンバ変数のvalidateFuncにチェック用の関数を代入します.チェック用メソッドであるvalidate()の中で実行されます.
falseStateはvalidateFuncの戻り値がfalseのとき,QValidator::Stateの戻り値をInvalidにするかIntermediateにするかを指定します.
validate()メソッドの中身を以下に記述します.親クラスのvalidate()を実行後,戻り値がInvalidでなければ,指定されたチェック関数を行います.
QValidator::State QLambdaDoubleValidator::validate(QString &text, int &pos) const {
    QIntValidator::State state = QDoubleValidator::validate(text, pos);
    if(state != QValidator::Invalid && !validateFunc.empty()) {
        bool isOk;
        double num = text.toDouble(&isOk);
        state = (validateFunc(num) && isOk ? QValidator::Acceptable : falseState);
    }
    return state;
}

使い方(使う人いないかもだけど..)

チェック用関数を別途定義

/**
* チェック関数の例
* @param num 調べる値
* @return true=偶数, false=奇数
*/
bool isEvenNumber(int num);

このチェック関数を指定します.
QLambdaIntValidator* validator = new QLambdaIntValidator(&isEvenNumber);
lineEdit->setValidator(validator);

チェックの仕方はいつもと変わりません.
int position = 0;
QStrint str = lineEdit->text();
QLambdaIntValidator* validator = (QLambdaIntValidator*)linEdit->validator();
QValidator::State state = validator->validate(str, position);

どうでしょうか?