3

I am creating a PyQt5 application in Python for a university project that uses QStackedLayout to make it a single-windowed app, however, I can not figure out how to nest a Stacked Widget inside a Stacked Widget.

When running the code (degraded for the question), you can see the main menu with a QPushButton in it. When clicking it, the window changes with a QListWidget in it.

What I want to achieve is a Stacked Widget to the right of the QListWidget, which changes respectively with the chosen list item.

Edit: is there a method to open one window where the other was positioned? Right now whenever I click a button the window pops up in the middle of the screen.

In the code, you can see the parts I am trying to implement with comment characters behind them.

The code:

import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, 
                             QLabel, QPushButton, QWidget,
                             QStackedLayout, QListWidget)
from PyQt5.QtCore import QRect, Qt

class Ui(QWidget):

    def setupUi(self, Main):

        Main.setObjectName("Main")
        Main.setFixedSize(900, 500)

        self.width = 900
        self.height = 500

        self.setFixedSize(self.width, self.height)

        '''MENU ON THE MAIN WINDOW'''
        self.menu = QStackedLayout()

        self. mainMenu = QWidget()
        self.howToMenu = QWidget()

        self. mainMenuUi()
        self.howToMenuUi()

        self.menu.addWidget(self. mainMenu)
        self.menu.addWidget(self.howToMenu)

        '''MENU ON THE HOWTO WINDOW'''        
        #self.howToMenuMenu = QStackedLayout()

        #self.howToOverView    = QWidget()
        #self.howToLevel       = QWidget()
        #self.howToTapeMeasure = QWidget()
        #self.howToTheodolite  = QWidget()

        #self.   overViewUi()
        #self.      levelUi()
        #self.tapeMeasureUi()
        #self. theodoliteUi()

        #self.howToMenuMenu.addWidget(self.howToOverView   )
        #self.howToMenuMenu.addWidget(self.howToLevel      )
        #self.howToMenuMenu.addWidget(self.howToTapeMeasure)
        #self.howToMenuMenu.addWidget(self.howToTheodolite )

    def mainMenuUi(self):

        self.mainMenu.setFixedSize(self.width, self.height)

        self.mainMenuText = QLabel(self.mainMenu)
        self.mainMenuText.setGeometry(QRect(30, 120, 480, 200))
        self.mainMenuText.setStyleSheet("font: 14pt Century Gothic")
        self.mainMenuText.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
        self.mainMenuText.setText("Welcome to the Surveying Traverse Calculator!")

        self.howToButton = QPushButton("HOW TO DO A TRAVERSE", self.mainMenu)

        self.howToButton.setGeometry(140, 180, 200, 30)

    def howToMenuUi(self):

        self.howToMenu.setFixedSize(self.width, self.height)

        self.menuButton1 = QPushButton("Back to main menu", self.howToMenu)   
        self.menuButton1.setGeometry(QRect(10, 10, 200, 30))

        self.howToTitle = QLabel(self.howToMenu)
        self.howToTitle.setGeometry(QRect(10, 50, self.width, 40))
        self.howToTitle.setStyleSheet("font: 14pt Century Gothic")
        self.howToTitle.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignVCenter)
        self.howToTitle.setText("How to Do a Traverse")

        self.howToSteps = QListWidget(self.howToMenu)
        self.howToSteps.setGeometry(QRect(10, 100, 200, 80))
        self.howToSteps.insertItem(0, "OVERVIEW"    )
        self.howToSteps.insertItem(1, "LEVEL"       )
        self.howToSteps.insertItem(2, "TAPE MEASURE")
        self.howToSteps.insertItem(3, "THEODOLITE"  )

    #def overViewUi(self):

    #def levelUi(self):

    #def tapeMeasureUi(self):

    #def theodoliteUi(self)

class Main(QMainWindow, Ui):

    def __init__(self):

        super(Main, self).__init__()

        self.setupUi(self)

        self.menuButton1.clicked.connect(self. menuWindow)
        self.howToButton.clicked.connect(self.howToWindow)

    def menuWindow(self):

        self.menu.setCurrentIndex(0)

    def howToWindow(self):

        self.menu.setCurrentIndex(1)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    M = Main()
    sys.exit(app.exec())

How do I solve this problem?

nathancy
  • 42,661
  • 14
  • 115
  • 137

2 Answers2

5

enter image description here You can add a QStackedWidget to the right of the QListWidget and control the chosen list object with the currentRowChanged signal. I would recommend you use QHBoxLayout, QVBoxLayout, or QGridLayout for layout management.

