Model/View结构的自定义代理

一、自定义代理功能
导入数据文件进行编辑时,QTableView组件为每个单元格提供的是默认的代理编辑组件,这是一个QLineEdit组件。在编辑框里可以输入任何数据,所以比较通用。但是有些情况下,我们希望根据数据的类型限定使用不同的编辑组件。

有些数据类型为整形、浮点型或者下拉框,那么就需要编辑组件为QSpinBox、QDoubleSpinBox、QComboBox。实现这些功能就需要为TableView组件的某列或某个单元格设置自定义代理组件。
以上为两个自定义代理类:
在这里插入图片描述
在这里插入图片描述

1、基于QDoubleSpinBox的类QmyFloatSpinDelegate,用于数字量的输入,可以设置小数位数,设置输入数据范围。
2、基于QComboBox的类QmyComboBoxDelegate,用于“固井质量”的数据录入,可以设置列表选择的内容。

二、自定义代理类的基本设计要求
PyQt5中有关代理的几个类的层级结构:
在这里插入图片描述
QAbstractItemDelegate是所有代理类的抽象基类,QStyledItemDelegate是视图组件使用的默认的代理类,QItemDelegate也是具有类似功能的类。QStyledItemDelegate与QItemDelegate的差别在于:QStyledItemDelegate可以使用当前的样式表设置来绘制组件,因此建议使用QStyledItemDelegate作为自定义代理组件的基类。

自定义代理类的4个函数:
createEditor()函数:创建用于编辑模型数据的widget组件,如一个QSpinBox组件或者QComboBox组件
setEditData()函数:从数据模型获取数据,供widget组件进行编辑
setModelData()函数:将widget上的数据更新到数据模型
updateEditorGeometry()函数:用于给widge图组件设置合适的大小

三、基于QDoubleSpinBox的自定义代理类

from PyQt5.QtWidgets import  QStyledItemDelegate,QDoubleSpinBox,QComboBox

from PyQt5.QtCore import  Qt

## ==============基于QDoubleSpinbox的代理组件====================
class QmyFloatSpinDelegate(QStyledItemDelegate):
   def __init__(self, minV=0,maxV=10000,digi=2,parent=None):    #构造函数传入3个参数
      super().__init__(parent)
	
	#定义3个私有变量存储3个参数
      self.__min=minV
      self.__max=maxV
      self.__decimals=digi

## 自定义代理组件必须继承以下4个函数
   def createEditor(self, parent, option, index):
      editor = QDoubleSpinBox(parent)    #创建使用的编辑器组件
      editor.setFrame(False)
      editor.setRange(self.__min,self.__max)
      editor.setDecimals(self.__decimals)
      return editor

   def setEditorData(self,editor,index):
      model=index.model()     #关联的数据模型
      text=model.data(index, Qt.EditRole)  #单元格文字
##      value = float(index.model().data(index, Qt.EditRole))
      editor.setValue(float(text))
        
   def setModelData(self,editor,model,index):
      value = editor.value()
      model.setData(index, value, Qt.EditRole)
##        digi="{:.%df}"%self.__decimals  # 获得 "{:.2f}", 会改变小数位数
##        text=digi.format(value)     #相当于 "{:.2f}.format(value)"
##        model.setData(index, text, Qt.EditRole)
    
   def updateEditorGeometry(self,editor,option,index):
      editor.setGeometry(option.rect)

分析以上代码:
QmyFloatSpinDelegate的父类是QStyledItemDelegate,使用的编辑器组件是QDoubleSpinBox。
QmyFloatSpinDelegate的构造函数中可以传入参数minV、maxV、digi,分别用于设置最小值、最大值和小数位数,并且这3个参数都有默认值。在类中定义了3个私有变量,分别用于存储这3个参数。
接着实现代理组件的4个基本函数:
(1)CreateEditor(parent, option, index):用于创建需要的编辑器组件,并作为函数的返回值。函数中的参数parent是代理组件的父容器对象;option是QStyleOptionViewItem类型变量,可以对创建的编辑器组件的效果做一些高级设置;index是QModelIndex变量,是关联数据项的模型索引,通过index.model()可以获取关联的数据模型。

在TableView组件上双击一个单元格进入编辑状态时,就会调用此函数创建一个QDoubleSpinBox组件并显示在单元格里。

(2)setEditorData(editor, index):用于从数据模型获取数据,设置为编辑器的显示值。函数传递来的参数editor指向代理编辑器组件,也就是在createEditor()函数中创建的QDoubleSpinBox对象。index是关联的数据单元的模型索引,index.model()指向关联的数据模型,然后通过模型的data(index, Qt.EditRole)函数获取单元格的显示文字text,再将text转换为浮点数后赋值为代理组件的显示值。

(3)**setModelData(editor, model, index):用于将代理编辑器上的值更新到数据模型,当用户在界面上完成编辑时会自动调用此函数。**参数editor是所创建的编辑器组件,model是关联的数据模型,index是关联的单元格的模型索引。程序里先获取代理组件编辑器editor的数值value,然后将这个值更新到数据模型。

(4)updateEditorGeometry(editor, option, index):用于为代理组件editor的显示效果进行设置,参数option是QStyleOptionViewItem类型变量,其rect属性定义了单元格适合显示代理组件的大小,程序中直接设置为此值。index是关联数据项的模型索引。

