0

I have a wizard with three pages. First page is BasicSettings(), second page is InstallPackages() and the last page (finish) is Summary(). BasicSettings() is for creating a virtual environment. If the 'Install and update Pip' checkbox is left unchecked, the wizard should go directly to the last page Summary(), else it should go on to InstallPackages() where the user can install some packages into the created virtual environment. And the last step is from there to go to Summary() where there is the 'finish' button.

In the code example below the createButton is connected to the method execute_venv_create(). When the method has executed the code, a message box pops up, indicating that the process has finished.

Now, when the user clicks Ok I want the wizard to automatically go to the next page defined by QWizard().nextId. I thought that I could achieve that by invoking QWizard().next(), but that doesen't work. I tried connecting createButton to it, but that doesn't have an effect.

How can I make the wizard automatically go on to the next page after execute_venv_create() finished executing code and the user has confirmed the info message box?


Code to reproduce:

# -*- coding: utf-8 -*-
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QApplication, QGridLayout, QLabel, QCheckBox,
                             QHBoxLayout, QVBoxLayout, QToolButton, QWizard,
                             QWizardPage, QComboBox, QTableView, QLineEdit,
                             QGroupBox, QPushButton, QMessageBox)


class VenvWizard(QWizard):
    """
    Wizard for creating and setting up a virtual environment.
    """
    def __init__(self):
        super().__init__()

        self.setWindowTitle("Venv Wizard")
        self.resize(635, 480)
        self.move(528, 153)

        basicSettings = BasicSettings()
        self.addPage(basicSettings)

        installId = self.addPage(InstallPackages())
        summaryId = self.addPage(Summary())

        # go to last page if pip wasn't selected else go to the install page
        basicSettings.nextId = (
            lambda: installId if basicSettings.withPipCBox.isChecked()
            else summaryId
        )

        # ...

class BasicSettings(QWizardPage):
    """
    Basic settings of the virtual environment to create.
    """
    def __init__(self):
        super().__init__()

        folder_icon = QIcon.fromTheme("folder")

        self.setTitle("Basic Settings")
        self.setSubTitle("..........................."
                         "...........................")

        interpreterLabel = QLabel("&Interpreter:")
        self.interprComboBox = QComboBox()
        interpreterLabel.setBuddy(self.interprComboBox)

        venvNameLabel = QLabel("Venv &name:")
        self.venvNameLineEdit = QLineEdit()
        venvNameLabel.setBuddy(self.venvNameLineEdit)

        venvLocationLabel = QLabel("&Location:")
        self.venvLocationLineEdit = QLineEdit()
        venvLocationLabel.setBuddy(self.venvLocationLineEdit)

        self.selectDirToolButton = QToolButton(icon=folder_icon)
        self.selectDirToolButton.setFixedSize(26, 27)

        placeHolder = QLabel()

        # options groupbox
        groupBox = QGroupBox("Options")

        self.withPipCBox = QCheckBox(
            "Install and update &Pip"
        )
        self.sitePackagesCBox = QCheckBox(
            "&Make system (global) site-packages dir available to venv"
        )
        self.symlinksCBox = QCheckBox(
            "Attempt to &symlink rather than copy files into venv"
        )
        self.launchVenvCBox = QCheckBox(
            "Launch a &terminal with activated venv after installation"
        )
        self.createButton = QPushButton(
            "&Create", self,
            clicked=self.execute_venv_create,
        )
        self.createButton.setFixedWidth(90)

        # box layout containing the create button
        h_BoxLayout = QHBoxLayout()
        h_BoxLayout.setContentsMargins(495, 5, 0, 0)
        h_BoxLayout.addWidget(self.createButton)

        # grid layout
        gridLayout = QGridLayout()
        gridLayout.addWidget(interpreterLabel, 0, 0, 1, 1)
        gridLayout.addWidget(self.interprComboBox, 0, 1, 1, 2)
        gridLayout.addWidget(venvNameLabel, 1, 0, 1, 1)
        gridLayout.addWidget(self.venvNameLineEdit, 1, 1, 1, 2)
        gridLayout.addWidget(venvLocationLabel, 2, 0, 1, 1)
        gridLayout.addWidget(self.venvLocationLineEdit, 2, 1, 1, 1)
        gridLayout.addWidget(self.selectDirToolButton, 2, 2, 1, 1)
        gridLayout.addWidget(placeHolder, 3, 0, 1, 2)
        gridLayout.addWidget(groupBox, 4, 0, 1, 3)
        gridLayout.addLayout(h_BoxLayout, 5, 0, 1, 0)
        self.setLayout(gridLayout)

        # options groupbox
        groupBoxLayout = QVBoxLayout()
        groupBoxLayout.addWidget(self.withPipCBox)
        groupBoxLayout.addWidget(self.sitePackagesCBox)
        groupBoxLayout.addWidget(self.symlinksCBox)
        groupBoxLayout.addWidget(self.launchVenvCBox)
        groupBox.setLayout(groupBoxLayout)

    def execute_venv_create(self):
        """
        Execute the creation process.
        """
        # do something, then display info message
        QMessageBox.information(self, "Done", "message text")
        # when user clicks 'Ok', go to the next page defined in .nextId


