Python QStackedWidget打开多个窗口

Python QStackedWidget打开多个窗口,python,pyqt,Python,Pyqt,我使用PyQt构建了一个小应用程序,据我所知,QStackedWidget用于制作多页应用程序。问题是它正在打开多个页面,而且我无法使用class Abc中的按钮重定向到class Xyz main.py from PyQt4 import QtGui, QtCore import sys class Window(QtGui.QMainWindow): def __init__(self, parent=None): super(Window, self).__ini

我使用PyQt构建了一个小应用程序,据我所知,QStackedWidget用于制作多页应用程序。问题是它正在打开多个页面,而且我无法使用
class Abc
中的按钮重定向到
class Xyz

main.py

from PyQt4 import QtGui, QtCore
import sys

class Window(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)

        self.stacked_widget = QtGui.QStackedWidget()

        layout = QtGui.QVBoxLayout()
        layout.setContentsMargins(0,0,0,0)
        layout.addWidget(self.stacked_widget)

        widget = QtGui.QWidget()
        widget.setLayout(layout)
        self.setCentralWidget(widget)

        widget_1 = Abc(self.stacked_widget)
        widget_2 = Xyz(self.stacked_widget)

        self.stacked_widget.addWidget(widget_1)
        self.stacked_widget.addWidget(widget_2)

        self.showMaximized()

class Abc(QtGui.QWidget):
    def __init__(self, stacked_widget, parent=None):
        super(Abc, self).__init__(parent)
        
        self.stacked_widget = stacked_widget

        self.frame = QtGui.QFrame()
        self.frame_layout = QtGui.QVBoxLayout()
        self.button = QtGui.QPushButton('Click me!')
        self.button.clicked.connect(self.click)
        self.frame_layout.addWidget(self.button)
        self.frame.setLayout(self.frame_layout)

        self.layout = QtGui.QVBoxLayout()
        self.layout.addWidget(self.frame)
        self.setLayout(self.layout)

        self.showMaximized()

    def click(self):
        print("You clicked me!")
        self.stacked_widget.setCurrentIndex(1)

class Xyz(QtGui.QWidget):
    def __init__(self, parent=None):
        super(Xyz, self).__init__(parent)

        self.frame_layout = QtGui.QStackedLayout()
        self.page1 = Page("Page 1", self.frame_layout)
        self.page2 = Page("Page 2", self.frame_layout)
        self.frame_layout.addWidget(self.page1)
        self.frame_layout.addWidget(self.page2)


class Page(QtGui.QWidget):
    def __init__(self, text, frame_layout, parent=None):
        super(Page, self).__init__(parent)

        print(self.width(), self.height())
        self.frame_layout = frame_layout

        self.frame = QtGui.QFrame()
        self.frame_layout = QtGui.QVBoxLayout()
        self.frame.setStyleSheet("background-color: rgb(191, 191, 191)")
        self.label = QtGui.QLabel()
        self.label.setText(text)
        self.frame_layout.addWidget(self.label, alignment=QtCore.Qt.AlignCenter)
        self.frame.setLayout(self.frame_layout)

        self.layout = QtGui.QVBoxLayout()
        self.layout.addWidget(self.frame)
        self.layout.setContentsMargins(0,0,0,0)
        self.setLayout(self.layout)

        self.showMaximized()
        print(self.width(), self.height())


if __name__ == "__main__":
    app = QtGui.QApplication([])
    window = Window()
    window.show()
    sys.exit(app.exec_())

主要问题是没有为
Xyz
小部件设置QStackedLayout,结果是所有页面实际上都将显示为顶级窗口

当一个小部件被添加到一个布局中时,它拥有它的所有权;如果布局已设置为小部件,则添加到布局的小部件将重新租给另一个小部件。如果随后设置布局,也会发生同样的情况

为什么它将第一页显示为一个单独的窗口?为什么不显示第二个呢?
当在没有父窗口的情况下创建新窗口小部件时,它将成为顶级窗口;当一个小部件被添加到一个新的堆叠布局中时,Qt会自动尝试显示它;您没有将布局设置为任何设置(这将按照前面的解释重新分配),结果是第一个页面显示为独立窗口

现在,由于第一个“屏幕”将仅第一次显示,您可以将该小部件设置为中心小部件,然后将Xyz实例(实际上是一个QStackedWidget子类)设置为新的中心小部件

请注意,如果QFrame是显示的唯一父窗口小部件,则不需要使用QWidget来添加QFrame:您可以将QFrame子类化

这是一个更简单、更清晰的代码版本:

from PyQt4 import QtGui, QtCore

