Beispiel für einen Lizenz-Assistenten

Das Beispiel des Lizenzassistenten zeigt, wie man komplexe Assistenten in Qt implementieren kann.

Screenshot of the License Wizard example

Die meisten Assistenten haben eine lineare Struktur, mit Seite 1, gefolgt von Seite 2 und so weiter bis zur letzten Seite. Das Beispiel Trivialer Assistent zeigt, wie man solche Assistenten erstellt.

Einige Assistenten sind insofern komplexer, als sie auf der Grundlage der vom Benutzer bereitgestellten Informationen verschiedene Durchlaufpfade ermöglichen. Das Beispiel des Lizenzassistenten veranschaulicht dies. Es bietet mehrere Assistentenseiten; je nachdem, welche Optionen ausgewählt werden, kann der Benutzer verschiedene Seiten erreichen.

The License Wizard pages

Das Beispiel besteht aus den folgenden Klassen:

  • LicenseWizard erbt QWizard und implementiert einen nicht-linearen fünfseitigen Assistenten, der den Benutzer durch den Prozess der Auswahl einer Lizenzvereinbarung führt.
  • IntroPage EvaluatePage, , und sind Unterklassen, die die Assistentenseiten implementieren. RegisterPage DetailsPage ConclusionPage QWizardPage

Die LicenseWizard-Klasse

Die Klasse LicenseWizard leitet sich von QWizard ab und bietet einen fünfseitigen Assistenten, der den Benutzer durch den Prozess der Registrierung seiner Kopie eines fiktiven Softwareprodukts führt. Hier ist die Klassendefinition:

class LicenseWizard : public QWizard
{
    Q_OBJECT

public:
    enum { Page_Intro, Page_Evaluate, Page_Register, Page_Details,
           Page_Conclusion };

    LicenseWizard(QWidget *parent = nullptr);

private slots:
    void showHelp();
};

Die öffentliche API der Klasse ist auf einen Konstruktor und eine Aufzählung beschränkt. Das Enum definiert die mit den verschiedenen Seiten verbundenen IDs:

KlassennameEnum-WertSeiten-ID
IntroPagePage_Intro0
EvaluatePagePage_Evaluate1
RegisterPagePage_Register2
DetailsPagePage_Details3
ConclusionPagePage_Conclusion4

In diesem Beispiel sind die IDs frei wählbar. Die einzige Einschränkung ist, dass sie eindeutig sein müssen und sich von -1 unterscheiden. Mit den IDs können wir auf Seiten verweisen.

LicenseWizard::LicenseWizard(QWidget *parent)
    : QWizard(parent)
{
    setPage(Page_Intro, new IntroPage);
    setPage(Page_Evaluate, new EvaluatePage);
    setPage(Page_Register, new RegisterPage);
    setPage(Page_Details, new DetailsPage);
    setPage(Page_Conclusion, new ConclusionPage);

    setStartId(Page_Intro);

Im Konstruktor erstellen wir die fünf Seiten, fügen sie mit QWizard::setPage() in den Assistenten ein und legen Page_Intro als erste Seite fest.

#ifndef Q_OS_MAC
    setWizardStyle(ModernStyle);
#endif

Der Stil wird auf allen Plattformen außer macOS auf ModernStyle gesetzt,

    setOption(HaveHelpButton, true);
    setPixmap(QWizard::LogoPixmap, QPixmap(":/images/logo.png"));

    connect(this, &QWizard::helpRequested, this, &LicenseWizard::showHelp);

    setWindowTitle(tr("License Wizard"));
}

Wir konfigurieren QWizard so, dass eine Schaltfläche Help angezeigt wird, die mit unserem Steckplatz showHelp() verbunden ist. Wir setzen auch LogoPixmap für alle Seiten, die eine Kopfzeile haben (d.h. EvaluatePage, RegisterPage und DetailsPage).

void LicenseWizard::showHelp()
{
    static QString lastHelpMessage;

    QString message;

    switch (currentId()) {
    case Page_Intro:
        message = tr("The decision you make here will affect which page you "
                     "get to see next.");
        break;
    ...
    default:
        message = tr("This help is likely not to be of any help.");
    }

    if (lastHelpMessage == message)
        message = tr("Sorry, I already gave what help I could. "
                     "Maybe you should try asking a human?");

    QMessageBox::information(this, tr("License Wizard Help"), message);

    lastHelpMessage = message;
}

