pyqt5中windows之间来回切换同时避免循环导入

骨灰

所以我有 2 个窗口我希望能够在LoginMainWindow之间切换,每个窗口分别QWidget位于自己的单独文件loginUI.pyMainUI.py中。

通过创建 MainWindow 的新实例,我可以在正确的身份验证后轻松地从登录切换到主。但在 MainWindow 中,我想要一个显示登录屏幕的“断开连接”按钮。

由于两者都在不同的文件中,因此在这种情况下导入会在 python 中引发循环导入错误。

我尝试的其他方法是:

  1. 使用信号并在中间文件中处理它们。这很好,但是当我添加更多按钮/窗口时,文件开始变得有点混乱。

  2. 将 Login 的实例传递给MainWindow.__init__(self, login),然后只使用self.login.show(). 这似乎是一个好方法,但是当我添加越来越多的窗口时,我担心它可能会影响性能,因为很多实例只是在后台运行。

这些是正确的方法还是我错过了更简单的方法


编辑:

  • login.py

    from PyQt5.QtWidgets import QWidget, QPushButton, QLineEdit
    from PyQt5.QtCore import QSize 
    from mainmenu import MainWindow
    from PyQt5 import QtWidgets
    import sys
    
    class Login(QWidget):
        def __init__(self):
            QWidget.__init__(self)
    
            self.setMinimumSize(QSize(300, 200))    
            self.setWindowTitle("Log in") 
    
            self.username = QLineEdit(self)
            self.username.move(50, 10)
            self.password = QLineEdit(self)
            self.password.move(50, 40)
            self.connect_button = QPushButton('Connect', self)
            self.connect_button.move(50, 100)
            self.connect_button.clicked.connect(self.handleConnexion)      
    
        def handleConnexion(self):
            if self.username.text() == "admin" and self.password.text()=="1":
                self.mw = MainWindow()
                self.mw.show()
                self.close()
                
    if __name__ == "__main__":
        app = QtWidgets.QApplication(sys.argv)
        mainWin = Login()
        mainWin.show()
        sys.exit( app.exec_() )

  • mainmenu.py

    from PyQt5.QtCore import QSize   
    from PyQt5.QtWidgets import QPushButton
    from PyQt5.QtWidgets import QMainWindow
    
    
    class MainWindow(QMainWindow):
        def __init__(self):
            QMainWindow.__init__(self)
    
            self.setMinimumSize(QSize(300, 200))    
            self.setWindowTitle("Main menu") 
    
            disconnect_button = QPushButton('Disconnect', self)
            disconnect_button.clicked.connect(self.handeDC)
    
        def handeDC(self):
            pass
            # here I either send a signal to handle it somewhere else
            # or 
            # if i pass the login instance in __init__, just do login.show()

音乐家

注意:由于这是一个常见问题,我将提供更广泛的答案,以更好地反映对象层次结构。

由于“主”窗口,顾名思义,是窗口,包含它的脚本应该是窗口,而登录窗口应该被导入并最终根据需要显示。

层次结构很重要:您不必考虑窗口的显示顺序,而是它们的相关性。

考虑到这一点,主脚本将:

  • 创建主窗口;
  • 如果需要,显示登录名;
  • 如果登录成功则显示主窗口;
  • 如果用户断开连接,再次显示登录信息;
  • 如果用户已更改,则清除内容(*见下文);

以上还说明了为什么不断创建新的窗口实例很少是一个好主意。

登录窗口也应该是一个QDialog,这让事情变得更简单:exec()方法是“阻塞”(用于函数执行,而不是用于事件循环),并等待对话框被接受或*拒绝。

main.py

from PyQt5.QtWidgets import *
from login import Login

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.setMinimumSize(300, 200)
        self.setWindowTitle('Main menu')

        disconnect_button = QPushButton('Disconnect')
        self.setCentralWidget(disconnect_button)

        # only for a *persistent* login dialog (*see below)
        self.login = Login()

        disconnect_button.clicked.connect(self.disconnect)

    def start(self):
        # put here some function that might check for a 'previous' logged in
        # state, possibly stored using QSettings.
        # in this case, we just assume that the user has never previously
        # logged in, so we automatically show the login window; if the above
        # function returns True instead, we can safely show the main window
        logged = False

        if logged:
            self.show()
        else:
            self.showLogin()

    def disconnect(self):
        self.hide()
        self.showLogin()

    def showLogin(self):
        if self.login.exec():
            self.show()

        # alternatively (*see below):
        login = Login()
        if login.exec():
            self.show()


if __name__ == '__main__':
    import sys
    app = QApplication(sys.argv)
    mainWindow = MainWindow()
    mainWindow.start()
    sys.exit(app.exec())

login.py

from PyQt5.QtWidgets import *

class Login(QDialog):
    def __init__(self, parent=None):
        super().__init__(parent)

        self.setMinimumSize(300, 200)
        self.setWindowTitle('Log in')

        self.username = QLineEdit()
        self.password = QLineEdit()
        self.password.setEchoMode(self.password.Password)
        self.connect_button = QPushButton('Connect', enabled=False)

        layout = QGridLayout(self)
        layout.addWidget(QLabel('Username:'), 0, 0)
        layout.addWidget(self.username, 0, 1)
        layout.addWidget(QLabel('Password:'), 1, 0)
        layout.addWidget(self.password, 1, 1)
        layout.addWidget(self.connect_button, 2, 0, 1, 2)

        self.connect_button.clicked.connect(self.handleConnexion)
        self.username.textChanged.connect(self.checkFields)
        self.password.textChanged.connect(self.checkFields)

    def checkFields(self):
        if self.username.text() and self.password.text():
            self.connect_button.setEnabled(True)
        else:
            self.connect_button.setEnabled(False)

    def handleConnexion(self):
        if self.username.text() == 'admin' and self.password.text() == '1':
            self.accept()
        else:
            QMessageBox.warning(self, 'Error', 
                'Invalid username or password!')

笔记:

  • 上面的代码将始终显示现有Login实例,因此用户名和密码字段将“记住”以前的条目;如果您不希望这样,您可以随时clear()通过覆盖来调用这些字段exec()(但请记住调用基本实现并返回其结果!);或者,不要创建inself.login并始终创建一个新的本地实例Login()in showLogin()
  • 您应始终使用布局管理器,并且永远不要依赖固定的几何图形;
  • QMainWindow 应该始终有一个中央小部件,不鼓励使用它作为父窗口创建主窗口的子窗口(除非您真的知道自己在做什么);如果您需要更多小部件,请使用基本的 QWidget,为其设置布局,添加孩子,最后调用setCentralWidget()
  • 更复杂的层次结构可能需要一个“控制器”(阅读有关MVC 模式的更多信息)来更好地组织整个程序并尊重 OOP 模式;这通常是通过一个基本类或通过继承 QApplication 来实现的;
  • 关于我的初始列表中的最后一个(*)点,并且与上面解释的内容相关:“控制器”可以/应该完全删除以前的“主窗口”(这将更容易和更安全)并最终显示一个新实例用户已断开连接;

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章