Qt Creator Manual

Creating Wizards in Code

Introduction

If the functionality provided by template-based custom wizards is not sufficient for your case, you can write wizards in code.

A wizard in Qt Creator is an instance of a class implementing the Core::IWizard interface that is registered with ExtensionSystem::PluginManager.

Implementing wizards requires:

  • Deciding on a base class:
    • Core::IWizard is a very generic interface that does not make any assumption about what the wizard does and what its UI looks like.
    • Core::BaseFileWizard should be used for wizards that generate files using a UI based on Utils::Wizard.
  • Providing a set of parameters that determine how the wizard shows up in the list of wizards in the New File or Project dialog.

    When deriving from Core::IWizard, virtual functions returning the values have to be implemented.

    When deriving from Core::BaseFileWizard, a parameter class Core::BaseFileWizardParameters needs to be passed to the constructor, on which the parameters can be set. This allows for easy creation of several wizard instances with slightly different parameters.

  • Implementing the wizard UI

    Typically, this will be a class derived from Utils::Wizard. Utils::Wizard extends QWizard with the functionality to show a progress bar on the left.

  • Implementing the wizard functionality

    When deriving from Core::BaseFileWizard, a list of Core::GeneratedFile needs to be populated with the files and their contents. Note: The files are not actually written to the disk. This will be done by Core::BaseFileWizard after performing overwrite checks and prompting the user accordingly.

Relevant Classes

ClassDescription
Core::IWizardQt Creator wizard interface, implementations of which are registered with ExtensionSystem::PluginManager.
Core::BaseFileWizardInherits Core::IWizard and provides a base class for generating files with a UI based on QWizard.
Core::BaseFileWizardParametersContains parameters for Core::BaseFileWizard.
Core::GeneratedFileA file as produced by Core::BaseFileWizard, containing name, contents and some attributes.
Utils::FileWizardPageIntroductory wizard page asking for file name and path.
Utils::FileWizardDialogA wizard dialog presenting a Utils::FileWizardPage, which can be extended by custom pages.
Utils::ProjectIntroPageIntroductory wizard page asking for project name and path.
ProjectExplorer::BaseProjectWizardDialogBase class for project wizard dialogs, presenting a Utils::ProjectIntroPage.

Parameters

The parameters listed below determine how the wizard shows up in the list of wizards in the New File or Project dialog.

Wizards in Qt Creator are grouped by categories.

TypeParameter NameDescription
Core::IWizard::WizardKindkindEnumeration value that indicates the type of the wizard (project, class, file).
QIconiconIcon to show.
QStringdescriptionDescriptive text.
QStringdisplayNameName to be shown in the list.
QStringidUnique identifier for the wizard. It also determines the order within a category.
QStringcategoryIdentifier of the category under which the wizard is to be listed. It also determines the order of the categories.
QStringdisplayCategoryDescription of the category.

Example

Introduction

In this example, we create a wizard for writing HTML files consisting of a title and a paragraph, making use of QXmlStreamWriter.

For the UI, we use Utils::FileWizardDialog and extend it by a page letting the user enter title and contents.

In our BaseFileWizard implementation, we create the file contents using QXmlStreamWriter.

The WebContentPageWizardPage Class

Let us start with the wizard page. We use a QLineEdit for the title and a QPlainTextEdit for the content, arranged in a QGridLayout with descriptive labels. Note: The QGridLayout was chosen to be able to accommodate the large vertical span of the QPlainTextEdit. For standard controls, a QFormLayout should be considered, which will lay out the labels according to the platform guide lines.

