python-3.xpyqt5qstackedwidget

How to make "finish" button in QStackedWidget


I trying to create "Finish" button in QStackedWidget.

In a "checkButtons" function i checking current page index and set click events and text. I tried to check it by class name, but it doesn't work too.

Here is a code:

import sys

from PyQt5.QtGui import QIcon
from PyQt5.QtCore import Qt
from PyQt5.QtWidgets import (QApplication, QDialog, QComboBox, QStackedWidget, QWidget,
            QPushButton, QLabel, QVBoxLayout, QHBoxLayout, QStyle)


class Main(QDialog):
    def __init__(self, parent=None):
        super(Main, self).__init__(parent)
        # Main window setup
        self.setWindowTitle("Stacked widget example")
        self.setWindowIcon(self.style().standardIcon(QStyle.SP_FileDialogNewFolder))
        self.setMinimumSize(400, 400)
        self.setMaximumSize(640, 480)

        self.rootVBox = QVBoxLayout()
        self.rootHBox = QHBoxLayout()
        self.rootHBox.addStretch()
        self.rootVBox.addStretch()

        self.pages = [FirstPage, SecondPage]
        self.stacked = QStackedWidget(self)
        for i in self.pages: self.stacked.addWidget(i(self))

        self.pageState = True
        self.buttonNext = QPushButton("Next")
        self.buttonNext.clicked.connect(self.buttonNextConnect)

        self.buttonBack = QPushButton("Back")
        self.buttonBack.clicked.connect(self.buttonBackConnect)

        self.rootHBox.addWidget(self.buttonBack)
        self.rootHBox.addWidget(self.buttonNext)

        self.rootVBox.addLayout(self.rootHBox)
        self.setLayout(self.rootVBox)

    def checkButtons(self):
        print(self.stacked.currentIndex())

        # I tried to check self.stacked.currentIndex() but it didn't work too
        # if self.stacked.currentWidget().__class__ == self.pages[-1]:
        if self.stacked.currentIndex() == len(self.pages) - 1:
            self.buttonNext.setText("Finish")
            self.buttonNext.clicked.connect(self.close)

        elif self.stacked.currentIndex() < len(self.pages) - 1:
            self.buttonNext.setText("Next")
            self.buttonNext.clicked.connect(self.buttonNextConnect)

    def buttonNextConnect(self):
        self.stacked.setCurrentIndex(self.stacked.currentIndex() + 1)
        self.checkButtons()

    def buttonBackConnect(self):
        self.stacked.setCurrentIndex(self.stacked.currentIndex() - 1)
        self.checkButtons()

    def finish(self):
        self.close()


class FirstPage(QWidget):
    def __init__(self, parent=None):
        super(FirstPage, self).__init__(parent)
        label = QLabel("First page")
        rootVBox = QVBoxLayout()
        rootHBox = QHBoxLayout()

        rootHBox.addWidget(label)
        rootVBox.addLayout(rootHBox)
        self.setLayout(rootVBox)


class SecondPage(QWidget):
    def __init__(self, parent=None):
        super(SecondPage, self).__init__(parent)
        label = QLabel("Second page")
        rootVBox = QVBoxLayout()
        rootHBox = QHBoxLayout()

        rootHBox.addWidget(label)
        rootVBox.addLayout(rootHBox)
        self.setLayout(rootVBox)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    main = Main()
    main.show()

    sys.exit(app.exec_())

If you try to press "next", "back" and then "next" again a program will be close. So, how can i fix it? Should i just make control buttons for each widget?


Solution

  • You must use the currentChanged signal of the QStackedWidget to know what page you are on and thus change the text, but in the buttonNextConnect slot you should check if you are already on the last page before switching to a new page, if you are then call to finish and if you do not change to another page

    class Main(QDialog):
        def __init__(self, parent=None):
            super(Main, self).__init__(parent)
            # Main window setup
            self.setWindowTitle("Stacked widget example")
            self.setWindowIcon(self.style().standardIcon(QStyle.SP_FileDialogNewFolder))
            self.setMinimumSize(400, 400)
            self.setMaximumSize(640, 480)
    
            rootVBox = QVBoxLayout(self)
            rootHBox = QHBoxLayout()
            rootHBox.addStretch()
            rootVBox.addStretch()
    
            self.pages = [FirstPage, SecondPage]
            self.stacked = QStackedWidget(self)
    
            for i in self.pages: self.stacked.addWidget(i(self))
    
            self.buttonNext = QPushButton("Next")
            self.buttonNext.clicked.connect(self.buttonNextConnect)
    
            self.buttonBack = QPushButton("Back")
            self.buttonBack.clicked.connect(self.buttonBackConnect)
    
            rootHBox.addWidget(self.buttonBack)
            rootHBox.addWidget(self.buttonNext)
    
            rootVBox.addLayout(rootHBox)
    
            self.stacked.currentChanged.connect(self.on_currentChanged)
    
        def buttonNextConnect(self):
            if self.stacked.currentIndex() == self.stacked.count() -1:
                self.finish()
            if self.stacked.currentIndex() < self.stacked.count() -1:
                self.stacked.setCurrentIndex(self.stacked.currentIndex() + 1)
    
        def buttonBackConnect(self):
            if self.stacked.currentIndex() > 0:
                self.stacked.setCurrentIndex(self.stacked.currentIndex() - 1)
    
        def on_currentChanged(self, index):
            if index == self.stacked.count() -1:
                self.buttonNext.setText("Finish")
            else:
                self.buttonNext.setText("Next")
    
        def finish(self):
            self.close()
    

    Another option is to use QWizard and QWizardPage:

    import sys
    from PyQt5 import QtCore, QtGui, QtWidgets
    
    class Main(QtWidgets.QWizard):
        def __init__(self, parent=None):
            super(Main, self).__init__(parent)
            buttons = [
                QtWidgets.QWizard.Stretch, 
                QtWidgets.QWizard.BackButton,
                QtWidgets.QWizard.NextButton, 
                QtWidgets.QWizard.FinishButton
            ]
            self.setButtonLayout(buttons)
            self.addPage(FirstPage())
            self.addPage(SecondPage())
    
    class FirstPage(QtWidgets.QWizardPage):
        def __init__(self, parent=None):
            super(FirstPage, self).__init__(parent)
            self.setTitle("First page")
    
    class SecondPage(QtWidgets.QWizardPage):
        def __init__(self, parent=None):
            super(SecondPage, self).__init__(parent)
            self.setTitle("Second page")
    
    if __name__ == '__main__':
        app = QtWidgets.QApplication(sys.argv)
        main = Main()
        main.show()
        sys.exit(app.exec_())