Model/View/Delegate基本用法

1.1 Qt中MVD的基本概念解析

模型视图委托(MVD)是Qt中特有的设计模式,类似MVC设计模式,将MVC设计模式中的Controller当做MVD中的Delegate,两者的概念基本相同。不同的是委托不是独立存在,而是包含在视图里面。
模型视图委托设计模式中,模型负责存储和管理数据;视图负责显示数据,其中界面的框架和基础信息是视图负责,具体数据的显示是委托负责;委托不仅仅负责数据的显示,还有一个重要的功能是负责数据的编辑,如在视图中双击就可以编辑数据。

1.2Model:存储数据的模型

Qt中的Model有许多种,均继承至QAbstractItemModel,常用的Model有以下几种QStandardItemModel,QFileSystemModel,QDirModel,QSqlTableModel等等几种模型,本例使用QStandardItemModel。

1.2.1数据在Model中的存储方式以及如何索引

对于QStandardItemModel,数据的存储是比较灵活的,存储形式可以是表格/列表/树等形式,但受View的约束,比如,listView就不能显示列表和树,Model中数据的索引是根据row与column,对于只有一行的数据,他的列索引号就是0。因此,对于QStandardItemModel不仅可以存储线性的列表,也可以存储表格形式的数据和树形结构的数据,对于树形结构的索引相对比较麻烦。

1.2.2Model的存储模型示意图
ModelIndex.png

需要注意的是,对于treeModel,row的定义与其余两个模型有所不同,因此在索引时需要一层一层索引。下边看如下代码。

void Widget::initData()
{
    model = new QStandardItemModel;
    for(int i = 0; i <50; i++){
        QStandardItem *item1 = new QStandardItem;
        QStandardItem *item2 = new QStandardItem;
        item1->setData(QVariant::fromValue(i),Qt::DisplayRole);
        item2->setData(QVariant::fromValue(i+5),Qt::DisplayRole);
        model->setItem(i,0,item1);
        model->setItem(i,1,item2);
        qInfo()<<i<<endl;
    }
    ui->listView->setModel(model);
    ui->tableView->setModel(model);
    ui->treeView->setModel(model);
}

上述代码中,对于不同的View设置相同的Model,Model中的数据是一张表格形式的,可以看到,对于listView只显示了Item1的内容,对于treeView和TableView显示出了表格的形式。

QStandardItem *itemFromIndex(const QModelIndex &index) const;
QStandardItem *item(int row, int column = 0) const;

以QModelIndex方式索引:需要知道QModelIndex,QModelIndex类用于定位数据模型中的数据,与用row,column定位相同,QModelIndex中也包含row,column值可供访问,这个类用作从QAbstractItemModel派生的项目模型的索引。项目视图、委托和选择模型使用索引来定位模型中的项目。
比较重要的一点是:
可以使用QModelIndex构造函数构造无效的模型索引。当引用模型中的顶级项时,无效索引通常用作父索引,模型索引引用模型中的项,并包含指定它们在那些模型中的位置所需的所有信息。QModelIndex通常是作为信号参数传递,通过传递的QModelIndex对象可以访问对应的项。
例如,QAbstractItemView的信号函数

void activated(const QModelIndex &index)
void clicked(const QModelIndex &index)
void doubleClicked(const QModelIndex &index)
void entered(const QModelIndex &index)
void iconSizeChanged(const QSize &size)
void pressed(const QModelIndex &index)
void viewportEntered()
1.3 View的基本使用

常见的View有ListView/TableView/TreeView/QHeaderView/等,分别用于显示列表形式/表格形式/树形结构/表头部分栏目等。View显示Model中的数据,Model/View结构决定了当Model中的数据更新了时,View中的数据也会自动更新,View的细节设置参考帮助手册。

1.4Delegate的基本用法

对于Qt中的MVD架构来说,Model用于存储数据,View用来展示数据,大多数教程对于Delegate的解释是Delegaet可以编辑数据并将数据回写到Model并展示,其实Delegate的功能不全是提供一个自定义或Qt内置的Item编辑窗体,对于不需要编辑的项目,Delegate也可以通过重绘,提供一个展示Item内容的功能,这就类似于View是数据显示的一个框架,骨干部分,Delegate也可以作为骨干部分的Item项目的展示形式的自定义。

1.4.1 使用编辑交互窗体实现

对于自定义代理,一般是继承至QStyledItemDelegate,对于编辑类型的代理,需要重载一下四个函数。


virtual QWidget *
createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
virtual void 
setEditorData(QWidget *editor, const QModelIndex &index) const override
virtual void 
setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override
virtual void 
updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override

createEditor()函数返回一个QWidget对象指针,该Widget就是当编辑数据时,展示的Widget。一般对于简单的项,可以是一个QComboBox项,可以是QSpinBox项等等,也可以是自定义窗体。

//创建代理编辑组件
    QSpinBox *editor = new QSpinBox(parent); //创建一个QSpinBox
    editor->setFrame(false); //设置为无边框
    editor->setMinimum(0);
    editor->setMaximum(10000);

    return editor;  //返回此编辑器

setEditorData()函数用于给编辑窗体中设置Model中的数据,通过ModelIndex索引。

//获取数据模型的模型索引指向的单元的数据
    int value = index.model()->data(index, Qt::EditRole).toInt();

    QSpinBox *spinBox = static_cast<QSpinBox*>(editor);  //强制类型转换
    spinBox->setValue(value); //设置编辑器的数值

setModelData()函数用户将用户编辑好的数据回写到MOdel中去。

//将代理组件的数据,保存到数据模型中
    QSpinBox *spinBox = static_cast<QSpinBox*>(editor); //强制类型转换
    spinBox->interpretText(); //解释数据,如果数据被修改后,就触发信号
    int value = spinBox->value(); //获取spinBox的值
    model->setData(index, value, Qt::EditRole); //更新到数据模型

updateEditorGeometry() 该函数可以根据指定的样式,更新当前的编辑器。

//设置组件大小
    editor->setGeometry(option.rect);
1.4.1 使用重绘实现

当一个项目需要比较复杂的样式展示而不需要编辑功能时,可以使用重绘实现。
同样继承至QStyledItemDelegate,不需要重写上述四个函数,只需要重写以下两个函数。

virtual void 
paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
virtual QSize 
sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override

sizeHint()定义绘制区域的大小。
paint()完成绘制,绘制好的项会自动更新到View中。

未完待续........

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容