Qt TextInput バリデーターの使い方:整数・数値・正規表現による入力制限

2025-04-26

TextInput.validator とは

Qt Quick (QML) における TextInput 要素の validator プロパティは、ユーザーが入力したテキストが特定の条件を満たしているかどうかを検証(バリデーション)するために使用されます。バリデーターを設定することで、不正な入力や意図しない形式の入力を防ぎ、アプリケーションのデータの整合性を保つことができます。

主な役割

  • データ整合性
    アプリケーションで使用するデータの形式を事前に保証することで、後続の処理でのエラーを防ぎます。
  • 入力フィードバック
    入力がバリデーションに失敗した場合、ユーザーに視覚的なフィードバック(例えば、テキストの色を変えるなど)を提供できます。
  • 入力制限
    ユーザーが入力できる文字の種類や形式を制限します。例えば、数字のみ、特定の文字セットのみ、特定のパターンに合致する文字列のみを受け付けるように設定できます。

設定方法

TextInput.validator プロパティには、以下のいずれかのオブジェクトを設定できます。

  1. Validator 型のオブジェクト
    これは抽象クラスであり、直接使用することは稀です。通常は、この派生クラスである IntValidatorDoubleValidatorRegExpValidator のいずれかを使用します。

  2. カスタムのバリデーターオブジェクト
    JavaScript で独自のバリデーションロジックを実装したオブジェクトを validator プロパティに割り当てることも可能です。

主なバリデーターの種類

  • RegExpValidator
    正規表現 (regExp) にマッチする入力を検証します。非常に柔軟なバリデーションが可能です。
  • DoubleValidator
    浮動小数点数の入力を検証します。最小値 (bottom)、最大値 (top)、小数点以下の桁数 (decimals) などを設定できます。
  • IntValidator
    整数の入力を検証します。最小値 (bottom) と最大値 (top) を設定できます。

使用例

以下に、それぞれのバリデーターの使用例を示します。

IntValidator の例 (整数のみ入力可能、1から100の範囲)

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    width: 200
    height: 50

    TextInput {
        id: integerInput
        anchors.fill: parent
        validator: IntValidator { bottom: 1; top: 100 }
        text: ""
        placeholderText: "1から100の整数を入力"
    }
}

DoubleValidator の例 (浮動小数点数のみ入力可能、0.0から10.0の範囲、小数点以下2桁まで)

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    width: 200
    height: 50

    TextInput {
        id: doubleInput
        anchors.fill: parent
        validator: DoubleValidator { bottom: 0.0; top: 10.0; decimals: 2 }
        text: ""
        placeholderText: "0.0から10.0の数値を入力 (小数点以下2桁まで)"
    }
}

RegExpValidator の例 (英数字のみ入力可能)

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    width: 200
    height: 50

    TextInput {
        id: alphanumericInput
        anchors.fill: parent
        validator: RegExpValidator { regExp: /^[a-zA-Z0-9]*$/ }
        text: ""
        placeholderText: "英数字を入力"
    }
}

カスタムバリデーターの例 (入力された文字列の長さが5文字以上であるか検証)

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    width: 200
    height: 50

    TextInput {
        id: customInput
        anchors.fill: parent
        validator: ({
            validate: function(text, pos) {
                if (text.length >= 5) {
                    return InputValidator.Acceptable;
                } else if (text.length < 5 && text.length + (text.length - pos) >= 5) {
                    return InputValidator.Intermediate;
                } else {
                    return InputValidator.Invalid;
                }
            }
        })
        text: ""
        placeholderText: "5文字以上入力"
    }
}

バリデーションの状態

バリデーターは、入力されたテキストの状態に応じて以下の値を返します。

  • InputValidator.Acceptable
    入力は有効です。
  • InputValidator.Intermediate
    入力は部分的または一時的に無効ですが、さらに文字が入力されることで有効になる可能性があります。
  • InputValidator.Invalid
    入力が完全に無効です。


よくあるエラーとトラブルシューティング

TextInput.validator を使用する際に遭遇しやすいエラーとその解決策を以下に示します。