class InstallPackages(QWizardPage):
    """
    Install packages via `pip` into the created virtual environment.
    """
    def __init__(self):
        super().__init__()

        self.setTitle("Install Packages")
        self.setSubTitle("..........................."
                         "...........................")

        verticalLayout = QVBoxLayout()
        gridLayout = QGridLayout(self)

        pkgNameLabel = QLabel("Package name:")
        self.pkgNameLineEdit = QLineEdit()
        self.searchButton = QPushButton("Search")
        resultsTable = QTableView(self)

        gridLayout.addWidget(pkgNameLabel, 0, 0, 1, 1)
        gridLayout.addWidget(self.pkgNameLineEdit, 0, 1, 1, 1)
        gridLayout.addWidget(self.searchButton, 0, 2, 1, 1)
        gridLayout.addWidget(resultsTable, 1, 0, 1, 3)

        verticalLayout.addLayout(gridLayout)

        # ...

class Summary(QWizardPage):
    def __init__(self):
        super().__init__()

        self.setTitle("Summary")
        self.setSubTitle("..........................."
                         "...........................")

        # ...

    def initializePage(self):
        pass


if __name__ == "__main__":
    import sys

    app = QApplication(sys.argv)
    wiz = VenvWizard()
    wiz.exec_()

    sys.exit(app.exec_())
Joey
  • 1,436
  • 2
  • 19
  • 33

1 Answers1

2

The change of pages (and the button next/finish, etc.) is handled by the QWizard and not the page itself.

Since you can't call methods from QWizard in your QWizardPage, the easiest way to change the page without clicking on the next button is to use a signal:

class VenvWizard(QWizard):
    """
    Wizard for creating and setting up a virtual environment.
    """
        def __init__(self):
        super().__init__()

        self.setWindowTitle("Venv Wizard")
        self.resize(635, 480)
        self.move(528, 153)

        self.basicSettings = BasicSettings()
        self.basicSettingsId = self.addPage(self.basicSettings)

        self.installId = self.addPage(InstallPackages())
        self.summaryId = self.addPage(Summary())


        self.basicSettings.done.connect(self.next)

        # go to last page if pip wasn't selected else go to the install page

    def nextId(self):
        # Process the flow only if the current page is basic settings
        if self.currentId() != self.basicSettingsId:
            return super().nextId()

        if self.basicSettings.withPipCBox.isChecked():
            return self.installId
        else:
            return self.summaryId
    done = pyqtSignal()
    def execute_venv_create(self):
        """
        Execute the creation process.
        """
        # do something, then display info message
        ok = QMessageBox.information(self, "Done", "message text")
        # when user clicks 'Ok', go to the next page defined in .nextId
        self.done.emit()
Dimitry Ernot
  • 6,256
  • 2
  • 25
  • 37
  • Seems like your code is missing something. When I'm on `InstallPackages()` I'm not able to go on to the last page, `Summary()`. Nothing happens if I click on the 'next' button. And if `self.basicSettings.withPipCBox.isChecked()` is unchecked, the wizard goes on to `Summary()`, but there is no 'finish' button anymore. Instead, there is another 'next' button. – Joey Aug 03 '19 at 22:15
  • This problem is not related to my code but to your implementation of `nextId`. If you want me to help you with that, you have to provide the flow of your wizard. – Dimitry Ernot Aug 03 '19 at 22:22
  • 1
    I've fixed the example: if the current page is not basic settings, it will call the default `nextId`. – Dimitry Ernot Aug 03 '19 at 23:01