您的位置:首页 > 编程语言 > Qt开发

Qt学习: Model/View实现表格和统计图

2016-04-13 11:25 751 查看
博主QQ:1356438802


一、简介

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
plain copy

#ifndef DATEDELEGATE_H

#define DATEDELEGATE_H

#include <QtGui>

class DateDelegate : public QItemDelegate

{

Q_OBJECT

public:

DateDelegate(QObject *parent = 0);

QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,

const QModelIndex &index) const;

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

void setModelData(QWidget *editor, QAbstractItemModel *model,

const QModelIndex &index) const;

void updateEditorGeometry(QWidget *editor,

const QStyleOptionViewItem &option, const QModelIndex &index) const;

};

#endif

[cpp] view
plain copy

#include "datedelegate.h"

DateDelegate::DateDelegate(QObject *parent)

: QItemDelegate(parent)

{

}

QWidget *DateDelegate::createEditor(QWidget *parent,

const QStyleOptionViewItem &/* option */,

const QModelIndex &/* index */) const

{

QDateTimeEdit *editor = new QDateTimeEdit(parent);

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

editor->setCalendarPopup(true);

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

return editor;

}

void DateDelegate::setEditorData(QWidget *editor,

const QModelIndex &index) const

{

QString dateStr = index.model()->data(index).toString();

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

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

edit->setDate(date);

}

void DateDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,

const QModelIndex &index) const

{

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

QDate date = edit->date();

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

}

void DateDelegate::updateEditorGeometry(QWidget *editor,

const QStyleOptionViewItem &option, const QModelIndex &/* index */) const

{

editor->setGeometry(option.rect);

}

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

(2)插入下拉列表框QComboBox

[cpp] view
plain copy

#include "combodelegate.h"

ComboDelegate::ComboDelegate(QObject *parent)

: QItemDelegate(parent)

{

}

QWidget *ComboDelegate::createEditor(QWidget *parent,

const QStyleOptionViewItem &/* option */,

const QModelIndex &/* index */) const

{

QComboBox *editor = new QComboBox(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));

return editor;

}

void ComboDelegate::setEditorData(QWidget *editor,

const QModelIndex &index) const

{

QString str = index.model()->data(index).toString();

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

int i = box->findText(str);

box->setCurrentIndex(i);

}

void ComboDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,

const QModelIndex &index) const

{

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

QString str = box->currentText();

model->setData(index, str);

}

void ComboDelegate::updateEditorGeometry(QWidget *editor,

const QStyleOptionViewItem &option, const QModelIndex &/* index */) const

{

editor->setGeometry(option.rect);

}

(3)插入微调器QSpinBox

[cpp] view
plain copy

#include "spindelegate.h"

SpinDelegate::SpinDelegate(QObject *parent)

: QItemDelegate(parent)

{

}

QWidget *SpinDelegate::createEditor(QWidget *parent,

const QStyleOptionViewItem &/* option */,

const QModelIndex &/* index */) const

{

QSpinBox *editor = new QSpinBox(parent);

editor->setRange(1000,10000);

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

return editor;

}

void SpinDelegate::setEditorData(QWidget *editor,

const QModelIndex &index) const

{

int value = index.model()->data(index).toInt();

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

spin->setValue(value);

}

void SpinDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,

const QModelIndex &index) const

{

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

int value = spin->value();

model->setData(index, value);

}

void SpinDelegate::updateEditorGeometry(QWidget *editor,

const QStyleOptionViewItem &option, const QModelIndex &/* index */) const

{

editor->setGeometry(option.rect);

}


2、柱状统计图

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

[cpp] view
plain copy

#ifndef HISTOGRAMVIEW_H

#define HISTOGRAMVIEW_H

#include <QtGui>

class HistogramView : public QAbstractItemView

{

Q_OBJECT

public:

HistogramView(QWidget *parent=0);

QRect visualRect(const QModelIndex &index)const;

void scrollTo(const QModelIndex &index, ScrollHint hint = EnsureVisible);

QModelIndex indexAt(const QPoint &point) const;

void paintEvent(QPaintEvent *);

void mousePressEvent(QMouseEvent *);

void setSelectionModel(QItemSelectionModel * selectionModel);

QRegion itemRegion(QModelIndex index);

protected slots:

void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);

