Qt学习之路(46): 自定义model之二

简介:
前面的例子已经比较清楚的给出了自定义model的方法,就是要覆盖我们所需要的那几个函数就可以了。但是,前面的例子仅仅是简单的展示数据,也就是说数据时只读的。那么,如何能做到读写数据呢?那就要来看进来的例子了。这个例子也是来自C++GUI Programming with Qt 4, 2nd Edition这本书的。
 
还是先来看代码吧:
 
citymodel.h
 
InBlock.gif class CityModel :  public QAbstractTableModel 
InBlock.gif
InBlock.gif        Q_OBJECT 
InBlock.gif 
InBlock.gif public
InBlock.gif        CityModel(QObject *parent = 0); 
InBlock.gif 
InBlock.gif         void setCities( const QStringList &cityNames); 
InBlock.gif         int rowCount( const QModelIndex &parent)  const
InBlock.gif         int columnCount( const QModelIndex &parent)  const
InBlock.gif        QVariant data( const QModelIndex &index,  int role)  const
InBlock.gif         bool setData( const QModelIndex &index,  const QVariant &value,  int role); 
InBlock.gif        QVariant headerData( int section, Qt::Orientation orientation,  int role)  const
InBlock.gif        Qt::ItemFlags flags( const QModelIndex &index)  const
InBlock.gif 
InBlock.gif private
InBlock.gif         int offsetOf( int row,  int column)  const
InBlock.gif 
InBlock.gif        QStringList cities; 
InBlock.gif        QVector< int> distances; 
InBlock.gif};
 
 
citymodel.cpp
InBlock.gifCityModel::CityModel(QObject *parent) 
InBlock.gif        : QAbstractTableModel(parent) 
InBlock.gif
InBlock.gif
InBlock.gif 
InBlock.gif int CityModel::rowCount( const QModelIndex & parent)  const 
InBlock.gif
InBlock.gif         return cities.count(); 
InBlock.gif
InBlock.gif 
InBlock.gif int CityModel::columnCount( const QModelIndex & parent)  const 
InBlock.gif
InBlock.gif         return cities.count(); 
InBlock.gif
InBlock.gif 
InBlock.gifQVariant CityModel::data( const QModelIndex &index,  int role)  const 
InBlock.gif
InBlock.gif         if (!index.isValid()) { 
InBlock.gif                 return QVariant(); 
InBlock.gif        } 
InBlock.gif 
InBlock.gif         if (role == Qt::TextAlignmentRole) { 
InBlock.gif                 return  int(Qt::AlignRight | Qt::AlignVCenter); 
InBlock.gif        }  else  if (role == Qt::DisplayRole) { 
InBlock.gif                 if (index.row() == index.column()) { 
InBlock.gif                         return 0; 
InBlock.gif                } 
InBlock.gif                 int offset = offsetOf(index.row(), index.column()); 
InBlock.gif                 return distances[offset]; 
InBlock.gif        } 
InBlock.gif         return QVariant(); 
InBlock.gif
InBlock.gif 
InBlock.gifQVariant CityModel::headerData( int section, Qt::Orientation orientation,  int role)  const 
InBlock.gif
InBlock.gif         if (role == Qt::DisplayRole) { 
InBlock.gif                 return cities[section]; 
InBlock.gif        } 
InBlock.gif         return QVariant(); 
InBlock.gif
InBlock.gif 
InBlock.gif bool CityModel::setData( const QModelIndex &index,  const QVariant &value,  int role) 
InBlock.gif
InBlock.gif         if (index.isValid() && index.row() != index.column() && role == Qt::EditRole) { 
InBlock.gif                 int offset = offsetOf(index.row(), index.column()); 
InBlock.gif                distances[offset] = value.toInt(); 
InBlock.gif 
InBlock.gif                QModelIndex transposedIndex = createIndex(index.column(), index.row()); 
InBlock.gif                emit dataChanged(index, index); 
InBlock.gif                emit dataChanged(transposedIndex, transposedIndex); 
InBlock.gif                 return  true
InBlock.gif        } 
InBlock.gif         return  false
InBlock.gif
InBlock.gif 
InBlock.gifQt::ItemFlags CityModel::flags( const QModelIndex &index)  const 
InBlock.gif
InBlock.gif        Qt::ItemFlags flags = QAbstractItemModel::flags(index); 
InBlock.gif         if (index.row() != index.column()) { 
InBlock.gif                flags |= Qt::ItemIsEditable; 
InBlock.gif        } 
InBlock.gif         return flags; 
InBlock.gif
InBlock.gif 
InBlock.gif void CityModel::setCities( const QStringList &cityNames) 
InBlock.gif
InBlock.gif        cities = cityNames; 
InBlock.gif        distances.resize(cities.count() * (cities.count() - 1) / 2); 
InBlock.gif        distances.fill(0); 
InBlock.gif        reset(); 
InBlock.gif
InBlock.gif 
InBlock.gif int CityModel::offsetOf( int row,  int column)  const 
InBlock.gif
InBlock.gif         if (row < column) { 
InBlock.gif                qSwap(row, column); 
InBlock.gif        } 
InBlock.gif         return (row * (row - 1) / 2) + column; 
InBlock.gif
 