バリデーターが期待通りに動作しない

  • トラブルシューティング
    • 設定の確認
      IntValidatorDoubleValidator の場合は bottomtopdecimals などのプロパティが意図した値になっているか確認してください。RegExpValidator の場合は、正規表現 (regExp) が目的のパターンに合致しているか、正規表現テスターなどを利用して検証してください。
    • カスタムバリデーターの確認
      カスタムバリデーター(JavaScript オブジェクト)を使用している場合は、validate 関数のロジックが正しいか、特に InputValidator.InvalidInputValidator.IntermediateInputValidator.Acceptable の戻り値が適切に設定されているか確認してください。pos 引数の扱いも注意が必要です。
    • 意図しないバリデーション
      他の場所で textChanged シグナルなどを利用してテキストを操作している場合、その処理がバリデーターの動作に影響を与えている可能性があります。関連するコードを見直してください。
    • バリデーターの再設定
      動的にバリデーターを変更している場合、タイミングや設定漏れがないか確認してください。
  • 原因
    バリデーターの設定が間違っている、または意図したロジックになっていない可能性があります。

入力が拒否されるべきでないのに拒否される

  • トラブルシューティング
    • 制約の見直し
      IntValidatorDoubleValidator の範囲設定、RegExpValidator の正規表現が、許可したい入力まで拒否していないか確認してください。
    • 中間状態の考慮
      validate 関数で InputValidator.Intermediate を適切に返しているか確認してください。例えば、数字入力中にマイナス記号や小数点などが一時的に入力されるのは許容したい場合があります。
  • 原因
    バリデーターの制約が厳しすぎる可能性があります。

入力が許可されるべきなのに拒否されない

  • トラブルシューティング
    • 制約の強化
      IntValidatorDoubleValidator の範囲設定、RegExpValidator の正規表現が、禁止したい入力を適切に捕捉しているか確認してください。
    • エッジケースのテスト
      想定される様々な入力パターンを試して、バリデーターが正しく動作するか確認してください。特に、境界値や異常な入力などをテストすることが重要です。
  • 原因
    バリデーターの制約が緩すぎる、または設定が不十分な可能性があります。

カスタムバリデーターでエラーが発生する

  • トラブルシューティング
    • コンソール出力
      console.log() などを使用して、validate 関数の引数や処理の途中の値を出力し、挙動を確認してください。
    • デバッガー
      Qt Creator のデバッガーを使用して、JavaScript のコードをステップ実行し、エラー箇所を特定してください。
    • エラーハンドリング
      必要に応じて try-catch ブロックなどでエラーハンドリングを行い、予期せぬエラーでバリデーションが停止しないように対策してください。
  • 原因
    JavaScript の構文エラーやロジックエラーが validate 関数内に存在している可能性があります。

バリデーションの結果がUIに反映されない

  • トラブルシューティング
    • シグナルとスロット
      TextInputtextChanged シグナルなどを利用して、バリデーションの結果に応じてテキストの色や他のUI要素の状態を変更する処理を実装してください。
    • プロパティバインディング
      バリデーションの状態を監視し、それに応じてUIプロパティをバインディングすることも有効です。
  • 原因
    バリデーションの結果に基づいてUIを更新する処理が実装されていない可能性があります。

パフォーマンスの問題 (複雑な正規表現など)

  • トラブルシューティング
    • 正規表現の最適化
      正規表現を見直し、より効率的なパターンに変更できないか検討してください。
    • 処理の最適化
      カスタムバリデーター内の処理を軽量化し、不要な処理を避けてください。
    • 非同期処理
      時間のかかるバリデーションが必要な場合は、非同期処理を検討し、UIスレッドをブロックしないようにしてください。
  • 原因
    非常に複雑な正規表現を使用したり、カスタムバリデーター内で時間のかかる処理を行ったりすると、UIの応答性が悪くなる可能性があります。
  • Qtのドキュメント
    Qtの公式ドキュメントは、各クラスやプロパティの詳細な情報を提供しています。困ったときは必ず参照してください。
  • 簡単なテストケース
    問題を再現する最小限のコードを作成し、そこでデバッグを行うと、原因を特定しやすくなります。
  • ログ出力
    バリデーターの動作に関する重要な情報をログに出力するように実装すると、問題の特定に役立ちます。


基本的な例

まず、最も基本的な例として、整数のみを入力可能にする TextInput を作成します。

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    width: 200
    height: 50

    TextInput {
        id: integerInput
        anchors.fill: parent
        validator: IntValidator {} // デフォルトではすべての整数を許可
        text: ""
        placeholderText: "整数を入力"
    }
}