import sys
from PyQt5.QtWidgets import (QApplication, QMainWindow, 
                             QLabel, QPushButton, QWidget,
                             QStackedLayout, QListWidget,
                             QVBoxLayout, QStackedWidget,
                             QGridLayout)
from PyQt5.QtCore import QRect, Qt

class Ui(QWidget):

    def setupUi(self, Main):

        Main.setObjectName("Main")
        Main.setFixedSize(900, 500)

        self.width = 900
        self.height = 500

        self.setFixedSize(self.width, self.height)

        '''MENU ON THE MAIN WINDOW'''
        self.menu = QStackedLayout()

        self.mainMenu = QWidget()
        self.howToMenu = QWidget()

        self.mainMenuUi()
        self.howToMenuUi()

        self.menu.addWidget(self.mainMenu)
        self.menu.addWidget(self.howToMenu)

        '''MENU ON THE HOWTO WINDOW'''        
        #self.howToMenuMenu = QStackedLayout()

        #self.howToOverView    = QWidget()
        #self.howToLevel       = QWidget()
        #self.howToTapeMeasure = QWidget()
        #self.howToTheodolite  = QWidget()

        #self.   overViewUi()
        #self.      levelUi()
        #self.tapeMeasureUi()
        #self. theodoliteUi()

        #self.howToMenuMenu.addWidget(self.howToOverView   )
        #self.howToMenuMenu.addWidget(self.howToLevel      )
        #self.howToMenuMenu.addWidget(self.howToTapeMeasure)
        #self.howToMenuMenu.addWidget(self.howToTheodolite )

    def mainMenuUi(self):

        self.mainMenu.setFixedSize(self.width, self.height)

        self.mainMenuText = QLabel(self.mainMenu)
        self.mainMenuText.setGeometry(QRect(30, 120, 480, 200))
        self.mainMenuText.setStyleSheet("font: 14pt Century Gothic")
        self.mainMenuText.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignTop)
        self.mainMenuText.setText("Welcome to the Surveying Traverse Calculator!")

        self.howToButton = QPushButton("HOW TO DO A TRAVERSE", self.mainMenu)

        self.howToButton.setGeometry(140, 180, 200, 30)

    def howToMenuUi(self):

        self.howToMenu_layout = QGridLayout()

        self.howToMenu.setFixedSize(self.width, self.height)

        self.menuButton1 = QPushButton("Back to main menu")
        self.menuButton1.setGeometry(QRect(10, 10, 200, 30))

        self.howToTitle = QLabel()
        self.howToTitle.setGeometry(QRect(10, 50, self.width, 40))
        self.howToTitle.setStyleSheet("font: 14pt Century Gothic")
        self.howToTitle.setAlignment(Qt.AlignLeading|Qt.AlignLeft|Qt.AlignVCenter)
        self.howToTitle.setText("How to Do a Traverse")

        self.howToSteps = QListWidget()
        self.howToSteps.setGeometry(QRect(10, 100, 200, 80))
        self.howToSteps.insertItem(0, "OVERVIEW"    )
        self.howToSteps.insertItem(1, "LEVEL"       )
        self.howToSteps.insertItem(2, "TAPE MEASURE")
        self.howToSteps.insertItem(3, "THEODOLITE"  )
        self.howToSteps.currentRowChanged.connect(self.display_traverse)

        self.overview_container = QWidget()
        self.level_container = QWidget()
        self.tape_measure_container = QWidget()
        self.theodolite_container = QWidget()

        self.overViewUi()
        self.levelUi()
        self.tapeMeasureUi()
        self.theodoliteUi()

        self.traverse_action = QStackedWidget()
        self.traverse_action.addWidget(self.overview_container) 
        self.traverse_action.addWidget(self.level_container) 
        self.traverse_action.addWidget(self.tape_measure_container) 
        self.traverse_action.addWidget(self.theodolite_container) 
        self.traverse_action.setCurrentIndex(0)

        self.howToMenu_left_layout = QVBoxLayout()
        self.howToMenu_left_layout.addWidget(self.menuButton1)
        self.howToMenu_left_layout.addWidget(self.howToTitle)
        self.howToMenu_left_layout.addWidget(self.howToSteps)

        self.howToMenu_layout.addLayout(self.howToMenu_left_layout,0,0,1,1)
        self.howToMenu_layout.addWidget(self.traverse_action,0,1,1,1)
        self.howToMenu.setLayout(self.howToMenu_layout)

    def overViewUi(self):
        self.overview_layout = QVBoxLayout()
        self.overview_button = QPushButton('Overview')
        self.overview_layout.addWidget(self.overview_button)
        self.overview_container.setLayout(self.overview_layout)

    def levelUi(self):
        self.level_layout = QVBoxLayout()
        self.level_button = QPushButton('Level')
        self.level_layout.addWidget(self.level_button)
        self.level_container.setLayout(self.level_layout)

    def tapeMeasureUi(self):
        self.tape_measure_layout = QVBoxLayout()
        self.tape_measure_button = QPushButton('Tape measure')
        self.tape_measure_layout.addWidget(self.tape_measure_button)
        self.tape_measure_container.setLayout(self.tape_measure_layout)

    def theodoliteUi(self):
        self.theodolite_layout = QVBoxLayout()
        self.theodolite_button = QPushButton('Theodolite')
        self.theodolite_layout.addWidget(self.theodolite_button)
        self.theodolite_container.setLayout(self.theodolite_layout)

    def display_traverse(self, index):
        self.traverse_action.setCurrentIndex(index)

