Essa postagem é na verdade a tradução de uma postagem que esta localizada em https://stackoverflow.com/questions/26698628/mvc-design-with-qt-designer-and-pyqt-pyside.
Como achei muito util resolvi traduzir e postar aqui, todos os creditos ao criador da postagem.
Em primeiro lugar, esteja ciente de que o Qt já usa o conceito de Views e Models, que em suma, é uma maneira de vincular automaticamente um widget (por exemplo, um QListView) a uma fonte de dados (por exemplo, um QStringListModel) para que as alterações nos dados do model apareçam automaticamente no widget e vice-versa. Esse é um recurso útil, mas diferente do design do MVC na escala de aplicativos, embora os dois possam ser usados juntos e ofereçam alguns atalhos óbvios. No entanto, o design MVC em escala de aplicativo deve ser programado manualmente.
Aqui está um exemplo de aplicativo MVC que possui uma única view, controller e model. A view possui 3 widgets que cada um escuta independentemente e reage a alterações nos dados no model. A caixa de rotação e o botão podem manipular dados no modelo através do controlador.
A estrutura dos arquivos sera a seguinte:
project/
mvc_app.py # Aplicação principal com a classe App
mvc_app_rc.py # arquivo de resources gerado automaticamente (usando pyrcc.exe ou algo equivalente)
controllers/
main_ctrl.py # controller principal com a classe MainController
other_ctrl.py
model/
model.py # model with Model class
resources/
mvc_app.qrc # arquivos de resource do qt
main_view.ui # arquivos gerados pelo qt designer
other_view.ui
img/
icon.png
views/
main_view.py # view principal com a classe MainView
main_view_ui.py # auto-generated ui file (using pyuic.exe or equivalent)
other_view.py
other_view_ui.py
Application
mvc_app.py
irá ser responsável por instanciar cada uma das views, contollers e models e passar referências entre eles. Isso pode ser bastante basíco: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_controller)
self.main_view.show()
if __name__ == '__main__':
app = App(sys.argv)
sys.exit(app.exec_())
views
Use o Qt designer para criar os arquivos de layout .ui na medida em que designe nomes de variáveis para widgets e ajuste suas propriedades básicas. Não se preocupe em adicionar sinais ou slots, pois geralmente é mais fácil conectá-los a funções da classe view.
Os arquivos de layout .ui são convertidos em arquivos de layout .py quando processados com pyuic ou pyside-uic. Os arquivos de exibição .py podem importar as classes geradas automaticamente relevantes dos arquivos de layout .py.
As classes de exibição devem conter o código mínimo necessário para conectar-se aos sinais provenientes dos widgets em seu layout. Os eventos de exibição podem chamar e passar informações básicas para um método na classe de exibição e para um método em uma classe de controller, onde qualquer lógica deve estar. Seria algo como:
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)
# conecta o widget ao 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))
# ouve sinais de eventos do model
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)
# atriui um valor padrão
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)
A view não faz muito além dos eventos do widget de link com a função relevante do controlador e escuta as alterações no modelo, que são emitidas como sinais Qt.
Controllers
As classes do controlador executam qualquer lógica e, em seguida, configuram os dados no model. exemplo:
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
# calcular par ou ímpar
self._model.even_odd = 'odd' if value % 2 else 'even'
# verifica o estado de ativação do botão
self._model.enable_reset = True if value else False
A fução
change_amount
pega o novo valor do widget, executa a lógica e define atributos no model.Model
A classe model armazena dados e estado do programa e alguma lógica mínima para anunciar alterações nesses dados. Este modelo não deve ser confundido com o modelo Qt (Veja: http://qt-project.org/doc/qt-4.8/model-view-programming.html) como não é realmente a mesma coisa.
O modelo pode parecer com isso:
from PyQt5.QtCore import QObject, pyqtSignal
class Model(QObject):
amount_changed = pyqtSignal(int)
even_odd_changed = pyqtSignal(str)
enable_reset_changed = pyqtSignal(bool)
@property
def amount(self):
return self._amount
@amount.setter
def amount(self, value):
self._amount = value
self.amount_changed.emit(value)
@property
def even_odd(self):
return self._even_odd
@even_odd.setter
def even_odd(self, value):
self._even_odd = value
self.even_odd_changed.emit(value)
@property
def enable_reset(self):
return self._enable_reset
@enable_reset.setter
def enable_reset(self, value):
self._enable_reset = value
self.enable_reset_changed.emit(value)
def __init__(self):
super().__init__()
self._amount = 0
self._even_odd = ''
self._enable_reset = False
As alterações no modelo emitem automaticamente sinais para quaisquer views de escuta via código na função decoradora
setter
. Como alternativa, o controlador pode disparar manualmente o sinal sempre que ele quiser.
No caso em que os tipos de model padrões do Qt (por exemplo, QStringListModel) foram conectados a um widget, a visualização que contém esse widget não precisa ser atualizada; isso acontece automaticamente através do Qt.
arquivo de codigo UI
Para conclusão, o exemplo o arquivo
main_view.ui
está incluído aqui:<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>93</width>
<height>86</height>
</rect>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QVBoxLayout">
<item>
<widget class="QSpinBox" name="spinBox_amount"/>
</item>
<item>
<widget class="QLabel" name="label_even_odd"/>
</item>
<item>
<widget class="QPushButton" name="pushButton_reset">
<property name="enabled">
<bool>false</bool>
</property>
</widget>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
</ui>
Ele deve ser convertido para
main_view_ui.py
com o comando:pyuic5 main_view.ui -o ..\views\main_view_ui.py
O arquivo de resource
mvc_app.qrc
deve ser convertido para mvc_app_rc.py
com o comando:pyrcc5 mvc_app.qrc -o ..\mvc_app_rc.py
0 Comentários