In showHelp() zeigen wir Hilfetexte an, die für die aktuelle Seite geeignet sind. Wenn der Benutzer zweimal auf Help für dieselbe Seite klickt, sagen wir: "Tut mir leid, ich habe schon so viel Hilfe gegeben, wie ich konnte. Vielleicht sollten Sie versuchen, einen Menschen zu fragen?"

Die IntroPage-Klasse

Die Seiten werden in licensewizard.h definiert und in licensewizard.cpp implementiert, zusammen mit LicenseWizard.

Hier ist die Definition und Implementierung von IntroPage:

class IntroPage : public QWizardPage
{
    Q_OBJECT

public:
    IntroPage(QWidget *parent = nullptr);

    int nextId() const override;

private:
    QLabel *topLabel;
    QRadioButton *registerRadioButton;
    QRadioButton *evaluateRadioButton;
};

IntroPage::IntroPage(QWidget *parent)
    : QWizardPage(parent)
{
    setTitle(tr("Introduction"));
    setPixmap(QWizard::WatermarkPixmap, QPixmap(":/images/watermark.png"));

    topLabel = new QLabel(tr("This wizard will help you register your copy of "
                             "<i>Super Product One</i>&trade; or start "
                             "evaluating the product."));
    topLabel->setWordWrap(true);

    registerRadioButton = new QRadioButton(tr("&Register your copy"));
    evaluateRadioButton = new QRadioButton(tr("&Evaluate the product for 30 "
                                              "days"));
    registerRadioButton->setChecked(true);

    QVBoxLayout *layout = new QVBoxLayout;
    layout->addWidget(topLabel);
    layout->addWidget(registerRadioButton);
    layout->addWidget(evaluateRadioButton);
    setLayout(layout);
}

Eine Seite erbt von QWizardPage. Wir setzen ein title und ein watermark pixmap. Indem wir kein subTitle setzen, stellen wir sicher, dass für diese Seite keine Kopfzeile angezeigt wird. (Unter Windows ist es üblich, dass Assistenten auf der ersten und letzten Seite ein Wasserzeichen-Pixmap anzeigen und auf den anderen Seiten eine Kopfzeile haben).

int IntroPage::nextId() const
{
    if (evaluateRadioButton->isChecked()) {
        return LicenseWizard::Page_Evaluate;
    } else {
        return LicenseWizard::Page_Register;
    }
}

Die Funktion nextId() gibt die ID für EvaluatePage zurück, wenn die Option Evaluate the product for 30 days aktiviert ist; andernfalls gibt sie die ID für RegisterPage zurück.

Die EvaluatePage-Klasse

Die Funktion EvaluatePage ist etwas komplizierter:

class EvaluatePage : public QWizardPage
{
    Q_OBJECT

public:
    EvaluatePage(QWidget *parent = nullptr);

    int nextId() const override;

private:
    QLabel *nameLabel;
    QLabel *emailLabel;
    QLineEdit *nameLineEdit;
    QLineEdit *emailLineEdit;
};

EvaluatePage::EvaluatePage(QWidget *parent)
    : QWizardPage(parent)
{
    setTitle(tr("Evaluate <i>Super Product One</i>&trade;"));
    setSubTitle(tr("Please fill both fields. Make sure to provide a valid "
                   "email address (e.g., john.smith@example.com)."));

    nameLabel = new QLabel(tr("N&ame:"));
    nameLineEdit = new QLineEdit;
    ...
    registerField("evaluate.name*", nameLineEdit);
    registerField("evaluate.email*", emailLineEdit);
    ...
}

Zuerst setzen wir die title und subTitle der Seite.

Dann erstellen wir die untergeordneten Widgets, erstellen die mit ihnen verknüpften wizard fields und fügen sie in Layouts ein. Die Felder werden mit einem Sternchen (*) neben ihrem Namen erstellt. Dadurch werden sie zu mandatory fields, d. h. zu Feldern, die ausgefüllt werden müssen, bevor der Benutzer die Schaltfläche Next (Continue unter macOS) drücken kann. Auf die Werte dieser Felder kann von jeder anderen Seite aus mit QWizardPage::field() zugegriffen werden.

Das Zurücksetzen der Seite bedeutet, dass die beiden Textfelder gelöscht werden.

int EvaluatePage::nextId() const
{
    return LicenseWizard::Page_Conclusion;
}

