Model-View-Delegate

模型视图委托(MVD)是PyQt中特有的设计模式,类似MVC设计模式,将MVC设计模式中的Controller当做MVD中的Delegate,两者的概念基本相同。不同的是委托不是独立存在,而是包含在视图里面。

模型视图委托设计模式中,模型负责存储和管理数据;视图负责显示数据,其中界面的框架和基础信息是视图负责,具体数据的显示是委托负责;委托不仅仅负责数据的显示,还有一个重要的功能是负责数据的编辑,如在视图中双击就可以编辑数据。

视图是怎么获取模型数据?首先初始化视图时需要给视图设置模型,然后通过索引获取模型中对应位置的数据。

模型

数据的存储一般是列表,表格和树,不同的存储方式有不同的操作和管理方法,为了适应这种差异性,PyQt中提供了一种统一的操作方法,如下图所示:

对于列表数据:

  • 根节点永远是NULL

  • row递增,column是0

对于表结构:

  • 根节点永远是NULL

  • row和column递增

对于树结构:

  • 根节点是NULL,父节点可变

  • row递增,column是0

模型类:

  • QStandardItemModel 通用存储,可以存储任意结构,最常用

  • QStringListModel 存储一组字符串

  • QDirModel 存储文件系统

  • QSqlQueryModel 对SQL查询的结果进行封装

  • QSqlTableModel 对SQL中的表格进行封装

例子:

import sys

from PyQt5.QtCore import Qt
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QApplication, QWidget, QTableView, QHBoxLayout class MyWidget(QWidget):
def __init__(self):
super(MyWidget, self).__init__() self.mode = QStandardItemModel()
root = self.mode.invisibleRootItem() item1 = QStandardItem()
item1.setData('', Qt.DisplayRole)
item2 = QStandardItem()
item2.setData('', Qt.DisplayRole)
item3 = QStandardItem()
item3.setData('', Qt.DisplayRole)
item4 = QStandardItem()
item4.setData('', Qt.DisplayRole) root.setChild(0, 0, item1)
root.setChild(0, 1, item2)
root.setChild(1, 0, item3)
root.setChild(1, 1, item4) # 表结构存储
tableView = QTableView(self)
tableView.setModel(self.mode) layout = QHBoxLayout()
layout.addWidget(tableView)
self.setLayout(layout) if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWidget()
w.resize(500, 300)
w.move(300, 300)
w.setWindowTitle('Simple')
w.show()
sys.exit(app.exec_())
import sys

from PyQt5.QtCore import Qt
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QTreeView class MyWidget(QWidget):
def __init__(self):
super(MyWidget, self).__init__() self.mode = QStandardItemModel()
root = self.mode.invisibleRootItem() item1 = QStandardItem()
item1.setData('', Qt.DisplayRole)
item2 = QStandardItem()
item2.setData('', Qt.DisplayRole)
item3 = QStandardItem()
item3.setData('', Qt.DisplayRole)
item4 = QStandardItem()
item4.setData('', Qt.DisplayRole) # 树结构存储
root.setChild(0, 0, item1)
item1.setChild(0, 0, item2)
item1.setChild(1, 0, item3)
item3.setChild(0, 0, item4) treeView = QTreeView(self)
treeView.setModel(self.mode) layout = QHBoxLayout()
layout.addWidget(treeView)
self.setLayout(layout) if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWidget()
w.resize(500, 300)
w.move(300, 300)
w.setWindowTitle('Simple')
w.show()
sys.exit(app.exec_())

视图

视图主要是用来显示数据,不同的视图对象用于显示不同存储结构的数据,主要的视图对象如下:

  • QListView 列表形式显示

  • QTableView 表格形式显示

  • QTreeView 树结构显示

单独的视图需要配合模型使用,因此PyQt对视图进行了再次封装,直接内部封装模型,主要对象如下:

  • QListWidget 列表形式显示的界面

  • QTableWidget 表格形式显示的界面

  • QTreeWidget 树结构形式显示的界面