この例では、TextInputvalidator プロパティに IntValidator を設定しています。これにより、ユーザーは整数以外の文字を入力できなくなります。

入力範囲を制限する例 (IntValidator)

次に、入力できる整数の範囲を制限する例です。

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    width: 200
    height: 50

    TextInput {
        id: rangedIntegerInput
        anchors.fill: parent
        validator: IntValidator { bottom: 0; top: 100 } // 0から100までの整数のみ許可
        text: ""
        placeholderText: "0から100までの整数を入力"
    }
}

ここでは、IntValidatorbottom プロパティと top プロパティを設定することで、入力可能な整数の最小値と最大値を指定しています。

浮動小数点数を入力可能にする例 (DoubleValidator)

浮動小数点数を入力可能にし、小数点以下の桁数を制限する例です。

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    width: 200
    height: 50

    TextInput {
        id: doubleInput
        anchors.fill: parent
        validator: DoubleValidator { decimals: 2 } // 小数点以下2桁まで許可
        text: ""
        placeholderText: "浮動小数点数を入力 (小数点以下2桁まで)"
    }
}

DoubleValidatordecimals プロパティで、許可する小数点以下の桁数を指定しています。bottomtop プロパティを使えば、数値の範囲も制限できます。

正規表現で入力を検証する例 (RegExpValidator)

特定のパターンに合致する入力のみを許可するために RegExpValidator を使用する例です。ここでは、英数字のみを入力可能にします。

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    width: 200
    height: 50

    TextInput {
        id: alphanumericInput
        anchors.fill: parent
        validator: RegExpValidator { regExp: /^[a-zA-Z0-9]*$/ } // 英数字のみ許可する正規表現
        text: ""
        placeholderText: "英数字を入力"
    }
}

RegExpValidatorregExp プロパティに、入力がマッチしなければならない正規表現を設定します。^[a-zA-Z0-9]*$ は、「行の先頭から末尾まで、英字(大文字・小文字)または数字が0回以上繰り返される」という意味の正規表現です。

カスタムバリデーターの例 (JavaScript)

JavaScript を使用して独自のバリデーションロジックを実装する例です。ここでは、入力された文字列の長さが5文字以上であることを検証します。

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    width: 200
    height: 50

    TextInput {
        id: customLengthInput
        anchors.fill: parent
        validator: ({
            validate: function(text, pos) {
                if (text.length >= 5) {
                    return InputValidator.Acceptable;
                } else if (text.length < 5 && text.length + (text.length - pos) >= 5) {
                    return InputValidator.Intermediate;
                } else {
                    return InputValidator.Invalid;
                }
            }
        })
        text: ""
        placeholderText: "5文字以上入力"
    }
}

この例では、validator プロパティに JavaScript オブジェクトを直接代入しています。このオブジェクトは validate 関数を持つ必要があります。validate 関数は、現在のテキスト (text) とカーソル位置 (pos) を引数として受け取り、以下のいずれかの値を返します。

  • InputValidator.Invalid: 入力は完全に無効です。
  • InputValidator.Intermediate: 入力は部分的または一時的に無効ですが、さらに文字が入力されることで有効になる可能性があります。
  • InputValidator.Acceptable: 入力は有効です。

このカスタムバリデーターは、入力された文字数が5文字以上であれば Acceptable を、5文字未満でありながら、削除操作によって5文字以上になる可能性がある場合は Intermediate を、それ以外の場合は Invalid を返します。

バリデーションの状態に応じたUIの変更

バリデーションの状態に応じて TextInput の見た目を変更する例です。ここでは、無効な入力の場合にテキストの色を赤に変更します。

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    width: 200
    height: 50

    TextInput {
        id: validatedInput
        anchors.fill: parent
        validator: IntValidator { bottom: 1; top: 10 }
        text: ""
        placeholderText: "1から10までの整数を入力"
        color: validator.validate(text, 0) === InputValidator.Invalid ? "red" : "black"
    }
}


textChanged シグナルとカスタムロジック

TextInputtextChanged シグナルを利用して、テキストが変更されるたびにカスタムの検証ロジックを実行する方法です。

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    width: 200
    height: 50

    TextInput {
        id: manualValidationInput
        anchors.fill: parent
        text: ""
        placeholderText: "何か入力"
        onTextChanged: {
            if (text.length > 10) {
                // エラー処理 (例: エラーメッセージの表示、UIの変更)
                console.log("入力が長すぎます");
                // 必要であればテキストを切り詰めるなどの処理も可能
                manualValidationInput.text = text.substring(0, 10);
            } else {
                // エラーがない場合の処理
            }
        }
    }
}