void selectionChanged(const QItemSelection & selected, const QItemSelection & deselected );

protected:

QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction,

Qt::KeyboardModifiers modifiers);

int horizontalOffset() const;

int verticalOffset() const;

bool isIndexHidden(const QModelIndex &index) const;

void setSelection ( const QRect&rect, QItemSelectionModel::SelectionFlags flags );

QRegion visualRegionForSelection(const QItemSelection &selection) const;

private:

QItemSelectionModel *selections;

QList<QRegion> listRegionM;

QList<QRegion> listRegionF;

QList<QRegion> listRegionS;

};

#endif

[cpp] view
plain copy

#include "histogramview.h"

HistogramView::HistogramView(QWidget *parent)

: QAbstractItemView(parent)

{}

void HistogramView::dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight)

{

QAbstractItemView::dataChanged(topLeft, bottomRight);

viewport()->update();

}

QRect HistogramView::visualRect(const QModelIndex &index) const

{}

void HistogramView::scrollTo(const QModelIndex &index, ScrollHint hint)

{}

QModelIndex HistogramView::indexAt(const QPoint &point) const

{

QPoint newPoint(point.x(),point.y());

QRegion region;

foreach(region,listRegionM)

{

if (region.contains(newPoint))

{

int row = listRegionM.indexOf(region);

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

return index;

}

}

return QModelIndex();

}

QModelIndex HistogramView::moveCursor(QAbstractItemView::CursorAction cursorAction,

Qt::KeyboardModifiers modifiers)

{}

int HistogramView::horizontalOffset() const

{

}

int HistogramView::verticalOffset() const

{}

bool HistogramView::isIndexHidden(const QModelIndex &index) const

{}

void HistogramView::setSelectionModel(QItemSelectionModel * selectionModel)

{

selections = selectionModel;

}

void HistogramView::mousePressEvent(QMouseEvent *e)

{

QAbstractItemView::mousePressEvent(e);

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

}

QRegion HistogramView::itemRegion(QModelIndex index)

{

QRegion region;

if (index.column() == 3)

region = listRegionM[index.row()];

return region;

}

void HistogramView::setSelection ( const QRect &rect, QItemSelectionModel::SelectionFlags flags )

{

int rows = model()->rowCount(rootIndex());

int columns = model()->columnCount(rootIndex());

QModelIndex selectedIndex;

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

{

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

{

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

QRegion region = itemRegion(index);

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

selectedIndex = index;

}

}

if(selectedIndex.isValid())

selections->select(selectedIndex,flags);

else

{

QModelIndex noIndex;

selections->select(noIndex, flags);

}

}

QRegion HistogramView::visualRegionForSelection(const QItemSelection &selection) const

{}

void HistogramView::selectionChanged(const QItemSelection & selected, const QItemSelection & deselected )

{

viewport()->update();

}

void HistogramView::paintEvent(QPaintEvent *)

{

QPainter painter(viewport());

painter.setPen(Qt::black);

int x0 = 40;

int y0 = 250;

// draw coordinate

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 (int i=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"));

int row;

// name

int posD = x0+20;

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

{

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

QString dep = model()->data(index).toString();

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

posD += 50;

}

// income

int posM = x0+20;

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

{

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

int income = model()->data(index).toDouble();

int width = 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));

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

listRegionM << regionM;

posM += 50;

}

}

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


四、总结

(1)Mode/View结构比较难于理解,在此先作简单的介绍,以后再进行更加深入的研究。

(2)感兴趣的可以下载源码分析,并在其基础上进行自己的开发,本人水平有限,也只能提供代码供读者延伸。

(3)源码已经打包上传到csdn上可登录下载(http://download.csdn.net/detail/taiyang1987912/7797583)。

(4)若有更好的设计建议,也可发邮件沟通,在此先感谢!邮箱地址yang.ao@i-soft.com.cn。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: