如何在 PyQt5 中制作嵌套的 StackedLayouts?

How to make nested StackedLayouts in PyQt5?

我正在 Python 中为大学项目创建一个 PyQt5 应用程序,该项目使用 QStackedLayout 使其成为单一 windowed 应用程序,但是,我不知道如何将 Stacked Widget 嵌套在 Stacked Widget 中。

当 运行 代码(因问题而降级)时,您可以看到带有 QPushButton 的主菜单。单击它时,window 会更改为其中的 QListWidget

我想要实现的是 QListWidget 右侧的 Stacked Widget,它会随着所选列表项的不同而变化。

编辑:有没有一种方法可以在另一个 window 所在的位置打开一个?现在,每当我单击一个按钮时,window 就会在屏幕中间弹出。

在代码中,你可以看到我试图实现的部分,后面有注释字符。

代码:

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

如何解决这个问题?

在下面的示例中,QHBoxLayout 被放置在 centralwidget 中,QListWidget 作为第一个元素,QStackedLayout 作为第二个元素。为了进行更改,我使用了来自 QListWidget

的 currentRowChanged 信号
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_())

您可以在 QListWidget 的右侧添加一个 QStackedWidget 并使用 currentRowChanged 信号控制所选列表对象。我建议您使用 QHBoxLayoutQVBoxLayoutQGridLayout 进行布局管理。

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