利点

  • 入力中の動的な調整
    入力中にテキストを修正したり、特定の形式に自動的に整形したりできます。
  • 詳細なフィードバック
    検証結果に応じて、より具体的なエラーメッセージをユーザーに表示したり、UIの特定の部分を変更したりできます。
  • 柔軟性
    複雑な検証ロジックを自由に実装できます。複数の条件を組み合わせたり、外部のデータソースと照合したりすることも可能です。

欠点

  • パフォーマンス
    複雑な検証を textChanged シグナルハンドラ内で行うと、入力のたびに処理が実行されるため、パフォーマンスに影響を与える可能性があります。
  • 実装の手間
    バリデーションロジックを自分で記述する必要があります。

focusLost シグナルと検証

TextInput がフォーカスを失ったタイミングで入力内容を検証する方法です。

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    width: 200
    height: 50

    TextInput {
        id: focusValidationInput
        anchors.fill: parent
        text: ""
        placeholderText: "何か入力"
        onFocusLost: {
            if (!isValidInput(text)) {
                // エラー処理
                console.log("入力が無効です");
                // UIにエラー表示など
            } else {
                // 有効な入力の場合の処理
            }
        }
    }

    function isValidInput(inputText) {
        // ここに検証ロジックを実装 (例: 空でないか、特定の形式かなど)
        return inputText.length > 0;
    }
}

利点

  • 最終的な検証
    ユーザーが入力を完了した時点でのみ検証を行いたい場合に適しています。
  • パフォーマンス
    入力中は検証を行わないため、textChanged よりもパフォーマンスへの影響が少ない場合があります。

欠点

  • リアルタイムフィードバックの遅延
    入力中にエラーがすぐに通知されないため、ユーザーエクスペリエンスが低下する可能性があります。

バインディングとカスタム検証関数

プロパティバインディングとJavaScriptのカスタム関数を組み合わせて検証を行う方法です。

import QtQuick 2.15
import QtQuick.Controls 2.15

Rectangle {
    width: 200
    height: 50

    TextInput {
        id: bindingValidationInput
        anchors.fill: parent
        text: ""
        placeholderText: "5以上の数値"
    }

    property bool isInputValid: validateNumber(bindingValidationInput.text)

    function validateNumber(text) {
        const num = Number(text);
        return !isNaN(num) && num >= 5;
    }

    Text {
        anchors.top: bindingValidationInput.bottom
        text: bindingValidationInput.text + (isInputValid ? " は有効です" : " は無効です")
        color: isInputValid ? "green" : "red"
    }
}

利点

  • UIとの連携
    バインディングを利用して、検証結果をUIに簡単に反映できます。
  • 宣言的な記述
    バリデーションの状態をプロパティとして宣言的に管理できます。

欠点

  • 入力制限
    validator のように直接入力を制限するわけではないため、無効な入力も TextInput には一時的に表示されます。入力制限も行う場合は、textChanged などと組み合わせる必要があります。

外部の検証ライブラリやコンポーネント

より複雑な検証ルールや再利用性を高めたい場合は、カスタムの検証ライブラリを作成したり、サードパーティのコンポーネントを利用したりすることも考えられます。

利点

  • 高度な機能
    より複雑な検証ルールや、特定のデータ形式に特化した検証機能などを実装できます。
  • 再利用性
    検証ロジックを複数の場所で共有できます。

欠点

  • 開発コスト
    ライブラリの作成や外部コンポーネントの導入にコストがかかる場合があります。
  • 大規模なアプリケーションや再利用性を重視する場合
    外部の検証ライブラリやコンポーネントの検討も視野に入れましょう。
  • 検証結果をUIに簡単に反映したい
    バインディングとカスタム検証関数の組み合わせが有効です。
  • 入力完了後の検証、パフォーマンス重視
    focusLost シグナルを利用した検証を検討してください。
  • 複雑なロジック、詳細なフィードバック、入力中の調整
    textChanged シグナルとカスタムロジックが適しています。
  • 簡単な数値や基本的なパターン検証
    TextInput.validator (特に IntValidator, DoubleValidator, RegExpValidator) が手軽で便利です。