Model/View结构
视图(view)是显示和编辑数据的界面组件,模型(model)是视图与原始数据之间的接口
基本原理
将界面组件与所编辑的数据分离开来,又通过数据源的方式连接起来,是处理界面与数据的一种较好的方式
模型、视图和代理之间使用信号和槽通信。当源数据发生变化时,数据模型发射信号通知视图组件;当用户在界面上操作数据时,视图组件发射信号表示这些操作信息;当编辑数据时,代理发射信号告知数据模型和视图组件编辑器的状态。
数据模型
上图中的抽象类是不能直接使用的,需要由子类继承来实现一些纯虚函数。常见的如下:
如果上述模型类无法满足需求,用户可以从QAbstractItemModel、QAbstractListModel或QAbstractTableModel继承,生成自己定制的数据模型类。
视图组件
QListView:用于显示单列的列表数据,适用于一维数据的操作
QTreeView:用于显示树状结构数据,适用于树状结构数据的操作
QTableView:用于显示表格状数据,适用于二维表格型数据的操作
QColumnView:用多个QListView显示树状层次结构,树状结构的一层用一个QListView显示
QHeaderView:提供行表头或列表头的视图组件,如QTableView的行表头和列表头
视图组件在显示数据时,只需调用视图类的setModel()函数,为视图设置一个数据模型就可以实现视图组件与数据模型之间的关联,在视图组件上的修改将自动保存到关联的数据模型里,一个数据模型可以同时在多个视图组件里显示数据。
代理
代理(Delegate)就是在视图组件上为编辑数据提供编辑器,负责从数据模型获取相应的数据,然后显示在编辑器里,修改数据后,又将其保存到数据模型中。
QAbstractItemDelegate是所有代理类的基类,作为抽象类,它不能直接使用。它的一个子类QStyleItemDelegate,是Qt的视图组件缺省使用的代理类。
概念
数据模型的表现形式
数据模型中存储数据的基本单元都是项(item),每个项有一个行号、一个列号,还有一个父项(parent item)。在列表和表格模式下,所有的项都有一个相同的顶层项(root item)。
模型索引
QModelIndex表示模型索引的类。模型索引提供数据存取的一个临时指针,用于通过数据模型提取或修改数据。
行列号
通过模型索引的行号和列号就可以存取数据。
父项
树状结构中,项一般称为节点,在构造数据项的模型索引时,必须指定正确的行号、列号和父节点。
自定义代理
功能
根据不同情况使用不同的编辑组件进行数据文件的修改
基本设计要求
层次结构
不管从QStyledItemDelegate还是QItemDelegate继承设计自定义代理组件,都必须实现如下的四个函数:
-
createEditor()函数创建用于编辑模型数据的widget组件,如一个QSpinBox组件,或一个QComboBox组件
-
setEditorData()函数从数据模型获取数据,提供widget组件进行编辑
-
setModelData()将widget上的数据更新到数据模型;
-
updateEditorGeometry()用于给widge组件设置一个合适的大小
自定义代理类的实现
-
关于QItemDelegate基类的自定义代理类实现
基于QComboBox的自定义代理类
#ifndef QWCOMBOBOXDELEGATE_H
#define QWCOMBOBOXDELEGATE_H
#include<QItemDelegate>
class QWComboBoxDelegate:public QItemDelegate
{
Q_OBJECT
public:
QWComboBoxDelegate(QObject *parent=0);
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const;
void setEditorData(QWidget *editor,const QModelIndex &index) const Q_DECL_OVERRIDE;
void setModelData(QWidget *editor,QAbstractItemModel *model,const QModelIndex &index) const Q_DECL_OVERRIDE;
void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option,const QModelIndex &index) const Q_DECL_OVERRIDE;
};
#endif // QWCOMBOBOXDELEGATE_H
#include "qwcomboboxdelegate.h"
#include<QComboBox>
QWComboBoxDelegate::QWComboBoxDelegate(QObject *parent):QItemDelegate(parent)
{
}
QWidget *QWComboBoxDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
QComboBox *comboBox=new QComboBox(parent);
comboBox->setFrame(false);
QStringList strList;
strList<<"优"<<"良"<<"一般"<<"不合格";
comboBox->addItems(strList);
return comboBox;
}
void QWComboBoxDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
//从数据模型获取数据,显示到代理组件中
QString str=index.model()->data(index,Qt::EditRole).toString();
QComboBox *comboBox=static_cast<QComboBox*>(editor);
comboBox->setCurrentText(str);
}
void QWComboBoxDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
//将代理组件的数据保存到数据模型中
QComboBox *comboBox=static_cast<QComboBox*>(editor);
QString str=comboBox->currentText();
model->setData(index,str,Qt::EditRole);
}
void QWComboBoxDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
editor->setGeometry(option.rect);
}
基于QSpinBox的自定义代理类的实现
#ifndef QWINTSPINDELEGATE_H
#define QWINTSPINDELEGATE_H
#include<QStyledItemDelegate>
class QWIntSpinDelegate:public QStyledItemDelegate
{
Q_OBJECT
public:
QWIntSpinDelegate(QObject *parent=0);
//自定义代理组件必须继承以下4个函数
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const Q_DECL_OVERRIDE;
void setEditorData(QWidget *editor,const QModelIndex &index) const Q_DECL_OVERRIDE;
void setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const Q_DECL_OVERRIDE;
void updateEditorGeometry(QWidget *editor,const QStyleOptionViewItem &option,const QModelIndex &index) const Q_DECL_OVERRIDE;
};
#endif // QWINTSPINDELEGATE_H
#include "qwintspindelegate.h"
#include<QSpinBox>
QWIntSpinDelegate::QWIntSpinDelegate(QObject *parent):QStyledItemDelegate(parent)
{
}
QWidget *QWIntSpinDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
//创建代理编辑组件
QSpinBox *editor=new QSpinBox(parent);
editor->setFrame(false);
editor->setMinimum(0);
editor->setMaximum(10000);
return editor;
}
void QWIntSpinDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
//从数据模型获取数据,显示到代理组件中
int value=index.model()->data(index,Qt::EditRole).toInt();
QSpinBox *spinBox=static_cast<QSpinBox*>(editor);
spinBox->setValue(value);
}
void QWIntSpinDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{//将代理组件的数据保存到数据模型中
QSpinBox *spinBox=static_cast<QSpinBox*>(editor);
spinBox->interpretText();
int value=spinBox->value();
model->setData(index,value,Qt::EditRole);
}
void QWIntSpinDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
//设置组件的大小
editor->setGeometry(option.rect);
}
自定义代理类的使用
mainwindow.h中加入头文件名
声明类
//自定义代理类的使用
QWIntSpinDelegate intSpinDelegate;//整形数
QWFloatSpinDelegate floatSpinDelegate;//浮点数
QWComboBoxDelegate comboBoxDelegate;//列表选择
mainwindow.cpp中使用
//为各列设置自定义代理组件
ui->tableView->setItemDelegateForColumn(0,&intSpinDelegate);
ui->tableView->setItemDelegateForColumn(1,&floatSpinDelegate);
ui->tableView->setItemDelegateForColumn(2,&floatSpinDelegate);
ui->tableView->setItemDelegateForColumn(3,&floatSpinDelegate);
ui->tableView->setItemDelegateForColumn(4,&comboBoxDelegate);