(四十)模型视图中的委托

来源:互联网 发布:为什么安装不了知乎 编辑:程序博客网 时间:2024/05/20 00:17

传统的MVC设计模式

模型负责组织数据

视图负责显示数据

控制器负责处理用户输入

 

Qt中模型视图设计模式

视图中继承了处理用户输入的功能                     

视图将用户输入作为内部独立的子功能而实现

 

 

模型视图中的委托

1、  抽象类QAbstractItemDelegate是委托类的基类

2、  Qt4.4版本后的标准委托是QStyledItemDelegate

3、  委托(Delegate)是视图中处理用户输入的部件

4、  视图可以设置委托对象用于处理用户输入

5、  委托对象负责创建和显示用户输入上下文(创建、显示编辑框)

6、  建议以QStyledItemDelegate为基类自定义委托

委托中的编辑器

1、  委托能够提供编辑时需要的上下文环境(编辑器)

2、  不同的委托提供的编辑器类型不同(文本框、单选框等等)

3、  编辑器从模型获取数据,并将编辑结果返回模型

 

交互流程

 

委托中的关键函数

virtual QWidget *

createEditor ( QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const

需要编辑数据是,创建编辑器组件

 

virtual void

updateEditorGeometry ( QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const

更新编辑器组件大小

 

virtual void

setEditorData ( QWidget * editor, const QModelIndex & index ) const

通过索引index 从模型中获取数据

 

virtual void

setModelData ( QWidget * editor, QAbstractItemModel * model, const QModelIndex & index ) const

将编辑后的新数据返回模型

 

委托中的关键信号

void

closeEditor ( QWidget * editor, QAbstractItemDelegate::EndEditHint hint = NoHint )

编辑器组件关闭信号

 

void

commitData ( QWidget * editor )

新数据提交信号

 

 

//验证上述交互流程

 

//main.cpp

 

#include <QtGui/QApplication>#include "Widget.h" int main(int argc, char *argv[]){   QApplication a(argc, argv);   Widget w;   w.show();      return a.exec();}



//SubStyledItemDelegate.h

 

#ifndef SUBSTYLEDITEMDELEGATE_H#define SUBSTYLEDITEMDELEGATE_H #include <QStyledItemDelegate> class SubStyledItemDelegate : publicQStyledItemDelegate{   Q_OBJECTprotected slots:   void onCloseEditor(QWidget* editor);    //关闭编辑器组件的信号的槽函数   void onCommitData(QWidget* editor);  //数据提交信号的槽函数public:      //下面的函数都由视图对象调用   explicit SubStyledItemDelegate(QObject* parent = 0);   QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem&option, const QModelIndex &index) const;   void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem&option, const QModelIndex &index) const;   void setEditorData(QWidget *editor, const QModelIndex &index) const;   void setModelData(QWidget *editor, QAbstractItemModel *model, constQModelIndex &index) const;}; #endif // SUBSTYLEDITEMDELEGATE_H


 

 

//SubStyledItemDelegate.cpp

 

#include"SubStyledItemDelegate.h"#include <QDebug> SubStyledItemDelegate::SubStyledItemDelegate(QObject*parent) :   QStyledItemDelegate(parent){   connect(this, SIGNAL(closeEditor(QWidget*)), this,SLOT(onCloseEditor(QWidget*)));   connect(this, SIGNAL(commitData(QWidget*)), this,SLOT(onCommitData(QWidget*)));} QWidget*SubStyledItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem&option, const QModelIndex &index) const  //创建编辑器组件{   qDebug() << "SubStyledItemDelegate::createEditor";    return QStyledItemDelegate::createEditor(parent, option, index);} voidSubStyledItemDelegate::updateEditorGeometry(QWidget *editor, constQStyleOptionViewItem &option, const QModelIndex &index) const    //更显编辑器组件的大小{   qDebug() <<"SubStyledItemDelegate::updateEditorGeometry";    QStyledItemDelegate::updateEditorGeometry(editor, option, index);} //将模型中的数据拿出来显示到编辑器中voidSubStyledItemDelegate::setEditorData(QWidget *editor, const QModelIndex&index) const     {   qDebug() << "SubStyledItemDelegate::setEditorData";    QStyledItemDelegate::setEditorData(editor, index);} //将编辑后的新数据返回模型voidSubStyledItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,const QModelIndex &index) const{   qDebug() << "SubStyledItemDelegate::setModelData";    return QStyledItemDelegate::setModelData(editor, model, index);} //编辑器组件关闭信号对应的槽函数voidSubStyledItemDelegate::onCloseEditor(QWidget* editor){   qDebug() << "SubStyledItemDelegate::onCloseEditor";} //新数据提交信号对应的槽函数,此信号在修改编辑器的内容后,在setModelData函数调用之前触发voidSubStyledItemDelegate::onCommitData(QWidget* editor){   qDebug() << "SubStyledItemDelegate::onCommitData";} 


 

//Widget.h


#ifndef WIDGET_H#define WIDGET_H #include <QtGui/QWidget>#include <QTableView>#include <QStandardItemModel>#include <QPushButton>#include"SubStyledItemDelegate.h" class Widget : public QWidget{   Q_OBJECT      QTableView m_view;   QStandardItemModel m_model;   QPushButton m_testBtn;   SubStyledItemDelegate m_delegate;    void initView();   void initModel(); private slots:   void onTestBtnClicked();public:   Widget(QWidget* parent = 0);   ~Widget();}; #endif // WIDGET_H


 

 

//Widget.cpp

 

#include "Widget.h"#include <QModelIndex>#include <QDebug> Widget::Widget(QWidget* parent) :QWidget(parent){   initView();   initModel();    m_view.setModel(&m_model);    m_testBtn.setParent(this);   m_testBtn.move(10, 120);   m_testBtn.resize(300, 30);   m_testBtn.setText("Test");    connect(&m_testBtn, SIGNAL(clicked()), this,SLOT(onTestBtnClicked()));} void Widget::initView(){   m_view.setParent(this);   m_view.move(10, 10);   m_view.resize(300, 100);   m_view.setItemDelegate(&m_delegate);} void Widget::initModel(){   QStandardItem* root = m_model.invisibleRootItem();   QStandardItem* itemA = new QStandardItem();   QStandardItem* itemB = new QStandardItem();   QStandardItem* itemC = new QStandardItem();   QStandardItem* itemD = new QStandardItem();    itemA->setData("A", Qt::DisplayRole);   itemB->setData("B", Qt::DisplayRole);   itemC->setData("C", Qt::DisplayRole);   itemD->setData("D", Qt::DisplayRole);    root->setChild(0, 0, itemA);   root->setChild(0, 1, itemB);   root->setChild(1, 0, itemC);   root->setChild(1, 1, itemD);} void Widget::onTestBtnClicked(){   qDebug() << "Model Data:";    for(int i=0; i<m_model.rowCount(); i++)    {        qDebug() << "Row: " <<i;       for(int j=0; j<m_model.columnCount(); j++)       {           QModelIndex index = m_model.index(i, j, QModelIndex());              QString text = index.data(Qt::DisplayRole).toString();              qDebug() << text;       }       qDebug() << endl;    }    qDebug() << "Current View Delegate: " <<m_view.itemDelegate();} Widget::~Widget(){   }


 

 

 

//输出

SubStyledItemDelegate::createEditorSubStyledItemDelegate::updateEditorGeometrySubStyledItemDelegate::setEditorDataSubStyledItemDelegate::onCommitDataSubStyledItemDelegate::setModelDataSubStyledItemDelegate::setEditorDataSubStyledItemDelegate::onCloseEditorModel Data:Row: 0"A""A+"  Row: 1"C""D"  Current View Delegate:  SubStyledItemDelegate(0x28fe60) 


 

委托的本质

1、  委托为视图提供数据编辑的上下文环境

2、  委托是产生界面元素的工厂类

3、  委托能够使用和设置模型中的数据

 

自定义委托类

自定义委托时需要重写的函数

virtual QWidget *

createEditor ( QWidget * parent, const QStyleOptionViewItem & option, const QModelIndex & index ) const

需要编辑数据是,创建编辑器组件

 

virtual void

updateEditorGeometry ( QWidget * editor, const QStyleOptionViewItem & option, const QModelIndex & index ) const

更新编辑器组件大小

 

virtual void

setEditorData ( QWidget * editor, const QModelIndex & index ) const

通过索引index 从模型中获取数据

 

virtual void

setModelData ( QWidget * editor, QAbstractItemModel * model, const QModelIndex & index ) const

将编辑后的新数据返回模型

 

virtual void

paint ( QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const         (可选)

绘制数据编辑器

 

伪代码

1、重写createEditor成员函数

//根据索引中值的类型创建编辑器组件

QWidget* createEditor(/*parameter*/) const{   QWidget* ret = NULL;      if(index.data().type() == QVariant::Bool){       /* create check box */   }else if(){       /* create combo box */   }else {       /*default*/    }           return ret;}


 

2、重写updateEditorGeometry成员函数

//根据参数中数据项的信息设置编辑器的位置和大小

void updateEditorGeometry(QWidget* editor,                          constQStyleOptionViewItem& option,                          constQModelIndex& index) const{   editor->setGeometry(option.rect);}


 

3、重写setEditorData成员函数

//根据参数中的数据索引设置编辑器中的初始数据

void setEditorData(QWidget* editor, constQModelIndex& index) const{   if( index.data().type() == QVariant::Bool ){       QCheckBox* cb = dynamic_cast<QCheckBox*>(editor);       /* set data to editor */   }else if( index.data().type() == QVariant::Char){       QCombobox* cb = dynamic_cast<QCombobox*>(editor);       /* set data to editor */         }else{       QItemDelegate::setEditorData(editor, index);       }}


 

4、重写setModelData成员函数

//根据参数中的数据索引更改模型中的数据

void setModelData(QWidget* editor,                  QAbstractItemModel* model,                  const QModelIndex& index)const{   if (index.data().type() == QVariant::Bool){       QCheckBox* cb = dynamic_cast<QCheckBox*>(editor);       /* set data to model from editor */   }else if(index.data().type() == QVariant::Char){       QCombobox* cb = dynamic_cast<QCombobox*>(editor);       /* set data to model from editor */   }else{       /* default action from parent class */    }}


 

5、重写paint成员函数(可选)

//根据参数中的信息绘制编辑器

void paint(QPainter* painter,          const QStyledOptionViewItem& option,          const QModelIndex& index) const{   if(/* condition */)    {       /* customized paint action */   }else{       QItemDelegate::paint(painter, option, index);           }}


 

 

// CustomizedItemDelegate.h

 

#ifndef CUSTOMIZEDITEMDELEGATE_H#define CUSTOMIZEDITEMDELEGATE_H #include <QItemDelegate>#include <QModelIndex> class CustomizedItemDelegate : public QItemDelegate{   Q_OBJECT    mutable QModelIndex m_index;     //被关键字mutable修饰的成员变量,即使是在const函数中也能被修改protected slots:   void onCloseEditor(QWidget*);public:         /*下面的函数都由视图对象调用,成员函数的参数携带了数据存取时需要的信息,所有函数的index参数都是当前被编辑的数据项的index,只不过paint函数会被调用多次,以对每一个数据项进行绘制,注意:要将函数createEditor、updateEditorGeometry、setEditorData、setModelData全部重写才会生成正常的editor*/   explicit CustomizedItemDelegate(QObject *parent = 0);   QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem&option, const QModelIndex &index) const;   void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem&option, const QModelIndex &index) const;   void setEditorData(QWidget *editor, const QModelIndex &index) const;   void setModelData(QWidget *editor, QAbstractItemModel *model, constQModelIndex &index) const;   void paint(QPainter *painter, const QStyleOptionViewItem &option,const QModelIndex &index) const;}; #endif // CUSTOMIZEDITEMDELEGATE_H


 

 

// CustomizedItemDelegate.cpp

 #include"CustomizedItemDelegate.h"#include <QCheckBox>#include <QComboBox> CustomizedItemDelegate::CustomizedItemDelegate(QObject*parent) :   QItemDelegate(parent){    connect(this,SIGNAL(closeEditor(QWidget*)), this, SLOT(onCloseEditor(QWidget*)));        } QWidget*CustomizedItemDelegate::createEditor(QWidget *parent, constQStyleOptionViewItem &option, const QModelIndex &index) const    //函数返回值为需要创建的Editor对象{   QWidget* ret = NULL;    m_index = index;         //根据不同的数据类型创建不同的editor   if( index.data().type() == QVariant::Bool )        //当前的数据项的类型是BOOL    {       QCheckBox* cb = new QCheckBox(parent);    //构造一个QCheckBox对象,以parent作为其父对象        cb->setText("Check to TRUE");        ret = cb;   //并以QCheckBox对象为返回值,即创建QCheckBox作为Editor    }   else if( index.data().type() == QVariant::Char )        //当前的数据项的类型是CHAR    {       QComboBox* cb = new QComboBox(parent);        cb->addItem("A");       cb->addItem("B");       cb->addItem("C");       cb->addItem("D");        ret = cb;   //赋值兼容性原则,此处相当于父类指针指向子类对象,再加上此函数为虚函数,运行时将会发生多态    }   else  //其余情况默认处理    {       ret = QItemDelegate::createEditor(parent, option, index);    }    return ret;} void CustomizedItemDelegate::updateEditorGeometry(QWidget*editor, const QStyleOptionViewItem &option, const QModelIndex &index)const{   editor->setGeometry(option.rect); //将editor大小设置为当前数据项的大小(option描述了当前数据项在视图里的外观信息)} voidCustomizedItemDelegate::setEditorData(QWidget *editor, const QModelIndex&index) const  //此函数是将数据项从模型拿到视图显示{        //视图仅仅是显示数据,模型存有数据,因为对不同数据类型进行了不同类型的editor创建,所以在将模型内的数据拿到editor中显示时,         //要数据类型与editor对应,因此需要判断类型   if( index.data().type() == QVariant::Bool )//如果当前的数据项的类型是BOOL(说明之前创建的editor是QCheckBox对象,因此将editor    {        //强制转换为QCheckBox类型       QCheckBox* cb = dynamic_cast<QCheckBox*>(editor);         //将传递进来的editor强制转换为QCheckBox对象        if( cb != NULL ) //dynamic_cast需要判断转换结果       {           cb->(index.data().toBool());     //根据数据项的值设置QCheckBox对象是否勾选       }    }   else if( index.data().type() == QVariant::Char )    {       QComboBox* cb = dynamic_cast<QComboBox*>(editor);     //由于是在父子类型间强制转换,所以使用dynamic_cast        if( cb != NULL )       {        //循环QComboBox对象中的每个项(委托从模型中拿出来的CHAR数据只有一个,但QComboBox有多个项,因此需要找到具体是哪个)           for(int i=0; i<cb->count(); i++)           {                if( cb->itemText(i) ==index.data().toString() ) //QComboBox的第i项的文本与委托从模型拿出来的数据一致                {                    cb->setCurrentIndex(i);   //将一致的项设为当前项                   break;                      }           }       }    }   else    {       QItemDelegate::setEditorData(editor, index);    }} voidCustomizedItemDelegate::setModelData(QWidget *editor, QAbstractItemModel*model, const QModelIndex &index) const{   if( index.data().type() == QVariant::Bool )        //视图,以及视图之下的委托只是显示数据,数据是存在模型中,修改数据值后,数据类    {        //型并没有改变,因此再次根据数据类型将值保存到模型(之前根据数据类型创建editor就是为了保证数据类型不变)此外,在使用默认创建的editor时,如果刻意去修改过数据类型(试验证明:默认editor在数据即使是char类型时也是是编辑框,刻意得将一个字符串写入editor时,没有存入模型),不能将修改过类型的数据成功存入模型       QCheckBox* cb = dynamic_cast<QCheckBox*>(editor);        if( cb != NULL ) //使用dynamic_cast后需要判断转换结果       {           model->setData(index, cb->isChecked(), Qt::DisplayRole);          }    }   else if( index.data().type() == QVariant::Char )    {       QComboBox* cb = dynamic_cast<QComboBox*>(editor);        if( cb != NULL )       {           model->setData(index, cb->currentText().at(0), Qt::DisplayRole);         //at(0) 取string类对象的第0个字符        }    }   else    {       QItemDelegate::setModelData(editor, model, index);    }}//paint函数会被调用row * column次,也就是说视图会针对每个数据项调用一次paint函数,index参数对应每次调用数据数据项在模型中的索引void CustomizedItemDelegate::paint(QPainter*painter, const QStyleOptionViewItem &option, const QModelIndex &index)const{   if( m_index != index )        //m_index为被编辑的数据项在模型中的索引,当前编辑的数据项索引不是index时    {       QItemDelegate::paint(painter, option, index);        //默认绘制    }} voidCustomizedItemDelegate::onCloseEditor(QWidget*){   m_index = QModelIndex();                //退出编辑器时将对象置为空索引,否则退出编辑之后paint函数中m_index的值和index相等,也就意味}     //m_index所代表的数据项的位置将不会被绘制,即退出edit状态之后他一直是空的

                                                                              

 

 

//Widget.h

 

#ifndef WIDGET_H#define WIDGET_H #include <QtGui/QWidget>#include <QTableView>#include <QStandardItemModel>#include <QPushButton>#include"CustomizedItemDelegate.h" class Widget : public QWidget{   Q_OBJECT      QTableView m_view;   QStandardItemModel m_model;   CustomizedItemDelegate m_delegate;    void initView();   void initModel(); public:    Widget(QWidget* parent = 0);   ~Widget();}; #endif // WIDGET_H


 

 

//Widget.cpp

 

#include "Widget.h"#include <QStandardItem>#include <QModelIndex>#include <QStringList>#include <QDebug> Widget::Widget(QWidget* parent) :QWidget(parent){   initView();   initModel();    m_view.setModel(&m_model);       //关联视图与模型,否则视图上无任何显示    for(int i=0; i<m_model.columnCount(); i++)    //显示的数量由模型决定(模型存储数据)    {       m_view.setColumnWidth(i, 125);    //显示 外观由视图决定(视图负责显示)    }} void Widget::initView(){   m_view.setParent(this);   m_view.move(10, 10);   m_view.resize(500, 200);   m_view.setItemDelegate(&m_delegate);      //设置委托} void Widget::initModel(){   QStandardItem* root = m_model.invisibleRootItem();  //得到虚拟的跟item   QStringList hl;   QStandardItem* itemA1 = new QStandardItem();   QStandardItem* itemB1 = new QStandardItem();   QStandardItem* itemC1 = new QStandardItem();   QStandardItem* itemA2 = new QStandardItem();   QStandardItem* itemB2 = new QStandardItem();   QStandardItem* itemC2 = new QStandardItem();                  if((itemA1 != NULL) && (itemB1 != NULL) && (itemC1 != NULL )                   \                   &&(itemA2 != NULL) && (itemB2 != NULL ) && (itemC2 != NULL) )         {                   hl.append("Language");                   hl.append("Level");                   hl.append("Script");                    m_model.setHorizontalHeaderLabels(hl);                    itemA1->setData("Delphi",Qt::DisplayRole);                   itemB1->setData(QChar('A'),Qt::DisplayRole);//此处一定要使用QChar构造函数QChar('A'),否则'A'模型接收到的数据是int                   itemC1->setData(false,Qt::DisplayRole);       //bool型的应该使用true或者false,不能使用1或者0,否则为int                    itemA2->setData("Perl",Qt::DisplayRole);                   itemB2->setData(QChar('B'),Qt::DisplayRole);                   itemC2->setData(true,Qt::DisplayRole);                    root->setChild(0,0, itemA1);                   root->setChild(0,1, itemB1);                   root->setChild(0,2, itemC1);                   root->setChild(1,0, itemA2);                   root->setChild(1,1, itemB2);                   root->setChild(1,2, itemC2);         }} Widget::~Widget(){   }


0 0