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 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<Gender>() );
}

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 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:

main.cpp
#include <QApplication>
#include "PersonListWidget.h"
 
int main( int argc, char *argv[] )
{
  QApplication app( argc, argv );
  PersonListWidget w;
 
  w.show();
 
  return app.exec();
}
Person.h
#ifndef PERSON_H
#define PERSON_H
 
#include <QString>
#include <QMetaType>
 
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
PersonDialog.h
#ifndef PERSONDIALOG_H
#define PERSONDIALOG_H
 
#include <QDialog>
#include "Person.h"
 
#include <QColorDialog>
 
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
PersonDialog.cpp
#include "PersonDialog.h"
 
#include "Person.h"
#include <QLineEdit>
#include <QSpinBox>
#include <QComboBox>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QFormLayout>
 
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<Gender>() );
}
PersonListWidget.h
#ifndef PERSONLISTWIDGET_H
#define PERSONLISTWIDGET_H
 
#include <QWidget>
#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
PersonListWidget.cpp
#include "PersonListWidget.h"
#include "PersonDialog.h"
#include <QPushButton>
#include <QListWidget>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QList>
 
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<QListWidgetItem *> items = personList->selectedItems();
  delete items.first();
}

Nun bekommen wir folgenden Dialog angezeigt: