在QtWidget中自定义Model

http://mobile.51cto.com/symbian-268967.htm

http://hi.baidu.com/creekspring/blog/item/3606e71569bfbedea6ef3f2d.html

 

Model-View这个结构是把数据存储与数据表示进行了分离,它与MVC都基于同样的思想,但它更简单一些。这种分离使得在几个不同的view上显示同一个数据成为可能,也可以重新实现新的view,而不必改变底层的数据结构。

AD:


    本篇介绍的是在Qt Widget中自定义Model 的内容,一直觉得Qt里的Model-View概念极其神秘, 因为看过很多一知半解的source code。

    这两天因为在写rssreader的关系,用到了MVC,总算有点压力学习学习ModelView的奥秘,而且也小有收获。 谨以此文献给MVC未入门的学弟学妹, 共勉!

    先来讲一些必备的背景知识。在讲MVC时有三个重要且基本的概念贯穿整个学习过程:Index, Data和Role。 就从Index开始。

    我们见过的View有单列的List结构, 有树状的层次结构,还有两维的表格结构, 归根结底,其实这些都是层次结构的变体。 比如下面的图:

    在QtWidget中自定义Model

    从这张图可以清楚的理解上文的观点。 在这几种结构中,都有一个隐含的根节点及与根节点联系的层次结构。 任何一种结构中都存在这样一个定式, 通过一个父节点及一组横纵座标(row,column)即可唯一的确定一个子节点, 这个规律在后面会经常用到。Index可以简单的理解成节点的指针, 前面说过通过三个要素即可唯一的确定一个节点, 所以Model提供的获得节点index函数亦即接受row,column和parentindex三个参数, 我们在写model时首先需要实现这样一个函数;

    第二个概念Data就更简单了,View要显示数据, 就要从Model中去获取需要显示的数据, 传什么参数呢? 不用动脑子也想的到咯,Index肯定算一个。 但仅仅Index并不够, 因为View要显示的可能不止一项数据,比如我的数据包含文本, 包含图标,包含链接甚至一些二进制数据, 我怎么知道View想要的是哪个呢? 这里就用到另外一个概念了 — Role, Role就用来表示View向Model索取哪个类型的数据。 View告诉Model:“我想要A节点下的N行M列数据的显示文本; 我想要A节点下的N行M列数据的图标…”, 这样Model就清楚的知道应该返回什么数据了。 data()函数在这里就充当了返回数据的责任,需要我们在实现Model的时候重点实现这个函数。

    目前定义好的Role可以参考下面的图(图中只标出了一部分Role, 其他的参见文档DisplayRole相关章节):

    在QtWidget中自定义Model

    作为Model必须决定为View提供多少数据,提供哪些类型的数据, 可以去满足View的请求,也可以忽略它, 有很大的自主权。 最简单的实现是不管什么Role都给它返回个字符串就好了。呵呵。 当然作为Model也不能太独断专行,因为毕竟要和View一起工作, 一定要与View的需求相配合才行。

    好,有了这些知识做基础, 写个Model出来其实是非常简单的, 稍微用点心就能应付了, 首先要选对参考文档, 如果是以写代码为目的, 推荐这一篇:

    Creating New Models

    要写code的话这篇最实用, 前面的N多篇都在讲一些概念性的内容, 大把大把的蚂蚁样的英文看了就头大, 还是直接看这篇比较有效。 简单来说分成几步来做:

    一、分析需求,确定基类

    先要确定你的数据是列表结构还是层次结构, 需要显示什么样的数据, 需不需要支持增删或编辑功能等。 根据需求来确定从哪个Model的基类派生,如一个显示字符串列表的Model可以采用QAbstractListModel, 树状层次就只能从QAbstractItemModel开始了。

    二、分析需求,确定需要实现哪些函数

    根据需求的不同,需要实现的函数也不尽相同。

    最简单的只读的列表结构只需要实现两个基本的函数:rowCount(), data(), 也就是只需要知道一共有多少行,每行都显示什么样的数据即可, 十分明了吧? 多列的情况下要实现columnCount(), 需要显示header的要去实现headerData(), 这些规则都太容易理解了。

    其次,如果是层次列表,则需要确定节点之间的层次关系,就需要实现index()和parent()两个函数, 一个是通过父指针和row,column座标确定一个子节点,一个是通过子节点知道它的父指针。

    再次,如果需要修改数据, 先要通知View我的Model数据是可以被编辑的, 就是要实现flags()这个函数, 此函数返回数据的属性,如可编辑、可被选中等; 编辑之后需要一个函数将编辑完成的数据传递给Model, 所以还要实现一个setData方法。

    再再次, 需要增删数据的Model还要告诉Model的底层:“我要增删数据了!”, “我要增删的数据是。。。”, 还有“我增删的操作已经做完了!”, 这些分别对应:调用beginInsertRows()和endInsertRows()。 根据笔者的经验,这部分不太好理解,而且容易出错。 文档里写的是加数据的时候调用insertRows(),不过没有提到说其实在QAbstractItemModel类里这个函数只是个空架子,根本就没有实现, 所以你如果按照文档去调用这个函数通知Model数据加进来了,只能得到一个return false, 不会有任何实际的作用, 很让人困惑。 正确的做法是在你增删数据的前后加上beginInsertRows和endInsertRows的调用,这样底层就能正确处理数据的变化, 并且将变化及时的反应到View中。

    小结:在Qt Widget中自定义Model 的内容介绍完了,上面提到的函数在Creating New Models这篇文章中都有具体的例子代码可供参考,相信照着例子做一定难不倒大家。 有用到以上函数的同志们,希望能帮你解决其中的问题吧。

    • 0
      点赞
    • 0
      收藏
      觉得还不错? 一键收藏
    • 0
      评论
    Qt使用QTableView来显示自定义控件的单元格,可以通过使用QItemDelegate类来实现。 首先,创建一个继承自QItemDelegate的自定义代理类,在这个类重写createEditor()方法和setEditorData()方法。在createEditor()方法,可以创建并返回要在单元格显示的自定义控件实例。例如,可以创建一个QLineEdit控件来显示文本。在setEditorData()方法,可以从模型获取数据,并将其设置到编辑器。 然后,使用setCellWidget()方法将自定义代理类应用到特定的单元格。这个方法需要传入要显示的自定义控件实例、要设置的行号和列号。 最后,将自定义代理类设置为QTableView的默认代理类,以使其在整个表格生效。使用setItemDelegate()方法,将自定义代理类对象作为参数传入。 以下是一个简单的示例代码: ```cpp #include <QApplication> #include <QTableView> #include <QLineEdit> #include <QStandardItemModel> #include <QItemDelegate> class CustomDelegate : public QItemDelegate { public: QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override { QLineEdit *editor = new QLineEdit(parent); return editor; } void setEditorData(QWidget *editor, const QModelIndex &index) const override { QString data = index.model()->data(index, Qt::DisplayRole).toString(); QLineEdit *lineEdit = static_cast<QLineEdit*>(editor); lineEdit->setText(data); } }; int main(int argc, char *argv[]) { QApplication a(argc, argv); QTableView tableView; QStandardItemModel model(4, 2); tableView.setModel(&model); CustomDelegate customDelegate; tableView.setItemDelegate(&customDelegate); QLineEdit *customWidget = new QLineEdit(); tableView.setIndexWidget(model.index(1, 1), customWidget); tableView.show(); return a.exec(); } ``` 这个示例,创建了一个QTableView并设置其模型为QStandardItemModel。然后,创建了一个自定义的代理类CustomDelegate,并将其设置为QTableView的默认代理类。然后,使用setIndexWidget()方法将自定义控件QLineEdit添加到特定的单元格(在此例,第2行第2列的单元格)。最后,展示了QTableView。 当运行程序时,你会看到QTableView的特定单元格显示了一个自定义的QLineEdit控件。你也可以根据自己的需要,修改代理类createEditor()方法和setEditorData()方法的实现,来满足其他自定义控件的显示要求。
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

    当前余额3.43前往充值 >
    需支付:10.00
    成就一亿技术人!
    领取后你会自动成为博主和红包主的粉丝 规则
    hope_wisdom
    发出的红包
    实付
    使用余额支付
    点击重新获取
    扫码支付
    钱包余额 0

    抵扣说明:

    1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
    2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

    余额充值