En esta página

Ejemplo de Asistente de Licencia

El ejemplo del Asistente de Licencia muestra cómo implementar asistentes complejos en Qt.

Primera página del asistente de licencias

La mayoría de los asistentes tienen una estructura lineal, con la página 1 seguida de la página 2 y así sucesivamente hasta la última página. El ejemplo del Asistente Trivial muestra cómo crear este tipo de asistentes.

Algunos asistentes son más complejos ya que permiten diferentes rutas de navegación basadas en la información proporcionada por el usuario. El ejemplo del Asistente de Licencia ilustra esto. Proporciona varias páginas de asistente; dependiendo de las opciones seleccionadas, el usuario puede acceder a diferentes páginas.

Complejo diagrama de flujo de la página del asistente

El ejemplo consta de las siguientes clases:

  • LicenseWizard hereda QWizard e implementa un asistente no lineal de cinco páginas que conduce al usuario a través del proceso de elección de un acuerdo de licencia.
  • IntroPage, EvaluatePage, RegisterPage, DetailsPage, y ConclusionPage son subclases de QWizardPage que implementan las páginas del asistente.

La clase LicenseWizard

La clase LicenseWizard deriva de QWizard y proporciona un asistente de cinco páginas que guía al usuario a través del proceso de registro de su copia de un producto de software ficticio. Esta es la definición de la clase:

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

La API pública de la clase se limita a un constructor y un enum. El enum define los ID asociados a las distintas páginas:

Nombre de la claseValor de la sumaID de página
IntroPagePage_Intro0
EvaluatePagePage_Evaluate1
RegisterPagePage_Register2
DetailsPagePage_Details3
ConclusionPagePage_Conclusion4

En este ejemplo, los ID son arbitrarios. Las únicas restricciones son que deben ser únicos y diferentes de -1. Los IDs nos permiten referirnos a las páginas.

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

En el constructor, creamos las cinco páginas, las insertamos en el asistente utilizando QWizard::setPage(), y establecemos que Page_Intro sea la primera página.

#ifndef Q_OS_MAC
    setWizardStyle(ModernStyle);
#endif

Establecemos el estilo ModernStyle en todas las plataformas excepto macOS,

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

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

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

Configuramos el QWizard para mostrar un botón Help, que está conectado a nuestra ranura showHelp(). También configuramos el LogoPixmap para todas las páginas que tienen cabecera (es decir, EvaluatePage, RegisterPage, y 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;
}

En showHelp(), mostramos textos de ayuda apropiados para la página actual. Si el usuario pulsa Help dos veces para la misma página, le decimos: "Lo siento, ya le he dado la ayuda que podía. ¿Quizá debería preguntar a un humano?".

La clase IntroPage

Las páginas se definen en licensewizard.h y se implementan en licensewizard.cpp, junto con LicenseWizard.

Aquí está la definición e implementación 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);
}

Una página hereda de QWizardPage. Establecemos un title y un watermark pixmap. Al no establecer ningún subTitle, nos aseguramos de que no se muestre ningún encabezado para esta página. (En Windows, es habitual que los asistentes muestren un pixmap de marca de agua en la primera y la última página, y que tengan una cabecera en las demás páginas).

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

La función nextId() devuelve el ID de EvaluatePage si la opción Evaluate the product for 30 days está marcada; en caso contrario, devuelve el ID de RegisterPage.

La clase EvaluatePage

La función EvaluatePage es un poco más complicada:

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

En primer lugar, establecemos la página title y subTitle.

Luego creamos los widgets hijos, creamos wizard fields asociado a ellos, y los ponemos en los layouts. Los campos se crean con un asterisco (*) junto a su nombre. Esto los convierte en mandatory fields, es decir, campos que deben rellenarse antes de que el usuario pueda pulsar el botón Next (Continue en macOS). Se puede acceder a los valores de los campos desde cualquier otra página utilizando QWizardPage::field().

Reiniciar la página equivale a borrar los dos campos de texto.

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

La página siguiente es siempre la ConclusionPage.

La clase ConclusionPage

Las páginas RegisterPage y DetailsPage son muy similares a EvaluatePage. Vayamos directamente a 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;
};

Esta vez, reimplementamos QWizardPage::initializePage() y QWidget::setVisible(), además de nextId(). También declaramos un slot privado: printButtonClicked().

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

La implementación por defecto de QWizardPage::nextId() devuelve la página con el siguiente ID, o -1 si la página actual tiene el ID más alto. Este comportamiento funcionaría aquí, porque Page_Conclusion es igual a 5 y no hay ninguna página con un ID más alto, pero para evitar depender de un comportamiento tan sutil, reimplementamos nextId() para que devuelva -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);
}

Utilizamos QWizard::hasVisitedPage() para determinar el tipo de acuerdo de licencia que ha elegido el usuario. Si el usuario rellenó el EvaluatePage, el texto de la licencia se refiere a un Acuerdo de Licencia de Evaluación. Si el usuario rellenó el DetailsPage, el texto de la licencia se refiere a un Acuerdo de Licencia de Primera Utilización. Si el usuario proporcionó una clave de actualización y omitió el DetailsPage, el texto de la licencia es un Contrato de Licencia de Actualización.

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

Queremos mostrar un botón Print en el asistente cuando el ConclusionPage esté listo. Una forma de conseguirlo es reimplementar QWidget::setVisible():

Proyecto de ejemplo @ code.qt.io

Ver también QWizard y Ejemplo de Asistente Trivial.

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