On top of that, we implement validation logic to ensure content is entered. We implement QWizardPage::isComplete() to return true when both input widgets have contents, enabling the Next button. For this to happen as the user enters text, we need to connect to the changed() signal of the controls and emit QWizardPage::completeChanged() once the complete status changes.

 class WebContentPageWizardPage : public QWizardPage
 {
     Q_OBJECT
 public:
     explicit WebContentPageWizardPage(QWidget *parent = 0);

     QString title() const;
     QString contents() const;

     virtual bool isComplete() const { return m_complete; }

 private slots:
     void slotChanged();

 private:
     QLineEdit *m_titleLineEdit;
     QPlainTextEdit *m_textEdit;
     bool m_complete;
 };

 WebContentPageWizardPage::WebContentPageWizardPage(QWidget *parent) :
     QWizardPage(parent),
     m_titleLineEdit(new QLineEdit),
     m_textEdit(new QPlainTextEdit),
     m_complete(false)
 {
     QGridLayout *layout = new QGridLayout(this);
     QLabel *titleLabel = new QLabel(tr("&Title"));
     layout->addWidget(titleLabel, 0, 0);
     layout->addWidget(m_titleLineEdit, 0, 1);
     QLabel *contentLabel = new QLabel(tr("&Content"));
     layout->addWidget(contentLabel, 1, 0, 1, 1, Qt::AlignTop);
     layout->addWidget(m_textEdit, 1, 1);
     titleLabel->setBuddy(m_titleLineEdit);
     contentLabel->setBuddy(m_textEdit);
     setTitle(tr("Web Page Contents"));

     connect(m_titleLineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotChanged()));
     connect(m_textEdit, SIGNAL(textChanged()), this, SLOT(slotChanged()));
 }

 QString WebContentPageWizardPage::title() const
 {
     return m_titleLineEdit->text();
 }

 QString WebContentPageWizardPage::contents() const
 {
     return m_textEdit->toPlainText();
 }

 void WebContentPageWizardPage::slotChanged()
 {
     const bool completeNow = !m_titleLineEdit->text().isEmpty()
             && m_textEdit->blockCount() > 0;
     if (completeNow != m_complete) {
         m_complete = completeNow;
         emit completeChanged();
     }
 }

The WebContentWizardDialog Class

The wizard dialog extends Utils::FileWizardDialog, which presents an introductory page asking for file name and path. We add our WebContentPageWizardPage after that.

 class WebContentWizardDialog : public Utils::FileWizardDialog
 {
     Q_OBJECT
 public:
     explicit WebContentWizardDialog(QWidget *parent = 0);

     QString title() const    { return m_contentPage->title(); }
     QString contents() const { return m_contentPage->contents(); }

 private:
     WebContentPageWizardPage *m_contentPage;
 };

 WebContentWizardDialog::WebContentWizardDialog(QWidget *parent) :
     Utils::FileWizardDialog(parent), m_contentPage(new WebContentPageWizardPage)
 {
     addPage(m_contentPage);
 }

The WebContentWizard Class

In our implementation of Core::BaseFileWizard, we overwrite createWizardDialog() to return an instance of our WebContentWizardDialog, initially set to the path passed in. We also add the extension pages we receive. Extension pages are for example the wizard pages asking for a project to add the files to and whether to add the files to a version control system.

In generateFiles(), we obtain the parameters from our wizard and populate the list of Core::GeneratedFile with our file. To generate the contents, we use QXmlStreamWriter.

 class WebPageWizard : public Core::BaseFileWizard
 {
 public:
     explicit WebPageWizard(const Core::BaseFileWizardParameters &parameters,
                            QObject *parent = 0);
 protected:
     virtual QWizard *createWizardDialog(QWidget *parent,
                                         const QString &defaultPath,
                                         const WizardPageList &extensionPages) const;
     virtual Core::GeneratedFiles generateFiles(const QWizard *w,
                                          QString *errorMessage) const;
 };

 WebPageWizard::WebPageWizard(const Core::BaseFileWizardParameters &parameters,
                              QObject *parent) :
     Core::BaseFileWizard(parameters, parent)
 {
 }

 QWizard *WebPageWizard::createWizardDialog(QWidget *parent,
                                     const QString &defaultPath,
                                     const WizardPageList &extensionPages) const
 {
     WebContentWizardDialog *dialog = new WebContentWizardDialog(parent);
     dialog->setPath(defaultPath);
     foreach (QWizardPage *p, extensionPages)
         dialog->addPage(p);
     return dialog;
 }

 Core::GeneratedFiles
     WebPageWizard::generateFiles(const QWizard *w,
                                      QString *) const
 {
     Core::GeneratedFiles files;
     const WebContentWizardDialog *dialog = qobject_cast<const WebContentWizardDialog*>(w);
     QTC_ASSERT(dialog, return files; )

     const QString fileName = Core::BaseFileWizard::buildFileName(dialog->path(), dialog->fileName(), QLatin1String("html"));

     Core::GeneratedFile file(fileName);

     QString contents;
     QXmlStreamWriter writer(&contents);
     writer.setAutoFormatting(true);
     writer.writeStartDocument();
     writer.writeStartElement(QLatin1String("html"));
     writer.writeStartElement(QLatin1String("head"));
     writer.writeTextElement(QLatin1String("title"), dialog->title());
     writer.writeEndElement(); // HEAD
     writer.writeStartElement(QLatin1String("body"));
     writer.writeTextElement(QLatin1String("h1"), dialog->title());
     writer.writeTextElement(QLatin1String("p"), dialog->contents());
     writer.writeEndElement(); // BODY
     writer.writeEndElement(); // HTML
     file.setAttributes(Core::GeneratedFile::OpenEditorAttribute);
     file.setContents(contents);
     files.push_back(file);
     return files;
 }

