python – 使用Qt Designer和PyQt / PySide进行MVC设计
作者:互联网
Python新手来自Java(SWT / Windowbuilder),我很难解决如何在Python / Qt4(QtDesigner)/ PySide中正确编写大型桌面应用程序的问题.
我想在.ui文件之外的控制器类中保留任何视图逻辑(并且它是.py转换).首先,逻辑独立于GUI框架,其次是.ui和结果.py文件在任何更改时都会被覆盖!
我发现只有一些例子将动作代码添加到单片MainWindow.py(从ui生成)或MyForm.py(也是从.ui生成)中.我看不到任何方法将POPO控制器类链接到QtDesigner中的操作.
有人能指出我在可扩展的MVC / P方法中使用QtDesigner创建大规模应用程序的工作流程吗?
解决方法:
首先,请注意Qt已经使用了视图和模型的概念,但实际上并不是你所追求的.简而言之,这是一种自动将小部件(例如QListView)链接到数据源(例如QStringListModel)的方式,以便模型中的数据的变化自动地出现在小部件中,反之亦然.这是一个有用的功能,但它与应用程序规模的MVC设计是不同的,虽然这两个可以一起使用,它确实提供了一些明显的快捷方式.然而,应用规模MVC设计必须手动编程.
这是一个示例MVC应用程序,它具有单个视图,控制器和模型.该视图有3个小部件,每个小部件独立监听并响应模型中数据的更改.旋转框和按钮都可以通过控制器操纵模型中的数据.
文件结构的安排如下:
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_controller)
self.main_view.show()
if __name__ == '__main__':
app = App(sys.argv)
sys.exit(app.exec_())
查看
使用Qt设计器创建.ui布局文件,以便将变量名称分配给窗口小部件并调整其基本属性.不要费心添加信号或插槽,因为通常更容易将它们连接到视图类中的函数.
使用pyuic或pyside-uic处理时,.ui布局文件将转换为.py布局文件.然后,.py视图文件可以从.py布局文件中导入相关的自动生成的类.
视图类应包含连接到布局中小部件的信号所需的最小代码.视图事件可以调用并将基本信息传递给视图类中的方法,并传递给控制器类中的方法,其中任何逻辑都应该是.它看起来像:
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
change_amount函数从窗口小部件获取新值,执行逻辑,并在模型上设置属性.
模型
模型类存储程序数据和状态以及用于通知对此数据的更改的一些最小逻辑.这个模型不应该与Qt模型(see http://qt-project.org/doc/qt-4.8/model-view-programming.html)混淆,因为它不是真的相同.
该模型可能如下所示:
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
写入模型会自动通过setter装饰函数中的代码向任何侦听视图发出信号.或者,控制器可以在其决定时手动触发信号.
在Qt模型类型(例如QStringListModel)已经与小部件连接的情况下,包含该小部件的视图根本不需要更新;这通过Qt框架自动发生.
UI源文件
为完成,此处包含示例main_view.ui文件:
<?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>
它通过调用转换为main_view_ui.py:
pyuic5 main_view.ui -o ..\views\main_view_ui.py
通过调用以下命令将资源文件mvc_app.qrc转换为mvc_app_rc.py:
pyrcc5 mvc_app.qrc -o ..\mvc_app_rc.py
有趣的链接
Why Qt is misusing model/view terminology?
标签:qt-designer,python,model-view-controller,pyside,pyqt 来源: https://codeday.me/bug/20191003/1850532.html