Python - PyQt5 - MVC - PullRequest
       27

Python - PyQt5 - MVC

0 голосов
/ 17 сентября 2018

Я создаю свое первое приложение на Python.В настоящее время я реализую первое представление с PyQt5 и паттерном MVC.Поэтому я создал «основной» класс, который запускает контроллер путем создания нового объекта контроллера (первым языком программирования, который я выучил, была Java, так что, возможно, это не обязательно?).В этом методе init объекта контроллера я создаю первое представление.Класс представления сам создает новый QWidget.После создания QWidget контроллер вызывает метод представления (QWidget).Этот метод должен показать экран входа в систему.Для отображения экрана входа в систему создается новый класс (Логин).Этот класс имеет тип QVBoxLayout и добавляется к основному виду (QWidget).Эта комбинация приводит к приложению, которое показывает окно с логином.Это означает основной класс -> контроллер -> главное окно (QWidget) -> логин (QVBoxLayout).

Здесь у меня возникает следующий вопрос: это правильный способ создания главного окна (QWidget) и использовать методы для добавления внутреннего макета в окно (используя другие файлы / классы)?Или все должно быть написано в одном классе?

Теперь я дошел до того, что мой макет и окно отображаются правильно.Чего не хватает, так это модели и того, как она вызывается.Я искал, как я могу проверить, была ли нажата кнопка.Я нашел button.clicked.connect(callfunction).Кажется, это правильный путь, но как я могу вызвать эту функцию из контроллера?Так что контроллер создает окно приложения и отображает логин внутри этого окна.Затем контроллер слушает и ждет, пока кнопка не будет нажата.Затем входные данные будут перенаправлены в модель, а в модели будут проверены учетные данные.Вот мой исходный код для контроллера с моей попыткой прослушивания кнопки:

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")

Код для моего класса входа в систему:

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()

И мой класс просмотра:

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)

Дополнительные вопросы:

  • Верно ли мое понимание шаблона MVC или я использую его для многих классов?
  • Должен ли я слушать кнопку в контроллере илиэто правильно, что представление вызывает метод в контроллере?
  • Далее я реализовал, что могу вызвать логин QVBoxLayout с кодом ошибки, чтобы отобразить ошибку в нижней части представления.Я не нашел способ динамически изменить представление из класса контроллера.Единственное решение, которое я могу себе представить, это то, что я «перекрашиваю» содержимое QWidget с добавленным сообщением об ошибке.Это правильное решение?

Заранее спасибо!

Ответы [ 2 ]

0 голосов
/ 21 июня 2019

Чтобы дополнить сказанное выше @eyllanesc и еще немного абстрагировать его, я использовал модель MVC для обработки практически всего, что связано с представлением информации пользователю. Таким образом, мой тип MVC также включает в себя некоторые другие модели, которые были впоследствии получены из MVC, и просто смотрите на все это как на разные вкусы MVC, иначе говоря, если его мороженое - мороженое, независимо от того, какие ароматизаторы вы добавили в него.

Во-первых, он был в основном разработан для обработки подключения к базе данных, при этом Модель - это база данных, а View - это графический интерфейс, а Controller - промежуточный уровень, который обрабатывал поток из базы данных в графический интерфейс и из графического интерфейса в базу данных. Однако в моей версии промежуточный уровень стал рабочей лошадкой процесса, поскольку он обрабатывает всю проверку данных, а также применяет любую бизнес-логику, которую необходимо применить к данным, независимо от того, в каком направлении они передаются.

После всего сказанного мы можем сделать шаг назад и посмотреть на приложение Python / PyQt следующим образом:

Модель = Back-End для управления данными, их хранения и / или производства (язык данных - SQL, C и т. Д.)

Контроллер = Среднего уровня обрабатывает проверку данных и применение большинства бизнес-правил Примечание: как я уже говорил выше, я немного отклоняюсь от традиционной версии в том, что я смещаю правила проверки и бизнес-правила в этот раздел, и я делаю это, потому что это означает, что данные затем просто становятся данными, и я могу применить это к вещам, которые не смогут реализовать проверку или правила, такие как последовательные порты низкого уровня и тому подобное. Конечно, если серверная часть является базой данных, в ней могут быть реализованы некоторые правила для данных, которые могут быть аналогичны бизнес-правилам, но в этом варианте они должны быть сведены к минимуму и больше касаться распространения данных и тому подобного. (Только Python)

View = GUI Front-End обрабатывает представление данных пользователю. Обратите внимание, что иногда здесь может происходить некоторая более базовая проверка данных, например, если она только числовая, убедитесь, что она только числовая. (Python / PyQt в основном последний)

Каждый из этих объектов должен быть создан таким образом, чтобы он был в значительной степени независим от другого, а интерфейс полностью отделен от интерфейса. Это означает, что если бы я хотел заменить Средний уровень, я должен был бы сделать это почти без проблем и без необходимости вносить изменения во Front-End или Back-End, и то же самое касается и Front-End, и Back. - Если я заменил их чем-то другим и установил те же связи со средним уровнем, что и раньше, то больше ничего не должно быть затронуто. Конечно, это не всегда происходит, потому что иногда есть изменения, которые затрагивают все 3 уровня, но тогда они все равно могут быть выполнены независимо, все, что должно быть согласовано, это то, что отправляется / принимается из графического интерфейса пользователя и серверной части и / или Средний уровень.

В Front-End это может быть реализовано во время кодирования, не устанавливая соединение при выполнении front-end, вы просто вызываете функцию-заполнитель (которая в конечном итоге будет заменена на промежуточный уровень), и вся эта функция делает это поставка данных, которые будут (или должны) поступить из среднего уровня. Со стороны GUI вас не волнует, как эти данные оказались в том формате, в котором они есть, и все, что вам нужно знать, это то, как они будут предоставлены. То же самое касается отправки данных, которые возвращают данные, просто создайте фиктивную функцию для внешнего интерфейса, который имитирует ситуацию. Таким образом, вы сосредоточены на том, чтобы просто представлять данные пользователю и получать ввод от пользователя, и это все, и помните, прежде всего, K.I.S.S. это - (Держите это Простым и Умным)

0 голосов
/ 17 сентября 2018

Идея заключается не в количестве классов, а в том, что является задачей каждого класса.

  • Модель - это объект, который хранит информацию.
  • Представление - это объект, который отображает информацию.
  • И контроллер - это тот, кто управляет потоком данных о зрении в соответствии с определенной логикой.

Так как вы думаете, ваши классы реализуют вышеупомянутое?

В следующем примере я показываю, как эти задачи перераспределяются, мой пример не соответствует всем классическим правилам MCV, подобным тем, на которые указывает @ 101 , поскольку в графических интерфейсах есть обработчик внутренних событий.

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())
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...