Plugin Registration

In order for the wizard to be found by the New dialog, we need to register it with ExtensionSystem::PluginManager, which also takes care of deleting it:

 bool MyPlugin::initialize(const QStringList &arguments, QString *errorMessage)
 {
     Core::BaseFileWizardParameters p(IWizard::FileWizard);
     p.setCategory(QLatin1String("P.web"));
     p.setDisplayCategory(tr("Web"));
     p.setDescription(tr("Creates a Web Page."));
     p.setDisplayName(tr("Web Page"));
     p.setId(QLatin1String("A.WebPage"));
     addAutoReleasedObject(new WebPageWizard(p));
 }

Complete Example Code

Here is the complete code of webpagewizard.h:

 #ifndef WEBPAGEFILEWIZARD_H
 #define WEBPAGEFILEWIZARD_H

 #include "basefilewizard.h"
 #include "utils/filewizarddialog.h"

 #include <QtGui/QWizardPage>

 class QLineEdit;
 class QPlainTextEdit;

 namespace MyPlugin {
 namespace Internal {

 class WebContentPageWizardPage : public QWizardPage
 {
     Q_OBJECT
 public:
     explicit WebContentPageWizardPage(QWidget *parent = 0);

     QString title() const;
     QString contents() const;

     virtual bool isComplete() const { return m_complete; }

 private slots:
     void slotChanged();

 private:
     QLineEdit *m_titleLineEdit;
     QPlainTextEdit *m_textEdit;
     bool m_complete;
 };


 class WebContentWizardDialog : public Utils::FileWizardDialog
 {
     Q_OBJECT
 public:
     explicit WebContentWizardDialog(QWidget *parent = 0);

     QString title() const    { return m_contentPage->title(); }
     QString contents() const { return m_contentPage->contents(); }

 private:
     WebContentPageWizardPage *m_contentPage;
 };


 class WebPageWizard : public Core::BaseFileWizard
 {
 public:
     explicit WebPageWizard(const Core::BaseFileWizardParameters &parameters,
                            QObject *parent = 0);
 protected:
     virtual QWizard *createWizardDialog(QWidget *parent,
                                         const QString &defaultPath,
                                         const WizardPageList &extensionPages) const;
     virtual Core::GeneratedFiles generateFiles(const QWizard *w,
                                          QString *errorMessage) const;
 };

 } // namespace Internal
 } // namespace MyPlugin

 #endif // WEBPAGEFILEWIZARD_H

