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

Posted

技术标签:

【中文标题】如何在 PyQt5 中制作嵌套的 StackedLayouts?【英文标题】:How to make nested StackedLayouts in PyQt5? 【发布时间】:2019-04-29 21:14:14 【问题描述】:

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

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

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

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

在代码中,您可以看到我正在尝试实现的部分,它们后面带有注释字符。

代码:

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

我该如何解决这个问题?

【问题讨论】:

【参考方案1】:

您可以在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())

【讨论】:

【参考方案2】:

在下面的示例中,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_())

【讨论】:

以上是关于如何在 PyQt5 中制作嵌套的 StackedLayouts?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 PyQt5 中制作自定义 QPushButton?

如何在pyqt5中为一个小部件制作一个清晰的板子功能

PyQt5 笔记(01):嵌套布局

如何在 Windows 10 上使用 Cx_Freeze 制作 exe,包括 PyQt5 和 OpenCV

PyQt5 和 QML 中的嵌套 ListView

PyQt5 制作子类小部件