C++ アプリケーションでのデザイナー UI ファイルの使用
Qt Widgets デザイナ UI ファイルは、XML 形式でフォームのウィジェット ツリーを表します。フォームは処理できます:
- つまり、フォームがコンパイル可能な C++ コードに変換されます。
- 実行時:フォームがQUiLoader クラスによって処理されます。このクラスは XML ファイルを解析しながらウィジェット ツリーを動的に構築します。
コンパイル時のフォーム処理
Qt Widgets Designer でユーザーインターフェイスコンポーネントを作成し、Qt の統合ビルドツールであるqmakeとuic を使用して、アプリケーションのビルド時にコンポーネントのコードを生成します。生成されたコードには、フォームのユーザーインターフェイスオブジェクトが含まれています。これはC++の構造体で、次のようなものが含まれています:
- フォームのウィジェット、レイアウト、レイアウト・アイテム、ボタン・グループ、アクションへのポインタ。
- 親ウィジェットにウィジェット・ツリーを構築する
setupUi()
というメンバ関数。 - フォームの文字列プロパティの変換を処理する
retranslateUi()
というメンバ関数。詳細は、「言語の変更に対応する」を参照してください。
生成されたコードはアプリケーションに含めることができ、アプリケーションから直接使用できます。また、標準ウィジェットのサブクラスを拡張するために使用することもできます。
コンパイル時に処理されたフォームは、次のいずれかの方法でアプリケーションで使用できます:
- 直接アプローチ: コンポーネントのプレースホルダとして使用するウィジェットを作成し、その中にユーザー・インターフェースを設定します。
- 単一継承アプローチ:フォームの基本クラス(QWidget やQDialog など)をサブクラス化し、フォームのユーザー・インターフェース・オブジェクトのプライベート・インスタンスを含める。
- 多重継承: フォームの基底クラスとユーザー・インターフェース・オブジェクトの両方をサブクラス化します。これにより、フォームで定義されたウィジェットをサブクラスのスコープ内から直接使用することができます。
デモンストレーションのために、シンプルな電卓フォームアプリケーションを作成します。これはオリジナルのCalculator Form の例に基づいています。
このアプリケーションは1つのソースファイル、main.cpp
と UI ファイルから構成されています。
Qt Widgets Designerでデザインしたcalculatorform.ui
:
CMake
を使用して実行ファイルをビルドする場合は、CMakeLists.txt
ファイルが必要です:
cmake_minimum_required(VERSION 3.16) project(calculatorform LANGUAGES CXX) set(CMAKE_AUTOMOC ON) set(CMAKE_AUTOUIC ON) find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets) qt_add_executable(calculatorform calculatorform.ui main.cpp) set_target_properties(calculatorform PROPERTIES WIN32_EXECUTABLE TRUE MACOSX_BUNDLE TRUE ) target_link_libraries(calculatorform PUBLIC Qt::Core Qt::Gui Qt::Widgets )
このフォームは、qt_add_executable()
の C++ ソース・ファイルの中にリストされています。CMAKE_AUTOUIC
オプションは、uic
ツールを実行して、ソース・ファイルで使用できるui_calculatorform.h
ファイルを作成するようにCMake
に指示します。
実行ファイルのビルドにqmake
を使用する場合は、.pro
ファイルが必要です:
TEMPLATE = app FORMS = calculatorform.ui SOURCES = main.cpp
このファイルの特長は、uic
で処理するファイルをqmake
に指示するFORMS
宣言です。この場合、calculatorform.ui
ファイルは、SOURCES
宣言にリストされているどのファイルでも使用できるui_calculatorform.h
ファイルを作成するために使用されます。
注意: Qt Creator を使って電卓フォームプロジェクトを作成することができます。これは、main.cpp、UI、および希望するビルドツール用のプロジェクトファイルを自動的に生成します。
直接アプローチ
直接アプローチを使うには、ui_calculatorform.h
ファイルをmain.cpp
に直接インクルードします:
#include "ui_calculatorform.h"
main
関数は、calculatorform.ui
ファイルで記述されたユーザーインターフェイスをホストするために使用する標準QWidget を構築することによって、電卓ウィジェットを作成します。
int main(int argc, char *argv[]) { QApplication app(argc, argv); QWidget widget; Ui::CalculatorForm ui; ui.setupUi(&widget); widget.show(); return app.exec(); }
この場合、Ui::CalculatorForm
はui_calculatorform.h
ファイルのインターフェース記述オブジェクトで、ダイアログのすべてのウィジェットとそのシグナルとスロット間の接続を設定します。
直接的なアプローチは、シンプルで自己完結的なコンポーネントをアプリケーションで使用するための迅速で簡単な方法を提供します。しかし、Qt Widgets Designerで作成されたコンポネントは、アプリケーションの他のコードと密接に統合する必要があります。例えば、上記のCalculatorForm
コードはコンパイルされ実行されますが、QSpinBox オブジェクトはQLabel と相互作用しません。QLabel で追加操作を実行し、結果を表示するにはカスタム・スロットが必要だからです。これを実現するには、単一継承アプローチを使用する必要があります。
単一継承アプローチ
単一継承を使用するには、標準のQtウィジェットをサブクラス化し、フォームのユーザー・インターフェース・オブジェクトのプライベート・インスタンスをインクルードします。これは
- メンバ変数
- ポインタ・メンバ変数
メンバ変数の使用
この方法では、Qt ウィジェットをサブクラス化し、コンストラクタ内でユーザー・インターフェースを設定します。この方法で使用されるコンポーネントは、フォームで使用されるウィジェットとレイアウトをQtウィジェットのサブクラスに公開し、ユーザーインターフェイスとアプリケーション内の他のオブジェクトとの間でシグナルとスロットの接続を行うための標準的なシステムを提供します。生成されたUi::CalculatorForm
構造体は、このクラスのメンバです。
このアプローチは電卓フォームの例で使われています。
ユーザー・インターフェースを確実に使えるようにするには、Ui::CalculatorForm
を参照する前に、uic
が生成するヘッダーファイルをインクルードする必要があります:
#include "ui_calculatorform.h"
プロジェクトファイルを更新して、calculatorform.h
をインクルードする必要があります。CMake
の場合:
qt_add_executable(calculatorform calculatorform.cpp calculatorform.h calculatorform.ui main.cpp )
インクルードディレクティブが相対パスを使用している以下の例のような特定のケースでは、AUTOUIC に依存する代わりにqt_add_ui を使用してui_calculatorform.h
ファイルを生成することができます。
#include "src/files/ui_calculatorform.h"
qt_add_ui(calculatorform SOURCES calculatorform.ui INCLUDE_PREFIX src/files)
qmake
の場合:
HEADERS = calculatorform.h
サブクラスは次のように定義される:
class CalculatorForm : public QWidget { Q_OBJECT public: explicit CalculatorForm(QWidget *parent = nullptr); private slots: void updateResult(); private: Ui::CalculatorForm ui; };
このクラスの重要な特徴は、ユーザー・インターフェースの設定と管理のためのコードを提供するプライベートなui
オブジェクトです。
サブクラスのコンストラクタは、ui
オブジェクトのsetupUi()
関数を呼び出すだけで、ダイアログのすべてのウィジェットとレイアウトを構築し、設定します。これが完了すると、必要に応じてユーザーインターフェイスを変更することができます。
CalculatorForm::CalculatorForm(QWidget *parent) : QWidget(parent) { ui.setupUi(this); connect(ui.inputSpinBox1, &QSpinBox::valueChanged, this, &CalculatorForm::updateResult); connect(ui.inputSpinBox2, &QSpinBox::valueChanged, this, &CalculatorForm::updateResult); }
ユーザ・インターフェイス・ウィジェットのシグナルとスロットは、on_<オブジェクト名> - 接頭辞を追加することで、通常の方法で接続できます。詳細はwidgets-and-dialogs-with-auto-connect を参照してください。
この方法の利点は、QWidget- ベースのインターフェイスを提供するために継承を簡単に使用できることと、ui
データ・メンバ内にユーザー・インターフェイス・ウィジェット変数をカプセル化できることです。このメソッドを使用して、同じウィジェット内に複数のユーザー・インタフェースを定義し、各ユーザー・インタフェースを独自の名前空間内に含め、それらをオーバーレイ(または合成)することができます。この方法は、既存のフォームから個々のタブを作成する場合などに使用できます。
ポインタ・メンバ変数の使用
あるいは、Ui::CalculatorForm
構造体をクラスのポインタ・メンバにすることもできます。ヘッダーは次のようになります:
namespace Ui { class CalculatorForm; } class CalculatorForm : public QWidget ... virtual ~CalculatorForm(); ... private: Ui::CalculatorForm *ui; ...
対応するソース・ファイルは以下のようになる:
#include "ui_calculatorform.h" CalculatorForm::CalculatorForm(QWidget *parent) : QWidget(parent), ui(new Ui::CalculatorForm) { ui->setupUi(this); } CalculatorForm::~CalculatorForm() { delete ui; }
この方法の利点は、ユーザー・インターフェース・オブジェクトを前方宣言できることで、生成されたui_calculatorform.h
ファイルをヘッダーに含める必要がない。依存するソース・ファイルを再コンパイルすることなく、フォームを変更できる。これは、クラスがバイナリ互換性の制限を受ける場合に特に重要です。
一般的に、ライブラリや大規模なアプリケーションにはこの方法を推奨します。詳細については、共有ライブラリの作成 を参照してください。
多重継承のアプローチ
Qt Widgets Designer で作成したフォームは、標準のQWidget ベースのクラスとともにサブクラス化できます。このアプローチにより、フォームで定義されたすべてのユーザー・インターフェース・コンポーネントにサブクラスのスコープ内で直接アクセスできるようになり、connect() 関数を使用して通常の方法でシグナルとスロットの接続を行うことができます。
以下のように、uic
がcalculatorform.ui
ファイルから生成するヘッダー・ファイルをインクルードする必要がある:
#include "ui_calculatorform.h"
このクラスは、単一継承で使用したものと同様の方法で定義されるが、今回はQWidget とUi::CalculatorForm
の両方を継承する:
class CalculatorForm : public QWidget, private Ui::CalculatorForm { Q_OBJECT public: explicit CalculatorForm(QWidget *parent = nullptr); private slots: void on_inputSpinBox1_valueChanged(int value); void on_inputSpinBox2_valueChanged(int value); };
Ui::CalculatorForm
を私的に継承することで、ユーザー・インターフェース・ オブジェクトがサブクラスで私的になるようにしています。また、ui
を public や protected にするのと同じように、public
やprotected
キーワードで継承することもできます。
サブクラスのコンストラクタは、単一継承の例で使用したコンストラクタと同じタスクを実行します:
この場合、ユーザー・インターフェースで使用されるウィジェットは、コードで作成されたウィジェットと同じようにアクセスできます。この場合、ユーザーインターフェイスで使用されるウィジェットは、コードで作成されたウィジェットと同じようにアクセスできます。ui
接頭辞を付けてアクセスする必要はありません。
言語変更への対応
Qt は、ユーザーインターフェースの言語が変更された場合、QEvent::LanguageChange タイプのイベントを送信してアプリケーションに通知します。ユーザー・インターフェース・オブジェクトのメンバ関数retranslateUi()
を呼び出すには、次のようにフォーム・クラスでQWidget::changeEvent()
を再実装します:
void CalculatorForm::changeEvent(QEvent *e) { QWidget::changeEvent(e); switch (e->type()) { case QEvent::LanguageChange: ui->retranslateUi(this); break; default: break; } }
ランタイムフォーム処理
別の方法として、フォームを実行時に処理し、動的に生成されたユーザー・インターフェースを生成することもできます。これにはQt Widgets Designer で作成されたフォームを処理するQUiLoader クラスを提供するQtUiTools モジュールを使用します。
UiToolsのアプローチ
実行時にフォームを処理するには、UIファイルを含むリソース・ファイルが必要です。また、QtUiTools モジュールを使用するようにアプリケーションを構成する必要があります。これは、CMake
プロジェクト・ファイルに以下の宣言を記述し、アプリケーションが適切にコンパイルされリンクされるようにすることで行われます。
find_package(Qt6 REQUIRED COMPONENTS Core Gui UiTools Widgets) target_link_libraries(textfinder PUBLIC Qt::Core Qt::Gui Qt::UiTools Qt::Widgets )
qmake
の場合:
QT += uitools
QUiLoader クラスは、ユーザー・インターフェースを構築するためのフォーム・ローダー・ オブジェクトを提供します。このユーザー・インターフェースは、プロジェクトのリソース・ファイルに格納されているフォームを取得するために、任意のQIODevice 、例えばQFile オブジェクトから取得することができます。QUiLoader::load ()関数は、ファイルに含まれるユーザー・インターフェース記述を使用してフォーム・ウィジェットを構築します。
QtUiTools モジュール・クラスは、以下のディレクティブを使ってインクルードできます:
#include <QtUiTools>
QUiLoader::load() 関数は、テキスト・ファインダーの例のコードに示されているように呼び出されます:
スタティックQWidget*loadUiFile(QWidget*parent) { QFilefile(u":/forms/textfinder.ui"_s);if(!file.open(QIODevice::ReadOnly)) qFatal("Cannot open resource file"); リターンQUiLoader().load(&file,parent); }
実行時にQtUiTools を使ってユーザー・インターフェースを構築するクラスでは、QObject::findChild() を使ってフォーム内のオブジェクトを見つけることができます。例えば、以下のコードでは、オブジェクト名とウィジェット・タイプに基づいてコンポーネントを検索しています:
ui_findButton = findChild<QPushButton*>("findButton"); ui_textEdit = findChild<QTextEdit*>("textEdit"); ui_lineEdit = findChild<QLineEdit*>("lineEdit");
実行時にフォームを処理することで、開発者はUIファイルを変更するだけで、プログラムのユーザー・インターフェースを自由に変更できる。これは、さまざまなユーザー・ニーズに合わせてプログラムをカスタマイズするときに便利です。たとえば、特別に大きなアイコンを表示したり、アクセシビリティをサポートするために異なる配色にしたりすることができます。
自動接続
コンパイル時またはランタイムフォーム用に定義されたシグナルとスロットの接続は、手動で設定することも、QMetaObject の機能を使用して、シグナルと適切な名前のスロット間の接続を自動的に行うこともできます。
一般的に、QDialog 、ユーザーが入力した情報を受理する前に処理したい場合、OKボタンからのclicked()シグナルをダイアログのカスタムスロットに接続する必要があります。まず、手作業でスロットを接続するダイアログの例を示し、次に自動接続を使用するダイアログと比較します。
自動接続を使用しないダイアログ
前と同じようにダイアログを定義しますが、コンストラクタに加えてスロットを定義します:
class ImageDialog : public QDialog, private Ui::ImageDialog { Q_OBJECT public: explicit ImageDialog(QWidget *parent = nullptr); private slots: void checkValues(); };
checkValues()
スロットは、ユーザーによって提供された値を検証するために使用されます。
ダイアログのコンストラクタでは、前と同じようにウィジェットをセットアップし、キャンセルボタンのclicked() シグナルをダイアログの reject() スロットに接続します。また、ダイアログがライン・エディットのリターン・キー・イベントの処理に干渉しないように、両方のボタンのautoDefault プロパティを無効にします:
ImageDialog::ImageDialog(QWidget *parent) : QDialog(parent) { setupUi(this); okButton->setAutoDefault(false); cancelButton->setAutoDefault(false); ... connect(okButton, &QAbstractButton::clicked, this, &ImageDialog::checkValues); }
OKボタンのclicked()シグナルをダイアログのcheckValues()スロットに接続します:
void ImageDialog::checkValues() { if (nameLineEdit->text().isEmpty()) { QMessageBox::information(this, tr("No Image Name"), tr("Please supply a name for the image."), QMessageBox::Cancel); } else { accept(); } }
このカスタムスロットは、ユーザーによって入力されたデータが有効であることを確認するために必要な最小限のことしか行いません。
自動接続のウィジェットとダイアログ
ダイアログにカスタムスロットを実装し、コンストラクタで接続するのは簡単ですが、代わりにQMetaObject の自動接続機能を使用して、OKボタンの clicked() シグナルをサブクラスのスロットに接続することができます。uic
は、ダイアログのsetupUi()
関数でこれを行うコードを自動的に生成するので、標準的な規約に従った名前のスロットを宣言して実装するだけです:
void on_<object name>_<signal name>(<signal parameters>);
注意: フォーム内のウィジェットの名前を変更する場合、スロット名もそれに合わせて変更する必要があります。このため、新しいコードではこの方法を使わないことをお勧めします。
この規約を使って、OKボタンのマウスクリックに応答するスロットを定義し、実装することができます:
class ImageDialog : public QDialog, private Ui::ImageDialog { Q_OBJECT public: explicit ImageDialog(QWidget *parent = nullptr); private slots: void on_okButton_clicked(); };
シグナルとスロットの自動接続のもう1つの例は、on_findButton_clicked()
スロットを持つテキストファインダーです。
シグナルとスロットの接続を有効にするには、QMetaObject'のシステムを使います:
QMetaObject::connectSlotsByName(this);
これにより、以下のようにスロットを実装することができる:
void TextFinder::on_findButton_clicked() { QString searchString = ui_lineEdit->text(); QTextDocument *document = ui_textEdit->document(); bool found = false; // undo previous change (if any) document->undo(); if (searchString.isEmpty()) { QMessageBox::information(this, tr("Empty Search Field"), tr("The search field is empty. " "Please enter a word and click Find.")); } else { QTextCursor highlightCursor(document); QTextCursor cursor(document); cursor.beginEditBlock(); ... cursor.endEditBlock(); if (found == false) { QMessageBox::information(this, tr("Word Not Found"), tr("Sorry, the word cannot be found.")); } } }
シグナルとスロットの自動接続は、標準的な命名規則と、ウィジェット・デザイナーが作業するための明示的なインターフェースの両方を提供します。与えられたインターフェイスを実装するソースコードを提供することで、ユーザー・インターフェイス設計者は、自分自身でコードを書くことなく、設計が実際に動作することを確認することができます。
© 2025 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.