前言
初次接触较大型的软件开发项目,找了几天可参照的项目无果,于是自己模仿django中的结构来做一个MVC开发模式规划,也有点闭门造车的意思,如有其他好的办法分享,下方评论见。
(本篇文章从零开始,以开发一个小应用的视角分享我的MVC规划)
(ps: 10.31的更新,目前发现了一个MVD的模式详见https://www.cnblogs.com/chusiyong/p/12941212.html)
1 快速开始
在这里将django文件结构一步步拆解,并考虑PyQt5的特点,进行复刻。
一般PyQt5会怎么使用?
①手写的
比较容易找到的一些PyQt5的文档,基本上都是手写,主要是因为要突出文章要介绍的功能,以及给读者复制即可使用的demo,方便理解。所以ui和功能部分都写在了同一个文件中,没有形成文件结构。当然了,估计也没人在开发时这么写。
class Test(QWidget):
def __init__(self):
super().__init__()
self.btn1 = QPushButton('按钮1', self)
self.btn1.move(100, 100)
self.btn1.clicked.connect(self.printer)
def printer(self):
print(1)
②生成的
主要来源于 PyQt5 Designer,这是一个绘画生成代码的工具,十分好用。一些结构性的东西通过绘画来生成,而进阶性操作就通过引用ui来添加。还是一样的代码,但清晰了不少,也具有了更高的可扩展性。按照这个思路写下去慢慢会遇到,静态文件的添加,样式的修改,类的抽象,小工具,数据库的引用等等…所以需要有一个完整的规划。
from test_ui import Ui_test
class Test(QWidget, Ui_test):
def __init__(self):
super().__init__()
self.setupUi(self)
self.btn1.clicked.connect(self.printer)
def printer(self):
print(1)
django的结构中给了什么?
这是一个开发完成了的django项目,静态文件,样式,模板等规划明确,所有的大的页面作为一个app,app里还有各类细微的规划,也有一些是富有django特性的东西,如urls,admin,apps这之类的不需要关注的东西。现在的目的很清晰,将一些特性化的东西舍去,拆解整个结构,转换成具有PyQt5特色的MVC开发结构。
一个小说爬虫阅读器的开发(可跳过)
已上传至github,建议结合文件目录食用 https://github.com/yejue/myReader
①功能分析
爬行状态页:输入网址->爬行开始->入库
小说阅读页:读数据库->显示
几个示例url:
https://read.qidian.com/chapter/HnWqMLj7BKecikCo3ZPkrg2/f-apiAeDULf6ItTi_ILQ7A2
https://read.qidian.com/chapter/AEFk0gzAWnGoUTngny7bXQ2/7Z2gygXUR7lOBDFlr9quQA2
②根据需求拆解django结构重组
先抛出几个概念:
- ui:使用PyQt5 Designer绘画生成的py文件,偏向于搭建一个结构
- controller:对Designer无法完成的操作,使用 controller 来做延伸操作,通常与 ui 挂钩。二者的关系类似于 html 与 js,controller 自行创建。
- view:仿写的view只是一个用于返回和接收数据的功能视图。
- loader:与django不同,PyQt5的数据需要自行搬运,定制一个介于view和controller之间的数据搬运器,用于数据之间的传输和调用controller。
如此,一个MVC结构出现了:
-
models + view —— 返回或接收数据库数据,对应 M
controller + ui —— 呈现初始页面以及修改页面,对应 V
loader —— 介于数据与“前端”之间,对应 C -
main_controller —— 在一个主要的控制器中实例页面,使得所有的页面在同一个作用域,这是页面间数据传递的基础(Qt 实在太广大了,此举仅限于本人目前的认知)。
拆解重组后总体结构如下:
- utils:存放一下小工具
logs:存放日志
static:用于存放一些静态文件
templates:用于存放页面模板
myreader.db:一个sqlite轻量级数据库
②爬行状态页
输入url,点击查找显示状态,确认无误后下载入库,发射信号重新加载阅读器的目录(只写了爬取起点xx章节链接的功能,如有需要可自行扩展)。
③阅读器页
查到当前有的文章标题,点击显示详情… (详情见代码 loaders和view部分)
④整体作用域和信号设置
在main_controller中实例化所有的窗口,如前面所述,所有的窗口都会在同一个函数作用域中,互相传递窗口实例地址,即可完成多窗口的窗口状态传输。
最终的信号设置以及整体的槽函数写在 allocating的分拨中心里,分拨中心会继承整个main_controller并整合所有的loaders,在这里定义好全局信号,按照需求设置对应槽函数,下发信号和需要操作的窗口。
2 最终结构
代码已上传至 github https://github.com/yejue/myReader
整个工作流程如下,分拨中心继承了主控controller并整合了所有的loaders,在初始化运行时,从主控controller展开所有的子窗口;当子窗口的控件被激活时,调用在分拨中心绑定好的槽函数 ,去做相应的动作,这些操作都应涉及到数据库(不涉及到数据库的通通扔到子窗口的controller),loader接收到views返回的数据后调用controller去变更ui。