QSpinBoxだけじゃない!Qtで数値を扱う代替ウィジェットを比較解説

2025-05-31

QtプログラミングにおけるQSpinBox::QSpinBox()は、QSpinBoxクラスのコンストラクタを指します。

QSpinBoxは、数値を選択するためのウィジェット(部品)です。上下の矢印ボタンをクリックするか、キーボードのUp/Downキーを押すことで、表示されている数値を増減させることができます。また、数値を直接入力することも可能です。主に整数値を扱いますが、QDoubleSpinBoxという派生クラスを使えば浮動小数点数も扱えます。

QSpinBox::QSpinBox()には、いくつかのオーバーロードされたコンストラクタがあります。これらは、QSpinBoxオブジェクトをどのように初期化するかを指定するために使われます。

主なコンストラクタの形式は以下の通りです。

    • これは最も基本的なコンストラクタです。
    • parent引数は、このスピンボックスが配置される親ウィジェットを指定します。nullptr(または指定しない場合)は、親を持たない独立したウィジェットとして作成されます。
    • このコンストラクタで作成した場合、デフォルトの最小値は0、最大値は99、ステップ値(1回増減する量)は1に設定されます。初期値も0です。
  1. QSpinBox(int minValue, int maxValue, int step = 1, QWidget *parent = nullptr)

    • このコンストラクタは、スピンボックスの初期範囲とステップ値を直接指定できます。
    • minValue: 許容される最小値。
    • maxValue: 許容される最大値。
    • step: 1回増減する量(デフォルトは1)。
    • parent: 親ウィジェット。

使用例

#include <QApplication>
#include <QSpinBox>
#include <QVBoxLayout>
#include <QWidget>

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);

    QWidget *window = new QWidget;
    QVBoxLayout *layout = new QVBoxLayout(window);

    // デフォルトのコンストラクタを使用
    QSpinBox *spinBox1 = new QSpinBox(window);
    layout->addWidget(spinBox1); // 0-99, ステップ1

    // 範囲とステップ値を指定するコンストラクタを使用
    QSpinBox *spinBox2 = new QSpinBox(-100, 100, 5, window); // -100から100まで、ステップ5
    spinBox2->setValue(0); // 初期値を0に設定
    layout->addWidget(spinBox2);

    window->setWindowTitle("QSpinBox Example");
    window->show();

    return a.exec();
}

この例では、2つのQSpinBoxを作成しています。

  • spinBox2は、-100から100までの範囲で5ずつ増減するように設定されています。
  • spinBox1はデフォルトのコンストラクタで作成され、0から99までの整数値を1ずつ増減できます。


QSpinBox のコンストラクタ自体で直接エラー(クラッシュなど)が発生することは非常に稀です。なぜなら、コンストラクタは単にオブジェクトを初期化するだけであり、多くの複雑なロジックを含まないからです。しかし、コンストラクタの引数の渡し方や、その後の QSpinBox の使用方法において、期待通りの動作をしない、あるいは論理的なエラーが発生することがあります。

範囲設定の論理的なエラー (minValue と maxValue)

エラーの症状

  • setValue() で設定しても反映されない。
  • 特定の数値しか選択できない、または全く選択できない。
  • スピンボックスの数値が期待通りに増減しない。

原因
コンストラクタで minValuemaxValue よりも大きい値に設定されている場合、またはその逆の場合。

// 誤った設定例: 最小値が最大値より大きい
QSpinBox *spinBox = new QSpinBox(100, 0, 1, parentWidget); // minValue: 100, maxValue: 0

トラブルシューティング

  • もしコンストラクタで設定した後に setMinimum()setMaximum() を使用している場合、それらの関数呼び出しの順序も確認してください。一般的には、setMinimum() を呼び出してから setMaximum() を呼び出すのが安全です。
  • コンストラクタで設定した後、デバッグのために spinBox->minimum()spinBox->maximum() の値をプリントして確認してください。
  • コンストラクタ引数で minValuemaxValue より小さいことを確認してください。

親ウィジェット (parent) の問題