class Window(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(Window, self).__init__(parent)

        self.startPage = Abc()
        self.setCentralWidget(self.startPage)

        self.startPage.startRequest.connect(self.buildPages)

    def buildPages(self):
        self.pages = Xyz()
        self.setCentralWidget(self.pages)


class Abc(QtGui.QFrame):
    startRequest = QtCore.pyqtSignal()
    def __init__(self, parent=None):
        super(Abc, self).__init__(parent)
        
        layout = QtGui.QVBoxLayout(self)

        self.button = QtGui.QPushButton('Click me!')
        self.button.clicked.connect(self.startRequest)
        layout.addWidget(self.button)


class Xyz(QtGui.QStackedWidget):
    def __init__(self, parent=None):
        super(Xyz, self).__init__(parent)

        self.page1 = Page("Page 1")
        self.page2 = Page("Page 2")
        self.addWidget(self.page1)
        self.addWidget(self.page2)

        self.page1.switchRequest.connect(lambda: self.setCurrentIndex(1))
        self.page2.switchRequest.connect(lambda: self.setCurrentIndex(0))


class Page(QtGui.QFrame):
    switchRequest = QtCore.pyqtSignal()
    def __init__(self, text, parent=None):
        super(Page, self).__init__(parent)

        layout = QtGui.QVBoxLayout(self)
        # set the background only for QFrame subclasses (which also includes 
        # QLabel), this prevents setting the background for other classes, 
        # such as the push button
        self.setStyleSheet('''
            QFrame {
                background-color: rgb(191, 191, 191)
            }
        ''')
        # when adding a widget to a layout, the layout tries to automatically 
        # make it as big as possible (based on the widget's sizeHint); so you
        # should not use the alignment argument for layout.addWidget(), but for 
        # the label instead
        self.label = QtGui.QLabel(text, alignment=QtCore.Qt.AlignCenter)
        layout.addWidget(self.label)

        self.switchButton = QtGui.QPushButton('Switch')
        layout.addWidget(self.switchButton)
        self.switchButton.clicked.connect(self.switchRequest)


if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())

你的问题有点模糊,就像你的代码一样。例如,
Xyz
小部件不包含任何内容,它的
page1
page2
小部件只是添加到没有设置为任何小部件的堆叠布局中(这就是为什么您将第一个页面视为单独的窗口,我将最终解释为什么第二个页面没有)。然后使用
frame\u layout
参数创建
页面
实例,将其设置为实例属性,然后将其覆盖。你能澄清一下小部件的结构是什么吗?@musicamante,我想要的是类
Abc
中定义的一个按钮,单击该按钮,我将被重定向到
Xyz
类,该类现在包含多个页面(在本例中为“第1页”和“第2页”)。然后,我想要一个在这些页面之间导航的功能。是否需要Xyz?据我所见,它似乎在任何方面都没有“专门化”,因此您的结构看起来有点过于复杂:您不能只使用一个QStackedWidget,直接将这两个页面添加到其中,然后添加函数在它们之间切换(索引1和索引2),忽略第一个页面。@musicamante,是的,有Xyz是绝对必要的。实际上,我的actaul项目包含50多页,我已经按照你的建议创建了这个项目,但是当我启动应用程序时,它会产生一种恼人的效果。看起来这些页面好像是一页一页地加载。这就是为什么我创建了一个带有
QStackedLayout
的类Xyz来避免这种情况。您面临的问题之一并不取决于是否有
Xyz
,而是因为您没有为该小部件设置布局(实际上它什么都不做),所以所有页面实际上都显示为独立的顶级窗口。开始页(带按钮的那一页)会在任何时候再次显示,还是只在开始时显示?这正是我所需要的。但仍然无法理解整个所有权概念。看起来PyQt不像我想象的那么简单。但是非常感谢,你已经解决了我的问题。@thedarklord我建议你仔细研究文档(我知道,它很长,要求很高,但相信我,它是值得的)。基本概念是
QObject
子类(包括所有QWidget子类)只能有一个父类(或者根本没有父类!);当您在文档中读到类似“x拥有y”的内容时,通常意味着
x
成为
y
的所有者。简单地说,当您将widgetY添加到在widgetX上设置的布局中时,widgetX将成为widgetY的父级。但这对于QWidgets来说有点复杂。我知道文档对此有点模糊(尤其是整个“布局拥有所有权”)。默认情况下,没有QWIDGET父级的QWIDGET总是显示为顶层窗口小部件(如果将小部件添加到尚未设置到另一个小部件的布局),也会发生这种情况。如果您想添加一个小部件作为另一个C++的子窗口,您可以用它的父窗口小部件参数初始化它,手动使用,或者将它添加到一个布局中,这样的布局早晚都设置为父级,否则它将是一个顶层窗口。而DOC是面向C++的,它也适用于python:PyQt 99%的参数和返回值都是相同的(请记住,
void
表示函数返回
None
)。虽然有,但并不总是全面或完整的;顺便说一句,您可以将其用作仅在PyQt上可用的专用函数的参考。@thedarklord您是什么意思?给什么尺寸?