Class Wizard Example¶
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
inheritsQWizard
and provides a three-step wizard that generates the skeleton of a C++ class based on the user’s input.
IntroPage
,ClassInfoPage
,CodeStylePage
,OutputFilesPage
, andConclusionPage
areQWizardPage
subclasses that implement the wizard pages.
ClassWizard Class Definition¶
We will see how to subclass
QWizard
to implement our own wizard. The concrete wizard class is calledClassWizard
and provides five pages:
The first page is an introduction page, telling the user what the wizard is going to do.
The second page asks for a class name and a base class, and allows the user to specify whether the class should have a
Q_OBJECT
macro and what constructors it should provide.The third page allows the user to set some options related to the code style, such as the macro used to protect the header file from multiple inclusion (e.g.,
MYDIALOG_H
).The fourth page allows the user to specify the names of the output files.
The fifth page is a conclusion page.
Although the program is just an example, if you press Finish (Done on macOS), actual C++ source files will actually be generated.
The ClassWizard Class¶
Here’s the
ClassWizard
definition:class ClassWizard : public QWizard { Q_OBJECT public: ClassWizard(QWidget *parent = nullptr); void accept() override; };The class reimplements
QDialog
‘saccept()
slot. This slot is called when the user clicks Finish.Here’s the constructor:
def __init__(self, parent): QWizard.__init__(self, parent): self.addPage(IntroPage()) self.addPage(ClassInfoPage()) self.addPage(CodeStylePage()) self.addPage(OutputFilesPage()) self.addPage(ConclusionPage()) self.setPixmap(QWizard.BannerPixmap, QPixmap(":/images/banner.png")) self.setPixmap(QWizard.BackgroundPixmap, QPixmap(":/images/background.png")) self.setWindowTitle(self.tr("Class Wizard"))We instantiate the five pages and insert them into the wizard using
addPage()
. The order in which they are inserted is also the order in which they will be shown later on.We call
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 isModernStyle
; the background is used as the dialog’s background inMacStyle
. (SeeElements of a Wizard Page
for more information.)def accept(self): className = self.field("className") baseClass = self.field("baseClass") macroName = self.field("macroName") baseInclude = self.field("baseInclude") outputDir = self.field("outputDir") header = self.field("header") implementation = self.field("implementation") ... QDialog.accept(self) }If the user clicks Finish, we extract the information from the various pages using
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 IntroPage Class¶
The pages are defined in
classwizard.h
and implemented inclasswizard.cpp
, together withClassWizard
. We will start with the easiest page:class IntroPage : public QWizardPage { Q_OBJECT public: IntroPage(QWidget *parent = nullptr); private: QLabel *label; }; class IntroPage (QWizardPage): def __init__(self, parent): QWizardPage.__init__(self, parent) self.setTitle(tr("Introduction")) self.setPixmap(QWizard.WatermarkPixmap, QPixmap(":/images/watermark1.png")) label = QLabel(self.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) layout = QVBoxLayout() layout.addWidget(label) self.setLayout(layout) }A page inherits from
QWizardPage
. We set atitle
and awatermark pixmap
. By not setting anysubTitle
, 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 ClassInfoPage Class¶
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; }; class ClassInfoPage(QWizardPage): def __init__(self, parent): QWizardPage.__init__(self, parent) self.setTitle(self.tr("Class Information")) self.setSubTitle(self.tr("Specify basic information about the class for which you " \ "want to generate skeleton source code files.")) self.setPixmap(QWizard.LogoPixmap, QPixmap(":/images/logo1.png")) classNameLabel = QLabel(self.tr("&Class name:")) classNameLineEdit = QLineEdit() classNameLabel.setBuddy(classNameLineEdit) baseClassLabel = QLabel(self.tr("B&ase class:")) baseClassLineEdit = QLineEdit() baseClassLabel.setBuddy(baseClassLineEdit) qobjectMacroCheckBox = QCheckBox(self.tr("Generate Q_OBJECT ¯o")) groupBox = QGroupBox(self.tr("C&onstructor")) ... registerField("className*", classNameLineEdit) registerField("baseClass", baseClassLineEdit) registerField("qobjectMacro", qobjectMacroCheckBox) registerField("qobjectCtor", qobjectCtorRadioButton) registerField("qwidgetCtor", qwidgetCtorRadioButton) registerField("defaultCtor", defaultCtorRadioButton) registerField("copyCtor", copyCtorCheckBox) groupBoxLayout = QVBoxLayout() ...First, we set the page’s
title
,subTitle
, andlogo pixmap
. The logo pixmap is displayed in the page’s header inClassicStyle
andModernStyle
.Then we create the child widgets, create
wizard fields
associated with them, and put them into layouts. TheclassName
field is created with an asterisk (*
) next to its name. This makes it amandatory field
, that is, a field that must be filled before the user can press the Next button (Continue on macOS). The fields’ values can be accessed from any other page usingfield()
, or from the wizard code usingfield()
.
The CodeStylePage Class¶
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; }; class CodeStylePage(QWizardPage): def __init__(self, parent): QWizardPage.__init__(self, parent) self.setTitle(tr("Code Style Options")) self.setSubTitle(tr("Choose the formatting of the generated code.")) self.setPixmap(QWizard.LogoPixmap, QPixmap(":/images/logo2.png")) commentCheckBox = QCheckBox(self.tr("&Start generated files with a comment")) ... self.setLayout(layout) } def initializePage(self): className = self.field("className") self.macroNameLineEdit.setText(className.upper() + "_H") baseClass = self.field("baseClass") self.includeBaseCheckBox.setChecked(len(baseClass)) self.includeBaseCheckBox.setEnabled(len(baseClass)) self.baseIncludeLabel.setEnabled(len(baseClass)) self.baseIncludeLineEdit.setEnabled(len(baseClass)) if not baseClass: self.baseIncludeLineEdit.clear() elsif QRegExp("Q[A-Z].*").exactMatch(baseClass): baseIncludeLineEdit.setText("<" + baseClass + ">") else: baseIncludeLineEdit.setText("\"" + baseClass.lower() + ".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 fromQWizardPage
and is used to initialize some of the page’s fields with values from the previous page (namely,className
andbaseClass
). For example, if the class name on page 2 isSuperDuperWidget
, the default macro name on page 3 isSUPERDUPERWIDGET_H
.The
OutputFilesPage
andConclusionPage
classes are very similar toCodeStylePage
, so we won’t review them here.See also
© 2022 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.