エラーの症状

  • スピンボックスがウィンドウを閉じても消えない(メモリリーク)。
  • スピンボックスが表示されない。

原因

  • 親ウィジェットがスコープ外に出て破棄されたときに、子ウィジェットも自動的に破棄されるのが Qt の仕組みですが、親が正しく設定されていないと、子ウィジェットがメモリに残ってしまうことがあります。
  • コンストラクタで parent を指定しなかった場合、または間違った親を指定した場合、スピンボックスが意図しない場所に表示されたり、全く表示されなかったりすることがあります。

トラブルシューティング

  • parentnullptr を指定した場合、スピンボックスは独立したトップレベルウィジェットとして表示されます。これは意図的な場合もありますが、通常はレイアウト内に配置されます。
  • QSpinBox *spinBox = new QSpinBox(this); のように、現在のウィジェットを親として渡すのが一般的です。
  • スピンボックスを配置したいレイアウトや別のウィジェットに、正しい親ウィジェットを指定しているか確認してください。

初期値の設定の誤解 (setValue())

エラーの症状

  • スピンボックスの初期値が期待通りにならない。

原因
コンストラクタでは初期値を直接設定するオーバーロードがないため、コンストラクタで範囲を設定した後に setValue() を呼び出す必要があります。setValue() を呼び出さなかった場合、デフォルト値(通常は0)が設定されます。

// コンストラクタで範囲を設定しても、初期値はデフォルトの0になる
QSpinBox *spinBox = new QSpinBox(10, 20, 1, parentWidget);
// 期待する初期値が15の場合、setValue()が必要
// spinBox->setValue(15); // これを忘れると初期値は0のまま

トラブルシューティング

  • 設定する値が、設定された minimummaximum の範囲内にあることを確認してください。範囲外の値を setValue() で設定しようとすると、最も近い有効な値に丸められます。
  • 範囲を設定した後に、setValue() を使用して適切な初期値を設定しているか確認してください。

シグナルとスロットの接続ミス

エラーの症状

  • スピンボックスの値を変更しても、関連する動作がトリガーされない。

原因
QSpinBox の値が変更されたときに特定の処理を行いたい場合、valueChanged() シグナルを適切なスロットに接続する必要があります。コンストラクタ自体の問題ではありませんが、QSpinBox の一般的な使用ミスです。

トラブルシューティング

  • スロットの引数の型がシグナルと一致していることを確認してください (int 型)。
  • connect() 関数を使って、spinBoxvalueChanged(int) シグナルを目的のスロットに接続しているか確認してください。
// 接続の確認例
connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged), this, &MyClass::onSpinBoxValueChanged);

// MyClassのヘッダーファイル
// private slots:
//     void onSpinBoxValueChanged(int value);

メモリ管理の問題

エラーの症状

  • アプリケーションがクラッシュする、またはメモリ使用量が増加し続ける。

原因
これは QSpinBox::QSpinBox() そのものの問題ではありませんが、Qt のオブジェクトツリーによるメモリ管理の原則を理解していない場合に発生し得ます。parent を正しく指定しないと、オブジェクトが自動的に破棄されず、メモリリークにつながることがあります。

トラブルシューティング

  • 親ウィジェットが破棄されるときに子ウィジェットも自動的に破棄されるため、手動で delete する必要はほとんどありません。ただし、親を持たないオブジェクトを明示的に delete する必要がある場合はあります。
  • new で作成した QSpinBox オブジェクトに対して、常に適切な親ウィジェットを指定してください。
  • Qt Designer を利用する
    まず Qt Designer で QSpinBox を配置し、プロパティを設定して期待通りの動作をするか確認するのも良い方法です。そこで問題がなければ、コードに問題がある可能性が高いです。
  • qDebug() を使用する
    コンストラクタ呼び出し後や、プロパティ設定後に qDebug() << spinBox->value(); のように出力して、現在の状態を確認します。
  • デバッガを使用する
    プログラムをステップ実行し、QSpinBox オブジェクトが正しく作成されているか、プロパティ(minimum, maximum, value など)が期待通りの値になっているかを確認します。