代码很长,但实际上和前面我们的那个例子非常相似。这个model也是用于table的,因此还是继承了QAbstractTableModel。CityModel内部有两个数据源:一个QStringList类型的对象,一个QVector&lt;int>类型的对象。前者用于保存城市的名字,需要用户显示的给出;后者是model内部维护的一个存放int的向量。这个CityModel就是要在table中显示两个城市之间的距离。同前面的例子一样,如果我们要把所有的数据都保存下来,显然会造成数据的冗余:城市A到城市B的距离同城市B到城市A的距离是一样的!因此我们还是自定义一个model。同样这个CityModel有个简单的空构造函数,rowCount()和columnCount()函数也是返回list的长度。data()函数根据role的不同返回不同的值。由于在table中坐标是由row和column给出的,因此需要有一个二维坐标到一维坐标的转换,这就是offsetOf()函数的作用。我们把主要精力放在setData()函数上面。
 
InBlock.gif bool CityModel::setData( const QModelIndex &index,  const QVariant &value,  int role) 
InBlock.gif
InBlock.gif         if (index.isValid() && index.row() != index.column() && role == Qt::EditRole) { 
InBlock.gif                 int offset = offsetOf(index.row(), index.column()); 
InBlock.gif                distances[offset] = value.toInt(); 
InBlock.gif 
InBlock.gif                QModelIndex transposedIndex = createIndex(index.column(), index.row()); 
InBlock.gif                emit dataChanged(index, index); 
InBlock.gif                emit dataChanged(transposedIndex, transposedIndex); 
InBlock.gif                 return  true
InBlock.gif        } 
InBlock.gif         return  false
InBlock.gif}
 
这个函数在用户编辑数据时会自动调用。也就是说,这时我们的数据已经不是只读的了。函数开始是一个长长的判断:index要是合法的;index的row和column不相等,也就是说两个城市是不同的;数据想的role是Qt::EditRole。如果满足了这三个条件,才会执行下面的操作。首先,由row和column坐标定位到表中的数据项在vector中的位置。然后用户新修改的数据被作为参数value传入,所以我们要把这个参数赋值给distances。createIndex()函数根据column和row值生成一个QModelIndex对象。请注意这里的顺序:row和column是颠倒的!这就把坐标为(row, column)的点关于主对角线对称的那个点(column, row)的index找到了。还记得我们的需求吗?当我们修改了一个数据时,对应的数据也要被修改,这就是这个功能的实现。我们需要emit dataChanged()信号,这个信号接收两个参数:一个是被修改的数据的左上角的坐标,一个是被修改的数据的右下角的坐标。为什么会有两个坐标呢?因此我们修改的数据不一定只是一个。像这里,我们只修改了一个数据,因此这两个值是相同的。数据更新了,我们用这个信号通知view刷新,这样就可以显示新的数据了。最后,如果函数数据修改成功就返回true,否则返回false。
 
