Verwendung einer UI-Datei aus dem „ Qt Widgets “-Designer in Ihrer C++-Anwendung
Qt Widgets Designer-UI-Dateien stellen den Widget-Baum des Formulars im XML-Format dar. Die Formulare können verarbeitet werden:
- Zur Kompilierungszeit, d. h., die Formulare werden in C++-Code umgewandelt, der kompiliert werden kann.
- zur Laufzeit, d. h., die Formulare werden von der Klasse „ QUiLoader “ verarbeitet, die den Widget-Baum beim Parsen der XML-Datei dynamisch aufbaut.
Formularverarbeitung zur Kompilierungszeit
Sie erstellen Benutzeroberflächenkomponenten mit dem „ Qt Widgets Designer“ und verwenden die integrierten Build-Tools von Qt, qmake und uic, um beim Erstellen der Anwendung Code dafür zu generieren. Der generierte Code enthält das Benutzeroberflächenobjekt des Formulars. Es handelt sich um eine C++-Struktur, die Folgendes enthält:
- Zeiger auf die Widgets, Layouts, Layout-Elemente, Schaltflächengruppen und Aktionen des Formulars.
- Eine Mitgliedsfunktion namens „
setupUi()“, um den Widget-Baum auf dem übergeordneten Widget aufzubauen. - Eine Mitgliedsfunktion namens „
retranslateUi()“, die die Übersetzung der Zeichenfolgen-Eigenschaften des Formulars übernimmt. Weitere Informationen finden Sie unter „Reagieren auf Sprachänderungen“.
Der generierte Code kann in Ihre Anwendung eingebunden und direkt von dort aus verwendet werden. Alternativ können Sie ihn zur Erweiterung von Unterklassen von Standard-Widgets nutzen.
Ein zur Kompilierungszeit verarbeitetes Formular kann in Ihrer Anwendung mit einem der folgenden Ansätze verwendet werden:
- Der direkte Ansatz: Sie erstellen ein Widget, das als Platzhalter für die Komponente dient, und richten darin die Benutzeroberfläche ein.
- Der Ansatz der einfachen Vererbung: Sie erstellen eine Unterklasse der Basisklasse des Formulars (z. B. `QWidget ` oder ` QDialog`) und fügen eine private Instanz des Benutzeroberflächenobjekts des Formulars ein.
- Der Ansatz der Mehrfachvererbung: Sie erstellen eine Unterklasse sowohl der Basisklasse des Formulars als auch des Benutzeroberflächenobjekts des Formulars. Dadurch können die im Formular definierten Widgets direkt aus dem Geltungsbereich der Unterklasse heraus verwendet werden.
Zur Veranschaulichung erstellen wir eine einfache „Calculator Form“-Anwendung. Sie basiert auf dem ursprünglichen „Calculator Form “-Beispiel.
Die Anwendung besteht aus einer Quelldatei, „ main.cpp “, und einer UI-Datei.
Die mit dem „ Qt Widgets -Designer“ erstellte Datei „ calculatorform.ui “ ist unten dargestellt:

