Qt浅谈之十九:Model/View实现表格和统计图
2014-08-21 16:35
225 查看
一、简介
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
#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
#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
#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
#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的表格数据进行显示。#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
#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。
相关文章推荐
- Qt浅谈之十九:Model/View实现表格和统计图
- Qt学习: Model/View实现表格和统计图
- Qt浅谈:Model/View实现表格和统计图
- Qt表格之Model/View实现
- ViewModel在MVC3中的应用:实现多字段表格的部分更新
- 在Qt的Model/View框架中实现Drag-Drop操作
- 在ASP.NET中实现Model-View-Presenter(MVP)
- (收藏)在ASP.NET中实现Model-View-Controller模式(二)
- 在 ASP.NET 中实现 Model-View-Controller
- [Silverlight入门系列]使用MVVM模式(7):ViewModel的INotifyPropertyChanged接口实现
- 在 ASP.NET 中实现 Model-View-Controller
- 用C#实现MVC(Model View Control)模式介绍
- 在ASP.NET中实现Model-View-Controller模式(一)
- MVMM 中的ViewModel 实现IsLoading进度条
- Qt4 model/view结构之实现自定义模型
- 使用ASP.NET实现Model View Presenter(MVP)
- 在 ASP.NET 中实现 Model-View-Controller
- (收藏)在ASP.NET中实现Model-View-Controller模式(三)
- 用C#实现MVC(Model View Control)模式介绍
- (收藏)在ASP.NET中实现Model-View-Controller模式(一)