目录
1 我们为什么要使用自定义代理类?
2 自定义代理类的基本设计要求
3 自定义代理的功能
4 基于QSpinBox的自定义代理类
5 自定义代理类的使用
1 我们为什么要使用自定义代理类?
传统的模型-视图框架可以让我们实现逻辑展示相分离,我们只需要关心模型就可以,在模型上做的任何更改都会走动更新到视图上去,其实就是观察者模式。Qt基于传统的模型-视图框架(MVC,model、view、controler),推出了MVD(model、view、delegate)框架。具体有什么用呢?
如果我们使用QTableView来作为表格控件,显示数据,但是突然有需求需要在里面添加一些交互的控件,例如:按钮、进度条之类的,直接使用模型是无法实现的。可以使用QTableWidget控件可以很轻松的实现,但是这个不支持模型-视图框架。代理的作用就来了,代理可以把控件委托给模型来代理,就可以实现在表格中添加控件进行交互了。
2 自定义代理类的基本设计要求
Qt中有关代理的几个类的层次结构如图5-12所示。
QAbstractItemDelegate是所有代理类的抽象基类,QStyledItemDelegate是视图组件使用的缺省的代理类,QItemDelegate也是类似功能的类。QStyledItemDelegate与QItemDelegate的差别在于:QstyledltemDelegate可以使用当前的样式表设置来绘制组件,因此建议使用QStyledItemDelegate作为自定义代理组件的基类。
不管从QStyledItemDelegate还是QItemDelegate继承设计自定义代理组件,都必须实现如下 的4个函数:
- createEditor()函数创建用于编辑模型数据的widget组件,如一个QSpinBox组件,或一个 QComboBox组件:
- setEditorData()函数从数据模型获取数据,供widget组件进行编辑;
- setModelData()将widget上的数据更新到数据模型;
- updateEditorGeometry()用于给widget组件设置一个合适的大小。
3 自定义代理的功能
在实例samp5_3中,导入数据文件进行编辑时,QTableView组件为每个单元格提供的 是缺省的代理编辑组件,就是一个QLineEdit组件。在编辑框里可以输入任何数据,所以比较通用。 但是有些情况下,希望根据数据的类型限定使用不同的编辑组件,例如在samp5_3的实例的数据中, 第1列“测深”是整数,使用QSpinBox作为编辑组件更合适;“垂深”“方位”“总位移”是浮点数, 使用QDoubleSpinBox更合适;而“固井质量”使用 一个QComboBox,从一组列表文字中选择更合适。
要实现这些功能,就需要为TableView的某列 或某个单元格设置自定义代理组件。本节在实例 samp5_3的基础上,为TableView增加自定义代理 组件功能。设定自定义代理组件之后的TableView 运行时,其编辑状态的效果如图5-11所示。
图5-11 设置自定义代理组件后的TableView编辑时的效果
4 基于QSpinBox的自定义代理类
1、自定义代理类的基本结构
下面设计一个基于QSpinBox类的自定义代理类,用于“测深”数据列的编辑。
定义一个自定义类的名称为QWIntSpinDelegate, 设置基类为QStyledItemDelegate,添加到项目里 。 在头文件qwintspindelegate.h中包含对自定义类QWIntSpinDelegate的定义,在其中添加4个 需要重定义的函数的定义,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;
};
自定义代理组件必须重新实现这4个函数,函数的原型都是固定的。
2、createEditor()函数的实现
createEditor()函数用于创建需要的编辑组件,QWIntSpinDelegate类希望创建一个QSpinBox 作为编辑组件,函数的实现如下:
QWidget *QWIntSpinDelegate::createEditor(QWidget *parent,const QStyleOptionViewItem &option, const QModelIndex &index) const
{ //创建代理编辑组件QSpinBox *editor = new QSpinBox(parent); //创建一个QSpinBoxeditor->setFrame(false); //设置为无边框editor->setMinimum(0);editor->setMaximum(10000);return editor; //返回此编辑器
}
这段代码创建了一个QSpinBox类型的编辑器editor,parent指向视图组件;然后对创建的editor做一些设置,将editor作为函数的返回值。
3、setEditorData()函数
setEditorData()函数用于从数据模型获取数值,设置为编辑器的显示值。当双击一个单元格进 入编辑状态时,就会自动调用此函数,其实现代码如下:
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); //设置编辑器的数值
}
函数传递来的参数editor指向代理编辑组件,index是关联的数据单元的模型索引。 通过强制类型转换将editor转换为QSpinBox类型组件spinBox,然后将获取的数值设置为 spinBox的值。
4、setModeIData()函数
setModelData()函数用于将代理编辑器上的值更新给数据模型,当用户在界面上完成编辑时会 自动调用此函数,将界面上的数据更新到数据模型。其代码如下:
void QWIntSpinDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{ //将代理组件的数据,保存到数据模型中QSpinBox *spinBox = static_cast<QSpinBox*>(editor); //强制类型转换spinBox->interpretText(); //解释数据,如果数据被修改后,就触发信号int value = spinBox->value(); //获取spinBox的值model->setData(index, value, Qt::EditRole); //更新到数据模型
}
程序先获取代理组件编辑器里的数值,然后利用传递来的数据模型model和模型索引参数 index将编辑器的最新值更新到数据模型里。
5、updateEditorGeometry()函数
updateEditorGeometry()函数用于为代理组件设置一个合适的大小,函数传递的参数option的 rect变量定义了单元格适合显示代理组件的大小,直接设置为此值即可。代码如下。
void QWIntSpinDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const
{ //设置组件大小editor->setGeometry(option.rect);
}
5 自定义代理类的使用
同样的,可以创建基于QDoubleSpinBox的自定义代理组件类QWFloatSpinDelegate,用于编 辑浮点数,还可以创建基于QComboBox的自定义组件类QWComboBoxDelegate。在主窗口的类 定义中定义3个代理类的实例变量(省略了其他定义内容):
class MainWindow : public QMainWindow
{Q_OBJECT
private:QWIntSpinDelegate intSpinDelegate; //整型数QWFloatSpinDelegate floatSpinDelegate; //浮点数QWComboBoxDelegate comboBoxDelegate; //列表选择
}
在MainWindow的构造函数中,为tableView的某些列设置自定义代理组件。增加了自定义代理组件的构造函数代码如下(去掉了初始化状态栏等一些不重要的内容):
MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow)
{ui->setupUi(this);theModel = new QStandardItemModel(2,FixedColumnCount,this); //创建数据模型theSelection = new QItemSelectionModel(theModel);//Item选择模型connect(theSelection,SIGNAL(currentChanged(QModelIndex,QModelIndex)),this,SLOT(on_currentChanged(QModelIndex,QModelIndex)));//为tableView设置数据模型ui->tableView->setModel(theModel); //设置数据模型ui->tableView->setSelectionModel(theSelection);//设置选择模型//为各列设置自定义代理组件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); //Combbox选择型
}
为TableView的某一列设置自定义代理组件,使用setltemDelegateForColumn()函数:为某一 行设置自定义代理组件,可使用setltemDelegateForRow()函数:若为整个TableView设置一个自定 义代理组件,则调用setltemDelegate()函数。
如此增加了自定义代理功能后,在编辑“测深”列时,会在单元格的位置出现一个 SpinBox 组件,用于输入整数;在编辑第2至4列的浮点数时,会出现一个DoubleSpinBox组件,用于输 入浮点数;而在编辑“固井质量”列时,会自动出现一个ComboBox组件,用于从下拉列表中选 择一个字符串。