I'm creating my first application in Python. Currently I'm implementing the first view with PyQt5 and the MVC pattern. Therefore I created a "main" class that is starting the controller by creating a new controller object (The first programming language I learned was Java, so maybe this is not necassary?). In this controller objects init method I'm creating the first view. The view class itself creates a new QWidget. After the QWidget is created the controller is calling a method of the view (QWidget). This method should show the login screen. To show the login screen a new class (Login) is created. That class is of the type QVBoxLayout and is added to the main view (QWidget). This combination leads to an application that is showing a window with the login. This means main class -> controller -> main window (QWidget) -> login (QVBoxLayout).
Here I have the following question: Is it the correct way to create a main window (QWidget) and use methods to add the inner layout to the window (using other files/classes)? Or should everything be written in one class?
Now I reached the point that my layout and window are displayed correctly. What is missing is the model and how the model is invoked. I searched how I can check if a button was pressed. I found button.clicked.connect(callfunction). This seems to be the right way but how can I call this function from the controller? So that the controller creates the application window and displays the login inside this window. Then the controller listens and waits until the button is pressed. Then the inputs will be forwarded to the model and in the model the credentials will be checked. Here is my sourcecode for the controller with my try on listening to the button:
class Controller(object):
def __init__(self):
# Applikation starten und Login anzeigen
app = QApplication(sys.argv)
widget = View()
widget.showLogin(0, "")
widget.loginButton.clicked.connect(self.loginPressed())
sys.exit(app.exec_())
def loginPressed(self):
widget.showLogin(1, "err1")
The code for my Login class:
def __init__(self):
super().__init__()
# Zum vertikalen zentrieren des Inhalts
self.addStretch()
# Label (Bild) erstellen und zum Layout hinzufügen
label = QLabel()
pixmap = QPixmap(pathLogo)
label.setPixmap(pixmap.scaledToWidth(logoWidth, Qt.SmoothTransformation))
label.setAlignment(Qt.AlignCenter)
self.addWidget(label)
# Label für den Nutzername
usernameLabel = QLabel("Username")
usernameLabel.setAlignment(Qt.AlignCenter)
usernameLabel.setStyleSheet("QLabel {color: #ffffff; font-size: 14px; font-weight:bold; "
"margin:50px, 0, 5px, 0 ;}")
self.addWidget(usernameLabel)
# Eingabefeld für den Nutzername
uihbox = QHBoxLayout()
uihbox.addStretch()
usernameInput = QLineEdit()
usernameInput.setFixedWidth(150)
usernameInput.setStyleSheet(
"QLineEdit {border-radius: 5px; padding: 4px; line-height:12px; padding-left: 5px;}")
uihbox.addWidget(usernameInput)
uihbox.addStretch()
self.addLayout(uihbox)
# Label für das Passwort
passwortLabel = QLabel("Passwort")
passwortLabel.setAlignment(Qt.AlignCenter)
passwortLabel.setStyleSheet("QLabel {color: #ffffff; font-size: 14px; font-weight:bold; "
"margin:15px, 0, 5px, 0 ;}")
self.addWidget(passwortLabel)
# Eingabefeld für den Nutzername
pihbox = QHBoxLayout()
pihbox.addStretch()
passwordInput = QLineEdit()
passwordInput.setFixedWidth(150)
passwordInput.setEchoMode(QLineEdit.Password)
passwordInput.setStyleSheet(
"QLineEdit {border-radius: 5px; padding: 4px; line-height:12px; padding-left: 5px;}")
pihbox.addWidget(passwordInput)
pihbox.addStretch()
self.addLayout(pihbox)
# Button erstellen
bihbox = QHBoxLayout()
bihbox.addStretch()
loginButton = QPushButton("Login")
loginButton.setStyleSheet("QPushButton {margin: 25px, 0, 0, 0; border-radius:5px; border: 1px solid white; "
"padding-right: 15px; padding-left: 15px; padding-top: 5px; padding-bottom:5px;"
"color:white; font-weight:bold; font-size: 14px;}")
loginButton.setCursor(QCursor(Qt.PointingHandCursor))
bihbox.addWidget(loginButton)
bihbox.addStretch()
self.addLayout(bihbox)
# Zum vertikalen zentrieren des Inhalts
self.addStretch()
def showError(self, errCode):
errMsg = QLabel(err1)
errMsg.setAlignment(Qt.AlignCenter)
errMsg.setStyleSheet("QLabel {color:red;}")
self.addWidget(errMsg)
self.addStretch()
And my View class:
class View(QWidget):
# Methode um das Fenster der Applikation zu initialisieren
def __init__(self):
super().__init__()
# Breite und Höhe setzen
self.resize(initWidth, initHeight)
# Titel und Icon setzen
self.setWindowTitle(appTitle)
self.setWindowIcon(QIcon(pathFavicon))
# Hintergrund mit der bgColor füllen
self.setAutoFillBackground(True)
p = self.palette()
p.setColor(self.backgroundRole(), QColor(bgColor))
self.setPalette(p)
# Anzeigen des Fensters
self.show()
# Methode, um Login zu zeigen
def showLogin(self, err, errcode):
# Laden des Inhalts mittels Login
if (err != 0):
vbox = Login()
vbox.showError(errcode)
if (err == 0):
vbox = Login()
self.setLayout(vbox)
Further questions:
QVBoxLayout with an error code to display an error at the bottom of the view. I have not found a way to dynamically change the view from the controller class. The only solution I could imagine is that I "repaint" the content of the QWidget with the added error message. Is this the correct solution?Thanks in advance!
The idea is not the number of classes, but what is the task of each class.
So do you think your classes implement the above?
In the following example I show how those tasks are redistributed, my example does not meet all the classic MCV rules like the one pointed out by @101 because the GUIs have an internal event handler.
import sys
from functools import partial
from PyQt5 import QtCore, QtGui, QtWidgets
class Model:
def __init__(self):
self.username = ""
self.password = ""
def verify_password(self):
return self.username == "USER" and self.password == "PASS"
class View(QtWidgets.QWidget):
verifySignal = QtCore.pyqtSignal()
def __init__(self):
super(View, self).__init__()
self.username = ""
self.password = ""
self.initUi()
def initUi(self):
lay = QtWidgets.QVBoxLayout(self)
title = QtWidgets.QLabel("<b>LOGIN</b>")
lay.addWidget(title, alignment=QtCore.Qt.AlignHCenter)
fwidget = QtWidgets.QWidget()
flay = QtWidgets.QFormLayout(fwidget)
self.usernameInput = QtWidgets.QLineEdit()
self.usernameInput.textChanged.connect(partial(setattr, self, "username"))
self.passwordInput = QtWidgets.QLineEdit(echoMode=QtWidgets.QLineEdit.Password)
self.passwordInput.textChanged.connect(partial(setattr, self, "password"))
self.loginButton = QtWidgets.QPushButton("Login")
self.loginButton.clicked.connect(self.verifySignal)
flay.addRow("Username: ", self.usernameInput)
flay.addRow("Password: ", self.passwordInput)
flay.addRow(self.loginButton)
lay.addWidget(fwidget, alignment=QtCore.Qt.AlignHCenter)
lay.addStretch()
def clear(self):
self.usernameInput.clear()
self.passwordInput.clear()
def showMessage(self):
messageBox = QtWidgets.QMessageBox(self)
messageBox.setText("your credentials are valid\n Welcome")
messageBox.exec_()
self.close()
def showError(self):
messageBox = QtWidgets.QMessageBox(self)
messageBox.setText("your credentials are not valid\nTry again...")
messageBox.setIcon(QtWidgets.QMessageBox.Critical)
messageBox.exec_()
class Controller:
def __init__(self):
self._app = QtWidgets.QApplication(sys.argv)
self._model = Model()
self._view = View()
self.init()
def init(self):
self._view.verifySignal.connect(self.verify_credentials)
def verify_credentials(self):
self._model.username = self._view.username
self._model.password = self._view.password
self._view.clear()
if self._model.verify_password():
self._view.showMessage()
else:
self._view.showError()
def run(self):
self._view.show()
return self._app.exec_()
if __name__ == '__main__':
c = Controller()
sys.exit(c.run())
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With