在MainWindow中替换CentralWidget

7
我有点新手,对于PySide不是很熟悉。我有一个主窗口对象,一次只显示一个widget。我一直在尝试更改QMainWindow类的中央widget,以便在按下按钮时替换窗口中可见的widget。问题在于按下的按钮位于Widget类中,而不是主窗口类中。
class App(QtGui.QMainWindow):

    def __init__(self):
        super(App, self).__init__()

        self.initUI()

    def initUI(self):

        self.statusBar().showMessage('Listo.') #Status Bar
        self.login_screen = LoginScreen()
        self.logged_in_screen = LoggedInScreen()

        self.setCentralWidget(self.login_screen)

        self.setGeometry(300, 300, 450, 600) #Window Size
        self.setWindowTitle('PyTransactio - Client') #Window Title
        self.setWindowIcon(QtGui.QIcon('icon.png')) #App Icon
        self.show()

按下的按钮位于login_screen实例中。当单击该按钮时调用的方法位于LoginScreen类中:

def login(self):
        """ Send login data to the server in order to log in """

        #Process

        self.setParent(None)

将父窗口小部件设置为None会将小部件(login_screen)从主窗口中移除。当按下loginButton(在login_screen小部件内部)时,我应该怎么做才能获得另一个小部件(例如logged_in_screen)作为主窗口的中心小部件呢?
也许登录方法应该在主窗口类中?如果是这样,我如何将在login_screen中按下的按钮与主窗口的方法连接起来?
2个回答

17
您可以使用QStackedWidget作为中央窗口部件,并将登录屏幕和“已登录”屏幕添加到其中。
一个示例用法:
from PyQt4 import QtCore, QtGui


class MainWindow(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MainWindow, self).__init__(parent)
        self.central_widget = QtGui.QStackedWidget()
        self.setCentralWidget(self.central_widget)
        login_widget = LoginWidget(self)
        login_widget.button.clicked.connect(self.login)
        self.central_widget.addWidget(login_widget)
    def login(self):
        logged_in_widget = LoggedWidget(self)
        self.central_widget.addWidget(logged_in_widget)
        self.central_widget.setCurrentWidget(logged_in_widget)


class LoginWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(LoginWidget, self).__init__(parent)
        layout = QtGui.QHBoxLayout()
        self.button = QtGui.QPushButton('Login')
        layout.addWidget(self.button)
        self.setLayout(layout)
        # you might want to do self.button.click.connect(self.parent().login) here


class LoggedWidget(QtGui.QWidget):
    def __init__(self, parent=None):
        super(LoggedWidget, self).__init__(parent)
        layout = QtGui.QHBoxLayout()
        self.label = QtGui.QLabel('logged in!')
        layout.addWidget(self.label)
        self.setLayout(layout)



if __name__ == '__main__':
    app = QtGui.QApplication([])
    window = MainWindow()
    window.show()
    app.exec_()

如果您不想使用这个小部件,那么我认为每次更改中央小部件时,您将不得不调用QMainWindow.setCentralWidget

至于login方法应该在哪里,这取决于情况。可能您可以为主窗口定义一个简单的接口,以添加/删除/显示特定的中央小部件,并从LoginScreenlogin方法中调用它。这样,LoginScreen类就不必了解实现细节,比如中央小部件是否实际上是一个QStackedWidget,或者这个东西是以其他方式完成的。


1
QStackedWidget完成了它的任务。而且,我从来没有想过self.parent().login。看来我得在面向对象编程方面多加努力。非常感谢! - saxarona
当将一个窗口小部件设置为主窗口小部件时,QMainWindow 将拥有该窗口小部件。当将另一个窗口小部件设置为主窗口小部件时,第一个窗口小部件将被销毁,这是不好的。 - Hrvoje T
@HrvojeT 是的,这就是为什么我的答案使用了 QStackedWidget 而不是那个 hack,后者可能需要一些丑陋的代码才能正常工作。 - Bakuriu
非常棒。使用 setCentralWidget 对我无效,但是这个方法有效,因为它不会销毁先前的小部件。 - Manoel Vilela

-3
你可以使用 QMainWindow.setCentralWidget 来实现这个功能(可以重复使用):
#! /usr/bin/env python3

from PySide import QtGui
from PySide import QtCore

import sys 

app = QtGui.QApplication(sys.argv)
mw = QtGui.QMainWindow()
w2 = QtGui.QWidget()
pb = QtGui.QPushButton('push me', w2) 

l1 = QtGui.QLabel('orig')
l2 = QtGui.QLabel('changed')
mw.setCentralWidget(l1)
pb.clicked.connect(lambda: mw.setCentralWidget(l2))
mw.show()
w2.show()
sys.exit(app.exec_())

我知道这是一个可能的解决方法,但销毁最后一个central_widget会产生可怕的副作用... - Manoel Vilela
当您设置一个新的中央小部件时,旧的小部件将被销毁。 - Erick

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接