许可向导示例
许可向导示例展示了如何在 Qt 中实现复杂的向导。
大多数向导都有线性结构,页面1后面是页面2,依此类推,直到最后一页。《简单向导》示例展示了如何创建这样的向导。
有些向导更复杂,因为它们允许用户根据提供的信息选择不同的遍历路径。许可向导示例说明了这一点。它提供了五个向导页面;根据所选选项,用户可以到达不同的页面。
该示例包含以下类
LicenseWizard
继承自 QWizard 并实现了一个非线性五页向导,它引导用户通过选择许可协议的过程。IntroPage
、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(); };
类的公共 API 限于构造函数和一个枚举。枚举定义了与各个页面相关联的 IDs
类名 | 枚举值 | 页面 ID |
---|---|---|
IntroPage | Page_Intro | 0 |
EvaluatePage | Page_Evaluate | 1 |
RegisterPage | Page_Register | 2 |
DetailsPage | Page_Details | 3 |
ConclusionPage | Page_Conclusion | 4 |
对于此示例,ID 是任意的。唯一的约束是它们必须是唯一的,并且与 -1 不同。IDs 允许我们引用页面。
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 以显示一个 帮助 按钮,该按钮连接到我们的 showHelp()
槽。我们还为具有标题的页面设置了 LogoPixmap(即 EvaluatePage
、RegisterPage
和 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; }
在 showHelp()
中,我们显示适用于当前页面的帮助文本。如果用户在同一个页面上连续两次单击 帮助,我们说:“对不起,我已经给出了我能给出的所有帮助。也许你应该尝试向人类提问?”
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。我们设置了一个 标题 和一个 水印Pixmap。因为没有设置任何 副标题,所以我们确保在此页面上不显示页眉。(在Windows中,通常在第一页和最后一页显示水印Pixmap,在其他页面上显示页眉。)
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., [email protected]).")); nameLabel = new QLabel(tr("N&ame:")); nameLineEdit = new QLineEdit; ... registerField("evaluate.name*", nameLineEdit); registerField("evaluate.email*", emailLineEdit); ... }
然后我们创建子小部件,为它们创建相关联的 向导字段,并将它们放入布局中。字段名称旁边有一个星号(*
)。这使得它们成为 必填字段,即必须在用户按下 下一步 按钮(在macOS上为 继续)之前填写。可以通过 QWizardPage::field() 从任何其他页面访问字段的值。
重置页面相当于清除两个文本字段。
int EvaluatePage::nextId() const { return LicenseWizard::Page_Conclusion; }
下一页始终是 ConclusionPage
。
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; };
这次,我们除了重写 QWizardPage::initializePage() 和 QWidget::setVisible(),还重写了 nextId()。我们还声明了一个私有槽: 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
出现时,我们想在向导中显示一个 打印 按钮。实现这一目标的一种方法是通过重写 QWidget::setVisible()
- 如果显示页面,我们将 CustomButton1 按钮的文本设置为 打印,启用 HaveCustomButton1 选项,并将 QWizard 的 customButtonClicked() 信号连接到我们的
printButtonClicked()
插槽。 - 如果页面隐藏,我们将禁用 HaveCustomButton1 选项并断开
printButtonClicked()
插槽。
© 2024 Qt 公司有限公司。本文件中包含的文档贡献的版权归其各自的所有者所有。本文件中提供的文档受 GNU自由文档许可1.3版 的条款约束,该许可由自由软件基金会发布。Qt及其相关标志是芬兰和/或其他国家的Qt公司在全球的商标。所有其他商标都是其所有者的财产。