委托

委托被封装在视图里面,主要是负责数据的显示和编辑功能。

数据的编辑主要涉及的方法:

  • createEditor 在双击进入编辑时,创建编辑器,如创建QLineEdit,QTextEdit

  • updateEditorGeometry 设置编辑器显示的位置和大小

  • setEditorData 更新数据到视图

  • setModeData 通过索引更新数据到模型

如果需要修改编辑时操作数据的方式,就需要重写上述方法。

视图主要是负责显示,其中涉及的方法:

  • paint 负责绘制

  • editorEvent 负责处理事件

如果要实现自定义视图显示,需要重写paint方法,在paint方法中绘制需要显示的控件,然后在editorEvent方法中处理事件,更新数据。

例子

在TableView中默认的整型数据编辑使用的是计数器控件,本例中是将计数器变成单行文本控件,实现数据的编辑功能。

import sys

from PyQt5.QtCore import Qt, QVariant
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QApplication, QWidget, QTableView, QHBoxLayout, QStyledItemDelegate, QLineEdit class MyTableView(QTableView):
def __init__(self):
super(MyTableView, self).__init__() class MyDelgate(QStyledItemDelegate):
def __init__(self):
super(MyDelgate, self).__init__() # 创建编辑器
def createEditor(self, parent, option, index):
print('createEditor')
if index.column() == 1:
return QLineEdit(parent)
else:
return QStyledItemDelegate.createEditor(self, parent, option, index) # 设置编辑器的位置
def updateEditorGeometry(self, edit, option, index):
print('updateEditorGeometry')
if index.column() == 1:
edit.setGeometry(option.rect)
else:
return QStyledItemDelegate.updateEditorGeometry(self, edit, option, index) # 设置数据到模型
def setModelData(self, edit, model, index):
print('setModelData')
if index.column() == 1:
model.setData(index, int(edit.text()), Qt.DisplayRole)
else:
return QStyledItemDelegate.setModelData(self, edit, model, index) # 设置数据到视图
def setEditorData(self, edit, index):
print('setEditorData')
if index.column() == 1:
edit.setText(str(index.data(Qt.DisplayRole)))
else:
return QStyledItemDelegate.setEditorData(self, edit, index) class MyWidget(QWidget):
def __init__(self):
super(MyWidget, self).__init__() self.mode = QStandardItemModel()
root = self.mode.invisibleRootItem() item1 = QStandardItem()
item1.setData('a', Qt.DisplayRole) item2 = QStandardItem()
item2.setData(1, Qt.DisplayRole) item3 = QStandardItem()
item3.setData(False, Qt.DisplayRole) item4 = QStandardItem()
item4.setData('b', Qt.DisplayRole) item5 = QStandardItem()
item5.setData(2, Qt.DisplayRole) item6 = QStandardItem()
item6.setData(True, Qt.DisplayRole) root.setChild(0, 0, item1)
root.setChild(0, 1, item2)
root.setChild(0, 2, item3)
root.setChild(1, 0, item4)
root.setChild(1, 1, item5)
root.setChild(1, 2, item6) tableView = MyTableView()
tableView.setModel(self.mode)
tableView.setItemDelegate(MyDelgate()) layout = QHBoxLayout()
layout.addWidget(tableView)
self.setLayout(layout) if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWidget()
w.resize(500, 300)
w.move(300, 300)
w.setWindowTitle('Simple')
w.show()
sys.exit(app.exec_())

在TableView中默认的整型显示是数字字符串,本例中将数字字符串变成进度条显示。

import sys

