C++アプリケーションでのQt Widgets Designer UIファイルの使用
Qt Widgets Designer UIファイルは、フォームのウィジェットツリーをXML形式で表現したものです。フォームは以下の方法で処理できます:
- コンパイル時。これは、フォームがコンパイル可能なC++コードに変換されることを意味します。
- 実行時。この場合、フォームはQUiLoader クラスによって処理され、XMLファイルの解析中にウィジェットツリーが動的に構築されます。
コンパイル時のフォーム処理
Qt Widgets Designer を使用してユーザーインターフェースコンポーネントを作成し、アプリケーションのビルド時に Qt の統合ビルドツールであるqmakeおよびuic を使用して、それらのコードを生成します。生成されたコードには、フォームのユーザーインターフェースオブジェクトが含まれます。これは、以下の要素を含む C++ の struct です:
- フォームのウィジェット、レイアウト、レイアウト項目、ボタングループ、およびアクションへのポインタ。
- 親ウィジェット上にウィジェットツリーを構築するための `
setupUi()` という名前のメンバ関数。 - フォームの文字列プロパティの変換を処理する
retranslateUi()というメンバ関数。詳細については、「言語の変更への対応」を参照してください。
生成されたコードは、アプリケーションに組み込んで直接使用することができます。あるいは、標準ウィジェットのサブクラスを拡張するために使用することもできます。
コンパイル時に処理されたフォームは、以下のいずれかの方法でアプリケーションで使用できます。
- 直接的なアプローチ:コンポーネントのプレースホルダーとして使用するウィジェットを構築し、その内部でユーザーインターフェースを設定します。
- 単一継承アプローチ:フォームの基底クラス(例:QWidget またはQDialog )をサブクラス化し、フォームのユーザーインターフェースオブジェクトのプライベートインスタンスを含めます。
- 多重継承アプローチ:フォームの基底クラスとフォームのユーザーインターフェースオブジェクトの両方をサブクラス化します。これにより、フォーム内で定義されたウィジェットを、サブクラスのスコープ内から直接使用できるようになります。
これを実証するために、簡単な「Calculator Form」アプリケーションを作成します。これは、オリジナルの「Calculator Form」サンプルに基づいています。
このアプリケーションは、1つのソースファイル(main.cpp )と1つの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 」オプションを指定すると、CMake はuic ツールを実行し、ソースファイルで使用できるui_calculatorform.h ファイルを生成します。
qmake を使用して実行ファイルをビルドする場合、.pro ファイルが必要です:
TEMPLATE = app
FORMS = calculatorform.ui
SOURCES = main.cppこのファイルの特長は、`FORMS `宣言であり、これにより`qmake `に対して、uic で処理すべきファイルを指定します。この場合、`calculatorform.ui `ファイルを使用して`ui_calculatorform.h `ファイルが生成され、このファイルは`SOURCES `宣言にリストされている任意のファイルで使用できます。
注: Qt Creator を使用して、Calculator Form プロジェクトを作成できます 。これにより、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 Widgetsウィジェットをサブクラス化し、フォームのユーザーインターフェースオブジェクトのプライベートインスタンスを含めます。これは次のような形になります:
- メンバ変数
- ポインタ型メンバー変数
メンバ変数の使用
このアプローチでは、Qt Widgetsのサブクラスを作成し、コンストラクタ内でユーザーインターフェースを設定します。この方法で使用されるコンポーネントは、フォームで使用されるウィジェットやレイアウトをQt Widgetsのサブクラスに公開し、ユーザーインターフェースとアプリケーション内の他のオブジェクトとの間でシグナルとスロットの接続を行うための標準的な仕組みを提供します。 生成されるUi::CalculatorForm 構造体は、クラスのメンバです。
この手法は、「Calculator Form」の例で使用されています。
ユーザーインターフェースを確実に使用できるようにするには、Ui::CalculatorForm を参照する前に、uic が生成するヘッダーファイルをインクルードする必要があります:
#include "ui_calculatorform.h"プロジェクトファイルを更新し、calculatorform.h をインクルードするようにする必要があります。CMake については:
qt_add_executable(calculatorform
calculatorform.cpp calculatorform.h calculatorform.ui
main.cpp
)以下の例のように、include ディレクティブで相対パスが使用されている場合など、特定の状況では、AUTOUIC に依存する代わりにqt_add_ui()を使用してui_calculatorform.h ファイルを生成することができます。
AUTOUIC ではなく qt_add_ui を優先すべき場合
#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 を private として継承することで、サブクラス内でユーザーインターフェースオブジェクトが private であることを保証します。また、前回の例で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 += uitoolsQUiLoader クラスは、ユーザーインターフェースを構築するためのフォームローダーオブジェクトを提供します。このユーザーインターフェースは、QIODevice (例:QFile オブジェクト)から取得することで、プロジェクトのリソースファイルに保存されているフォームを取得できます。QUiLoader::load()関数は、ファイルに含まれるユーザーインターフェースの記述を使用して、フォームウィジェットを構築します。
QtUiTools モジュールのクラスは、次のディレクティブを使用してインクルードできます:
#include <QtUiTools>QUiLoader::load() 関数は、Text Finderのサンプルコードに示されているように呼び出されます。
staticQWidget*loadUiFile(QWidget*parent)
{
QFile file(u":/forms/textfinder.ui"_s);
if(!file.open(QIODevice::ReadOnly))
qFatal("Cannot open resource file");
returnQUiLoader().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() スロットは、ユーザーが指定した値の妥当性を検証するために使用されます。
ダイアログのコンストラクタでは、以前と同様にウィジェットを設定し、[Cancel]ボタンのclicked()シグナルをダイアログのreject()スロットに接続します。また、ダイアログがラインエディットのReturnキーイベントの処理に干渉しないように、両方のボタンの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() スロットを持つ「Text Finder」が挙げられます。
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."));
}
}
}信号とスロットの自動的な関連付けにより、ウィジェット設計者が従うべき標準的な命名規則と明示的なインターフェースの両方が提供されます。所定のインターフェースを実装したソースコードが提供されることで、ユーザーインターフェース設計者は、自らコードを書くことなく、自分の設計が実際に機能するかどうかを確認することができます。
© 2026 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.