Wenn Sie die ausführbare Datei mit „ CMake “ erstellen, ist eine „ CMakeLists.txt “-Datei erforderlich:
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
)Das Formular ist in „ qt_add_executable() “ unter den C++-Quelldateien aufgeführt. Die Option „ CMAKE_AUTOUIC “ weist „ CMake “ an, das Tool „ uic “ auszuführen, um eine „ ui_calculatorform.h “-Datei zu erstellen, die von den Quelldateien verwendet werden kann.
Bei der Verwendung von „ qmake “ zum Erstellen der ausführbaren Datei ist eine „ .pro “-Datei erforderlich:
TEMPLATE = app
FORMS = calculatorform.ui
SOURCES = main.cppDas Besondere an dieser Datei ist die Deklaration „ FORMS “, die „ qmake “ mitteilt, welche Dateien mit „ uic “ verarbeitet werden sollen. In diesem Fall wird die Datei „ calculatorform.ui “ verwendet, um eine „ ui_calculatorform.h “-Datei zu erstellen, die von jeder in der Deklaration „ SOURCES “ aufgeführten Datei verwendet werden kann.
Hinweis: Sie können Qt Creator verwenden, um das Projekt „Calculator Form“ zu erstellen. Dabei werden automatisch die Datei „main.cpp“, die Benutzeroberfläche sowie eine Projektdatei für das gewünschte Build-Tool generiert, die Sie anpassen können.
Der direkte Ansatz
Um den direkten Ansatz zu verwenden, binden wir die Datei „ ui_calculatorform.h “ direkt in „ main.cpp “ ein:
#include "ui_calculatorform.h"Die Funktion ` main ` erstellt das Rechner-Widget, indem sie ein Standard-` QWidget ` erstellt, das wir als Container für die in der Datei ` calculatorform.ui ` beschriebene Benutzeroberfläche verwenden.
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QWidget widget;
Ui::CalculatorForm ui;
ui.setupUi(&widget);
widget.show();
return app.exec();
}In diesem Fall ist „ Ui::CalculatorForm “ ein Objekt zur Beschreibung der Benutzeroberfläche aus der Datei „ ui_calculatorform.h “, das alle Widgets des Dialogs sowie die Verbindungen zwischen deren Signalen und Slots einrichtet.
Der direkte Ansatz bietet eine schnelle und einfache Möglichkeit, einfache, in sich geschlossene Komponenten in Ihren Anwendungen zu verwenden. Mit dem „ Qt Widgets Designer“ erstellte Komponenten erfordern jedoch oft eine enge Integration in den übrigen Anwendungscode. Beispielsweise lässt sich der oben angegebene Code „ CalculatorForm “ zwar kompilieren und ausführen, doch die Objekte „ QSpinBox “ interagieren nicht mit dem „ QLabel “, da wir einen benutzerdefinierten Slot benötigen, um die Additionsoperation durchzuführen und das Ergebnis im „ QLabel “ anzuzeigen. Um dies zu erreichen, müssen wir den Ansatz der einfachen Vererbung verwenden.
Der Ansatz der einfachen Vererbung
Um den Ansatz der einfachen Vererbung zu nutzen, erstellen wir eine Unterklasse eines Standard-Qt-Widgets und fügen eine private Instanz des Benutzeroberflächenobjekts des Formulars ein. Dies kann wie folgt aussehen:
- Eine Member-Variable
- Eine Zeiger-Mitgliedsvariable
Verwendung einer Member-Variablen
Bei diesem Ansatz erstellen wir eine Unterklasse eines Qt Widgets und richten die Benutzeroberfläche innerhalb des Konstruktors ein. Auf diese Weise verwendete Komponenten machen die im Formular verwendeten Widgets und Layouts für die Qt Widget-Unterklasse zugänglich und bieten ein Standardsystem für die Herstellung von Signal- und Slot-Verbindungen zwischen der Benutzeroberfläche und anderen Objekten in Ihrer Anwendung. Die generierte Struktur ` Ui::CalculatorForm ` ist ein Mitglied der Klasse.
Dieser Ansatz wird im Beispiel „Calculator Form “ verwendet.
Um sicherzustellen, dass wir die Benutzeroberfläche nutzen können, müssen wir die von „ uic “ generierte Header-Datei einbinden, bevor wir auf „ Ui::CalculatorForm “ verweisen:
#include "ui_calculatorform.h"Die Projektdatei muss aktualisiert werden, um calculatorform.h einzubinden. Für CMake gilt:
qt_add_executable(calculatorform
calculatorform.cpp calculatorform.h calculatorform.ui
main.cpp
)In bestimmten Fällen, wie beispielsweise im folgenden Beispiel, in dem die Include-Anweisung einen relativen Pfad verwendet, kann qt_add_ui() verwendet werden, um die Datei „ ui_calculatorform.h “ zu generieren, anstatt sich auf AUTOUIC zu verlassen.
Wann sollte qt_add_ui gegenüber AUTOUIC bevorzugt werden?
#include "src/files/ui_calculatorform.h"qt_add_ui(calculatorform SOURCES calculatorform.ui
INCLUDE_PREFIX src/files)Für „ qmake “:
HEADERS = calculatorform.hDie Unterklasse ist wie folgt definiert:
class CalculatorForm : public QWidget
{
Q_OBJECT
public:
explicit CalculatorForm(QWidget *parent = nullptr);
private slots:
void updateResult();
private:
Ui::CalculatorForm ui;
};Das wichtigste Merkmal der Klasse ist das private Objekt ` ui `, das den Code für die Einrichtung und Verwaltung der Benutzeroberfläche bereitstellt.
Der Konstruktor der Unterklasse erstellt und konfiguriert alle Widgets und Layouts für das Dialogfeld, indem er lediglich die Funktion ` setupUi() ` des Objekts ` ui ` aufruft. Sobald dies geschehen ist, kann die Benutzeroberfläche nach Bedarf angepasst werden.
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);
}Wir können Signale und Slots in Widgets der Benutzeroberfläche auf die übliche Weise verbinden, indem wir das Präfix „on_<Objektname>“ hinzufügen. Weitere Informationen finden Sie unter widgets-and-dialogs-with-auto-connect.
Die Vorteile dieses Ansatzes liegen in der einfachen Nutzung der Vererbung zur Bereitstellung einer auf „ QWidget “ basierenden Schnittstelle sowie in der Kapselung der Variablen der Benutzeroberflächen-Widgets innerhalb des Datenelements „ ui “. Mit dieser Methode können wir mehrere Benutzeroberflächen innerhalb desselben Widgets definieren, die jeweils in einem eigenen Namensraum enthalten sind, und diese übereinanderlegen (oder zusammenstellen). Dieser Ansatz eignet sich beispielsweise dazu, einzelne Registerkarten aus bestehenden Formularen zu erstellen.
Verwendung einer Zeiger-Member-Variablen
Alternativ kann die Struktur „ Ui::CalculatorForm “ als Zeiger-Member der Klasse definiert werden. Der Header sieht dann wie folgt aus:
namespace Ui {
class CalculatorForm;
}
class CalculatorForm : public QWidget
...
virtual ~CalculatorForm();
...
private:
Ui::CalculatorForm *ui;
...Die entsprechende Quelldatei sieht wie folgt aus:
#include "ui_calculatorform.h"
CalculatorForm::CalculatorForm(QWidget *parent) :
QWidget(parent), ui(new Ui::CalculatorForm)
{
ui->setupUi(this);
}
CalculatorForm::~CalculatorForm()
{
delete ui;
}Der Vorteil dieses Ansatzes besteht darin, dass das Benutzeroberflächenobjekt vorwärts deklariert werden kann, was bedeutet, dass wir die generierte Datei „ ui_calculatorform.h “ nicht in den Header einbinden müssen. Das Formular kann dann geändert werden, ohne dass die abhängigen Quelldateien neu kompiliert werden müssen. Dies ist besonders wichtig, wenn die Klasse Einschränkungen hinsichtlich der Binärkompatibilität unterliegt.
Wir empfehlen diesen Ansatz generell für Bibliotheken und große Anwendungen. Weitere Informationen finden Sie unter „Erstellen von gemeinsam genutzten Bibliotheken“.
Der Ansatz der Mehrfachvererbung
Mit dem „ Qt Widgets -Designer“ erstellte Formulare können zusammen mit einer Standardklasse auf Basis von „ QWidget “ unterklassifiziert werden. Durch diesen Ansatz sind alle im Formular definierten Benutzeroberflächenkomponenten im Geltungsbereich der Unterklasse direkt zugänglich, und Signal- und Slot-Verbindungen können wie gewohnt mit der Funktion „ connect()“ hergestellt werden.
Wir müssen die Header-Datei, die „ uic “ aus der Datei „ calculatorform.ui “ generiert, wie folgt einbinden:
#include "ui_calculatorform.h"Die Klasse wird ähnlich wie beim Ansatz der einfachen Vererbung definiert, mit dem Unterschied, dass wir diesmal sowohl von ` QWidget ` als auch von ` Ui::CalculatorForm` erben, und zwar wie folgt:
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);
};Wir erben „ Ui::CalculatorForm “ privat, um sicherzustellen, dass die Objekte der Benutzeroberfläche in unserer Unterklasse privat sind. Wir könnten sie auch mit den Schlüsselwörtern „ public “ oder „ protected “ erben, genauso wie wir im vorherigen Fall „ ui “ als „public“ oder „protected“ hätten definieren können.
Der Konstruktor der Unterklasse erfüllt viele der gleichen Aufgaben wie der im Beispiel zur einfachen Vererbung verwendete Konstruktor:
In diesem Fall kann auf die in der Benutzeroberfläche verwendeten Widgets auf dieselbe Weise zugegriffen werden wie auf ein Widget, das manuell im Code erstellt wurde. Wir benötigen das Präfix „ ui “ nicht mehr, um auf sie zuzugreifen.
Reaktion auf Sprachänderungen
Qt benachrichtigt Anwendungen über eine Änderung der Sprache der Benutzeroberfläche, indem es ein Ereignis vom Typ „ QEvent::LanguageChange “ sendet. Um die Memberfunktion „ retranslateUi() “ des Benutzeroberflächenobjekts aufzurufen, implementieren wir „ QWidget::changeEvent() “ in der Form-Klasse wie folgt neu:
void CalculatorForm::changeEvent(QEvent *e)
{
QWidget::changeEvent(e);
switch (e->type()) {
case QEvent::LanguageChange:
ui->retranslateUi(this);
break;
default:
break;
}
}Formularverarbeitung zur Laufzeit
Alternativ können Formulare zur Laufzeit verarbeitet werden, wodurch dynamisch generierte Benutzeroberflächen entstehen. Dies kann mithilfe des Moduls „ QtUiTools “ erfolgen, das die Klasse „ QUiLoader “ zur Verarbeitung von Formularen bereitstellt, die mit dem „ Qt Widgets Designer“ erstellt wurden.
Der UiTools-Ansatz
Zur Verarbeitung von Formularen zur Laufzeit ist eine Ressourcendatei erforderlich, die eine UI-Datei enthält. Außerdem muss die Anwendung für die Verwendung des Moduls „ QtUiTools “ konfiguriert werden. Dies geschieht durch Einfügen der folgenden Deklarationen in eine „ CMake “-Projektdatei, wobei sicherzustellen ist, dass die Anwendung ordnungsgemäß kompiliert und verknüpft wird.
find_package(Qt6 REQUIRED COMPONENTS Core Gui UiTools Widgets)
target_link_libraries(textfinder PUBLIC
Qt::Core
Qt::Gui
Qt::UiTools
Qt::Widgets
)Für „ qmake “:
QT += uitoolsDie Klasse „ QUiLoader “ stellt ein Formular-Ladeobjekt zur Erstellung der Benutzeroberfläche bereit. Diese Benutzeroberfläche kann von jedem „ QIODevice “ abgerufen werden, z. B. von einem „ QFile “-Objekt, um ein in der Ressourcendatei eines Projekts gespeichertes Formular zu erhalten. Die Funktion „ QUiLoader::load()“ erstellt das Formular-Widget anhand der in der Datei enthaltenen Beschreibung der Benutzeroberfläche.
Die Klassen des Moduls „ QtUiTools “ können mit der folgenden Direktive eingebunden werden:
#include <QtUiTools>Die Funktion ` QUiLoader::load()` wird wie in diesem Codeausschnitt aus dem Beispiel „Text Finder“ aufgerufen:
static QWidget*loadUiFile(QWidget*parent)
{
QFile file(u":/forms/textfinder.ui"_s);
if (!file.open(QIODevice::ReadOnly))
qFatal("Cannot open resource file");
return QUiLoader().load(&file, parent);
}In einer Klasse, die „ QtUiTools “ verwendet, um ihre Benutzeroberfläche zur Laufzeit aufzubauen, können wir Objekte im Formular mithilfe von „ QObject::findChild()“ lokalisieren. Im folgenden Code lokalisieren wir beispielsweise einige Komponenten anhand ihrer Objektnamen und Widget-Typen:
ui_findButton = findChild<QPushButton*>("findButton");
ui_textEdit = findChild<QTextEdit*>("textEdit");
ui_lineEdit = findChild<QLineEdit*>("lineEdit");Die Verarbeitung von Formularen zur Laufzeit gibt dem Entwickler die Freiheit, die Benutzeroberfläche eines Programms zu ändern, indem er einfach die UI-Datei anpasst. Dies ist nützlich, wenn Programme an verschiedene Benutzerbedürfnisse angepasst werden sollen, beispielsweise durch besonders große Symbole oder ein anderes Farbschema zur Unterstützung der Barrierefreiheit.
Automatische Verbindungen
Die für Formulare zur Kompilierungs- oder Laufzeit definierten Signal- und Slot-Verbindungen können entweder manuell oder automatisch eingerichtet werden, wobei die Fähigkeit von „ QMetaObject “, Verbindungen zwischen Signalen und entsprechend benannten Slots herzustellen, genutzt wird.
Im Allgemeinen gilt: Wenn wir in einem „ QDialog “ die vom Benutzer eingegebenen Informationen vor deren Bestätigung verarbeiten möchten, müssen wir das Signal „clicked()“ der Schaltfläche „OK“ mit einem benutzerdefinierten Slot in unserem Dialog verbinden. Wir zeigen zunächst ein Beispiel für einen Dialog, in dem der Slot manuell verbunden ist, und vergleichen diesen anschließend mit einem Dialog, der die automatische Verbindung nutzt.
Ein Dialog ohne automatische Verbindung
Wir definieren den Dialog auf dieselbe Weise wie zuvor, fügen nun aber zusätzlich zum Konstruktor einen Slot ein:
class ImageDialog : public QDialog, private Ui::ImageDialog
{
Q_OBJECT
public:
explicit ImageDialog(QWidget *parent = nullptr);
private slots:
void checkValues();
};Der Slot „ checkValues() “ dient dazu, die vom Benutzer eingegebenen Werte zu validieren.
Im Konstruktor des Dialogs richten wir die Widgets wie zuvor ein und verbinden das Signal „ clicked()“ der Schaltfläche „Abbrechen“ mit dem Slot „reject()“ des Dialogs. Außerdem deaktivieren wir die Eigenschaft „ autoDefault “ bei beiden Schaltflächen, um sicherzustellen, dass der Dialog die Art und Weise, wie das Zeilenfeld Return-Tastenereignisse verarbeitet, nicht beeinträchtigt:
ImageDialog::ImageDialog(QWidget *parent)
: QDialog(parent)
{
setupUi(this);
okButton->setAutoDefault(false);
cancelButton->setAutoDefault(false);
...
connect(okButton, &QAbstractButton::clicked, this, &ImageDialog::checkValues);
}Wir verbinden das Signal „ clicked()“ der „OK“-Schaltfläche mit dem Slot „checkValues()“ des Dialogs, den wir wie folgt implementieren:
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();
}
}Dieser benutzerdefinierte Slot führt nur das Nötigste durch, um sicherzustellen, dass die vom Benutzer eingegebenen Daten gültig sind – er akzeptiert die Eingabe nur, wenn ein Name für das Bild angegeben wurde.
Widgets und Dialoge mit automatischer Verknüpfung
Obwohl es einfach ist, einen benutzerdefinierten Slot im Dialog zu implementieren und ihn im Konstruktor zu verbinden, könnten wir stattdessen die automatischen Verbindungsfunktionen von QMetaObject nutzen, um das Signal „clicked()“ der OK-Schaltfläche mit einem Slot in unserer Unterklasse zu verbinden. uic generiert hierfür automatisch Code in der Funktion „ setupUi() “ des Dialogs, sodass wir lediglich einen Slot mit einem Namen deklarieren und implementieren müssen, der einer Standardkonvention entspricht:
void on_<object name>_<signal name>(<signal parameters>);Hinweis: Beim Umbenennen von Widgets im Formular müssen die Slot-Namen entsprechend angepasst werden, was zu einem Wartungsproblem werden kann. Aus diesem Grund raten wir davon ab, dies in neuem Code zu verwenden.
Unter Verwendung dieser Konvention können wir einen Slot definieren und implementieren, der auf Mausklicks auf die Schaltfläche „OK“ reagiert:
class ImageDialog : public QDialog, private Ui::ImageDialog
{
Q_OBJECT
public:
explicit ImageDialog(QWidget *parent = nullptr);
private slots:
void on_okButton_clicked();
};Ein weiteres Beispiel für die automatische Signal- und Slot-Verbindung wäre der Text Finder mit seinem Slot „ on_findButton_clicked() “.
Wir nutzen das System von „ QMetaObject “, um Signal- und Slot-Verbindungen herzustellen:
QMetaObject::connectSlotsByName(this);Dadurch können wir den Slot wie unten gezeigt implementieren:
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."));
}
}
}Die automatische Verknüpfung von Signalen und Slots bietet sowohl eine einheitliche Namenskonvention als auch eine explizite Schnittstelle, an der sich Widget-Designer orientieren können. Durch die Bereitstellung von Quellcode, der eine bestimmte Schnittstelle implementiert, können Benutzeroberflächen-Designer überprüfen, ob ihre Entwürfe tatsächlich funktionieren, ohne selbst Code schreiben zu müssen.
© 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.