from PyQt5.QtCore import Qt, QEvent
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QApplication, QWidget, QTableView, QHBoxLayout, QStyledItemDelegate, QStyle, QStyleOptionProgressBar, QStyleOptionButton class MyTableView(QTableView):
def __init__(self):
super(MyTableView, self).__init__() # 一定要注意复写方法的返回值
class MyDelgate(QStyledItemDelegate):
def __init__(self):
super(MyDelgate, self).__init__() # 委托负责具体数据的显示,因此重新paint方法
def paint(self, painter, option, index):
if index.column() == 1:
style = QStyleOptionProgressBar()
style.minimum = 0
style.maximum = 10
style.progress= index.data(Qt.DisplayRole)
style.rect = option.rect
QApplication.style().drawControl(QStyle.CE_ProgressBar, style, painter) elif index.column() == 2:
style = QStyleOptionButton()
if index.data(Qt.DisplayRole) == True:
style.state = QStyle.State_On
else:
style.state = QStyle.State_Off
style.state |= QStyle.State_Enabled
style.rect = option.rect
style.rect.setX(option.rect.x() + option.rect.width() / 2 - 7)
QApplication.style().drawControl(QStyle.CE_CheckBox, style, painter)
else:
return QStyledItemDelegate.paint(self, painter, option, index) def editorEvent(self, event, model, option, index):
if index.column() == 2:
if event.type() == QEvent.MouseButtonPress and option.rect.contains(event.pos()):
data = not index.data(Qt.DisplayRole)
model.setData(index, data, Qt.DisplayRole)
return True
else:
return QStyledItemDelegate.editorEvent(self, event, model, option, index) class MyWidget(QWidget):
def __init__(self):
super(MyWidget, self).__init__() self.mode = QStandardItemModel()
root = self.mode.invisibleRootItem() item1 = QStandardItem()
item1.setData('a', Qt.DisplayRole) item2 = QStandardItem()
item2.setData(1, Qt.DisplayRole) item3 = QStandardItem()
item3.setData(False, Qt.DisplayRole) item4 = QStandardItem()
item4.setData('b', Qt.DisplayRole) item5 = QStandardItem()
item5.setData(2, Qt.DisplayRole) item6 = QStandardItem()
item6.setData(True, Qt.DisplayRole) root.setChild(0, 0, item1)
root.setChild(0, 1, item2)
root.setChild(0, 2, item3)
root.setChild(1, 0, item4)
root.setChild(1, 1, item5)
root.setChild(1, 2, item6) tableView = MyTableView()
tableView.setModel(self.mode)
tableView.setItemDelegate(MyDelgate()) layout = QHBoxLayout()
layout.addWidget(tableView)
self.setLayout(layout) if __name__ == '__main__':
app = QApplication(sys.argv)
w = MyWidget()
w.resize(500, 300)
w.move(300, 300)
w.setWindowTitle('Simple')
w.show()
sys.exit(app.exec_())

总结:如果修改视图中数据的显示方式,需要重写委托的paint方法,editorEvent方法是处理视图中的点击事件,根据需要绝对是否重写;如果要修改视图中数据的编辑方式,需要重写createEditor方法、updateEditorGeometry方法、setEditorData方法以及setModeData方法。

