许可证向导示例
许可证向导示例展示了如何在 Qt 中实现复杂的向导。
大多数向导都是线性结构,第 1 页之后是第 2 页,依次类推,直到最后一页。琐碎向导示例展示了如何创建此类向导。
有些向导更为复杂,它们允许根据用户提供的信息选择不同的遍历路径。许可证向导示例就说明了这一点。它提供了多个向导页面;根据选择的选项,用户可以进入不同的页面。
该示例由以下类组成:
LicenseWizard
继承于 并实现了一个非线性的五页向导,引导用户完成选择许可协议的过程。QWizardIntroPage
EvaluatePage
, , 和 是 的子类,实现了向导页面。RegisterPage
DetailsPage
ConclusionPage
QWizardPage
许可证向导类
LicenseWizard
类派生自QWizard ,它提供了一个五页向导,引导用户完成注册虚构软件产品副本的过程。下面是该类的定义:
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(); };
该类的公共应用程序接口仅限于一个构造函数和一个枚举。枚举定义了与不同页面相关的 ID:
类名 | 枚举值 | 页面 ID |
---|---|---|
IntroPage | Page_Intro | 0 |
EvaluatePage | Page_Evaluate | 1 |
RegisterPage | Page_Register | 2 |
DetailsPage | Page_Details | 3 |
ConclusionPage | Page_Conclusion | 4 |
在本例中,ID 是任意的。ID 允许我们引用页面。
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);
在构造函数中,我们创建了五个页面,使用QWizard::setPage() 将它们插入向导,并将Page_Intro
设置为第一页。
#ifndef Q_OS_MAC setWizardStyle(ModernStyle); #endif
在除 macOS 之外的所有平台上,我们将样式设置为ModernStyle 、
setOption(HaveHelpButton, true); setPixmap(QWizard::LogoPixmap, QPixmap(":/images/logo.png")); connect(this, &QWizard::helpRequested, this, &LicenseWizard::showHelp); setWindowTitle(tr("License Wizard")); }
我们将QWizard 配置为显示Help 按钮,该按钮与showHelp()
插槽相连。我们还为所有有页眉的页面(即EvaluatePage
、RegisterPage
和DetailsPage
)设置了LogoPixmap 。
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; }
在showHelp()
中,我们会显示适合当前页面的帮助文本。如果用户对同一页面点击两次Help ,我们会说:"对不起,我已经提供了我能提供的帮助。也许你应该试着询问一下其他人?
IntroPage 类
页面在licensewizard.h
中定义,在licensewizard.cpp
中与LicenseWizard
一起实现。
下面是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); }
一个页面继承自QWizardPage 。我们设置了title 和watermark pixmap 。通过不设置任何subTitle ,我们确保该页面不显示页眉。(在 Windows 中,向导通常会在第一页和最后一页显示水印像素图,而在其他页面显示页眉)。
int IntroPage::nextId() const { if (evaluateRadioButton->isChecked()) { return LicenseWizard::Page_Evaluate; } else { return LicenseWizard::Page_Register; } }
如果选中Evaluate the product for 30 days 选项,函数nextId()
将返回EvaluatePage
的 ID;否则,将返回RegisterPage
的 ID。
EvaluatePage 类
EvaluatePage
涉及的内容稍多一些:
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); ... }
然后创建子部件,创建与之相关的wizard fields ,并将它们放入布局中。创建字段时,会在其名称旁边加上星号 (*
)。这使它们成为mandatory fields ,即用户在按下Next 按钮(在 macOS 上为Continue )之前必须填写的字段。这些字段的值可以通过QWizardPage::field() 从任何其他页面访问。
重置页面相当于清除这两个文本字段。
int EvaluatePage::nextId() const { return LicenseWizard::Page_Conclusion; }
下一页始终是ConclusionPage
。
结论页类
RegisterPage
和DetailsPage
与EvaluatePage
非常相似。让我们直接访问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; };
这一次,除了nextId() 之外,我们还重新实现了QWizardPage::initializePage() 和QWidget::setVisible() 。我们还声明了一个私有槽:printButtonClicked()
。
int IntroPage::nextId() const { if (evaluateRadioButton->isChecked()) { return LicenseWizard::Page_Evaluate; } else { return LicenseWizard::Page_Register; } }
QWizardPage::nextId() 的默认实现会返回下一个 ID 的页面,如果当前页面的 ID 最高,则返回-1。由于Page_Conclusion
等于 5,且不存在 ID 更高的页面,因此这种行为在这里是可行的,但为了避免依赖这种微妙的行为,我们重新实现了nextId() 以返回-1。
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); }
我们使用QWizard::hasVisitedPage() 来确定用户选择的许可协议类型。如果用户填写了EvaluatePage
,则许可证文本指的是评估许可协议。如果用户填写了DetailsPage
,则许可证文本为首次许可协议。如果用户提供了升级密钥并跳过了DetailsPage
,则许可证文本为更新许可协议。
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); } }
我们希望在ConclusionPage
时在向导中显示Print 按钮。实现这一目标的方法之一是重新实现QWidget::setVisible() :
- 如果页面已显示,我们将CustomButton1 按钮的文本设置为 Print启用HaveCustomButton1 选项,并将QWizard 的customButtonClicked() 信号连接到
printButtonClicked()
插槽。 - 如果页面被隐藏,我们将禁用HaveCustomButton1 选项,并断开
printButtonClicked()
插槽。
另请参阅 QWizard 和Trivial Wizard 示例。
© 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.