class Main(QMainWindow, Ui):

    def __init__(self):

        super(Main, self).__init__()

        self.setupUi(self)

        self.menuButton1.clicked.connect(self.menuWindow)
        self.howToButton.clicked.connect(self.howToWindow)

    def menuWindow(self):

        self.menu.setCurrentIndex(0)

    def howToWindow(self):

        self.menu.setCurrentIndex(1)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    M = Main()
    sys.exit(app.exec())
nathancy
  • 42,661
  • 14
  • 115
  • 137
1

In the following example a QHBoxLayout is placed in the centralwidget, and as a first element the QListWidget and as a second element the QStackedLayout. To make the change I use the currentRowChanged signal from QListWidget

from PyQt5 import QtCore, QtGui, QtWidgets


class MainWindow(QtWidgets.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        central_widget = QtWidgets.QWidget()
        self.setCentralWidget(central_widget)
        hlay = QtWidgets.QHBoxLayout(central_widget)

        self.overview_widget = QtWidgets.QWidget()
        self.fill_overview()
        self.level_widget = QtWidgets.QWidget()
        self.fill_level()
        self.tape_measure_widget = QtWidgets.QWidget()
        self.fill_tape_measure()
        self.theodolite_widget = QtWidgets.QWidget()
        self.fill_theodolite()

        self.list_widget = QtWidgets.QListWidget()

        self.slay = QtWidgets.QStackedLayout()
        hlay.addWidget(self.list_widget)
        hlay.addLayout(self.slay)
        hlay.setStretch(0, 0)
        hlay.setStretch(1, 1)

        self.list_widget.currentRowChanged.connect(self.slay.setCurrentIndex)

        for w, text in (
            (self.overview_widget, "OVERVIEW"),
            (self.level_widget, "LEVEL"),
            (self.tape_measure_widget, "TAPE MEASURE"),
            (self.theodolite_widget, "THEODOLITE"),
        ):
            self.slay.addWidget(w)
            self.list_widget.addItem(text)

    def fill_overview(self):
        hlay = QtWidgets.QVBoxLayout(self.overview_widget)
        hlay.addWidget(
            QtWidgets.QLabel("Overview", alignment=QtCore.Qt.AlignCenter)
        )
        self.overview_widget.setStyleSheet("background-color:green;")

    def fill_level(self):
        hlay = QtWidgets.QVBoxLayout(self.level_widget)
        hlay.addWidget(
            QtWidgets.QLabel("Level", alignment=QtCore.Qt.AlignCenter)
        )
        self.level_widget.setStyleSheet("background-color:blue;")

    def fill_tape_measure(self):
        hlay = QtWidgets.QVBoxLayout(self.tape_measure_widget)
        hlay.addWidget(
            QtWidgets.QLabel("Tape Measure", alignment=QtCore.Qt.AlignCenter)
        )
        self.tape_measure_widget.setStyleSheet("background-color:red;")

    def fill_theodolite(self):
        hlay = QtWidgets.QVBoxLayout(self.theodolite_widget)
        hlay.addWidget(
            QtWidgets.QLabel("Theodolite", alignment=QtCore.Qt.AlignCenter)
        )
        self.theodolite_widget.setStyleSheet("background-color:gray;")


if __name__ == "__main__":
    import sys

    app = QtWidgets.QApplication(sys.argv)
    w = MainWindow()
    w.resize(640, 240)
    w.show()
    sys.exit(app.exec_())
eyllanesc
  • 235,170
  • 19
  • 170
  • 241