PyQt5模型视图委托的更多相关文章

  1. Qt 模型/视图/委托

    模型.视图.委托 模型/视图架构基于MVC设计模式发展而来.MVC中,模型(Model)用来表示数据:视图(View)是界面,用来显示数据:控制(Controller)定义界面对用户输入的反应方式. ...

  2. Qt模型/视图框架----简单的例子

    #include<qapplication.h> #include<qfilesystemmodel.h> #include<qtreeview.h> #inclu ...

  3. 【转】Qt之模型/视图

    [本文转自]http://blog.sina.com.cn/s/blog_a6fb6cc90101hh20.html   作者: 一去丶二三里 关于Qt中MVC的介绍与使用,助手中有一节模型/视图编程 ...

  4. Qt模型/视图、委托

    MVC视图和控制器对象相结合,其结果是模型/视图结构,仍然分离了数据与呈现给用户的方式,使得它可以在几个不同的视图中显示相同的数据,并实现新类型的视图而无需改变底层的数据结构.为了灵活的处理数据输入, ...

  5. Qt之模型/视图(委托)

    概念 不同于模型 - 视图 - 控制器模式,模型/视图设计不包括用于管理与用户交互的一个完全独立的组件.一般情况,视图负责将模型数据呈现给用户以及处理用户输入.为了输入更加具有灵活性,则由委托来执行交 ...

  6. 设计模式 --- 模型-视图-控制器(Model View Controller)

    模型-视图-控制器(Model-View-Controller,MVC)是Xerox PARC在20世纪80年代为编程语言Smalltalk-80发明的一种软件设计模式,至今已广泛应用于用户交互应用程 ...

  7. Qt之模型/视图(自定义进度条)

    简述 在之前的章节中分享过关于QHeaderView表头排序.添加复选框等内容,相信大家模型/视图.自定义风格有了一定的了解,下面我们来分享一个更常用的内容-自定义进度条. 实现方式: 从QAbstr ...

  8. Qt之模型/视图(自定义风格)

    Qt之模型/视图(自定义风格) 关于自定义风格是针对视图与委托而言的,使用事件与QSS都可以进行处理,今天关于美化的细节讲解一下. 先看下图: 先撇开界面的美观性(萝卜青菜,各有所爱),就现有的这些风 ...

  9. Qt之模型/视图(实时更新数据)

    上两节简单介绍了Qt中对于模型/视图的编程,大部分助手里说的很清楚了,现在就开始实战部分吧! 在实际应用中,视图展示的数据往往并非一成不变的,那么如何实时更新成了一个很重要的问题!功能:(1)添加委托 ...

随机推荐

  1. django xadmin 配置过程

    1.拷贝xadmin的一个下的一个xadmin文件夹放到项目里

  2. android自定义控件onLayout方法

    onLayout设置子控件的位置,对应一些普通的控件例如Button.TextView等控件,不存在子控件,所以可以不用复写该方法. 向线性布局.相对布局等存在子控件,可以覆写该方法去控制子控件的位置 ...

  3. 【转载】提高访问 github 的速度

    原文地址:https://www.cnblogs.com/liuchao888/p/11733996.html 工具地址:http://tool.chinaz.com/dns?type=1&h ...

  4. LIMS产品 - Labware

    软件架构 客户端:Labware软件,部分C/S功能 Web服务:Apache Tomcat等,部分B/S功能 后台服务:计划.调度程序,环境监控样.稳定性研究等 数据库驱动:ODBC 报表工具:水晶 ...

  5. JNI调用Cython生成库‘undefined symbol: PyInit_’问题

    最近项目需要提升所有 Python 算法的执行时间,并给 Java 框架调用,根据 Python一键转Jar包,Java调用Python新姿势!的思路可以用 Cython 将 Python 代码转换为 ...

  6. egret Exml自定义组件

    有个需求,在A组件里面包裹 B组件: 碰到了一些奇怪的问题,这些问题是由于编辑器bug引起的.创建了皮肤后并没有在default.thm.json直接添加进去,造成拖动组件莫名其妙的bug,这里忘记保 ...

  7. org.hibernate.LazyInitializationException异常解决办法

    org.hibernate.LazyInitializationException异常failed to lazily initialize a collection...的解决方案使用hiberna ...

  8. Excel表格中第一个输入的零不显示怎么办?

    Excel表格是办公的人经常要用到的软件,经常用它来统计和记录各种数据,但是有时候表格中第一个数字是零的时候,经常第一个零输入时不显示的,这个情况我们怎么解决呢?这里小编跟大家讲一下希望能帮助大家. ...

  9. vue与react对比总结(一)

    一.react和vue设计上的共同理念 1.使用 Virtual DOM 2.提供了响应式 (Reactive) 和组件化 (Composable) 的视图组件. 3.将注意力集中保持在核心库,而将其 ...

  10. 状压DP之互不侵犯

    题目描述 这里 在\(N*N\) 的棋盘里面放\(k\)个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 输入格式 只有 ...