Model/View实现表格技术


[+]

一、简介

Model/View结构使数据管理与相应的数据显示相互独立,并提供了一系列标准的函数接口和用于Model模块与View模块之间的通信。它从MVC演化而来,MVC由三种对象组成,Model是应用程序对象,View是它的屏幕表示,Controller定义了用户界面如何对用户输入进行响应。把MVC中的View和Controller合在一起,就形成了Model/View结构。

二、运行图

(1)为了灵活对用户的输入进行处理,引入了Delegate,Model、View、Delegate三个模块之间通过信号与槽机制实现,当自身的状态发生改变时会发出信号通知其他模块。它们间的关系如下图1所示。

QAbstractItemModel是所有Model的基类,但一般不直接使用QAbstractItemModel,而是使用它的子类。Model模块本身并不存储数据,而是为View和Delegate访问数据提供标准的接口。

View模块从Model中获得数据项显示给用户,Qt提供了一些常用的View模型,如QTreeView、QTableView和QListView。

Delegate的基本接口在QAbstractItemDelegate类中定义,通过实现paint()和sizeHint()以达到渲染数据项的目的。

(2)程序运行图如下图2所示。


三、详解1、表格中嵌入控件

利用Delegate的方式实现表格中嵌入各种不同控件的效果,控件在需要编辑数据项时才出现。

(1)插入日历编辑框QDateLineEdit


[cpp]view plaincopy

#ifndefDATEDELEGATE_H

#defineDATEDELEGATE_H

#include<QtGui>

classDateDelegate:publicQItemDelegate

{

Q_OBJECT

public:

DateDelegate(QObject*parent=0);

QWidget*createEditor(QWidget*parent,constQStyleOptionViewItem&option,

constQModelIndex&index)const;

voidsetEditorData(QWidget*editor,constQModelIndex&index)const;

voidsetModelData(QWidget*editor,QAbstractItemModel*model,

constQModelIndex&index)const;

voidupdateEditorGeometry(QWidget*editor,

constQStyleOptionViewItem&option,constQModelIndex&index)const;

};

#endif

[cpp]view plaincopy

#include"datedelegate.h"

DateDelegate::DateDelegate(QObject*parent)

:QItemDelegate(parent)

{

}

QWidget*DateDelegate::createEditor(QWidget*parent,

constQStyleOptionViewItem&/*option*/,

constQModelIndex&/*index*/)const

{

QDateTimeEdit*editor=newQDateTimeEdit(parent);

editor->setDisplayFormat("yyyy-MM-dd");

editor->setCalendarPopup(true);

editor->installEventFilter(const_cast<DateDelegate*>(this));

returneditor;

}

voidDateDelegate::setEditorData(QWidget*editor,

constQModelIndex&index)const

{

QStringdateStr=index.model()->data(index).toString();

QDatedate=QDate::fromString(dateStr,Qt::ISODate);

QDateTimeEdit*edit=static_cast<QDateTimeEdit*>(editor);

edit->setDate(date);

}

voidDateDelegate::setModelData(QWidget*editor,QAbstractItemModel*model,

constQModelIndex&index)const

{

QDateTimeEdit*edit=static_cast<QDateTimeEdit*>(editor);

QDatedate=edit->date();

model->setData(index,QVariant(date.toString(Qt::ISODate)));

}

voidDateDelegate::updateEditorGeometry(QWidget*editor,

constQStyleOptionViewItem&option,constQModelIndex&/*index*/)const

{

editor->setGeometry(option.rect);

}

分析:DateDelegate继承QItemDelegate,一般需要重定义声明中的几个虚函数。createEditor()函数完成创建控件的工作;setEditorDate()设置控件显示的数据,把Model数据更新至Delegate,相当于初始化工作;setModelDate()把Delegate中对数据的更改更新至Model中;updateEditor()更析控件区的显示。


(2)插入下拉列表框QComboBox


[cpp]view plaincopy

#include"combodelegate.h"

ComboDelegate::ComboDelegate(QObject*parent)

:QItemDelegate(parent)

{

}

QWidget*ComboDelegate::createEditor(QWidget*parent,

constQStyleOptionViewItem&/*option*/,

constQModelIndex&/*index*/)const

