正确设置QStackedWidget,信号看不到function/slot

Setting up QStackedWidget properly, signal cannot see function/slot

我正在使用 PyQt5 开发 GUI。这是我进入 OOP 的第一步,我正在努力自学。我正在努力理解 classes 何时继承 methods/attributes 等以及它们可用的方法——我想这是一个与范围相关的问题?我在下面为我的 GUI 制作了一个 MWE。总的来说,会有更多的页面和 signals/slots.

我想要的:

堆栈应初始化为 "MainMenu" widget/object 显示(下图左)。单击 "Next Page" 按钮应切换堆栈顺序以将 "OtherPage" widget/object 放在顶部(下右图)​​。我将每个页面创建为 class,认为这是组织我的项目的好方法。这是好事还是坏事?

现在发生了什么:

如果行 nextPg.clicked.connect(self.drawOtherPage()) 被注释掉,则 GUI 工作(初始化),但是当然,然后单击按钮什么也不做。我可以切换初始堆栈顺序,使 "other" 小部件位于堆栈顶部并且显示正常,因此我认为 class 也可以正常工作。当代码中包含上述行时,会抛出以下错误:

in __init__
     nextPg.clicked.connect(self.drawOtherPage())
AttributeError: 'MainMenu' object has no attribute 'drawOtherPage'

我试过的

我认为对 super() 的调用应该允许子 class(在本例中为 MainMenu)从父 class (RootInit) 继承方法。因此,我认为这应该使 drawOtherPage 方法可用于按钮连接信号。显然,该错误是该方法不可用的结果。

我做错了什么?我应该改为在方法中创建这些 "page" 小部件吗?它们需要位于 RootInit class 下还是可以位于 .py 文件的顶层?我正在尝试遵循最佳实践,因为项目最终会变得非常大。幸运的是,其中大部分应该是根据点击按钮到达那里而有所变化的页面——因此我认为 classes 会有所帮助。请对代码和我的 python/PyQt 白话苛刻,努力学习 -- 谢谢!

import sys, os
from PyQt5.QtWidgets import *
from PyQt5 import QtGui, QtCore

class RootInit(QMainWindow):
    # root window
    def __init__(self, parent=None):
        QMainWindow.__init__(self)
        self.root = QWidget()
        self.stack = QStackedWidget()
        rootLayout = QVBoxLayout()
        rootLayout.addWidget(self.stack)
        self.root.setLayout(rootLayout)
        self.setCentralWidget(self.root)

        self.initializeGUI()

    def initializeGUI(self):
        self.main = MainMenu(self) # build MainMenu (class)
        self.other = OtherPage(self) # build OtherPage (class)
        self.stack.addWidget(self.main)
        self.stack.addWidget(self.other)

    def drawMain(self):
        self.stack.setCurrentIndex(0)

    def drawOtherPage(self):
        self.stack.setCurrentIndex(1)


class MainMenu(QWidget):
    # class for main menu
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        super().__init__()

        mainLayout = QGridLayout() # layout for entire main menu
        quitBtn = QPushButton("Quit")
        quitBtn.clicked.connect(QtCore.QCoreApplication.instance().quit)
        nextPg = QPushButton("Next page")
        nextPg.clicked.connect(self.drawOtherPage())

        mainLayout.addWidget(quitBtn, 0, 0)
        mainLayout.addWidget(nextPg, 0, 1)
        self.setLayout(mainLayout)


class OtherPage(QWidget):
    # class for another menu
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        label = QLabel("test label")
        layout = QGridLayout() #
        layout.addWidget(label, 0, 0)
        self.setLayout(layout)


if __name__ == '__main__':
    app = QApplication(sys.argv)
    root = RootInit()
    root.setWindowTitle("Title")
    root.show()
    sys.exit(app.exec_())

您的代码存在以下错误:

  • 变量 self 指的是 class 的同一个实例,在你的例子中 self 指的是 MainMenu 的一个实例,并且如果我们观察 MainMenu 它没有任何 drawOtherPage() 方法。

  • 你的另一个错误是两次调用父类的构造函数:


class MainMenu(QWidget):
    # class for main menu
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)
        super().__init__()

在第一个构造函数中,您分配了父级,而在第二个中,您没有。要在 python 中澄清,有几种方法可以调用父级的构造函数:

    QWidget.__init__(self, parent)
    super(MainMenu, self).__init__(parent)
    super().__init__(parent)

所以你应该只使用其中之一。

  • 另一个错误是信号是通过函数名连接的,函数不能用括号求值

  • 最后一次使用涉及多个对象的函数或方法应该在两个对象都可以访问的地方完成,在你的情况下你可以利用你将要使用的东西RootInit 作为 MainMenu 的父级:self.main = MainMenu(self),并通过方法 parent().

  • 访问到该元素的连接

以上所有内容都需要将 MainMenu class 修改为以下内容:

class MainMenu(QWidget):
    def __init__(self, parent=None):
        QWidget.__init__(self, parent)

        mainLayout = QGridLayout() # layout for entire main menu
        quitBtn = QPushButton("Quit")
        quitBtn.clicked.connect(QtCore.QCoreApplication.instance().quit)
        nextPg = QPushButton("Next page")
        nextPg.clicked.connect(self.parent().drawOtherPage)

        mainLayout.addWidget(quitBtn, 0, 0)
        mainLayout.addWidget(nextPg, 0, 1)
        self.setLayout(mainLayout)