pythonpython-3.xpyqtpyqt5qwizardpage

Skip the next QWizardPage depending on QCheckBox.isChecked() from the page before


I'm working on a Wizard for installing downloaded packages. At a particular point I want to skip the next QWizardPage, depending whether a checkbox on the page before is checked or not.

The page I want to skip is the InstallPackages class and the case when it should be skipped is if the checkbox self.withPipCBox is unchecked.

I know that I have to use QCheckBox.isChecked() for that, but how to use it?


Code:

from PyQt5 import QtWidgets, QtGui, QtCore
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt, QObject, QTimer, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import (QApplication, QFileDialog, QGridLayout, QLabel,
                             QVBoxLayout, QWizard, QWizardPage, QProgressBar,
                             QCheckBox, QLineEdit, QGroupBox, QToolButton,
                             QComboBox, QDialog, QHBoxLayout)



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

        self.setWindowTitle("Venv Wizard")
        self.resize(535, 430)
        self.move(578, 183)

        self.setStyleSheet(
            """
            QToolTip {
                background-color: rgb(47, 52, 63);
                border: rgb(47, 52, 63);
                color: rgb(210, 210, 210);
                padding: 2px;
                opacity: 325
            }
            """
        )

        self.addPage(BasicSettings())
        self.addPage(InstallPackages())
        self.addPage(Summary())

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

        folder_icon = QIcon.fromTheme("folder")

        self.setTitle("Basic Settings")
        self.setSubTitle("This wizard will help you to create and set up "
                         "a virtual environment for Python 3. ")

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

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

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

        selectFolderToolButton = QToolButton()
        selectFolderToolButton.setFixedSize(26, 27)
        selectFolderToolButton.setIcon(folder_icon)
        selectFolderToolButton.setToolTip("Browse")

        placeHolder = QLabel()

        # the '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")

        # 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(selectFolderToolButton, 2, 2, 1, 1)
        gridLayout.addWidget(placeHolder, 3, 0, 1, 2)
        gridLayout.addWidget(groupBox, 4, 0, 1, 3)
        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)

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

        self.setTitle("Install Packages")
        self.setSubTitle("Specify the packages which you want Pip to "
                         "install into the virtual environment.")

        # just some content for testing
        TestLabel = QLabel("This is a test label:", self)
        TestLineEdit = QLineEdit(self)
        TestLabel.setBuddy(TestLineEdit)

        TestLabel2 = QLabel("This is a test label:", self)
        TestLineEdit2 = QLineEdit(self)
        TestLabel2.setBuddy(TestLineEdit2)

        v_layout = QVBoxLayout(self)
        v_layout.addWidget(TestLabel)
        v_layout.addWidget(TestLineEdit)
        v_layout.addWidget(TestLabel2)
        v_layout.addWidget(TestLineEdit2)
        self.setLayout(v_layout)


    def initializePage(self):
        pass


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

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



if __name__ == "__main__":
    import sys

    app = QApplication(sys.argv)
    wizard = VenvWizard()
    wizard.show()

    sys.exit(app.exec_())


Solution

  • You need to implement the nextId() of the wizard page or the wizard itself (which by default calls currentPage().nextId()).

    In the first case, you'll need to keep or get a reference to the two pages, but that can also be done by overriding the nextId() of the instance: it is a virtual function, meaning that it can be overridden in the instance from the wizard itself, allowing you to simplify things a bit, since you've access to the pageId once you add them:

            basicSettings = BasicSettings()
            self.addPage(basicSettings)
            installId = self.addPage(InstallPackages())
            summaryId = self.addPage(Summary())
            basicSettings.nextId = lambda: installId if basicSettings.interprComboBox.isChecked() else summaryId
    

    You can do almost the same in the nextId() override of the wizard, as long as you keep a reference to the page that contains the checkbox at least.
    Here's a slightly different approach to the method shown above:

    class VenvWizard(QWizard):
        def __init__(self):
            super().__init__()
            [...]
            self.basicSettings = BasicSettings()
            self.addPage(self.basicSettings)
            [...]
    
        def nextId(self):
            if self.currentPage() == self.basicSettings:
                # since python bools can behave as integers, you can add
                # 0 or 1 to the pageId using the isChecked() result
                return self.currentId() + 1 + self.basicSettins.interprComboBox.isChecked()
                # if you need the opposite (skip if unchecked) use this instead
                #return self.currentId() + 1 + (not self.basicSettins.interprComboBox.isChecked())
    
            return QtWidgets.QWizard.nextId(self)
    

    Btw, the example above will only work for continuous id numbers, meaning that it won't behave properly if you use "non linear" ids with setPage(id, page) instead of the basic addPage(page) (the same goes if you want to skip more than one page, obviously); if that's the case, you'll need a reference to the two page ids:

    class VenvWizard(QWizard):
        def __init__(self):
            super().__init__()
            [...]
            self.basicSettings = BasicSettings()
            self.addPage(self.basicSettings)
            self.installId = self.addPage(InstallPackages())
            self.summaryId = 20
            self.setPage(self.summaryId, Summary())
    
        def nextId(self):
            if self.currentPage() == self.basicSettings:
                if self.basicSettins.interprComboBox.isChecked():
                    return self.installId
                return self.summaryId
            return QtWidgets.QWizard.nextId(self)
    

    PS: please, try to keep examples as minimal as possible. There's really no need to post everything your code contains: it only makes annoying to tell apart between code that's meaningful or useless to the question, discouraging people willing to help from even trying to check out your code.