{

QComboBox*editor=newQComboBox(parent);

editor->addItem(QString::fromLocal8Bit("工人"));

editor->addItem(QString::fromLocal8Bit("农民"));

editor->addItem(QString::fromLocal8Bit("医生"));

editor->addItem(QString::fromLocal8Bit("律师"));

editor->addItem(QString::fromLocal8Bit("军人"));

editor->installEventFilter(const_cast<ComboDelegate*>(this));

returneditor;

}

voidComboDelegate::setEditorData(QWidget*editor,

constQModelIndex&index)const

{

QStringstr=index.model()->data(index).toString();

QComboBox*box=static_cast<QComboBox*>(editor);

inti=box->findText(str);

box->setCurrentIndex(i);

}

voidComboDelegate::setModelData(QWidget*editor,QAbstractItemModel*model,

constQModelIndex&index)const

{

QComboBox*box=static_cast<QComboBox*>(editor);

QStringstr=box->currentText();

model->setData(index,str);

}

voidComboDelegate::updateEditorGeometry(QWidget*editor,

constQStyleOptionViewItem&option,constQModelIndex&/*index*/)const

{

editor->setGeometry(option.rect);

}


(3)插入微调器QSpinBox

[cpp]view plaincopy

#include"spindelegate.h"

SpinDelegate::SpinDelegate(QObject*parent)

:QItemDelegate(parent)

{

}

QWidget*SpinDelegate::createEditor(QWidget*parent,

constQStyleOptionViewItem&/*option*/,

constQModelIndex&/*index*/)const

{

QSpinBox*editor=newQSpinBox(parent);

editor->setRange(1000,10000);

editor->installEventFilter(const_cast<SpinDelegate*>(this));

returneditor;

}

voidSpinDelegate::setEditorData(QWidget*editor,

constQModelIndex&index)const

{

intvalue=index.model()->data(index).toInt();

QSpinBox*spin=static_cast<QSpinBox*>(editor);

spin->setValue(value);

}

voidSpinDelegate::setModelData(QWidget*editor,QAbstractItemModel*model,

constQModelIndex&index)const

{

QSpinBox*spin=static_cast<QSpinBox*>(editor);

intvalue=spin->value();

model->setData(index,value);

}

voidSpinDelegate::updateEditorGeometry(QWidget*editor,

constQStyleOptionViewItem&option,constQModelIndex&/*index*/)const

{

editor->setGeometry(option.rect);

}


2、柱状统计图

自定义的View实现一个柱状统计图对TableModel的表格数据进行显示。

[cpp]view plaincopy

#ifndefHISTOGRAMVIEW_H

#defineHISTOGRAMVIEW_H

#include<QtGui>

classHistogramView:publicQAbstractItemView

{

Q_OBJECT

public:

HistogramView(QWidget*parent=0);

QRectvisualRect(constQModelIndex&index)const;

voidscrollTo(constQModelIndex&index,ScrollHinthint=EnsureVisible);

QModelIndexindexAt(constQPoint&point)const;

voidpaintEvent(QPaintEvent*);

voidmousePressEvent(QMouseEvent*);

voidsetSelectionModel(QItemSelectionModel*selectionModel);

QRegionitemRegion(QModelIndexindex);

protectedslots:

voiddataChanged(constQModelIndex&topLeft,constQModelIndex&bottomRight);

voidselectionChanged(constQItemSelection&selected,constQItemSelection&deselected);

protected:

QModelIndexmoveCursor(QAbstractItemView::CursorActioncursorAction,

Qt::KeyboardModifiersmodifiers);

inthorizontalOffset()const;

intverticalOffset()const;

boolisIndexHidden(constQModelIndex&index)const;

voidsetSelection(constQRect&rect,QItemSelectionModel::SelectionFlagsflags);

QRegionvisualRegionForSelection(constQItemSelection&selection)const;

private:

QItemSelectionModel*selections;

QList<QRegion>listRegionM;

QList<QRegion>listRegionF;

QList<QRegion>listRegionS;

};

#endif

[cpp]view plaincopy

#include"histogramview.h"

HistogramView::HistogramView(QWidget*parent)

:QAbstractItemView(parent)

{}

voidHistogramView::dataChanged(constQModelIndex&topLeft,constQModelIndex&bottomRight)

{

QAbstractItemView::dataChanged(topLeft,bottomRight);

viewport()->update();

}

