自定义委托代理控件
代理在视图与模型之间交互操作时提供临时编辑组件的功能。模型向视图提供数据是单向的,一般仅用于显示。当需要在视图上编辑数据时,代理功能会为编辑数据提供一个编辑器,这个编辑器获取模型的数据、接受用户编辑的数据后又提交给模型。
例如在QTableView组件上双击一个单元格编辑数据时,在单元格里就会出现一个QLineEdit组件,这个编辑框就是代理提供的临时编辑器。代理的主要任务就是为视图组件提供代理编辑器。
对于一些特殊的数据编辑需求,例如只允许输入整型数,使用一个QSpinBox作为代理组件更合适;
从列表中选择一个数据,使用一个QComboBox作为代理组件更好。这时就需要从QStyledItemDelegate继承创建自定义代理类。
代理委托作用
Delcgate(代理或委托)的作用如下
- 绘制视图中来自模型的数据,委托会参考项目的角色和数据进行绘制,不同的伯e色和数据有不同的展示效果。
- 在视图与模型之间交互操作时提供临时编辑组件的功能,该编辑器会位于视图的0顶层。
当默认委托(QStyledItemDelegate)提供的这两方面作用无法满足需求时就可以考虑自定义委托。
例如,如果要呈现更复杂的可视化,或者使用 QComBox 来编辑整数,默认的QLineEdit 无法满足需求,那么需要使用自定义委托。
本章提供两种委托方式:
-
一种是结合自定义模型的自定义委托,另一种是适用子通用模型的泛型委托。
-
这两种委托都需要重新实QStyledItemDelegate 的一些方法对于
-
第1种委托,唯一必须重写的函数是 paint()。如果要支持可编辑,则必须重写函数 createEditor()、setEditorDate()和 setModelData()。
如果在编辑过程中要使用 QLineEdit或QTextEdit,通常也会重写 commitAndCloseEditor()函数。可以根据需要重写 sizeHint()函数。
-
对于第2种委托,只需要重写函数 createEditor()、setEditorDate()和 setModelData()即可,其他函数可以根据情况重写。这种委托相对简单,适用于多个模型,代码可以重复使用,是比较推荐的方式。
-
- createEditor()创建用于编辑模型数据的widget组件,如一个QSpinBox或一个QComboBox组件
- setEditorData()从模型获得数据,供widget组件进行编辑
- setModelData()将widget上的数据更新到数据模型
- updateEditorGeometry()用于给widget组件设置合适的大小
QStyledItemDelegate自定义代理
QStyledItemDelegate类为模型中的数据项提供了显示和编辑功能。
from PySide6.QtWidgets import QStyledItemDelegate
QStyledItemDelegate(parent: Union[PySide6.QtCore.QObject, NoneType] = None)-> None
当在Qt项目视图(例如QTableView)中显示来自模型的数据时,单个项目由代理绘制。此外,当编辑项目时,它会提供一个编辑器小部件,在进行编辑时,该小部件会放置在项目视图的顶部。QStyledItemDelegate是所有Qt项目视图的默认委托,并在创建时安装在它们上。
QStyledItemDelegate类是模型/视图类之一,也是Qt模型/视图框架的一部分。代理允许项目的显示和编辑独立于模型和视图进行开发。
模型中项目的数据被分配了一个ItemDataRole;每个项目可以为每个角色存储一个QVariant。QStyledItemDelegate实现了用户期望的最常见数据类型的显示和编辑,包括布尔值、整数和字符串。
根据他们在模型中所扮演的角色,数据将以不同的方式绘制。下表描述了代理可以为每个角色处理的角色和数据类型。通常,只要确保模型为每个角色返回适当的数据,以确定项目在视图中的外观就足够了。
Role | Accepted Types |
---|---|
BackgroundRole | QBrush |
CheckStateRole | CheckState |
DecorationRole | QIcon , QPixmap , QImage and QColor |
DisplayRole | QString 和具有字符串表示形式的类型 |
EditRole | 有关详细信息,请参阅QItemEditorFactory |
FontRole | QFont |
SizeHintRole | QSize |
TextAlignmentRole | Alignment |
ForegroundRole | QBrush |
编辑器是使用QItemEditorFactory创建的;QItemEditorFactory提供的默认静态实例安装在所有项目委托上。您可以使用setItemEditorFactory()设置自定义工厂,也可以使用setDefaultFactory(()设置新的默认工厂。被编辑的是存储在具有EditRole的项目模型中的数据。有关项目编辑器工厂的更高级介绍,请参阅QItemEditorFactory类。"颜色编辑器工厂"示例显示了如何使用工厂创建自定义编辑器。
子类
如果代理不支持绘制所需的数据类型,或者您想自定义项的绘制,则需要对QStyledItemDelegate进行子类化,并重新实现paint()和sizeHint()。paint()函数是为每个项目单独调用的,使用sizeHint()可以为每个项目指定提示。
当重新实现paint()时,通常会处理想要绘制的数据类型,并将超类实现用于其他类型。
复选框指示器的绘制由当前样式执行。该样式还指定了为不同的数据角色绘制数据的大小和边界矩形。项目本身的边界矩形也是由样式计算的。因此,当绘制已经支持的数据类型时,最好询问这些边界矩形的样式。QStyle类描述对此进行了更详细的描述。
如果您希望更改由复选框指示器的样式或绘制计算的任何边界矩形,可以对QStyle进行子类化。但是,请注意,重新实现sizeHint()也会影响项的大小。
自定义委托可以在不使用编辑器项工厂的情况下提供编辑器。在这种情况下,必须重新实现以下虚拟功能:
- createEditor()返回用于更改模型中数据的小部件,并且可以重新实现以自定义编辑行为。
- setEditorData()为小部件提供了要操作的数据。
- updateEditorGeometry()确保编辑器相对于项目视图正确显示。
- setModelData()将更新后的数据返回给模型。
Star Delegate示例通过重新实现这些方法来创建编辑器。
QStyledItemDelegate 与 QItemDelegate
自Qt 4.4以来,有两个委托类:QItemDelegate和QStyledItemDelegate。但是,默认的委托是QStyledItemDelegate。这两个类是绘制和为视图中的项目提供编辑器的独立替代方案。它们之间的区别在于QStyledItemDelegate使用当前样式来绘制其项目。因此,我们建议在实现自定义委托或使用Qt样式表时使用QStyledItemDelegate作为基类。除非自定义代理需要使用绘图样式,否则任何一个类所需的代码都应该相等。
如果希望自定义项目视图的绘制,则应实现自定义样式。有关详细信息,请参阅QStyle类文档。
方法 | 描述 |
---|---|
QStyledItemDelegate([parent=None]) | 构造具有给定父级的项委托。 |
displayText(value, locale:PySide6.QtCore.QLocale)->str | 此函数返回委托将用于在区域设置中显示模型的DisplayRole的字符串。value是模型提供的DisplayRole的值。 默认实现使用toString将值转换为QString。 对于空模型索引,即模型返回无效QVariant的索引,不调用此函数。 |
initStyleOption(option:PySide6.QtWidgets.QStyleOptionViewItem, index:PySide6.QtCore.QModelIndex) | 使用索引索引的值初始化选项。当子类需要QStyleOptionViewItem,但又不想自己填写所有信息时,此方法对它们很有用。 |
itemEditorFactory()->PySide6.QtWidgets.QItemEditorFactory | 返回项委托使用的编辑器工厂。如果没有设置编辑器工厂,则函数将返回null。 |
setItemEditorFactory(factory:PySide6.QtWidgets.QItemEditorFactory) | 将项代理要使用的编辑器工厂设置为指定的工厂。如果未设置编辑器工厂,则项代理将使用默认的编辑器工厂。 |
QItemDelegate代理
QItemDelegate类为模型中的数据项提供了显示和编辑功能。
from PySide6.QtWidgets import QItemDelegate
QItemDelegate(parent: Union[PySide6.QtCore.QObject, NoneType] = None)-> None
-
QItemDelegate.clipping: bool
如果代理应该剪裁绘制事件,则此属性保持不变。
此属性将绘制片段设置为项目的大小。默认值为on。这在图像大于项目大小等情况下很有用。
QItemDelegate代理官方描述
QItemDelegate可用于为基于QAbstractItemView子类的项目视图提供自定义显示功能和编辑器小部件。为此目的使用委托可以独立于模型和视图自定义和开发显示和编辑机制。
QItemDelegate类是模型/视图类之一,也是Qt模型/视图框架的一部分。请注意,QStyledItemDelegate已经接管了绘制Qt的项目视图的工作。我们建议在创建新委托时使用QStyledItemDelegate。
在标准视图中显示自定义模型中的项目时,通常只需确保模型为决定项目在视图中外观的每个角色返回适当的数据就足够了。Qt的标准视图使用的默认委托使用此角色信息以用户期望的大多数常见形式显示项目。但是,有时有必要对项的外观进行比默认委托所能提供的更多的控制。
此类提供用于在视图中绘制项目数据和编辑项目模型中的数据的函数的默认实现。提供了在QAbstractItemDelegate中定义的paint()和sizeHint()虚拟函数的默认实现,以确保委托实现视图所期望的正确基本行为。您可以在子类中重新实现这些函数,以自定义项的外观。
在项目视图中编辑数据时,QItemDelegate提供了一个编辑器小部件,这是一个在进行编辑时放置在视图顶部的小部件。编辑器是使用QItemEditorFactory创建的;QItemEditorFactory提供的默认静态实例安装在所有项目委托上。您可以使用setItemEditorFactory()设置自定义工厂,也可以使用setDefaultFactory(()设置新的默认工厂。被编辑的是存储在具有EditRole的项目模型中的数据。
此处仅重新实现基于小部件的委托的标准编辑功能:
- createEditor()返回用于更改模型中数据的小部件,并且可以重新实现以自定义编辑行为。
- setEditorData()为小部件提供了要操作的数据。
- updateEditorGeometry()确保编辑器相对于项目视图正确显示。
- setModelData()将更新后的数据返回给模型。
closeEditor()信号表示用户已经完成了对数据的编辑,并且编辑器小部件可以被销毁。
标准角色和数据类型
Qt提供的标准视图使用的默认委托将每个标准角色(由ItemDataRole定义)与某些数据类型相关联。返回这些类型数据的模型可能会影响代理的外观,如下表所述。
Role | Accepted Types |
---|---|
BackgroundRole | QBrush |
CheckStateRole | CheckState |
DecorationRole | QIcon , QPixmap and QColor |
DisplayRole | QString 和具有字符串表示形式的类型 |
EditRole | 有关详细信息,请参阅QItemEditorFactory |
FontRole | QFont |
SizeHintRole | QSize |
TextAlignmentRole | Alignment |
ForegroundRole | QBrush |
如果默认委托不允许您出于显示目的或编辑数据所需的自定义级别,则可以将QItemDelegate子类化以实现所需的行为。
子类
当对QItemDelegate进行子类化以创建使用自定义渲染器显示项目的委托时,重要的是确保该委托能够针对所有所需状态适当地渲染项目;例如,选中、禁用、选中。paint()函数的文档中包含一些提示,说明如何实现这一点。
您可以使用QItemEditorFactory提供自定义编辑器。"颜色编辑器工厂示例"显示了如何使自定义编辑器可用于具有默认项目编辑器工厂的代理。这样,就不需要对QItemDelegate进行子类化。另一种选择是重新实现createEditor()、setEditorData()、setModelData()和updateEditorGeometry()。此过程在"旋转框代理示例"中进行了描述。
QStyledItemDelegate vs. QItemDelegate
自Qt 4.4以来,有两个委托类:QItemDelegate和QStyledItemDelegate。但是,默认的委托是QStyledItemDelegate。这两个类是绘制和为视图中的项目提供编辑器的独立替代方案。它们之间的区别在于QStyledItemDelegate使用当前样式来绘制其项目。因此,我们建议在实现自定义委托或使用Qt样式表时使用QStyledItemDelegate作为基类。除非自定义代理需要使用绘图样式,否则任何一个类所需的代码都应该相等。
方法 | 描述 |
---|---|
decoration(option: PySide6.QtWidgets.QStyleOptionViewItem, variant)->QPixmap | |
doCheck(option, bounding:QRect, variant)->QRect | |
drawBackground(painter:QPainter, option:QStyleOptionViewItem, index:.QModelIndex) | 使用给定的painter和style选项渲染给定索引的项目背景。 |
hasClipping()->bool | 属性剪辑的Getter。 |
itemEditorFactory()->PySide6.QtWidgets.QItemEditorFactory | 返回项委托使用的编辑器工厂。如果没有设置编辑器工厂,则函数将返回null。 |
rect(option:PySide6.QtWidgets.QStyleOptionViewItem, index:PySide6.QtCore.QModelIndex, role:int)->QRect | |
setClipping(clip:bool) | 属性剪辑的设置程序。 |
setItemEditorFactory(factory:PySide6.QtWidgets.QItemEditorFactory) | 将项代理要使用的编辑器工厂设置为指定的工厂。如果未设置编辑器工厂,则项代理将使用默认的编辑器工厂。 |
setOptions(index:PySide6.QtCore.QModelIndex, option:PySide6.QtWidgets.QStyleOptionViewItem)->PySide6.QtWidgets.QStyleOptionViewItem | |
textRectangle(painter:Painter, rect:QRect, font:QFont, text:str)->QRect | |
drawCheck(painter:QPainter, option:QStyleOptionViewItem, rect:QRect, state:PySide6.QtCore.Qt.CheckState) | 使用给定的painter和style选项,使用给定的状态,在rect指定的矩形内渲染检查指示器。 |
drawDecoration(painter:QPainter, option:QStyleOptionViewItem, rect:QRect, pixmap:QPixmap) | 使用给定的painter和style选项在rect指定的矩形内渲染装饰像素图。 |
drawDisplay(painter:QPainter, option:QStyleOptionViewItem, rect:QRect, pixmap:QPixmap) | 使用给定的painter和style选项在rect指定的矩形内渲染项目视图文本。 |
drawFocus(painter:QPainter, option:QStyleOptionViewItem, rect:QRect) | 使用给定的painter和style选项,渲染rect指定的矩形内的区域,表明该区域具有焦点。 |
[static]selectedPixmap(pixmap:QPixmap, palette:QPalette, enabled:bool)->QPixmap |
-
PySide6.QtCore.Qt.CheckState
此枚举描述可检查项、控件和小部件的状态。
Constant Description Qt.Unchecked 该项目未选中。 Qt.PartiallyChecked 项目已部分检查。如果检查了层次模型中的项目的部分子项(而不是全部子项),则可以对其进行部分检查。 Qt.Checked 项目已选中。
方法介绍
在视图控件中双击某个数据项,可以修改数据项当前显示的值,即可以输入新的值。
- 输人新值时,并不是直接在视图控件上输入(视图控件只具有显示数据的功能),而是在视图控件的单元格位置出现一个新的可以输入数据的控件,
- 例如 QLieEdit。QLineEdit 读取数据项的值作为初始值,供用户修改,修改完成后通过数据项的索引把数据保存到数据模型中,并通知视图控件显示新的数据,像这种为视图控件提供编辑功能的控件称为代理控件或委托控件。
系统为每种数据类型定义了默认的代理控件,用户也可以自定义代理控件。
-
例如某个数据项存储性别值该数据项只有"男"和"女"两个选择可以用QComboBox作为代理控件双击该数据项,弹出QComboBox 控件,从QComboBox 的列表中选择"男"或"女";
-
再如对于存储成绩的数据项,用QDoubleSpinBox作为代理控件,设置其可以输人1位小数定义代理控件需要用QStyledItemDelegate类或 QItemDelegate类创建子类,这两个类都继承自QAbstractItemDelegate类。
-
这两个类的主要区别是前者可以使用当前的样式表来设置代理控件的样式因此建议使用前者来定义代理控件。
-
在QStyledItemDelegate或QItemDelegate 的子类中定义代理控件的类型位置,以及如何读取和返回数据。
-
视图控件都有从 QAbstractItemView 继承而来的:
- setItemDelegate(delegate: QAbstractltemDelegate)方法为所有的数据项设置代理控件
- setItemDelegateForColumn(column; int,delegate: QAbstractItemDelegate)方法为列数据项设置代理控件
- setItemDelegateForRow(row;int,delegate:QAbstractItemDelegate)方法为行数据项设置代理控件
-
创建代理控件可以用项编辑器工厂QItemEditorFactory定义默认的代理控件,也可以自定义代理控件的类型本书讲解自定义代理控件。自定义代理控件需要重写 QStyledItemDelegate 类或 QItemDelegate 类的下面4个函数:
- createEditor(parent: QWidget,option: QStyleOptionViewItem,index: QModelIndex)->QWidget函数用于创建代理控件的实例对象并返回该实例对象
- setEditorData(editor:QWidget,index:QModelIndex)-> None 函数,用于读取视图控件的数据项的值到代理控件中。
- setModelData(editor: QWidget,model: QAbstractItemModel,index:QModelIndex)->None函数,用于将编辑后的代理控件的值返回到数据模型中
- updateEditorGeometry(editor: QWidget,option: QStyleOptionViewItem,index:QModelIndex)->None 函数,用于设置代理控件显示的位置。
- createEditor)函数中的参数 parent 指代理控件所在的窗口,通常取视图控件所在的窗体;
- 其他3个函数的editor 指 createEditor()返回的代理控件,用于传递数据;
- QModelIndex是数据项的索引,系统会给实参传递索引;
- QStyleOptionViewItem 传递的一些属性用于确定代理控件的位置和外观,其属性如表所示。其中枚举值QStyleOptionViewltem.Position 可取:
- QStyleOptionViewItem.Left
- QStyleOptionViewItem.Right
- QStyleOptionViewltem.op
- QStyleOptionViewItem.Bottom;
- 枚举值 QStyleOptionViewItem.ViewItemFeatures 可取:
- QStyleOptionViewltem None
- QStyleOptionViewItem.WrapText
- QStyleOptionViewltem.Alternate
- QStyleOptionViewItem.HasCheckIndicator
- OStyleOptionViewltem.HasDisplay
- QStyleOptionViewItem.HasDecoration;
- 枚举值 QStyleOptionViewItem.ViewItemPosition 可取:
- QStyleOptionViewItem.Beginning
- QStyleOptionViewItem.Middle
- QStyleOptionViewItem.End
- QStyleOptionViewItem.OnlyOne(行中只有一个项.两端对齐)。
QStyleOptionViewltem的属性 | 属性值的类型 | 说明 |
---|---|---|
backgroundBrush | QBrush | 项的背景画刷 |
checkState | Qt.CheckState | 项的勾选状态 |
decorationAlignment | Qt.Alignment | 项的图标对齐位置 |
decorationPosition | QStyleOptionViewItem.Position | 项的图标位置 |
decorationSize | QSize | 项的图标尺寸 |
displayAlignment | Qt.Alignment | 项的文字对齐位置 |
features | QStyleOptionViewItem.ViewItemFeatures | 项所具有的特征 |
font | QFont | 项的字体 |
icon | QIcon | 项图标 |
index | QModelIndex | 项的模型索引 |
showDecorationSelected | bool | 项是否显示图标 |
text | QString | 项显示的文本 |
textElideMode | Qt.TextElideMode | 省略号的模式 |
viewItemPosition | QStyleOptionViewltem.ViewItemPosition | 项在行中的位置 |
direction | Qt.LayoutDirection | 布局方向 |
palette | QPalette | 调色板 |
Tect | QRect | 项的矩形区域 |
styleObject | QObject | 项的窗口类型 |
version | int | 版本 |
QTableView 控件结合自定义委托实例
一是创建自定义委托类 StudentTableDelegate,这个类只适用于自定义模型 StudentTableModel,不适合用于其他模型。在这个类中,重写了常用的函数 paint()、createEditor()、setEditorDate()和 setModelData(),以及函数 commitAndCloseEditor()和sizeHint()。
二是创建日期和整数两列的自定义委托,这两种委托也可以用于其他模型,方便重复。这也是第 2 种委托,即泛型委托,是比较推荐的一种方式。笔者追求的效果比较简单,每种委托只重写了 createEditor(、setEditorData0和 setModelData()这 3个函数
三是程序启动主体,本案例创建了 tableView 和 tableView2 两个表格,前者对应第1种委托,后者对应第2种委托,方便对照。在init model2 中对 tableView2的不同列设置了不同的数据,从而与自定义委托进行匹配
tableView,"科目"和"姓名"使用 OLineEdit 委托,"分数"使用QSpinBox 委托,"说明"使用 QTextEdit 委托,用来显示 HTML。中间的几个按钮的行为也都基于 tableView。
对于下面的 tableView2,前两列使用默认委托(QLineEdit),第3 列使用 QSpinBox委托,第4列使用 QDateEdit 委托。QSpinBox 委托和QDateEdit 委托也可以结合其他模型使用。
# -*- coding: UTF-8 -*-
# File date: Hi_2023/3/27 0:07
# File_name: 01-QTableView 控件结合自定义委托实例.py
from PySide6.QtWidgets import *
from PySide6.QtGui import *
from PySide6.QtCore import *
import re
import sys
import datetime
from qt_QTableModel import StudentTableModel
import os
os.chdir(os.path.dirname(__file__))
SUBJECT,NAME,SCORE,DESCRIPTION = range(4)
class StudentTableDelegate(QStyledItemDelegate):
def __init__(self,parent=None):
super(StudentTableDelegate,self).__init__(parent)
def paint(self,painter,option,index):
if index.column()== DESCRIPTION:
text = index.model().data(index)
if text[-2:] == '优秀':
text = f'{text[:-2]}<font color=red><b>优秀</b></font>'
index.model().setData(index,value=text)
elif text[-2:] == '良好':
text = f'{text[:-2]}<font color=green><b>良好</b></font>'
index.model().setData(index,value=text)
palette = QApplication.palette()
document = QTextDocument()
document.setDefaultFont(option.font)
if option.state & QStyle.State_Selected:
document.setHtml("<font color={}>{}</font>".format(
palette.highlightedText().color().name(),text))
else:
document.setHtml(text)
color =(palette.highlight().color()
if option.state & QStyle.State_Selected
else QColor(index.model().data(index,Qt.BackgroundRole)))
painter.save()
painter.fillRect(option.rect,color)
painter.translate(option.rect.x(),option.rect.y())
document.drawContents(painter)
painter.restore()
else:
QStyledItemDelegate.paint(self,painter,option,index)
def sizeHint(self,option,index):
fm = option.fontMetrics
if index.column()== SCORE:
return QSize(fm.averageCharWidth(),fm.height())
if index.column()== DESCRIPTION:
text = index.model().data(index)
document = QTextDocument()
document.setDefaultFont(option.font)
document.setHtml(text)
return QSize(document.idealWidth()+ 5,fm.height())
return QStyledItemDelegate.sizeHint(self,option,index)
def createEditor(self,parent,option,index):
if index.column()== SCORE:
spinbox = QSpinBox(parent)
spinbox.setRange(0,100)
spinbox.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
return spinbox
elif index.column()in(NAME,SUBJECT):
editor = QLineEdit(parent)
# self.connect(editor,SIGNAL("returnPressed()"),self.commitAndCloseEditor)
return editor
elif index.column()== DESCRIPTION:
editor = QTextEdit()
# self.connect(editor,SIGNAL("returnPressed()"),self.commitAndCloseEditor)
return editor
else:
return QStyledItemDelegate.createEditor(self,parent,option,index)
def commitAndCloseEditor(self):
editor = self.sender()
if isinstance(editor,(QTextEdit,QLineEdit)):
self.emit(SIGNAL("commitData(QWidget*)"),editor)
self.emit(SIGNAL("closeEditor(QWidget*)"),editor)
def setEditorData(self,editor,index):
text = index.model().data(index,Qt.DisplayRole)
if index.column()== SCORE:
try:
value = int(float(text)+ 0.5)
except:
value = 0
editor.setValue(value)
elif index.column()in(NAME,SUBJECT):
editor.setText(text)
elif index.column()== DESCRIPTION:
editor.setHtml(text)
else:
QStyledItemDelegate.setEditorData(self,editor,index)
def setModelData(self,editor,model,index):
if index.column()== SCORE:
model.setData(index,editor.value())
elif index.column()in(NAME,SUBJECT):
model.setData(index,editor.text())
elif index.column()== DESCRIPTION:
model.setData(index,editor.toHtml())
else:
QStyledItemDelegate.setModelData(self,editor,model,index)
class DateColumnDelegate(QStyledItemDelegate):
def __init__(self,minimum=QDate(),maximum=QDate.currentDate(),format="yyyy-MM-dd",parent=None):
super(DateColumnDelegate,self).__init__(parent)
self.minimum = minimum
self.maximum = maximum
self.format = format
def createEditor(self,parent,option,index):
dateedit = QDateEdit(parent)
dateedit.setDateRange(self.minimum,self.maximum)
dateedit.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
dateedit.setDisplayFormat(self.format)
dateedit.setCalendarPopup(True)
return dateedit
def setEditorData(self,editor,index):
value = index.model().data(index,Qt.DisplayRole)
try:
date = datetime.datetime.strptime(value,'%Y-%m-%d').date()
editor.setDate(QDate(date.year,date.month,date.day))
except:
print(value,index)
editor.setDate(QDate())
def setModelData(self,editor,model,index):
model.setData(index,editor.date().toString('yyyy-MM-dd'))
class IntegerColumnDelegate(QStyledItemDelegate):
def __init__(self,minimum=0,maximum=100,parent=None):
super(IntegerColumnDelegate,self).__init__(parent)
self.minimum = minimum
self.maximum = maximum
def createEditor(self,parent,option,index):
spinbox = QSpinBox(parent)
spinbox.setRange(self.minimum,self.maximum)
spinbox.setAlignment(Qt.AlignRight | Qt.AlignVCenter)
return spinbox
def setEditorData(self,editor,index):
value = int(index.model().data(index,Qt.DisplayRole))
editor.setValue(value)
def setModelData(self,editor,model,index):
editor.interpretText()
model.setData(index,editor.value())
class QTableViewDemo(QMainWindow):
def __init__(self,parent=None):
super(QTableViewDemo,self).__init__(parent)
self.setWindowTitle("QTableDelegate案例")
self.resize(550,600)
# 方式1:基于自定义模型的自定义委托
self.tableView = QTableView()
self.model = StudentTableModel()
self.delegate = StudentTableDelegate()
self.model.initData()
self.tableView.setModel(self.model)
self.selectModel = QItemSelectionModel()
self.tableView.setSelectionModel(self.selectModel)
self.tableView.setItemDelegate(self.delegate)
self.tableView.horizontalHeader().setStretchLastSection(True)
# 方式2:通用模型的通用委托
self.tableView2 = QTableView()
self.model2 = QStandardItemModel(5,4)
self.init_model2()
self.tableView2.setModel(self.model2)
self.delegate2 = IntegerColumnDelegate()
self.tableView2.setItemDelegateForColumn(2,self.delegate2)
self.tableView2.setItemDelegateForColumn(3,DateColumnDelegate())
self.buttonAddRow = QPushButton('增加行')
self.buttonInsertRow = QPushButton('插入行')
self.buttonDeleteRow = QPushButton('删除行')
self.buttonAddRow.clicked.connect(self.onAdd)
self.buttonInsertRow.clicked.connect(self.onInsert)
self.buttonDeleteRow.clicked.connect(self.onDelete)
self.model.setData(self.model.index(3,1),'Python',role=Qt.EditRole)
layout = QVBoxLayout(self)
layout.addWidget(self.tableView)
layoutH = QHBoxLayout()
layoutH.addWidget(self.buttonAddRow)
layoutH.addWidget(self.buttonInsertRow)
layoutH.addWidget(self.buttonDeleteRow)
layout.addLayout(layoutH)
layout.addWidget(self.tableView2)
widget = QWidget()
self.setCentralWidget(widget)
widget.setLayout(layout)
def init_model2(self):
for row in range(self.model2.rowCount()):
for column in range(self.model2.columnCount()):
if column == 2:
value = column + row
elif column == 3:
date = datetime.datetime.strptime('2022-01-01','%Y-%m-%d')+ datetime.timedelta(days=column * row)
value = datetime.datetime.strftime(date,'%Y-%m-%d')
else:
value ="row %s,col %s"%(row,column)
item = QStandardItem(str(value))
self.model2.setItem(row,column,item)
def onAdd(self):
rowCount = self.model.rowCount()
self.model.insertRow(rowCount)
def onInsert(self):
index = self.tableView.currentIndex()
row = index.row()
self.model.insertRow(row)
def onDelete(self):
index = self.tableView.currentIndex()
row = index.row()
self.model.removeRow(row)
if __name__ =="__main__":
app = QApplication(sys.argv)
demo = QTableViewDemo()
demo.show()
sys.exit(app.exec())
QConcatenateTablesProxyModel代理链接
QConcateateTablesProxyModel类代理多个源模型,将它们的行连接起来。
QConcatenateTablesProxyModel采用多个源模型并连接它们的行。
换句话说,代理将具有第一个源模型的所有行,然后是第二个源模型,依此类推。
如果源模型的列数不相同,则代理的列数将仅与列数最小的源模型的数相同。其他源模型中的其他列将被简单地忽略。
可以在运行时添加和删除源模型,并相应地调整列数。
此代理不继承自QAbstractProxyModel,因为它使用多个源模型,而不是单个源模型。
仅支持平面模型(列表和表),不支持树模型。
from PySide6.QtCore import QConcatenateTablesProxyModel
QConcatenateTQConcatenateTablesProxyModel(parent: Union[PySide6.QtCore.QObject, NoneType] = None)-> NoneablesProxyModel
构造具有给定父级的连接行代理模型。
方法 | 描述 |
---|---|
addSourceModel(self, sourceModel: PySide6.QtCore.QAbstractItemModel)-> None | 在以前添加的所有源模型下方添加源模型sourceModel。 sourceModel的所有权不受此影响。 同一源模型不能添加多次。 |
mapFromSource(self, sourceIndex: Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex])-> PySide6.QtCore.QModelIndex | 返回给定sourceIndex的代理索引,该索引可以来自任何源模型。 |
mapToSource(self, proxyIndex: Union[PySide6.QtCore.QModelIndex, PySide6.QtCore.QPersistentModelIndex])-> PySide6.QtCore.QModelIndex | 返回给定proxyIndex的源索引。 |
removeSourceModel(self, sourceModel: PySide6.QtCore.QAbstractItemModel)-> None | 删除以前添加到此代理的源模型sourceModel。 sourceModel的所有权不受此影响。 |
sourceModels(self)-> List[PySide6.QtCore.QAbstractItemModel] | 返回已添加为此代理模型的源模型的模型列表。 |