[英]Switch back and forth between windows in pyqt5 while avoiding circular imports
所以我有 2 個我希望能夠在Login和MainWindow之間切換的窗口,每個窗口都是QWidget
在它自己的單獨文件loginUI.py和MainUI.py中。
通過創建 MainWindow 的新實例,我可以在正確的身份驗證后輕松地從登錄切換到主。 但在 MainWindow 中,我想要一個顯示登錄屏幕的“斷開連接”按鈕。
由於兩者都在不同的文件中,因此在這種情況下導入會在 python 中引發循環導入錯誤。
我嘗試的其他方法是:
使用信號並在中間文件中處理它們。 這很好,但是當我添加更多按鈕/窗口時,文件開始變得有點混亂。
將 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()
在創建MainWindow
小部件時不必關閉Login
小部件,您可以只隱藏該小部件,這將節省創建新實例的開銷並保持連接的插槽完好無損。
然后在您的MainWindow
上,您可以創建一個diconnected
連接的信號,當用戶單擊斷開連接按鈕時應該發出該信號。
登錄窗口可以監聽信號並調用它的show
方法。
我在下面的示例中進行了更改的地方做了內聯注釋:
主窗口.py
from PyQt5.QtCore import pyqtSignal # added this
class MainWindow(QMainWindow):
disconnected = pyqtSignal() # added this
def __init__(self):
QMainWindow.__init__(self)
...
disconnect_button = QPushButton('Disconnect', self)
disconnect_button.clicked.connect(self.handeDC)
def handeDC(self):
# ... do some stuff
self.disconnected.emit() # added this
登錄.py
class Login(QWidget):
def __init__(self):
QWidget.__init__(self)
...
def handleConnexion(self):
if self.username.text() == "admin" and self.password.text()=="1":
self.mw = MainWindow()
self.mw.disconnected.connect(self.show) # added this
self.mw.show()
self.hide() # changed this
...
注意:由於這是一個常見問題,我將提供更廣泛的答案,以更好地反映對象層次結構。
由於“主”窗口顧名思義是主窗口,因此包含它的腳本應該是主窗口,而登錄窗口應該被導入並最終根據需要顯示。
層次結構很重要:您不必考慮窗口的顯示順序,而是它們的相關性。
考慮到這一點,主腳本將:
以上還說明了為什么不斷創建新的窗口實例很少是一個好主意。
登錄窗口也應該是一個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
實例,因此用戶名和密碼字段將“記住”以前的條目; 如果你不想這樣,你總是可以通過覆蓋exec()
clear()
) (但記得調用基本實現並返回它的結果!); 或者,不要創建self.login
並始終在showLogin()
中創建一個新的本地Login()
實例;setCentralWidget()
;
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.