QSpinBox を使うには、通常、QApplicationQWidget (または QMainWindow などのメインウィンドウ)、レイアウトマネージャー (QVBoxLayout など) が必要です。

基本的な QSpinBox の作成

最もシンプルな形式で QSpinBox を作成し、親ウィジェットに配置する例です。この場合、デフォルトの範囲 (0-99) とステップ (1) が適用されます。

main.cpp

#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QSpinBox>
#include <QLabel> // オプション: スピンボックスの値を表示するため

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);

    // メインウィンドウとなるウィジェットを作成
    QWidget *window = new QWidget();
    window->setWindowTitle("QSpinBox 基本例");

    // レイアウトを作成し、ウィジェットを配置
    QVBoxLayout *layout = new QVBoxLayout(window);

    // QSpinBox の作成(親ウィジェットを指定)
    // QSpinBox::QSpinBox(QWidget *parent = nullptr) コンストラクタを使用
    QSpinBox *spinBox = new QSpinBox(window);

    // スピンボックスの値を表示するためのラベル (オプション)
    QLabel *valueLabel = new QLabel("現在の値: 0", window);

    // レイアウトにウィジェットを追加
    layout->addWidget(spinBox);
    layout->addWidget(valueLabel);

    // スピンボックスの値が変更されたときにラベルを更新するシグナルとスロットの接続
    QObject::connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged),
                     valueLabel, [=](int value){
        valueLabel->setText(QString("現在の値: %1").arg(value));
    });

    // ウィンドウを表示
    window->show();

    return a.exec();
}

解説

  • valueChanged(int) シグナルを QLabel のテキスト更新に接続しています。
  • デフォルトでは、QSpinBox の最小値は0、最大値は99、1回の増減ステップは1です。
  • new QSpinBox(window): QSpinBox オブジェクトを作成し、window を親ウィジェットとして設定します。これにより、window が破棄されるときに spinBox も自動的に破棄されます。

範囲とステップ値を指定した QSpinBox の作成

コンストラクタで直接、最小値、最大値、ステップ値を指定する例です。

main.cpp (一部抜粋)

// ... (QApplication, QWidget, QVBoxLayout, QLabel のインクルードなどは省略)

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    QWidget *window = new QWidget();
    window->setWindowTitle("QSpinBox 範囲指定例");
    QVBoxLayout *layout = new QVBoxLayout(window);

    // QSpinBox の作成(最小値、最大値、ステップ値を指定)
    // QSpinBox::QSpinBox(int minValue, int maxValue, int step = 1, QWidget *parent = nullptr) コンストラクタを使用
    QSpinBox *spinBox = new QSpinBox(-50, 50, 5, window); // -50から50まで、5ステップで増減

    // 初期値を設定(このコンストラクタではデフォルト値が minValue になるため、必要に応じて設定)
    spinBox->setValue(0); // 例えば、初期値を0に設定

    // その他の設定 (例: 接頭辞、接尾辞)
    spinBox->setPrefix("金額: ");
    spinBox->setSuffix(" 円");

    QLabel *valueLabel = new QLabel("現在の値: 0", window);

    layout->addWidget(spinBox);
    layout->addWidget(valueLabel);

    QObject::connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged),
                     valueLabel, [=](int value){
        valueLabel->setText(QString("現在の値: %1").arg(value));
    });

    window->show();
    return a.exec();
}

解説

  • setValue(0) で明示的に初期値を0に設定しています。このコンストラクタを使用した場合、初期値はデフォルトで minValue になりますので、必要に応じて変更します。
  • setPrefix()setSuffix() を使って、表示される値の前に "金額: "、後に " 円" を追加しています。これにより、ユーザーにとってより分かりやすい表示になります。
  • new QSpinBox(-50, 50, 5, window): 最小値を-50、最大値を50、ステップ値を5として QSpinBox を作成します。

QSpinBox の様々なプロパティ設定例

コンストラクタで作成した後、各種セッター関数を使って詳細な設定を行う例です。

main.cpp (一部抜粋)

// ... (QApplication, QWidget, QVBoxLayout, QLabel のインクルードなどは省略)