QRectHistogramView::visualRect(constQModelIndex&index)const

{}

voidHistogramView::scrollTo(constQModelIndex&index,ScrollHinthint)

{}

QModelIndexHistogramView::indexAt(constQPoint&point)const

{

QPointnewPoint(point.x(),point.y());

QRegionregion;

foreach(region,listRegionM)

{

if(region.contains(newPoint))

{

introw=listRegionM.indexOf(region);

QModelIndexindex=model()->index(row,3,rootIndex());

returnindex;

}

}

returnQModelIndex();

}

QModelIndexHistogramView::moveCursor(QAbstractItemView::CursorActioncursorAction,

Qt::KeyboardModifiersmodifiers)

{}

intHistogramView::horizontalOffset()const

{

}

intHistogramView::verticalOffset()const

{}

boolHistogramView::isIndexHidden(constQModelIndex&index)const

{}

voidHistogramView::setSelectionModel(QItemSelectionModel*selectionModel)

{

selections=selectionModel;

}

voidHistogramView::mousePressEvent(QMouseEvent*e)

{

QAbstractItemView::mousePressEvent(e);

setSelection(QRect(e->pos().x(),e->pos().y(),1,1),QItemSelectionModel::SelectCurrent);

}

QRegionHistogramView::itemRegion(QModelIndexindex)

{

QRegionregion;

if(index.column()==3)

region=listRegionM[index.row()];

returnregion;

}

voidHistogramView::setSelection(constQRect&rect,QItemSelectionModel::SelectionFlagsflags)

{

introws=model()->rowCount(rootIndex());

intcolumns=model()->columnCount(rootIndex());

QModelIndexselectedIndex;

for(introw=0;row<rows;++row)

{

for(intcolumn=1;column<columns;++column)

{

QModelIndexindex=model()->index(row,column,rootIndex());

QRegionregion=itemRegion(index);

if(!region.intersected(rect).isEmpty())

selectedIndex=index;

}

}

if(selectedIndex.isValid())

selections->select(selectedIndex,flags);

else

{

QModelIndexnoIndex;

selections->select(noIndex,flags);

}

}

QRegionHistogramView::visualRegionForSelection(constQItemSelection&selection)const

{}

voidHistogramView::selectionChanged(constQItemSelection&selected,constQItemSelection&deselected)

{

viewport()->update();

}

voidHistogramView::paintEvent(QPaintEvent*)

{

QPainterpainter(viewport());

painter.setPen(Qt::black);

intx0=40;

inty0=250;

//drawcoordinate

painter.drawLine(x0,y0,40,30);

painter.drawLine(38,32,40,30);

painter.drawLine(40,30,42,32);

painter.drawText(5,45,tr("income"));

for(inti=1;i<5;i++){

painter.drawLine(-1,-i*50,1,-i*50);

painter.drawText(-20,-i*50,tr("%1").arg(i*5));

}

//x轴

painter.drawLine(x0,y0,540,250);

painter.drawLine(538,248,540,250);

painter.drawLine(540,250,538,252);

painter.drawText(500,270,tr("name"));

introw;

//name

intposD=x0+20;

for(row=0;row<model()->rowCount(rootIndex());row++)

{

QModelIndexindex=model()->index(row,0,rootIndex());

QStringdep=model()->data(index).toString();

painter.drawText(posD,y0+20,dep);

posD+=50;

}

//income

intposM=x0+20;

for(row=0;row<model()->rowCount(rootIndex());row++)

{

QModelIndexindex=model()->index(row,3,rootIndex());

intincome=model()->data(index).toDouble();

intwidth=10;

if(selections->isSelected(index))

painter.setBrush(QBrush(Qt::darkBlue,Qt::SolidPattern));

else

painter.setBrush(Qt::blue);

painter.drawRect(QRectF(posM+10,y0-income/25,width,income/25));

QRegionregionM(posM+10,y0-income/25,width,income/25);

listRegionM<<regionM;

posM+=50;

}

}

分析:对父类的QAbstractItemView中的所有纯虚函数都必须进行声明,纯虚函数包括visualRect()、scrollTo()、indexAt()、moveCursor()、horizontalOffset()、verticalOffset()、isIndexHidden()、setSelection()和visualRegionForSelection(),这些函数并不一定都要实现,根据功能要求选择实现。