====== Dialoge selbst erstellen ====== Im vorherigen Kapitel haben wir gesehen wie wir vordefinierte Dialoge verwenden können. Diese Dialoge werden häufig benötigt und sind deshalb sehr praktisch. Trotzdem werden wir in manchen Fällen trotzdem einen benutzerdefinierten Dialog benötigen. Im folgenden beschränken wir uns darauf, dass ein Objekt von unserem Dialog angelegt wird und die eingegeben Daten über eine Methode abgefragt werden können. Nun wollen wir Schritt für Schritt einen eigenen Dialog implementieren. ===== Einzugebende Daten festlegen ===== Dieser Schritt ist von einem Anwendungsfall zum anderen verschieden. In jedem Fall ist es empfehlenswert, sich eine eigene Klasse für diese Daten anzulegen. In unserem Beispiel wollen wir Informationen über eine Person abfragen, die wir über die Attribute Vorname, Nachname, Alter und Geschlecht definieren. Die Klasse für unsere Daten würde entsprechend so aussehen: enum class Gender { MALE, FEMALE }; Q_DECLARE_METATYPE(Gender) class Person { public: Person( const QString& firstName, const QString& lastName, const unsigned char age, const Gender gender ) : m_firstName( firstName ), m_lastName( lastName ), m_age( age ), m_gender( gender ) {} const QString& firstName() const { return m_firstName; } const QString& lastName() const { return m_lastName; } int age() const { return m_age; } Gender gender() const { return m_gender; } private: QString m_firstName, m_lastName; unsigned char m_age; Gender m_gender; }; Diese Implementierung enthält nur jene für die Demonstration notwendige Funktionalität. ===== Design der Eingabemaske ===== Wir wollen unseren Dialog relativ schlicht halten. Deshalb verwenden wir zwei ''QLineEdit''-Widgets für die beiden Namen, eine ''QSpinBox'' für das Alter und eine ''QComboBox'' für das Geschlecht. Damit der Benutzer weiß, welche Daten eingegeben werden sollen, wollen wir eine kurze Beschreibung vor dem jeweiligen Eingabefeld. Dafür eignet sich am besten ein [[frameworks:qt:gui:basic:layouts#formularlayout|Formular-Layout]]. Soweit zu den Daten. Was noch fehlt ist ein Button zum Bestätigen der Eingabe, den wir über ein ''QVBoxLayout'' mit dem Formular verbinden und auf unseren Dialog anwenden. ===== Implementierung des Dialogs ===== Wir könnten unseren Dialog natürlich auch basiert auf ''QWidget'' erstellen, jedoch nimmt uns ''QDialog'' einiges an Arbeit ab. class PersonDialog : public QDialog Wie wir wissen, wird ein Dialog mit einem Parent-Objekt automatisch an oberster Stelle und zentriert über dem Eltern-Widget angezeigt. Dieses Verhalten übernehmen wir von ''QDialog'' und bieten einen entsprechenden Konstruktor mit einem optionalen ''QWidget'' als Parameter an. Der Rest des Konstruktors legt die weiteren Eigenschaften der einzelnen Widgets fest und platziert sie in einem Layout. Hervorzuheben ist noch die Verbindung zwischen der ''QDialogButtonBox'' und den ''accept()''- bzw. ''reject()''-Slots des Dialogs. Beide Slots beenden den Dialog und setzen den Status-Code entsprechend auf ''QDialog::Accepted'' bzw. ''QDialog::Rejected''. PersonDialog::PersonDialog( QWidget *parent ) : QDialog( parent ), m_firstName( new QLineEdit() ), m_lastName( new QLineEdit() ), m_age( new QSpinBox() ), m_gender( new QComboBox() ), m_buttons( new QDialogButtonBox( QDialogButtonBox::Save | QDialogButtonBox::Cancel ) ) { m_age->setRange( 0, 150 ); m_gender->addItem( "Male", QVariant::fromValue( Gender::MALE ) ); m_gender->addItem( "Female", QVariant::fromValue( Gender::FEMALE ) ); connect( m_buttons->button( QDialogButtonBox::Save ), &QPushButton::clicked, this, &QDialog::accept ); connect( m_buttons->button( QDialogButtonBox::Cancel ), &QPushButton::clicked, this, &QDialog::reject ); QVBoxLayout *vLayout = new QVBoxLayout( this ); QFormLayout *fLayout = new QFormLayout(); fLayout->addRow( "First Name:", m_firstName ); fLayout->addRow( "Last Name:", m_lastName ); fLayout->addRow( "Age:", m_age ); fLayout->addRow( "Gender:", m_gender ); vLayout->addLayout( fLayout ); QHBoxLayout *hLayout = new QHBoxLayout(); hLayout->addStretch(); hLayout->addWidget( m_buttons ); vLayout->addLayout( hLayout ); setWindowTitle( "Person" ); } Als nächstes wollen wir noch ein Signal auslösen, wenn der Dialog bestätigt wurde. Hierfür überschreiben wir die Methode ''QDialog::done()'' entsprechend. void PersonDialog::done( int result ) { QDialog::done( result ); if ( result == Accepted ) emit personSelected( person() ); } Die einzige Methode, die wir noch brauchen, ist jene für die Abfrage der eingegebenen Daten. Sie übernimmt die in die Widgets eingegeben Daten und schreibt sie in unser ''Person''-Objekt, das dann zurückgegeben wird. Person PersonDialog::person() const { return Person( m_firstName->text(), m_lastName->text(), m_age->value(), m_gender->itemData( m_gender->currentIndex() ).value() ); } ===== Verwendung des Dialogs ===== Unsere Dialog-Klasse kann nun über Signale und Slots verwendet werden. Für dieses Beispiel erstellen wir ähnlich wie im Kapitel zu [[frameworks:qt:gui:basic:predefdialogs:object|Dialog-Objekten]] eine Liste - dieses Mal jedoch aus Personen. Über den Dialog können neue Person zur Liste hinzugefügt werden. Es reicht dabei, das ''personSelected()''-Signal mit einem passenden Slot zu verbinden. connect( dialog, &PersonDialog::personSelected, this, &PersonListWidget::addPerson ); Die Implementierung von ''addPerson'' könnte folgendermaßen aussehen: void PersonListWidget::addPerson( const Person& person ) { personList->addItem( person.firstName() + " " + person.lastName() + ", " + QString::number( person.age() ) + ", " + ( person.gender() == Gender::MALE ? "Male" : "Female" ) ); } ===== Gesamter Beispielcode, Screenshot und Ausgabe ===== Zusammenfassend noch der gesamte Code des Beispiels: #include #include "PersonListWidget.h" int main( int argc, char *argv[] ) { QApplication app( argc, argv ); PersonListWidget w; w.show(); return app.exec(); } #ifndef PERSON_H #define PERSON_H #include #include enum class Gender { MALE, FEMALE }; Q_DECLARE_METATYPE(Gender) class Person { public: Person( const QString& firstName, const QString& lastName, const unsigned char age, const Gender gender ) : m_firstName( firstName ), m_lastName( lastName ), m_age( age ), m_gender( gender ) {} const QString& firstName() const { return m_firstName; } const QString& lastName() const { return m_lastName; } int age() const { return m_age; } Gender gender() const { return m_gender; } private: QString m_firstName, m_lastName; unsigned char m_age; Gender m_gender; }; #endif // PERSON_H #ifndef PERSONDIALOG_H #define PERSONDIALOG_H #include #include "Person.h" #include class QLineEdit; class QSpinBox; class QComboBox; class QDialogButtonBox; class PersonDialog : public QDialog { Q_OBJECT public: PersonDialog( QWidget *parent = nullptr ); Person person() const; protected: void done(int result); signals: void personSelected( const Person& person ); private: QLineEdit *m_firstName, *m_lastName; QSpinBox *m_age; QComboBox *m_gender; QDialogButtonBox *m_buttons; }; #endif // PERSONDIALOG_H #include "PersonDialog.h" #include "Person.h" #include #include #include #include #include #include #include #include PersonDialog::PersonDialog( QWidget *parent ) : QDialog( parent ), m_firstName( new QLineEdit() ), m_lastName( new QLineEdit() ), m_age( new QSpinBox() ), m_gender( new QComboBox() ), m_buttons( new QDialogButtonBox( QDialogButtonBox::Save | QDialogButtonBox::Cancel ) ) { m_age->setRange( 0, 150 ); m_gender->addItem( "Male", QVariant::fromValue( Gender::MALE ) ); m_gender->addItem( "Female", QVariant::fromValue( Gender::FEMALE ) ); connect( m_buttons->button( QDialogButtonBox::Save ), &QPushButton::clicked, this, &QDialog::accept ); connect( m_buttons->button( QDialogButtonBox::Cancel ), &QPushButton::clicked, this, &QDialog::reject ); QVBoxLayout *vLayout = new QVBoxLayout( this ); QFormLayout *fLayout = new QFormLayout(); fLayout->addRow( "First Name:", m_firstName ); fLayout->addRow( "Last Name:", m_lastName ); fLayout->addRow( "Age:", m_age ); fLayout->addRow( "Gender:", m_gender ); vLayout->addLayout( fLayout ); QHBoxLayout *hLayout = new QHBoxLayout(); hLayout->addStretch(); hLayout->addWidget( m_buttons ); vLayout->addLayout( hLayout ); setWindowTitle( "Person" ); } void PersonDialog::done( int result ) { QDialog::done( result ); if ( result == Accepted ) emit personSelected( person() ); } Person PersonDialog::person() const { return Person( m_firstName->text(), m_lastName->text(), m_age->value(), m_gender->currentData().value() ); } #ifndef PERSONLISTWIDGET_H #define PERSONLISTWIDGET_H #include #include "Person.h" class QListWidget; class QPushButton; class PersonDialog; class PersonListWidget : public QWidget { Q_OBJECT public: PersonListWidget(); private: QListWidget *personList; QPushButton *addButton, *removeButton; PersonDialog *dialog; private slots: void addPerson( const Person& person ); void removeSelectedMessage(); }; #endif // PERSONLISTWIDGET_H #include "PersonListWidget.h" #include "PersonDialog.h" #include #include #include #include #include PersonListWidget::PersonListWidget() : personList( new QListWidget() ), addButton( new QPushButton ("+" ) ), removeButton( new QPushButton( "-" ) ), dialog( new PersonDialog( this ) ) { QVBoxLayout *vLayout = new QVBoxLayout(); QHBoxLayout *hLayout = new QHBoxLayout(); // Es kann nur ein Element der Liste selektiert werden. personList->setSelectionMode( QAbstractItemView::SingleSelection ); // Verbindungen aufbauen connect( addButton, &QPushButton::clicked, dialog, &QDialog::open ); connect( removeButton, &QPushButton::clicked, this, &PersonListWidget::removeSelectedMessage ); connect( dialog, &PersonDialog::personSelected, this, &PersonListWidget::addPerson ); // Widgets in Layout einfügen hLayout->addStretch(); hLayout->addWidget( addButton ); hLayout->addWidget( removeButton ); vLayout->addWidget( personList ); vLayout->addLayout( hLayout ); setWindowTitle( "Persons" ); setLayout( vLayout ); resize( 400, 400 ); } void PersonListWidget::addPerson( const Person& person ) { personList->addItem( person.firstName() + " " + person.lastName() + ", " + QString::number( person.age() ) + ", " + ( person.gender() == Gender::MALE ? "Male" : "Female" ) ); } void PersonListWidget::removeSelectedMessage() { QList items = personList->selectedItems(); delete items.first(); } Nun bekommen wir folgenden Dialog angezeigt: {{:frameworks:qt:gui:basic:persondialog.png?|}}