int main(int argc, char *argv[]) {
    QApplication a(argc, argv);
    QWidget *window = new QWidget();
    window->setWindowTitle("QSpinBox 詳細設定例");
    QVBoxLayout *layout = new QVBoxLayout(window);

    QSpinBox *spinBox = new QSpinBox(window); // デフォルトコンストラクタで作成

    // 範囲の設定
    spinBox->setRange(1, 100); // 1から100まで

    // ステップ値の設定
    spinBox->setSingleStep(10); // 10ずつ増減

    // 初期値の設定
    spinBox->setValue(50); // 初期値を50に設定

    // 接頭辞と接尾辞
    spinBox->setPrefix("数量: ");
    spinBox->setSuffix(" 個");

    // 特殊値テキスト (最小値に達したときに表示されるテキスト)
    spinBox->setSpecialValueText("無制限"); // 最小値(1)になったら「無制限」と表示

    // 循環モード (最大値/最小値に達したときにループするか)
    spinBox->setWrapping(true); // 循環モードを有効にする

    // ボタンの表示形式
    spinBox->setButtonSymbols(QAbstractSpinBox::PlusMinus); // 上下矢印ではなく +/- を表示

    // 読み取り専用にする (ユーザーが直接入力できないようにする)
    // spinBox->setReadOnly(true);

    QLabel *valueLabel = new QLabel(QString("現在の値: %1").arg(spinBox->value()), window);

    layout->addWidget(spinBox);
    layout->addWidget(valueLabel);

    QObject::connect(spinBox, QOverload<int>::of(&QSpinBox::valueChanged),
                     valueLabel, [=](int value){
        valueLabel->setText(QString("現在の値: %1").arg(value));
    });

    window->show();
    return a.exec();
}
  • setButtonSymbols(QAbstractSpinBox::PlusMinus): 通常の上下矢印の代わりに +- の記号でボタンを表示します。
  • setWrapping(true): ラップモード(循環モード)を有効にします。これにより、最大値を超えると最小値に戻り、最小値を下回ると最大値に戻るようになります。
  • setSpecialValueText("無制限"): 値が minimum() に設定されたときに、数値の代わりに指定したテキストを表示します。
  • setSingleStep(10): 1回のクリックで10ずつ値が変化するように設定します。
  • setRange(1, 100): 最小値を1、最大値を100に設定します。