Die nächste Seite ist immer die ConclusionPage.

Die ConclusionPage-Klasse

Die Klassen RegisterPage und DetailsPage sind EvaluatePage sehr ähnlich. Gehen wir direkt zur ConclusionPage:

class ConclusionPage : public QWizardPage
{
    Q_OBJECT

public:
    ConclusionPage(QWidget *parent = nullptr);

    void initializePage() override;
    int nextId() const override;
    void setVisible(bool visible) override;

private slots:
    void printButtonClicked();

private:
    QLabel *bottomLabel;
    QCheckBox *agreeCheckBox;
};

Diesmal implementieren wir QWizardPage::initializePage() und QWidget::setVisible(), zusätzlich zu nextId(). Wir deklarieren auch einen privaten Slot: printButtonClicked().

int IntroPage::nextId() const
{
    if (evaluateRadioButton->isChecked()) {
        return LicenseWizard::Page_Evaluate;
    } else {
        return LicenseWizard::Page_Register;
    }
}

Die Standardimplementierung von QWizardPage::nextId() gibt die Seite mit der nächsten ID zurück, oder -1, wenn die aktuelle Seite die höchste ID hat. Dieses Verhalten würde hier funktionieren, da Page_Conclusion gleich 5 ist und es keine Seite mit einer höheren ID gibt, aber um zu vermeiden, dass wir uns auf ein solch subtiles Verhalten verlassen, implementieren wir nextId() neu, um -1 zurückzugeben.

void ConclusionPage::initializePage()
{
    QString licenseText;

    if (wizard()->hasVisitedPage(LicenseWizard::Page_Evaluate)) {
        licenseText = tr("<u>Evaluation License Agreement:</u> "
                         "You can use this software for 30 days and make one "
                         "backup, but you are not allowed to distribute it.");
    } else if (wizard()->hasVisitedPage(LicenseWizard::Page_Details)) {
        const QString emailAddress = field("details.email").toString();
        licenseText = tr("<u>First-Time License Agreement:</u> "
                         "You can use this software subject to the license "
                         "you will receive by email sent to %1.").arg(emailAddress);
    } else {
        licenseText = tr("<u>Upgrade License Agreement:</u> "
                         "This software is licensed under the terms of your "
                         "current license.");
    }
    bottomLabel->setText(licenseText);
}

Wir verwenden QWizard::hasVisitedPage(), um die Art der Lizenzvereinbarung zu ermitteln, die der Benutzer gewählt hat. Wenn der Benutzer das Feld EvaluatePage ausgefüllt hat, bezieht sich der Lizenztext auf eine Evaluierungslizenzvereinbarung. Wenn der Benutzer das Formular DetailsPage ausgefüllt hat, handelt es sich bei dem Lizenztext um eine Erstlizenzvereinbarung. Wenn der Benutzer einen Upgrade-Schlüssel angegeben und DetailsPage übersprungen hat, handelt es sich bei dem Lizenztext um eine Update-Lizenzvereinbarung.

void ConclusionPage::setVisible(bool visible)
{
    QWizardPage::setVisible(visible);

    if (visible) {
        wizard()->setButtonText(QWizard::CustomButton1, tr("&Print"));
        wizard()->setOption(QWizard::HaveCustomButton1, true);
        connect(wizard(), &QWizard::customButtonClicked,
                this, &ConclusionPage::printButtonClicked);
    } else {
        wizard()->setOption(QWizard::HaveCustomButton1, false);
        disconnect(wizard(), &QWizard::customButtonClicked,
                   this, &ConclusionPage::printButtonClicked);
    }
}

Wir möchten im Assistenten eine Schaltfläche Print anzeigen, wenn die ConclusionPage aktiv ist. Eine Möglichkeit, dies zu erreichen, ist die Neuimplementierung von QWidget::setVisible():

  • Wenn die Seite angezeigt wird, setzen wir den Text der Schaltfläche CustomButton1 auf Print, wir aktivieren die Option HaveCustomButton1 und verbinden das Signal customButtonClicked() von QWizard mit unserem Slot printButtonClicked().
  • Wenn die Seite ausgeblendet ist, deaktivieren wir die Option HaveCustomButton1 und trennen die Verbindung zum Slot printButtonClicked().

Beispielprojekt @ code.qt.io

Siehe auch QWizard und Triviales Assistentenbeispiel.

© 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.