Sur cette page

Exemple d'assistant de licence

L'exemple de l'assistant de licence montre comment mettre en œuvre des assistants complexes dans Qt.

Première page de l'assistant de licence

La plupart des assistants ont une structure linéaire, avec la page 1 suivie de la page 2 et ainsi de suite jusqu'à la dernière page. L'exemple de l'assistant trivial montre comment créer de tels assistants.

Certains assistants sont plus complexes en ce sens qu'ils permettent différents parcours en fonction des informations fournies par l'utilisateur. L'exemple de l'assistant de licence en est l'illustration. Il fournit plusieurs pages d'assistant ; en fonction des options sélectionnées, l'utilisateur peut accéder à différentes pages.

Organigramme d'une page d'assistant complexe

L'exemple se compose des classes suivantes :

  • LicenseWizard hérite de QWizard et met en œuvre un assistant non linéaire de cinq pages qui guide l'utilisateur tout au long du processus de sélection d'un contrat de licence.
  • IntroPageLes classes EvaluatePage, RegisterPage, DetailsPage et ConclusionPage sont des sous-classes de QWizardPage qui implémentent les pages de l'assistant.

La classe LicenseWizard

La classe LicenseWizard dérive de QWizard et fournit un assistant de cinq pages qui guide l'utilisateur tout au long du processus d'enregistrement de sa copie d'un produit logiciel fictif. Voici la définition de la classe :

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();
};

L'API publique de la classe se limite à un constructeur et à une énumération. L'énumération définit les identifiants associés aux différentes pages :

Nom de la classeValeur de l'énumérationID de la page
IntroPagePage_Intro0
EvaluatePagePage_Evaluate1
RegisterPagePage_Register2
DetailsPagePage_Details3
ConclusionPagePage_Conclusion4

Dans cet exemple, les identifiants sont arbitraires. Les seules contraintes sont qu'ils doivent être uniques et différents de -1. Les ID nous permettent de faire référence aux pages.

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);

Dans le constructeur, nous créons les cinq pages, les insérons dans l'assistant à l'aide de QWizard::setPage() et définissons Page_Intro comme étant la première page.

#ifndef Q_OS_MAC
    setWizardStyle(ModernStyle);
#endif

Nous définissons le style sur ModernStyle sur toutes les plateformes sauf macOS,

    setOption(HaveHelpButton, true);
    setPixmap(QWizard::LogoPixmap, QPixmap(":/images/logo.png"));

    connect(this, &QWizard::helpRequested, this, &LicenseWizard::showHelp);

    setWindowTitle(tr("License Wizard"));
}

Nous configurons le site QWizard pour qu'il affiche un bouton Help, qui est connecté à notre emplacement showHelp(). Nous configurons également le style LogoPixmap pour toutes les pages qui ont un en-tête (c'est-à-dire EvaluatePage, RegisterPage et 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;
}

Dans showHelp(), nous affichons les textes d'aide correspondant à la page en cours. Si l'utilisateur clique deux fois sur Help pour la même page, nous lui disons : "Désolé, j'ai déjà donné toute l'aide possible. Peut-être devriez-vous essayer de demander à un humain ?"

La classe IntroPage

Les pages sont définies dans licensewizard.h et implémentées dans licensewizard.cpp, ainsi que dans LicenseWizard.

Voici la définition et l'implémentation de 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>&trade; 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);
}

Une page hérite de QWizardPage. Nous définissons un title et un watermark pixmap. En ne définissant pas de subTitle, nous nous assurons qu'aucun en-tête n'est affiché pour cette page. (Sous Windows, il est d'usage que les assistants affichent un pixmap en filigrane sur la première et la dernière page, et qu'ils aient un en-tête sur les autres pages).

int IntroPage::nextId() const
{
    if (evaluateRadioButton->isChecked()) {
        return LicenseWizard::Page_Evaluate;
    } else {
        return LicenseWizard::Page_Register;
    }
}

La fonction nextId() renvoie l'ID de EvaluatePage si l'option Evaluate the product for 30 days est cochée ; sinon, elle renvoie l'ID de RegisterPage.

La classe EvaluatePage

La fonction EvaluatePage est un peu plus complexe :

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>&trade;"));
    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);
    ...
}

Tout d'abord, nous définissons les pages title et subTitle.

Ensuite, nous créons les widgets enfants, nous créons wizard fields qui leur est associé et nous les plaçons dans des présentations. Les champs sont créés avec un astérisque (*) à côté de leur nom. Cela en fait des mandatory fields, c'est-à-dire des champs qui doivent être remplis avant que l'utilisateur ne puisse appuyer sur le bouton Next (Continue sur macOS). Les valeurs des champs sont accessibles depuis n'importe quelle autre page en utilisant QWizardPage::field().

La réinitialisation de la page revient à effacer les deux champs de texte.

int EvaluatePage::nextId() const
{
    return LicenseWizard::Page_Conclusion;
}

La page suivante est toujours la page ConclusionPage.

La classe ConclusionPage

Les pages RegisterPage et DetailsPage sont très similaires à la page EvaluatePage. Passons directement à la page 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;
};

Cette fois, nous réimplémentons QWizardPage::initializePage() et QWidget::setVisible(), en plus de nextId(). Nous déclarons également un slot privé : printButtonClicked().

int IntroPage::nextId() const
{
    if (evaluateRadioButton->isChecked()) {
        return LicenseWizard::Page_Evaluate;
    } else {
        return LicenseWizard::Page_Register;
    }
}

L'implémentation par défaut de QWizardPage::nextId() renvoie la page avec l'ID suivant, ou -1 si la page actuelle a l'ID le plus élevé. Ce comportement fonctionnerait ici, parce que Page_Conclusion est égal à 5 et qu'il n'y a pas de page avec un ID supérieur, mais pour éviter de dépendre d'un comportement aussi subtil, nous réimplémentons nextId() pour qu'il renvoie -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);
}

Nous utilisons QWizard::hasVisitedPage() pour déterminer le type d'accord de licence choisi par l'utilisateur. Si l'utilisateur a rempli le formulaire EvaluatePage, le texte de la licence fait référence à un contrat de licence d'évaluation. Si l'utilisateur a rempli le formulaire DetailsPage, le texte de la licence est un contrat de licence de première utilisation. Si l'utilisateur a fourni une clé de mise à niveau et a sauté la case DetailsPage, le texte de la licence est un contrat de licence de mise à jour.

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);
    }
}

Nous voulons afficher un bouton Print dans l'assistant lorsque la page ConclusionPage est ouverte. Une façon d'y parvenir est de réimplémenter QWidget::setVisible() :

Exemple de projet @ code.qt.io

Voir aussi QWizard et Trivial Wizard Example.

© 2026 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.