しかし、「代替手段」という言葉をどう捉えるかによって、いくつかの異なる文脈で説明できます。

  1. QSpinBox 以外のウィジェットで数値を扱う方法
    QSpinBox が常に最適な選択肢とは限らない場合、Qt には他にも数値入力を扱うウィジェットがあります。

    • QLineEdit + QIntValidator / QDoubleValidator
      これは非常に一般的な代替手段です。QLineEdit はテキスト入力フィールドですが、QIntValidator (整数用) や QDoubleValidator (浮動小数点数用) を設定することで、入力可能な文字を数値に制限し、範囲を指定できます。

      利点

      • より柔軟な入力形式(例: 小数点以下の桁数を厳密に制御、特定の記号の許可)。
      • スピンボックスの矢印ボタンがない、シンプルな見た目を好む場合。
      • テキスト入力に特化した機能(コピー&ペースト、アンドゥ/リドゥなど)を最大限に活用したい場合。

      欠点

      • ユーザーが値を増減させるための視覚的なボタンがないため、手動で入力する必要があります。
      • バリデーションロジックを自分で管理する必要があります(無効な入力があった場合のフィードバックなど)。

      コード例

      #include <QApplication>
      #include <QWidget>
      #include <QVBoxLayout>
      #include <QLineEdit>
      #include <QIntValidator>
      #include <QLabel>
      
      int main(int argc, char *argv[]) {
          QApplication a(argc, argv);
      
          QWidget *window = new QWidget();
          window->setWindowTitle("QLineEdit + QIntValidator 例");
          QVBoxLayout *layout = new QVBoxLayout(window);
      
          QLineEdit *lineEdit = new QLineEdit(window);
          // 整数バリデーターを設定: 0から100の範囲
          QIntValidator *validator = new QIntValidator(0, 100, lineEdit);
          lineEdit->setValidator(validator);
          lineEdit->setText("50"); // 初期値
      
          QLabel *valueLabel = new QLabel("現在の値: 50", window);
      
          layout->addWidget(lineEdit);
          layout->addWidget(valueLabel);
      
          // テキストが変更されたときにラベルを更新
          QObject::connect(lineEdit, &QLineEdit::textChanged, valueLabel, [=](const QString &text){
              // バリデーションに成功した値のみを処理するなどの追加ロジックが必要になる場合がある
              valueLabel->setText(QString("現在の値: %1").arg(text));
          });
      
          window->show();
          return a.exec();
      }
      
    • QSlider
      数値の範囲が広く、連続的な値の選択が主な目的の場合、QSlider が適しています。

      利点

      • 視覚的に範囲と現在の位置が分かりやすい。
      • ドラッグ操作で素早く大まかな値を設定できる。

      欠点

      • 正確な数値を入力するには不向き。
      • 通常、入力フィールドと組み合わせて使われることが多い。
    • QComboBox
      特定の事前定義された値のセットから選択させたい場合、QComboBox が使えます。

      利点

      • 選択肢が明確。
      • タイプミスを防げる。

      欠点

      • 無限の範囲や自由な数値入力をサポートしない。
    • カスタムウィジェット
      非常に特殊な数値入力要件がある場合、QAbstractSpinBox クラスを継承して完全に独自のウィジェットを作成することも可能です。これは、QSpinBox が提供する機能では不十分な場合(例: 非線形なステップ、カスタムのテキスト表示形式、特定のデータ構造を直接操作する場合など)に検討されます。しかし、これは最も複雑なアプローチです。

      例: QAbstractSpinBox を継承して、2進数で値を表示するカスタムスピンボックスを作成する (概念のみ、実装は複雑)

      // MyBinarySpinBox.h
      #ifndef MYBINARYSPINBOX_H
      #define MYBINARYSPINBOX_H
      
      #include <QAbstractSpinBox>
      
      class MyBinarySpinBox : public QAbstractSpinBox
      {
          Q_OBJECT
      public:
          explicit MyBinarySpinBox(QWidget *parent = nullptr);
      
          int value() const;
          void setValue(int value);
      
      protected:
          // 仮想関数を再実装
          QAbstractSpinBox::StepEnabled stepEnabled() const override;
          void stepBy(int steps) override;
          QString textFromValue(int value) const override;
          int valueFromText(const QString &text) const override;
          QValidator::State validate(QString &text, int &pos) const override;
      
      private:
          int m_value;
          int m_minimum;
          int m_maximum;
      };
      
      #endif // MYBINARYSPINBOX_H
      

      この方法は、QSpinBox の機能を大幅に拡張または変更したい場合にのみ検討すべきです。

  2. QSpinBox オブジェクトの作成方法の代替(実際には「代替」ではないが、文脈として)

    • Qt Designer での作成
      コーディングせずに、Qt Designer (Qt Creator に統合されているビジュアルUIエディタ) を使って QSpinBox をフォームにドラッグ&ドロップし、プロパティエディタでその属性 (min, max, step, prefix, suffix など) を設定できます。その後、UIファイルをC++コードに変換するか、動的にロードして使用します。

      利点

      • 視覚的にUIをデザインできるため、レイアウトや外観の調整が簡単。
      • プロパティ設定が簡単で、タイプミスを減らせる。

      欠点

      • 動的な要素の追加や複雑なロジックはコードで記述する必要がある。
    • 親なしで作成し、後で親を設定する (非推奨)
      QSpinBox *spinBox = new QSpinBox(); のように、コンストラクタで親ウィジェットを指定しないことも可能ですが、これはあまり推奨されません。この場合、スピンボックスは独立したトップレベルウィンドウとして表示されます。通常は、layout->addWidget(spinBox); のようにレイアウトに追加することで、暗黙的にそのレイアウトを持つウィジェットが親となりますが、メモリ管理の観点からは明示的に親を指定する方が安全です。