最后,我们在main()函数中显示出来这个model:
 
InBlock.gif int main( int argc,  char *argv[]) 
InBlock.gif
InBlock.gif        QApplication a(argc, argv); 
InBlock.gif        QStringList cities; 
InBlock.gif        cities <<  "Arvika" <<  "Boden" <<  "Eskilstuna" <<  "Falun"
InBlock.gif 
InBlock.gif        CityModel cityModel; 
InBlock.gif        cityModel.setCities(cities); 
InBlock.gif 
InBlock.gif        QTableView tableView; 
InBlock.gif        tableView.setModel(&cityModel); 
InBlock.gif        tableView.setAlternatingRowColors( true); 
InBlock.gif        tableView.setWindowTitle(QObject::tr( "Cities")); 
InBlock.gif        tableView.show(); 
InBlock.gif         return a.exec(); 
InBlock.gif}
 
这样,我们就把这个model做完了。最后来看看效果吧!
 
2010-1-19


本文转自 FinderCheng 51CTO博客,原文链接:
http://blog.51cto.com/devbean/267972





相关文章
|
4月前
|
存储 机器学习/深度学习 人工智能
Qt魔法书:打造自定义鼠标键盘脚本(二)
Qt魔法书:打造自定义鼠标键盘脚本
176 0
|
1月前
|
搜索推荐 C++
【Qt 学习笔记】Qt窗口 | 对话框 | 创建自定义对话框
【Qt 学习笔记】Qt窗口 | 对话框 | 创建自定义对话框
34 4
|
1月前
【qt】自定义对话框(2)
【qt】自定义对话框(2)
15 0
|
1月前
【qt】自定义对话框(1)
【qt】自定义对话框(1)
12 0
|
2月前
|
C++
Qt中的信号与槽如何学习?(包括自定义信号)这篇文章告诉你
以现实中的事件来举例的话,例如有两把不同颜色的信号枪,分别是红色,绿色,打响不通颜色的信号枪会触发不同的槽发生,比如说打响红色这个人就跑步,绿色就走步,但是还有一个很重要的机制,那就是连接,我们需要把信号枪去跟这个人的动作连接起来。 如果上面理解没问题的话我们可以把信号和槽看成两个工具,我们最重要的是如何去把这两个工具连接起来。 它的作用可以让我们更加灵活的去使用不同窗口间的切换以及某些事件的连接。
|
4月前
|
监控 数据可视化 Linux
Qt Model&View&Delegate(模型-视图-代理) 介绍和使用
Qt Model&View&Delegate(模型-视图-代理) 介绍和使用
Qt Model&View&Delegate(模型-视图-代理) 介绍和使用
|
4月前
|
C++
【qt】自定义代理类
【qt】自定义代理类
42 0
|
4月前
|
搜索推荐
【qt】自定义界面类
【qt】自定义界面类
36 0
|
2月前
|
数据安全/隐私保护 C++ 计算机视觉
Qt(C++)开发一款图片防盗用水印制作小工具
文本水印是一种常用的防盗用手段,可以将文本信息嵌入到图片、视频等文件中,用于识别和证明文件的版权归属。在数字化和网络化的时代,大量的原创作品容易被不法分子盗用或侵犯版权,因此加入文本水印成为了保护原创作品和维护知识产权的必要手段。 通常情况下,文本水印可以包含版权声明、制作者姓名、日期、网址等信息,以帮助识别文件的来源和版权归属。同时,为了增强防盗用效果,文本水印通常会采用字体、颜色、角度等多种组合方式,使得水印难以被删除或篡改,有效地降低了盗用意愿和风险。 开发人员可以使用图像处理技术和编程语言实现文本水印的功能,例如使用Qt的QPainter类进行文本绘制操作,将文本信息嵌入到图片中,
135 1
Qt(C++)开发一款图片防盗用水印制作小工具
|
1月前
|
监控 C++ 容器
【qt】MDI多文档界面开发
【qt】MDI多文档界面开发
39 0

推荐镜像

更多