四、基于QComboBox的自定义代理类

## ==============基于QComboBox的代理组件====================
class QmyComboBoxDelegate(QStyledItemDelegate):
   def __init__(self, parent=None):
      super().__init__(parent)
      self.__itemList=[]
      self.__isEditable=False
        

   def setItems(self,itemList, isEditable=False):
      self.__itemList=itemList
      self.__isEditable=isEditable

## 自定义代理组件必须继承以下4个函数
   def createEditor(self, parent, option, index):
      editor = QComboBox(parent)
      editor.setFrame(False)
      editor.setEditable(self.__isEditable)
      editor.addItems(self.__itemList)
      return editor

   def setEditorData(self,editor,index):
      model=index.model()
      text = model.data(index, Qt.EditRole)
##      text = str(index.model().data(index, Qt.EditRole))
      editor.setCurrentText(text)
        
   def setModelData(self,editor,model,index):
      text = editor.currentText()
      model.setData(index, text, Qt.EditRole)
    
   def updateEditorGeometry(self,editor,option,index):
      editor.setGeometry(option.rect)

QmyComboBoxDelegate的构造函数定义了私有变量self.__itemList用于存储QComboBox组件的下拉列表的内容,私有变量self.__isEditable用于设置QComboBox组件是否可编辑。

自定义接口函数setItems()用于为这两个私有变量赋值。在createEditor()函数里创建QComboBox组件editor后,用这两个变量设置editor的下拉列表内容,以及editable属性的值。

五、自定义代理类的使用

#导入myDelegates.py文件中定义的两个代理类
from myDelegates import QmyFloatSpinDelegate, QmyComboBoxDelegate

class QmyMainWindow(QMainWindow): 
   def __init__(self, parent=None):
      super().__init__(parent)    #调用父类构造函数,创建窗体
      self.ui=Ui_MainWindow()     #创建UI对象
      self.ui.setupUi(self)       #构造UI界面

      self.setCentralWidget(self.ui.splitter)
      self.__buildStatusBar()

      self.COL_COUNT=6  #常数,列数=6
      self.itemModel = QStandardItemModel(5,self.COL_COUNT,self)  # 数据模型,10行6列

##      headerList=["测深(m)","垂深(m)","方位(°)","总位移(m)","固井质量","测井取样"]
##      self.itemModel.setHorizontalHeaderLabels(headerList) #设置表头文字

      self.selectionModel = QItemSelectionModel(self.itemModel) #Item选择模型
      self.selectionModel.currentChanged.connect(self.do_currentChanged)
      self.__lastColumnFlags=(Qt.ItemIsSelectable | Qt.ItemIsUserCheckable | Qt.ItemIsEnabled)
      self.__lastColumnTitle="测井取样"
      
      #为tableView设置数据模型
      self.ui.tableView.setModel(self.itemModel) #设置数据模型
      self.ui.tableView.setSelectionModel(self.selectionModel)    #设置选择模型
      
      oneOrMore=QAbstractItemView.ExtendedSelection
      self.ui.tableView.setSelectionMode(oneOrMore) #可多选

      itemOrRow=QAbstractItemView.SelectItems
      self.ui.tableView.setSelectionBehavior(itemOrRow)   #单元格选择

      
      self.ui.tableView.verticalHeader().setDefaultSectionSize(22)#缺省行高
      self.ui.tableView.setEnabled(False)  #先禁用tableView
##      self.__resetTable(5)

      #创建自定义代理组件并设置
      self.spinCeShen=QmyFloatSpinDelegate(0,10000, 0,self)    #用于 测深
      self.spinLength=QmyFloatSpinDelegate(0,6000,  2,self)    #垂深,总位移
      self.spinDegree=QmyFloatSpinDelegate(0,360,   1,self)    #用于 方位

      self.ui.tableView.setItemDelegateForColumn(0,self.spinCeShen)  #测深
      self.ui.tableView.setItemDelegateForColumn(1,self.spinLength)  #垂深
      self.ui.tableView.setItemDelegateForColumn(3,self.spinLength)  #总位移
      self.ui.tableView.setItemDelegateForColumn(2,self.spinDegree)  #方位角

      qualities=["优","良","合格","不合格"]
      self.comboDelegate=QmyComboBoxDelegate(self)
      self.comboDelegate.setItems(qualities,False) #不可编辑
      self.ui.tableView.setItemDelegateForColumn(4,self.comboDelegate)  #固井质量

在QmyMainWindow类的构造函数中增加设置代理组件的代码。

创建了3个QmyFloatSpinDelegate类型的代理组件,但是设置了不同的数据范围和小数位数,以便用于不同的数据列。

创建了一个QmyComboBoxDelegate类型的代理组件,并调用其setItems()函数对下拉列表和editable属性进行了初始化设置。

可以为QTableView组件的某一行、某一列或整个表格设置代理组件,用到QTableView的函数如下

(1)setItemDelegateForColumn(column, delegate),为某一列column设置代理组件delegate。本示例中,一列为一个字段,所以用setItemDelegateForColumn()函数为列设置代理组件。
(2)setItemDelegateForRow(row, delegate),为某一行row设置代理组件delegate
(3)setItemDelegate(delegate),为整个TableView组件设置代理组件delegate

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值