The Class Wizard example shows how to implement linear wizards using QWizard .
Most wizards have a linear structure, with page 1 followed by page 2 and so on until the last page. Some wizards are more complex in that they allow different traversal paths based on the information provided by the user. The License Wizard example shows how to create such wizards.
The Class Wizard example consists of the following classes:
ClassWizard
继承
QWizard
and provides a three-step wizard that generates the skeleton of a C++ class based on the user's input.
IntroPage
,
ClassInfoPage
,
CodeStylePage
,
OutputFilesPage
,和
ConclusionPage
are
QWizardPage
subclasses that implement the wizard pages.
We will see how to subclass
QWizard
to implement our own wizard. The concrete wizard class is called
ClassWizard
and provides five pages:
Q_OBJECT
macro and what constructors it should provide.
MYDIALOG_H
).
Although the program is just an example, if you press Finish ( Done on macOS), actual C++ source files will actually be generated.
这里是
ClassWizard
定义:
class ClassWizard : public QWizard { Q_OBJECT public: ClassWizard(QWidget *parent = nullptr); void accept() override; };
The class reimplements QDialog 's accept() slot. This slot is called when the user clicks Finish .
Here's the constructor:
ClassWizard::ClassWizard(QWidget *parent) : QWizard(parent) { addPage(new IntroPage); addPage(new ClassInfoPage); addPage(new CodeStylePage); addPage(new OutputFilesPage); addPage(new ConclusionPage); setPixmap(QWizard::BannerPixmap, QPixmap(":/images/banner.png")); setPixmap(QWizard::BackgroundPixmap, QPixmap(":/images/background.png")); setWindowTitle(tr("Class Wizard")); }
We instantiate the five pages and insert them into the wizard using QWizard::addPage (). The order in which they are inserted is also the order in which they will be shown later on.
调用 QWizard::setPixmap () to set the banner and the background pixmaps for all pages. The banner is used as a background for the page header when the wizard's style is ModernStyle ; the background is used as the dialog's background in MacStyle 。(见 向导页面的元素 for more information.)
void ClassWizard::accept() { QByteArray className = field("className").toByteArray(); QByteArray baseClass = field("baseClass").toByteArray(); QByteArray macroName = field("macroName").toByteArray(); QByteArray baseInclude = field("baseInclude").toByteArray(); QString outputDir = field("outputDir").toString(); QString header = field("header").toString(); QString implementation = field("implementation").toString(); ... QDialog::accept(); }
If the user clicks Finish , we extract the information from the various pages using QWizard::field () and generate the files. The code is long and tedious (and has barely anything to do with noble art of designing wizards), so most of it is skipped here. See the actual example in the Qt distribution for the details if you're curious.
The pages are defined in
classwizard.h
and implemented in
classwizard.cpp
, together with
ClassWizard
. We will start with the easiest page:
class IntroPage : public QWizardPage { Q_OBJECT public: IntroPage(QWidget *parent = nullptr); private: QLabel *label; }; IntroPage::IntroPage(QWidget *parent) : QWizardPage(parent) { setTitle(tr("Introduction")); setPixmap(QWizard::WatermarkPixmap, QPixmap(":/images/watermark1.png")); label = new QLabel(tr("This wizard will generate a skeleton C++ class " "definition, including a few functions. You simply " "need to specify the class name and set a few " "options to produce a header file and an " "implementation file for your new C++ class.")); label->setWordWrap(true); QVBoxLayout *layout = new QVBoxLayout; layout->addWidget(label); setLayout(layout); }
A page inherits from QWizardPage . We set a title 和 watermark pixmap . By not setting any subTitle , we ensure that no header is displayed for this page. (On Windows, it is customary for wizards to display a watermark pixmap on the first and last pages, and to have a header on the other pages.)
Then we create a QLabel and add it to a layout.
The second page is defined and implemented as follows:
class ClassInfoPage : public QWizardPage { Q_OBJECT public: ClassInfoPage(QWidget *parent = nullptr); private: QLabel *classNameLabel; QLabel *baseClassLabel; QLineEdit *classNameLineEdit; QLineEdit *baseClassLineEdit; QCheckBox *qobjectMacroCheckBox; QGroupBox *groupBox; QRadioButton *qobjectCtorRadioButton; QRadioButton *qwidgetCtorRadioButton; QRadioButton *defaultCtorRadioButton; QCheckBox *copyCtorCheckBox; }; ClassInfoPage::ClassInfoPage(QWidget *parent) : QWizardPage(parent) { setTitle(tr("Class Information")); setSubTitle(tr("Specify basic information about the class for which you " "want to generate skeleton source code files.")); setPixmap(QWizard::LogoPixmap, QPixmap(":/images/logo1.png")); classNameLabel = new QLabel(tr("&Class name:")); classNameLineEdit = new QLineEdit; classNameLabel->setBuddy(classNameLineEdit); baseClassLabel = new QLabel(tr("B&ase class:")); baseClassLineEdit = new QLineEdit; baseClassLabel->setBuddy(baseClassLineEdit); qobjectMacroCheckBox = new QCheckBox(tr("Generate Q_OBJECT ¯o")); groupBox = new QGroupBox(tr("C&onstructor")); ... registerField("className*", classNameLineEdit); registerField("baseClass", baseClassLineEdit); registerField("qobjectMacro", qobjectMacroCheckBox); registerField("qobjectCtor", qobjectCtorRadioButton); registerField("qwidgetCtor", qwidgetCtorRadioButton); registerField("defaultCtor", defaultCtorRadioButton); registerField("copyCtor", copyCtorCheckBox); QVBoxLayout *groupBoxLayout = new QVBoxLayout; ... }
First, we set the page's title , subTitle ,和 logo pixmap . The logo pixmap is displayed in the page's header in ClassicStyle and ModernStyle .
Then we create the child widgets, create
wizard fields
associated with them, and put them into layouts. The
className
field is created with an asterisk (
*
) next to its name. This makes it a
mandatory field
, that is, a field that must be filled before the user can press the
下一
button (
Continue
on macOS). The fields' values can be accessed from any other page using
QWizardPage::field
(), or from the wizard code using
QWizard::field
().
The third page is defined and implemented as follows:
class CodeStylePage : public QWizardPage { Q_OBJECT public: CodeStylePage(QWidget *parent = nullptr); protected: void initializePage() override; private: QCheckBox *commentCheckBox; QCheckBox *protectCheckBox; QCheckBox *includeBaseCheckBox; QLabel *macroNameLabel; QLabel *baseIncludeLabel; QLineEdit *macroNameLineEdit; QLineEdit *baseIncludeLineEdit; }; CodeStylePage::CodeStylePage(QWidget *parent) : QWizardPage(parent) { setTitle(tr("Code Style Options")); setSubTitle(tr("Choose the formatting of the generated code.")); setPixmap(QWizard::LogoPixmap, QPixmap(":/images/logo2.png")); commentCheckBox = new QCheckBox(tr("&Start generated files with a " ... setLayout(layout); } void CodeStylePage::initializePage() { QString className = field("className").toString(); macroNameLineEdit->setText(className.toUpper() + "_H"); QString baseClass = field("baseClass").toString(); includeBaseCheckBox->setChecked(!baseClass.isEmpty()); includeBaseCheckBox->setEnabled(!baseClass.isEmpty()); baseIncludeLabel->setEnabled(!baseClass.isEmpty()); baseIncludeLineEdit->setEnabled(!baseClass.isEmpty()); QRegularExpression rx("Q[A-Z].*"); if (baseClass.isEmpty()) { baseIncludeLineEdit->clear(); } else if (rx.match(baseClass).hasMatch()) { baseIncludeLineEdit->setText('<' + baseClass + '>'); } else { baseIncludeLineEdit->setText('"' + baseClass.toLower() + ".h\""); } }
The code in the constructor is very similar to what we did for
ClassInfoPage
, so we skipped most of it.
The
initializePage()
function is what makes this class interesting. It is reimplemented from
QWizardPage
and is used to initialize some of the page's fields with values from the previous page (namely,
className
and
baseClass
). For example, if the class name on page 2 is
SuperDuperWidget
, the default macro name on page 3 is
SUPERDUPERWIDGET_H
.
The
OutputFilesPage
and
ConclusionPage
classes are very similar to
CodeStylePage
, so we won't review them here.