Qt模型/视图原理(3):自定义委托(QAbstractItemDelegate)
本文为原创文章,转载请注明出处,或注明转载自“黄邦勇帅(原名:黄勇)
本文出自本人原创著作《Qt5.10 GUI完全参考手册》网盘地址:
https://pan.baidu.com/s/1iqagt4SEC8PUYx6t3ku39Q
《C++语法详解》网盘地址:https://pan.baidu.com/s/1dIxLMN5b91zpJN2sZv1MNg
若对C++语法不熟悉,建议参阅本人所著《C++语法详解》一书,电子工业出版社出版,该书语法示例短小精悍,对查阅C++知识点相当方便,并对语法原理进行了透彻、深入详细的讲解,可确保读者彻底弄懂C++的原理,彻底解惑C++,使其知其然更知其所以然。此书是一本全面了解C++不可多得的案头必备图书。
8.5.1 QAbstractItemDelegate基本原理
QAbstractItemDelegate类继承自QObject。委托用于显示视图中的单个项目,并处理模型数据的编辑。QAbstractDelegate的子类QItemDelegate和QStyleItemDelegate是Qt提供的对QAbstractDelegate类的默认实现。
若需要以自定义方式渲染项目,则必须重新实现paint()和sizeHint()函数。可使用如下两种方法实现自定义的编辑:
1)、方法1:创建一个编辑器部件,并将其设置为项目的编辑器,此方法必须重新实现createEditor()函数,并使用setEditorData()函数从模型中获取数据用于编辑器,使用setModelData()把编辑器的内容写入模型中。
2)、方法2:重新实现editorEvent()函数,直接处理用户事件。
8.5.2 QAbstractItemDelegate类中的函数
注意:QAbstractItemDelegate类中的函数都是虚函数,这些函数的参数都附带有必要的信息,比如对于paint() 函数的index参数,就是表示需要绘制的模型的索引,且index.data()函数包含有来自模型的数据。在重新实现这些虚函数时,应合理使用这些参数所附带的信息。
1、QAbstractItemDelegate(QObject* parne = Q_NULLPTR); //构造函数
2、纯虚函数(自定义渲染)
3、编辑器
4、事件
5、信号
8.5.3 QStyleOptionViewItem类
QStyleOptionViewItem类继承自QStyleOption。
1、Qt绘制控件基本原理
Qt内置的部件的外观几乎都是由QStyle类的成员函数进行绘制的,使用这些函数绘制部件时需要向函数提供一些所需绘制图形元素的信息,而这些信息是由QStyleOption类及其子类进行描述的,QStyleOption类的不同子类描述了不同图形元素所需的信息,比如QStyleOptionButton描述了绘制按钮所需的有关信息等。因此模型/视图结构中的QStyleOptionViewItem类描述了绘制数据项所需的有关信息
2、QStyleOptionViewItem类中的函数
3、QStyleOptionViewItem类中的成员变量
4、除以上成员变量外,从QStyleOption继承而来的成员变量对项目的绘制也有影响,其中有两个比较重要,如下
示例8.9:使用委托绘制自定义的项目
//m.h文件的内容
#ifndef M_H
#define M_H
#include<QtWidgets>
#include<QDebug>
//本示例仅重新实现了QAbstractItemDelegate类中的两个纯虚函数
class T:public QAbstractItemDelegate{
public: T(QObject *parent = 0):QAbstractItemDelegate(parent){} //构造函数
//重新实现paint函数,以绘制每个单元格(项目)
void paint(QPainter *painter,const QStyleOptionViewItem &p1,
const QModelIndex &index) const{
//为第1行1列的项目绘制一个进度条
if (index.row()==1&&index.column() == 1) {
int i = index.data().toInt();
QStyleOptionProgressBar p; //该类用于设置绘制进度条时所需的信息
p.rect = p1.rect; /*设置绘制进度条的区域(使用Qt自动获取的区域)。此项必须设置,否则图形不知绘制于何处。*/
p.minimum = 0; //进度条的最小值
p.maximum = 100; //进度条的最大值
p.progress = i; //进度条的当前进度(来自模型中的数据)
p.text = QString::number(i) + "%"; //进度条当前显示的值
p.textVisible = true;
/*使用QStyle::drawControl()成员函数绘制进度条,由此可见,项目上具体需要显示什么,完全可以由程序员自行绘制。*/
QApplication::style()->drawControl(
QStyle::CE_ProgressBar, //该参数表示绘制进度条
&p,painter); }
//绘制第2行1列的项目,该项目会绘制一个图标和一个文本
else if(index.row()==2 && index.column() == 1){
QStyleOptionViewItem p; //该类用于设置绘制模型/视图的数据项时所需的信息
p.index=index; //设置需要绘制的项目的索引,此项不是必须设置项
p.rect=p1.rect; /*绘制项目的区域(使用Qt自动获取的区域),此项必须设置,否则图形不知绘制于何处。*/
//要显示图标,以下选项必须设置
p.features=QStyleOptionViewItem::HasDecoration //要显示图标,必须包含该特征
|QStyleOptionViewItem::HasDisplay;
p.decorationSize=QSize(55,55); /*设置装饰(通常为图标)的大小,若不设置该选项,则因装饰大小为无效大小(-1,-1)而无法绘制。*/
p.icon=index.data(Qt::DecorationRole).value<QIcon>(); //需要绘制的图标
//要使项目被选择时高亮显示,需设置以下选项
p.state=p1.state; //设置项目的样式标志(即项目是否被选择,是否启用等)
qDebug()<<p1.state; //读者可观察项目样式标志的变化情况
p.showDecorationSelected=true; //设置为true。
if (p1.state & QStyle::State_Selected) //若项目被选择,则高亮绘制其矩形
painter->fillRect(p1.rect, p1.palette.highlight());
//其他一些设置
p.decorationPosition=QStyleOptionViewItem::Bottom; //装饰(图标)显示在文字下方
p.displayAlignment=Qt::AlignLeft|Qt::AlignHCenter; //设置项目文本的对齐方式
p.text=index.data().toString(); //设置项目需要显示的文本
//绘制项目
QApplication::style()->drawControl(
QStyle::CE_ItemViewItem, //该参数表示,绘制模型/视图的项目
&p, painter); }
//绘制第2行0列的项目,本示例将该项目绘制于其他地方(即该项目未位于视图的单元格之内)
else if(index.row()==2&&index.column() == 0){
QStyleOptionViewItem p;
p.features=QStyleOptionViewItem::HasDisplay
|QStyleOptionViewItem::HasCheckIndicator; //该项目可被选中
p.rect=QRect(199,144,111,44); //该项目位于此处
p.state=p1.state;
p.showDecorationSelected=true;
p.text=index.data().toString();
p.backgroundBrush=QBrush(QColor(1,111,1)); //设置背景色(绿色)
QFont f; f.setPixelSize(22); p.font=f; //设置字体
p.displayAlignment=Qt::AlignLeft|Qt::AlignVCenter; //设置对齐方式
QApplication::style()->drawControl( QStyle::CE_ItemViewItem, &p,painter);}
//绘制其他项目
else { QStyleOptionViewItem p;
p.features=QStyleOptionViewItem::HasDisplay;
p.index=index; p.rect=p1.rect; p.state=p1.state;
p.showDecorationSelected=true;/*设置为true,否则无内容的项目被选择时不会高亮显示。*/
p.text=index.data().toString();
QApplication::style()->drawControl(QStyle::CE_ItemViewItem,&p, painter);
} } //函数paint结束
//此函数对本例没有影响,可返回任意值。
QSize sizeHint(const QStyleOptionViewItem &option,const QModelIndex &index) const
{ return QSize(0,0); }
}; //类T结束
class B:public QWidget{ Q_OBJECT
public: QStandardItemModel *d; QTableView *pv2; T *pt;
B(QWidget *p=0):QWidget(p){
pv2=new QTableView(this); pv2->move(22,22); pv2->resize(333,222);
d=new QStandardItemModel(3,3,this);
//向模型中添加数据
d->setData(d->index(0,0,QModelIndex()),"111",Qt::DisplayRole);
d->setData(d->index(1,0,QModelIndex()),222);
d->setData(d->index(1,1,QModelIndex()),33);
d->setData(d->index(2,0,QModelIndex()),"XXXX");
d->setData(d->index(2,1,QModelIndex()),QIcon("F:/1i.png"),Qt::DecorationRole);
d->setData(d->index(2,1,QModelIndex()),11);
d->setData(d->index(2,1,QModelIndex()),11,Qt::ToolTipRole);
d->setData(d->index(2,2,QModelIndex()),333);
pv2->setModel(d); //设置模型
//设置视图的委托为自定义委托
pt=new T(this); pv2->setItemDelegate(pt); }};
#endif // M_H
//m.cpp文件的内容
#include "m.h"
int main(int argc, char *argv[]){
QApplication aa(argc,argv);
B w; w.resize(444,355);
w.show();return aa.exec();}
运行结果及说明见图8-35
示例8.10:使用自定义的编辑器
//本示例只需把以下代码直接复制到示例8.9的类T之中即可。
//重新实现createEditor以便为每个单元格创建各自的编辑器
QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem &option,
const QModelIndex &index) const {
//创建两种类型的编辑器,以用于不同数据类型
QLineEdit *pe=new QLineEdit(parent); QSpinBox *ps=new QSpinBox(parent);
pe->setObjectName("EEE"); ps->setObjectName("SSS");
//若单元格的数据是文本或无效数据,则使用pe编辑器进行编辑
if(index.data().type()==QMetaType::QString||index.data().isNull()){
pe->setFocusPolicy(Qt::TabFocus); return pe; }
//否则使用ps编辑器进行编辑
else { ps->setMaximum(1000); return ps; }
}
//重新实现setEditorData以便把单元格的数据读入到编辑器中
void setEditorData(QWidget *e, const QModelIndex &index) const{
//若单元格之前是文本或无效数据,则把模型中的数据读入到QLineEdit类型的编辑器中
if(index.data().type()==QMetaType::QString||index.data().isNull()){
((QLineEdit*)e)->setText(index.data().toString()); }
//否则把模型中的数据读入到QSpinBox类型的编辑器中
else { ((QSpinBox*)e)->setValue(index.data().toInt());}
}
//重新实现setModelData以便把编辑器中的数据写入到单元格(即模型)
void setModelData(QWidget *e, QAbstractItemModel *m,const QModelIndex &index) const{
//若单元格之前是文本或无效数据,则把QLineEdit类型编辑器的数据写入模型
if(index.data().type()==QMetaType::QString||index.data().isNull())
{ m->setData(index,((QLineEdit*)e)->text()); }
//否则把QSpinBox类型编辑器的数据写入模型
else { m->setData(index,((QSpinBox*)e)->value());}
}
//设置编辑器的几何尺寸
void updateEditorGeometry(QWidget*e,const QStyleOptionViewItem &option,
const QModelIndex &index)const{
e->setGeometry(option.rect); //只需把编辑器的尺寸(位置和大小)设置为单元格的尺寸即可。
}
//该函数可以不需重新实现,可使用默认的实现方式
void destroyEditor(QWidget *e, const QModelIndex &index) const{
qDebug()<<e->objectName(); //查看删除的编辑器
delete e; } //使用delete删除编辑器,当然,你也可以不删除编辑器。
运行结果及说明见图8-36
本文作者:黄邦勇帅(原名:黄勇)