Qt QSpinBox: displayIntegerBaseで数値表示を自由自在に操る
QSpinBox::displayIntegerBase
は、Qt の QSpinBox
ウィジェットのプロパティの一つです。QSpinBox
は整数値を表示・編集するためのコントロールで、ユーザーが上下ボタンをクリックしたり、キーボードの矢印キーを押したり、直接値を入力したりして値を変更できます。
displayIntegerBase
プロパティは、QSpinBox が表示する数値の基数(進数) を決定します。
setDisplayIntegerBase(int base)
: 表示基数を設定します。displayIntegerBase()
: 現在設定されている表示基数を取得します。
デフォルト値は 10 (10進数) です。
具体的な使用例
このプロパティを変更することで、例えば、10進数以外の数値を QSpinBox に表示させることができます。
- 8進数 (Octal):
spinBox->setDisplayIntegerBase(8);
- 2進数 (Binary):
spinBox->setDisplayIntegerBase(2);
- 16進数 (Hexadecimal):
spinBox->setDisplayIntegerBase(16);
- 10進数 (Decimal):
spinBox->setDisplayIntegerBase(10);
(デフォルト)
重要: displayIntegerBase
は表示形式にのみ影響します。QSpinBox
が内部的に保持する値(value()
メソッドで取得できる値)は、常に10進数の整数として扱われます。したがって、displayIntegerBase(16)
を設定しても、value()
が返すのは10進数の値です。
16進数表示の例
例えば、QSpinBox の値を16進数で表示したい場合、次のように設定します。
#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 *spinBox = new QSpinBox(window);
spinBox->setRange(0, 255); // 0 から 255 の範囲を設定
spinBox->setDisplayIntegerBase(16); // 表示基数を16進数に設定
spinBox->setPrefix("0x"); // 16進数であることを示すために "0x" をプレフィックスとして追加
spinBox->setValue(10); // 初期値として10進数の10を設定 (表示は "0xA" となる)
layout->addWidget(spinBox);
window->setLayout(layout);
window->show();
return a.exec();
}
このコードを実行すると、QSpinBox は初期値として "0xA" と表示されます。ユーザーが値を変更すると、それも16進数で表示されます(例: "0xF", "0x10" など)。
QSpinBox::displayIntegerBase
は、数値の表示形式を制御する便利な機能ですが、誤解や不適切な使用によっていくつかの問題が発生する可能性があります。
表示形式の誤解 (一番よくある間違い)
問題: displayIntegerBase
を設定したのに、value()
メソッドが期待する値(例えば、16進数で入力した値の16進数表現)を返さない、または、valueChanged()
シグナルが16進数の値を送ってこない。
原因: displayIntegerBase
は、表示形式にのみ影響します。QSpinBox
が内部的に保持する値、および value()
メソッドが返す値は、常に10進数の整数です。valueChanged(int)
シグナルも10進数の値を送ります。
トラブルシューティング:
textChanged(const QString&)
シグナルを使えば、表示されている文字列(プレフィックスやサフィックスを含む)を取得できます。この文字列から基数に応じた値を取得したい場合は、QSpinBox::valueFromText()
メソッドをオーバーライドするか、自分で文字列解析を行う必要があります。- 例えば、ユーザーが16進数で入力した値をプログラム内で16進数として扱いたい場合は、
value()
で得られた10進数の値を別途16進数に変換する必要があります。 - 内部的な値が10進数で処理されることを念頭に置いてプログラムを設計してください。
displayIntegerBase
はあくまでユーザーインターフェースの表示のためだと理解してください。
プレフィックス/サフィックスとの組み合わせ
問題: 16進数表示のために displayIntegerBase(16)
を設定し、さらに "0x" などのプレフィックスを追加したいが、表示がうまくいかない。
原因: setPrefix()
メソッドを適切に使用しないと、期待通りの表示にならないことがあります。また、displayIntegerBase
自体はプレフィックスやサフィックスを追加しません。
トラブルシューティング:
QSpinBox
は内部的にtextFromValue()
およびvalueFromText()
を使用して数値と文字列の変換を行っています。これらのメソッドはprefix
とsuffix
の設定を考慮します。spinBox->setPrefix("0x");
のように明示的にプレフィックスを設定してください。
不適切な基数 (base) の設定
問題: displayIntegerBase
に無効な値(例: 5や20)を設定してしまった。
原因: displayIntegerBase
には、2, 8, 10, 16 などの一般的な基数を設定する必要があります。Qt のドキュメントでサポートされている基数を確認してください。通常、これ以外の値を設定すると、期待通りの動作をしないか、エラーが発生する可能性があります。
トラブルシューティング:
- 範囲外の基数を設定する必要がある場合は、
QSpinBox
をサブクラス化し、textFromValue()
とvalueFromText()
を独自に実装する必要があります。 - ドキュメントでサポートされている基数 (2, 8, 10, 16) のいずれかを設定するようにしてください。
QDoubleSpinBox との混同
問題: 浮動小数点数を扱いたいのに QSpinBox
と displayIntegerBase
を使おうとしている。
原因: QSpinBox
は整数値専用です。
トラブルシューティング:
- 浮動小数点数を扱いたい場合は、
QDoubleSpinBox
を使用してください。QDoubleSpinBox
にはdisplayIntegerBase
に相当するプロパティはありませんが、setDecimals()
で小数点以下の桁数を制御できます。
最大値/最小値の考慮不足
問題: 16進数表示にした際、設定した maximum
や minimum
が直感的に合わない。
原因: maximum
と minimum
は常に10進数で設定されます。例えば maximum
を 255 に設定し、displayIntegerBase
を 16 にすると、表示上は "FF" が最大値となります。ユーザーが入力できる値も10進数で 0~255 の範囲に制限されます。
トラブルシューティング:
- ユーザーが特定の基数で最大の値を入力できることを期待する場合は、その基数での最大値に相当する10進数値を
maximum
に設定する必要があります。例えば、2バイトの16進数(0x0000〜0xFFFF)を扱いたい場合は、spinBox->setRange(0, 65535);
と設定します。 maximum
とminimum
は、常に10進数で考えて設定してください。
大文字/小文字の表示
問題: 16進数表示で、A-F が小文字 (a-f) で表示される、またはその逆。
原因: QSpinBox
のデフォルトの表示は、通常、小文字の16進数を使用します。
トラブルシューティング:
- 大文字で表示したい場合は、
textFromValue()
をオーバーライドしてQString::toUpper()
を使用するか、QFont::setCapitalization(QFont::AllUppercase)
を使用してフォントのキャピタリゼーションを設定する方法があります。ただし、後者は見た目のみに影響し、入力された値の内部的な変換には直接影響しません。
// 例:大文字の16進数表示
QSpinBox *spinBox = new QSpinBox();
spinBox->setRange(0, 255);
spinBox->setDisplayIntegerBase(16);
spinBox->setPrefix("0x");
// 方法1: フォントのキャピタリゼーションを設定 (見た目のみ)
QFont font = spinBox->font();
font.setCapitalization(QFont::AllUppercase);
spinBox->setFont(font);
// 方法2: textFromValue() をオーバーライド (より確実)
// これはQSpinBoxをサブクラス化して行う必要があります。
// 例: HexSpinBox クラスを定義し、その中で textFromValue をオーバーライドする
class HexSpinBox : public QSpinBox {
public:
HexSpinBox(QWidget *parent = nullptr) : QSpinBox(parent) {
setDisplayIntegerBase(16);
setPrefix("0x");
}
protected:
QString textFromValue(int val) const override {
return QString("0x%1").arg(val, 0, 16).toUpper(); // 大文字に変換
}
// valueFromText も必要に応じてオーバーライド
int valueFromText(const QString &text) const override {
QString cleanText = text;
cleanText.remove(0, prefix().length()); // プレフィックスを削除
bool ok;
int value = cleanText.toInt(&ok, 16); // 16進数として解釈
if (ok) {
return value;
}
return QSpinBox::valueFromText(text); // 失敗したらデフォルトの挙動
}
};
// ...
// HexSpinBox *hexSpinBox = new HexSpinBox();
QSpinBox::displayIntegerBase
は、QSpinBox が表示する数値の基数(進数)を設定するために使われます。以下に、一般的な使用例と、より高度なカスタマイズの例を挙げます。
基本的な10進数、16進数、2進数表示
最も基本的な例として、異なる基数で数値を表示するQSpinBoxを作成します。
#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);
// 10進数 (Decimal) のQSpinBox (デフォルト)
QLabel *decimalLabel = new QLabel("10進数 (Decimal):", window);
QSpinBox *decimalSpinBox = new QSpinBox(window);
decimalSpinBox->setRange(0, 255);
// setDisplayIntegerBase(10) はデフォルトなので省略可能
// decimalSpinBox->setDisplayIntegerBase(10);
decimalSpinBox->setValue(42); // 初期値
layout->addWidget(decimalLabel);
layout->addWidget(decimalSpinBox);
// 16進数 (Hexadecimal) のQSpinBox
QLabel *hexLabel = new QLabel("16進数 (Hexadecimal):", window);
QSpinBox *hexSpinBox = new QSpinBox(window);
hexSpinBox->setRange(0, 255); // 0x00 から 0xFF の範囲
hexSpinBox->setDisplayIntegerBase(16); // 16進数表示に設定
hexSpinBox->setPrefix("0x"); // "0x" をプレフィックスとして追加
hexSpinBox->setValue(42); // 初期値 (10進数の42は16進数で0x2A)
layout->addWidget(hexLabel);
layout->addWidget(hexSpinBox);
// 2進数 (Binary) のQSpinBox
QLabel *binaryLabel = new QLabel("2進数 (Binary):", window);
QSpinBox *binarySpinBox = new QSpinBox(window);
binarySpinBox->setRange(0, 255); // 0b00000000 から 0b11111111 の範囲
binarySpinBox->setDisplayIntegerBase(2); // 2進数表示に設定
binarySpinBox->setPrefix("0b"); // "0b" をプレフィックスとして追加
binarySpinBox->setValue(42); // 初期値 (10進数の42は2進数で0b00101010)
layout->addWidget(binaryLabel);
layout->addWidget(binarySpinBox);
// 値変更シグナルを接続して、コンソールに出力する例
QObject::connect(decimalSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
[](int val){ qDebug() << "10進数 QSpinBox 値: " << val; });
QObject::connect(hexSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
[](int val){ qDebug() << "16進数 QSpinBox 値 (内部は10進数): " << val; });
QObject::connect(binarySpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
[](int val){ qDebug() << "2進数 QSpinBox 値 (内部は10進数): " << val; });
window->setLayout(layout);
window->show();
return a.exec();
}
この例では、異なる displayIntegerBase
を持つ3つの QSpinBox
を作成し、それぞれの表示がどのように変化するかを示します。setPrefix()
を使用して、各基数を示す一般的なプレフィックス(0x
、0b
)を追加しています。
16進数を大文字で表示するカスタマイズ例
QSpinBox::displayIntegerBase(16)
を設定すると、デフォルトでは16進数の文字(A-F)は小文字で表示されます。これを大文字で表示したい場合、QSpinBox
を継承して textFromValue()
メソッドをオーバーライドするのが一般的な方法です。
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QSpinBox>
#include <QLabel>
#include <QString>
// 16進数を大文字で表示するQSpinBox
class HexSpinBox : public QSpinBox {
public:
HexSpinBox(QWidget *parent = nullptr) : QSpinBox(parent) {
setDisplayIntegerBase(16); // 基数を16進数に設定
setPrefix("0x"); // プレフィックスを設定
}
protected:
// QSpinBox::textFromValue() をオーバーライド
// 内部の10進数値を文字列に変換する際に呼び出される
QString textFromValue(int val) const override {
// val を16進数文字列に変換し、toUpper() で大文字にする
// arg(val, 0, 16) は、val を16進数でフォーマットすることを意味する
return prefix() + QString::number(val, 16).toUpper();
}
// QSpinBox::valueFromText() をオーバーライド
// 文字列を内部の10進数値に変換する際に呼び出される
int valueFromText(const QString &text) const override {
QString cleanText = text;
// プレフィックスを取り除く
if (cleanText.startsWith(prefix())) {
cleanText.remove(0, prefix().length());
}
bool ok;
// 16進数として解釈して10進数に変換
int value = cleanText.toInt(&ok, 16);
if (ok) {
return value;
}
// 変換に失敗した場合はデフォルトの挙動にフォールバック
return QSpinBox::valueFromText(text);
}
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QWidget *window = new QWidget();
window->setWindowTitle("カスタムQSpinBox (16進数大文字)");
QVBoxLayout *layout = new QVBoxLayout(window);
QLabel *hexUpperLabel = new QLabel("16進数 (大文字):", window);
HexSpinBox *hexUpperSpinBox = new HexSpinBox(window);
hexUpperSpinBox->setRange(0, 255); // 0x00 から 0xFF
hexUpperSpinBox->setValue(170); // 初期値 (10進数の170は16進数で0xAA)
layout->addWidget(hexUpperLabel);
layout->addWidget(hexUpperSpinBox);
QObject::connect(hexUpperSpinBox, QOverload<int>::of(&QSpinBox::valueChanged),
[](int val){ qDebug() << "カスタム Hex QSpinBox 値: " << val; });
window->setLayout(layout);
window->show();
return a.exec();
}
この HexSpinBox
クラスは、textFromValue()
をオーバーライドして、16進数文字を常に大文字で表示するようにしています。また、valueFromText()
もオーバーライドして、ユーザーが手動で大文字の16進数を入力した場合でも正しく10進数に変換できるようにしています。
実行時の基数切り替え
ユーザーが実行時にQSpinBoxの表示基数を切り替えられるようにする例です。
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QSpinBox>
#include <QComboBox>
#include <QLabel>
#include <QObject>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QWidget *window = new QWidget();
window->setWindowTitle("QSpinBox 基数切り替え");
QVBoxLayout *layout = new QVBoxLayout(window);
QLabel *valueLabel = new QLabel("数値:", window);
QSpinBox *spinBox = new QSpinBox(window);
spinBox->setRange(0, 1024); // 例として0から1024の範囲
spinBox->setValue(255);
QLabel *baseLabel = new QLabel("表示基数を選択:", window);
QComboBox *baseComboBox = new QComboBox(window);
baseComboBox->addItem("10進数", 10); // データとして基数を格納
baseComboBox->addItem("16進数", 16);
baseComboBox->addItem("8進数", 8);
baseComboBox->addItem("2進数", 2);
layout->addWidget(valueLabel);
layout->addWidget(spinBox);
layout->addWidget(baseLabel);
layout->addWidget(baseComboBox);
// コムボボックスの選択が変更されたら、QSpinBoxの表示基数を更新
QObject::connect(baseComboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),
[=](int index) {
int base = baseComboBox->itemData(index).toInt();
spinBox->setDisplayIntegerBase(base);
// プレフィックスも基数に応じて変更
if (base == 16) {
spinBox->setPrefix("0x");
} else if (base == 2) {
spinBox->setPrefix("0b");
} else if (base == 8) {
spinBox->setPrefix("0o"); // 8進数の標準的なプレフィックス
} else {
spinBox->setPrefix(""); // 10進数の場合はプレフィックスなし
}
});
// 初期表示に合わせて設定
baseComboBox->setCurrentIndex(0); // デフォルトで10進数を選択
window->setLayout(layout);
window->show();
return a.exec();
}
この例では、QComboBox
を使ってユーザーがQSpinBoxの表示基数を選択できるようにしています。QComboBox::itemData()
に基数の整数値を格納し、選択が変更された際にそのデータを取り出して setDisplayIntegerBase()
に設定しています。また、基数に合わせて setPrefix()
も動的に変更しています。
QSpinBox::displayIntegerBase
の代替・拡張方法
QSpinBox のサブクラス化と textFromValue() / valueFromText() のオーバーライド
これは、displayIntegerBase
で提供される機能(2, 8, 10, 16進数表示)よりも柔軟な表示形式を実現したい場合に最も強力な方法です。
QSpinBox
は、内部的な整数値と表示されるテキストの間で変換を行うために、以下の仮想関数を使用します。
int valueFromText(const QString &text) const
: ユーザーが入力したtext
を内部のQSpinBox
の値(10進数)に変換するために呼び出されます。QString textFromValue(int value) const
: 内部のvalue
(10進数)をQSpinBox
に表示されるテキストに変換するために呼び出されます。
これらのメソッドをオーバーライドすることで、以下のようなカスタマイズが可能です。
- 入力時の自動修正: ユーザーが入力した不正な形式のテキストを自動的に修正する。
- ローカライズされた数値表示: 特定のロケールでの数値表示ルールに厳密に従う(Qtのデフォルトロケール設定でほとんどの場合十分ですが、より厳密な制御が必要な場合)。
- 複数の値の表現: "32x32" のように、複数の数値を1つの文字列で表現し、内部的に1つの整数値(例: 32)にマッピングする。
- 特殊なフォーマット: "100KB", "25px" のように、数値に単位やカスタム文字列を付加する。
- 任意の基数表示: 5進数、12進数など、Qtが標準でサポートしない基数での表示。
例 (カスタム単位付きのSpinBox):
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QSpinBox>
#include <QLabel>
#include <QString>
class CustomUnitSpinBox : public QSpinBox {
public:
CustomUnitSpinBox(const QString& unit, QWidget *parent = nullptr)
: QSpinBox(parent), m_unit(unit) {
setRange(0, 1000); // 任意の範囲
setValue(100); // 初期値
}
protected:
QString textFromValue(int val) const override {
// 数値に単位を追加して表示
return QString("%1 %2").arg(val).arg(m_unit);
}
int valueFromText(const QString &text) const override {
// 単位を取り除き、数値として解釈
QString cleanText = text;
cleanText.remove(m_unit); // 単位部分を削除
cleanText = cleanText.trimmed(); // 前後の空白を削除
bool ok;
int value = cleanText.toInt(&ok);
if (ok) {
return value;
}
// 変換に失敗した場合、デフォルトのQSpinBoxの動作にフォールバックするか、適切な値を返す
return QSpinBox::valueFromText(text);
}
private:
QString m_unit;
};
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QWidget *window = new QWidget();
window->setWindowTitle("カスタム単位 QSpinBox");
QVBoxLayout *layout = new QVBoxLayout(window);
QLabel *pxLabel = new QLabel("ピクセル値:", window);
CustomUnitSpinBox *pxSpinBox = new CustomUnitSpinBox("px", window);
layout->addWidget(pxLabel);
layout->addWidget(pxSpinBox);
QLabel *mmLabel = new QLabel("ミリメートル値:", window);
CustomUnitSpinBox *mmSpinBox = new CustomUnitSpinBox("mm", window);
layout->addWidget(mmLabel);
layout->addWidget(mmSpinBox);
window->setLayout(layout);
window->show();
return a.exec();
}
QLocale の利用
QSpinBox
は、デフォルトでシステムまたはアプリケーションの QLocale
設定を使用して数値をフォーマットします。これは、displayIntegerBase
の直接的な代替ではありませんが、地域に応じた数値の表示形式(小数点記号、桁区切りなど)を制御する上で重要です。
QLocale::setDefault(const QLocale &locale)
: アプリケーション全体のデフォルトロケールを設定します。QSpinBox::setLocale(const QLocale &locale)
: QSpinBoxが数値の表示と解析に使用するロケールを設定します。
これにより、例えば、英語圏では "1,234.56" と表示される数値が、ドイツ語圏では "1.234,56" と表示されるように自動的に調整されます。
QLineEdit と QValidator の組み合わせ
QSpinBox
の代わりに、より柔軟な入力制御が可能な QLineEdit
を使用し、QValidator
を組み合わせて独自の数値システムを実装することも可能です。これは、QSpinBox
のスピンボタン機能が必要ない場合や、非常に複雑な入力形式を扱いたい場合に検討されます。
QValidator
: 入力されたテキストの妥当性をチェックするための抽象ベースクラス。QIntValidator
: 整数値の範囲を検証。QDoubleValidator
: 浮動小数点数の範囲と小数点以下の桁数を検証。QRegularExpressionValidator
: 正規表現に基づいてテキストを検証。
QLineEdit
: テキスト入力フィールド。
例 (QLineEdit と QRegularExpressionValidator で16進数入力):
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QLineEdit>
#include <QLabel>
#include <QRegularExpressionValidator>
#include <QRegularExpression>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QWidget *window = new QWidget();
window->setWindowTitle("QLineEdit + Validator で16進数入力");
QVBoxLayout *layout = new QVBoxLayout(window);
QLabel *label = new QLabel("16進数 (00-FF):", window);
QLineEdit *hexLineEdit = new QLineEdit(window);
// 0からFFまでの16進数を許可する正規表現
// ^[0-9a-fA-F]{1,2}$ : 1文字または2文字の16進数
// QRegularExpressionValidator はQt5で推奨される
QRegularExpression hexRegex("^[0-9a-fA-F]{1,2}$");
QRegularExpressionValidator *validator = new QRegularExpressionValidator(hexRegex, hexLineEdit);
hexLineEdit->setValidator(validator);
hexLineEdit->setText("A5"); // 初期値
// 入力値が変更されたら、10進数に変換してコンソールに出力
QObject::connect(hexLineEdit, &QLineEdit::textChanged,
[](const QString &text) {
bool ok;
int value = text.toInt(&ok, 16); // 16進数として解釈
if (ok) {
qDebug() << "入力された16進数: " << text << " (10進数: " << value << ")";
} else {
qDebug() << "不正な16進数入力: " << text;
}
});
layout->addWidget(label);
layout->addWidget(hexLineEdit);
window->setLayout(layout);
window->show();
return a.exec();
}
この方法では、スピンボタンは提供されませんが、QLineEdit
の高い柔軟性と QValidator
による強力な入力制限・検証が可能になります。ユーザーが任意の形式で入力できるが、その形式を厳密に制御したい場合に有効です。
QStyleOptionSpinBox と QStyle のカスタマイズ
これは表示の「見た目」に関するもので、displayIntegerBase
の機能とは少し異なりますが、QSpinBox の表示を根本的に変えたい場合に考慮されることがあります。QSpinBox
は QStyle
を使用して描画されます。QStyle
をサブクラス化し、drawPrimitive()
や drawControl()
などのメソッドをオーバーライドすることで、数値の表示方法(例: フォント、色、配置など)をより詳細に制御できます。これは高度なカスタマイズであり、通常は推奨されません。
- ロケールに応じた表示:
QLocale
を利用します。 - スピンボタンが不要で、純粋なテキスト入力とカスタム検証が必要:
QLineEdit
とQValidator
(特にQRegularExpressionValidator
またはカスタムQValidator
サブクラス) の組み合わせを検討します。 - Qtがサポートしない基数、カスタム単位、複雑なフォーマット:
QSpinBox
のサブクラス化とtextFromValue()
/valueFromText()
のオーバーライドが必須です。 - 特殊なプレフィックス/サフィックス、または大文字/小文字の制御:
QSpinBox::setPrefix()
,QSpinBox::setSuffix()
とQSpinBox::displayIntegerBase
を組み合わせるか、QSpinBox
のサブクラス化とtextFromValue()
/valueFromText()
のオーバーライドを検討します。 - 単純な基数表示(2, 8, 10, 16進数):
QSpinBox::displayIntegerBase
が最も簡単で直接的な方法です。