The complete code of webpagewizard.cpp looks as follows:

 #include "webpagewizard.h"

 #include <utils/filewizarddialog.h>
 #include <utils/qtcassert.h>

 #include <QtCore/QXmlStreamWriter>

 #include <QtGui/QGridLayout>
 #include <QtGui/QLabel>
 #include <QtGui/QLineEdit>
 #include <QtGui/QPlainTextEdit>

 namespace MyPlugin {
 namespace Internal {

 WebContentPageWizardPage::WebContentPageWizardPage(QWidget *parent) :
     QWizardPage(parent),
     m_titleLineEdit(new QLineEdit),
     m_textEdit(new QPlainTextEdit),
     m_complete(false)
 {
     QGridLayout *layout = new QGridLayout(this);
     QLabel *titleLabel = new QLabel(tr("&Title"));
     layout->addWidget(titleLabel, 0, 0);
     layout->addWidget(m_titleLineEdit, 0, 1);
     QLabel *contentLabel = new QLabel(tr("&Content"));
     layout->addWidget(contentLabel, 1, 0, 1, 1, Qt::AlignTop);
     layout->addWidget(m_textEdit, 1, 1);
     titleLabel->setBuddy(m_titleLineEdit);
     contentLabel->setBuddy(m_textEdit);
     setTitle(tr("Web Page Contents"));

     connect(m_titleLineEdit, SIGNAL(textChanged(QString)), this, SLOT(slotChanged()));
     connect(m_textEdit, SIGNAL(textChanged()), this, SLOT(slotChanged()));
 }

 QString WebContentPageWizardPage::title() const
 {
     return m_titleLineEdit->text();
 }

 QString WebContentPageWizardPage::contents() const
 {
     return m_textEdit->toPlainText();
 }

 void WebContentPageWizardPage::slotChanged()
 {
     const bool completeNow = !m_titleLineEdit->text().isEmpty()
             && m_textEdit->blockCount() > 0;
     if (completeNow != m_complete) {
         m_complete = completeNow;
         emit completeChanged();
     }
 }


 WebContentWizardDialog::WebContentWizardDialog(QWidget *parent) :
     Utils::FileWizardDialog(parent), m_contentPage(new WebContentPageWizardPage)
 {
     addPage(m_contentPage);
 }


 WebPageWizard::WebPageWizard(const Core::BaseFileWizardParameters &parameters,
                              QObject *parent) :
     Core::BaseFileWizard(parameters, parent)
 {
 }

 QWizard *WebPageWizard::createWizardDialog(QWidget *parent,
                                     const QString &defaultPath,
                                     const WizardPageList &extensionPages) const
 {
     WebContentWizardDialog *dialog = new WebContentWizardDialog(parent);
     dialog->setPath(defaultPath);
     foreach (QWizardPage *p, extensionPages)
         dialog->addPage(p);
     return dialog;
 }

 Core::GeneratedFiles
     WebPageWizard::generateFiles(const QWizard *w,
                                      QString *) const
 {
     Core::GeneratedFiles files;
     const WebContentWizardDialog *dialog = qobject_cast<const WebContentWizardDialog*>(w);
     QTC_ASSERT(dialog, return files; )

     const QString fileName = Core::BaseFileWizard::buildFileName(dialog->path(), dialog->fileName(), QLatin1String("html"));

     Core::GeneratedFile file(fileName);

     QString contents;
     QXmlStreamWriter writer(&contents);
     writer.setAutoFormatting(true);
     writer.writeStartDocument();
     writer.writeStartElement(QLatin1String("html"));
     writer.writeStartElement(QLatin1String("head"));
     writer.writeTextElement(QLatin1String("title"), dialog->title());
     writer.writeEndElement(); // HEAD
     writer.writeStartElement(QLatin1String("body"));
     writer.writeTextElement(QLatin1String("h1"), dialog->title());
     writer.writeTextElement(QLatin1String("p"), dialog->contents());
     writer.writeEndElement(); // BODY
     writer.writeEndElement(); // HTML
     file.setAttributes(Core::GeneratedFile::OpenEditorAttribute);
     file.setContents(contents);
     files.push_back(file);
     return files;
 }


 } // namespace Internal
 } // namespace MyPlugin

The registration of the wizard in the initialize() method of a plugin looks like:

 bool MyPlugin::initialize(const QStringList &arguments, QString *errorMessage)
 {
     Core::BaseFileWizardParameters p(IWizard::FileWizard);
     p.setCategory(QLatin1String("P.web"));
     p.setDisplayCategory(tr("Web"));
     p.setDescription(tr("Creates a Web Page."));
     p.setDisplayName(tr("Web Page"));
     p.setId(QLatin1String("A.WebPage"));
     addAutoReleasedObject(new WebPageWizard(p));
 }