Beispiel für einen Lizenz-Assistenten
Das Beispiel des Lizenzassistenten zeigt, wie man komplexe Assistenten in Qt implementieren kann.
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.
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:
Klassenname | Enum-Wert | Seiten-ID |
---|---|---|
IntroPage | Page_Intro | 0 |
EvaluatePage | Page_Evaluate | 1 |
RegisterPage | Page_Register | 2 |
DetailsPage | Page_Details | 3 |
ConclusionPage | Page_Conclusion | 4 |
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>™ 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>™")); 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()
.
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.