Дизайн MVC с Qt Designer и PyQt / PySide

новичок Python, поступающий из Java (+SWT/Windowbuilder), и мне трудно понять, как правильно кодировать большое настольное приложение в Python/Qt4 (QtDesigner)/PySide.

Я хотел бы сохранить любую логику представления в классе контроллера вне .ui-файл (и это .py conversion). Во-первых, как тогда логика не зависит от GUI framework и, во-вторых, как .ui и результирующий .файл py перезаписывается при любых изменениях!.

только примеры, которые я нашел добавить код действия в монолитный MainWindow.py (генерируется из пользовательского интерфейса) или MyForm.py (также генерируется из .пользовательский интерфейс.) Я не вижу способа связать класс контроллера POPO с действиями в QtDesigner.

может ли кто-нибудь указать мне рабочие процессы для создания крупномасштабного приложения с использованием QtDesigner в масштабируемой методологии MVC/P?

1 ответов


во-первых, просто имейте в виду, что Qt уже использует концепцию представлений и моделей, но на самом деле это не то, что вам нужно. Короче говоря, это способ автоматически связать виджет (например, QListView) с источником данных (например, QStringListModel), чтобы изменения данных в модели автоматически появлялись в виджете и наоборот. Это полезная функция, но она отличается от дизайна MVC масштаба приложения, хотя они могут использоваться вместе, и она предлагает некоторые очевидные ярлыки. Однако масштаб приложения MVC-дизайн должен быть запрограммирован вручную.

вот пример приложения MVC, которое имеет один вид, контроллер и модель. В представлении есть 3 виджета, каждый из которых независимо прослушивает и реагирует на изменения данных в модели. Коробка и кнопка закрутки могут оба манипулировать данными в модели через регулятор.

mvc_app

файловая структура устроена следующим образом это:

project/
    mvc_app.py              # main application with App class
    mvc_app_rc.py           # auto-generated resources file (using pyrcc.exe or equivalent)
    controllers/
        main_ctrl.py        # main controller with MainController class
        other_ctrl.py
    model/
        model.py            # model with Model class
    resources/
        mvc_app.qrc         # Qt resources file
        main_view.ui        # Qt designer files
        other_view.ui
        img/
            icon.png
    views/
        main_view.py        # main view with MainView class
        main_view_ui.py     # auto-generated ui file (using pyuic.exe or equivalent)
        other_view.py
        other_view_ui.py

приложение

mvc_app.py будет отвечать за создание экземпляров каждого представления, контроллеров и моделей и передачу ссылок между ними. Это может быть совсем минимальным:

import sys
from PyQt5.QtWidgets import QApplication
from model.model import Model
from controllers.main_ctrl import MainController
from views.main_view import MainView


class App(QApplication):
    def __init__(self, sys_argv):
        super(App, self).__init__(sys_argv)
        self.model = Model()
        self.main_controller = MainController(self.model)
        self.main_view = MainView(self.model, self.main_ctrl)
        self.main_view.show()


if __name__ == '__main__':
    app = App(sys.argv)
    sys.exit(app.exec_())

вид

используйте Qt designer для создания .файлы макета пользовательского интерфейса в той мере, в какой вы назначаете виджетам имена переменных и настраиваете их основные свойства. Не беспокойтесь о добавлении сигналов или слотов, как правило, проще просто подключить их функции из класса view.

The .файлы макета пользовательского интерфейса преобразуются .файлы макета py при обработке pyuic или pyside-uic. То .затем файлы py view могут импортировать соответствующие автоматически сгенерированные классы из.файлы макета py.

класс(ы) представления должен содержать минимальный код, необходимый для подключения к сигналам, поступающим от виджетов в вашем макете. События View могут вызывать и передавать основную информацию методу в классе view и методу в класс контроллера, где должна быть любая логика. Это будет выглядеть примерно так:

from PyQt5.QtWidgets import QMainWindow
from PyQt5.QtCore import pyqtSlot
from views.main_view_ui import Ui_MainWindow


class MainView(QMainWindow):
    def __init__(self, model, main_controller):
        super().__init__()

        self._model = model
        self._main_controller = main_controller
        self._ui = Ui_MainWindow()
        self._ui.setupUi(self)

        # connect widgets to controller
        self._ui.spinBox_amount.valueChanged.connect(self._main_controller.change_amount)
        self._ui.pushButton_reset.clicked.connect(lambda: self._main_controller.change_amount(0))

        # listen for model event signals
        self._model.amount_changed.connect(self.on_amount_changed)
        self._model.even_odd_changed.connect(self.on_even_odd_changed)
        self._model.enable_reset_changed.connect(self.on_enable_reset_changed)

        # set a default value
        self._main_controller.change_amount(42)

    @pyqtSlot(int)
    def on_amount_changed(self, value):
        self._ui.spinBox_amount.setValue(value)

    @pyqtSlot(str)
    def on_even_odd_changed(self, value):
        self._ui.label_even_odd.setText(value)

    @pyqtSlot(bool)
    def on_enable_reset_changed(self, value):
        self._ui.pushButton_reset.setEnabled(value)

представление не делает много, кроме событий виджета ссылки на соответствующую функцию контроллера, и прослушивает изменения в модели, которые излучаются как сигналы Qt.

контроллеры

класс(ы) контроллера выполняет любую логику, а затем устанавливает данные в модели. Пример:

from PyQt5.QtCore import QObject, pyqtSlot


class MainController(QObject):
    def __init__(self, model):
        super().__init__()

        self._model = model

    @pyqtSlot(int)
    def change_amount(self, value):
        self._model.amount = value

        # calculate even or odd
        self._model.even_odd = 'odd' if value % 2 else 'even'

        # calculate button enabled state
        self._model.enable_reset = True if value else False

на