Qt MVC结构之Model模型介绍

MVC简介

MVC 就是Model-View-Control模式的简称,包括模型层(Model), 视图层(View), 控制层(Controller).
Model主要负责管理数据,View主要用来显示数据,Controller主要用来操作数据,控制View联动。
Qt也采用了这个模式,模型层用Model,视图层用View,控制层改名叫了代理Delegate。

QFileSystemModel

我们可以举个简单的例子,用QFileSystemModel来实现文件夹内容的展示,QFileSystemModel是Qt给我们提供的处理本地文件系统的文件和目录。

  1. int main(int argc, char *argv[])
  2. {
  3. QApplication a(argc, argv);
  4. QSplitter * splitter = new QSplitter;
  5. QFileSystemModel * model = new QFileSystemModel;
  6. model->setRootPath(QDir::currentPath());
  7. QTreeView * tree = new QTreeView(splitter);
  8. tree->setModel(model);
  9. tree->setRootIndex(model->index(QDir::currentPath()));
  10. QListView * list = new QListView(splitter);
  11. list->setModel(model);
  12. list->setRootIndex(model->index(QDir::currentPath()));
  13. splitter->setWindowTitle("Two views onto the same file system model");
  14. splitter->resize(1000,800);
  15. splitter->show();
  16. return a.exec();
  17. }

为model设置根目录,目录为当前文件夹。
创建树型视图,视图加载model,并且设置视图的根索引为model的当前目录的索引。

模型介绍

模型分为几种,有列表模型,表格模型,以及树模型。详细可以参考Qt的帮主文档,搜索Model/View Programming。
https://cdn.llfc.club/1671158788602.jpg

模型有一个最基本的类QStandardItemModel,我们基于这个模型类实现一个树形模型。

  1. int main(int argc, char *argv[])
  2. {
  3. QApplication a(argc, argv);
  4. //创建标准itemmodel
  5. QStandardItemModel model;
  6. //获取模型根项,根项不可见
  7. QStandardItem * parentItem = model.invisibleRootItem();
  8. //创建文本显示,图标,和工具显示
  9. QStandardItem * item0 = new QStandardItem();
  10. item0->setText("item0");
  11. QPixmap pixmap0(50,50);
  12. pixmap0.fill(Qt::red);
  13. item0->setIcon(pixmap0);
  14. item0->setToolTip("indexA");
  15. parentItem->appendRow(item0);
  16. //将新的项设置为父节点项
  17. parentItem = item0;
  18. QStandardItem * item1 = new QStandardItem();
  19. item1->setText("item1");
  20. QPixmap pixmap1(50,50);
  21. pixmap1.fill(Qt::blue);
  22. item1->setIcon(pixmap1);
  23. item1->setToolTip("indexB");
  24. parentItem->appendRow(item1);
  25. QStandardItem * item2 = new QStandardItem();
  26. item2->setData("item2",Qt::EditRole);
  27. QPixmap pixmap2(50,50);
  28. pixmap2.fill(Qt::green);
  29. item2->setData(QIcon(pixmap2), Qt::DecorationRole);
  30. item2->setData("indexC",Qt::ToolTipRole);
  31. parentItem->appendRow(item2);
  32. //取出根节点第0行0列的模型索引
  33. QModelIndex indexA = model.index(0,0, QModelIndex());
  34. qDebug() << "model indexA row count is " << model.rowCount(indexA);
  35. //获取indexA节点下0行0列
  36. QModelIndex indexB = model.index(0,0,indexA);
  37. qDebug() << "indexB text is " << model.data(indexB,Qt::EditRole).toString();
  38. qDebug() << "indexB icon is " << model.data(indexB,Qt::DecorationRole);
  39. qDebug() << "indexB tool tip is " << model.data(indexB,Qt::ToolTipRole).toString();
  40. QTreeView view;
  41. view.setModel(&model);
  42. view.show();
  43. return a.exec();
  44. }

运行后程序效果图
https://cdn.llfc.club/1671159654393.jpg
item0节点下挂载了item1和item2两个节点。程序用两种方式为item设置图标,提示,文本。
一种是setText,setIcon, setToolTip方式,另一种是setData方式。
setData的方式可以参考Qt的文档,文档里列举了一些可以设置的角色。
EditRole是可编辑角色,DecorationRole类似于图片显示,ToolTipRole类似于提示的角色。

特定模型

除了QStandardItemModel之外,还有一些其他集成好的特殊模型,如果我们要实现树形模型就子类化QStandardItemModel。
如果想实现列表模型就子类化QAbstractListModel,如果像实现表格模型就子类化QAbstractTableModel。
我们子类化QAbstractListModel,实现一个列表模型。

  1. class StringListModel:public QAbstractListModel
  2. {
  3. Q_OBJECT
  4. public:
  5. StringListModel(const QStringList & strings, QObject* parent = 0);
  6. int rowCount(const QModelIndex& parent = QModelIndex()) const;
  7. QVariant data(const QModelIndex& index, int role) const;
  8. QVariant headerData(int section, Qt::Orientation orientation,
  9. int role = Qt::DisplayRole) const;
  10. private:
  11. QStringList stringList;
  12. };

这个模型类包含一个QStringList,用来管理数据

  1. StringListModel::StringListModel(const QStringList & strings,
  2. QObject* parent):
  3. QAbstractListModel(parent), stringList(strings)
  4. {
  5. }
  6. int StringListModel::rowCount(const QModelIndex& parent ) const{
  7. return stringList.count();
  8. }
  9. QVariant StringListModel::data(const QModelIndex& index, int role) const{
  10. if(!index.isValid()){
  11. return QVariant();
  12. }
  13. if(index.row() >= stringList.size()){
  14. return QVariant();
  15. }
  16. if(role == Qt::DisplayRole){
  17. return stringList.at(index.row());
  18. }else{
  19. return QVariant();
  20. }
  21. }
  22. QVariant StringListModel::headerData(int section, Qt::Orientation orientation,
  23. int role ) const{
  24. if(role != Qt::DisplayRole){
  25. return QVariant();
  26. }
  27. if(orientation == Qt::Horizontal){
  28. return QString("Column %1").arg(section);
  29. }else{
  30. return QString("Row %1").arg(section);
  31. }
  32. }

headerData函数内根据水平还是垂直判断,显示表头。
data函数内根据角色返回索引对应的数据。
在main函数中可以分别用一个listview和treeview显示

  1. int main(int argc, char *argv[])
  2. {
  3. QApplication a(argc, argv);
  4. QStringList list;
  5. list <<"a" << "b" << "c";
  6. StringListModel model(list);
  7. QListView listView;
  8. listView.setModel(&model);
  9. listView.show();
  10. QTableView tableView;
  11. tableView.setModel(&model);
  12. tableView.show();
  13. return a.exec();
  14. }

https://cdn.llfc.club/1671174663824.jpg

设置可编辑选项

我们为自定义的listmodel模型添加两个函数flags()和setData()函数。
flags函数用来判断模型索引对应的项目的属性,通过标记按位或的方式获取。
setData用来设置模型索引对应的项,并且设置他的编辑属性。

  1. Qt::ItemFlags StringListModel::flags(const QModelIndex& index) const{
  2. if(!index.isValid())
  3. return Qt::ItemIsEnabled;
  4. return QAbstractItemModel::flags(index) | Qt::ItemIsEditable;
  5. }

如果是无效的数据则返回ItemIsEnabled标记,否则在原来的标记基础上增加ItemIsEditable。
当我们修改数据时,会触发setData函数, 该函数根据项的角色为EditRole替换原来的字符串。

  1. bool StringListModel::setData(const QModelIndex& index,
  2. const QVariant &value, int role ){
  3. if(index.isValid() && role == Qt::EditRole){
  4. stringList.replace(index.row(), value.toString());
  5. emit dataChanged(index, index);
  6. return true;
  7. }
  8. return false;
  9. }

并且发送了dataChanged,这个信号第一个参数为左上角的index,第二个参数为右下角index。
dataChanged通知View视图刷新数据,从而完成数据的修改。

另外Views显示数据时会根据data返回的数据显示,所以要将data函数的显示逻辑中添加Qt::EditRole。

  1. QVariant StringListModel::data(const QModelIndex& index, int role) const{
  2. if(!index.isValid()){
  3. return QVariant();
  4. }
  5. if(index.row() >= stringList.size()){
  6. return QVariant();
  7. }
  8. if(role == Qt::DisplayRole || role == Qt::EditRole){
  9. return stringList.at(index.row());
  10. }else{
  11. return QVariant();
  12. }
  13. }

添加行与删除行

添加行和删除行都需要在添加和删除之前调用begin操作,操作完之后调用end操作

  1. bool StringListModel::insertRows(int position, int rows,
  2. const QModelIndex & index ){
  3. beginInsertRows(QModelIndex(), position, position + rows -1);
  4. for(int row = 0; row < rows; ++ row){
  5. stringList.insert(position, "");
  6. }
  7. endInsertRows();
  8. return true;
  9. }
  10. bool StringListModel::removeRows(int position, int rows,
  11. const QModelIndex & index ){
  12. beginRemoveRows(QModelIndex(), position, position+ rows-1);
  13. for(int row=0; row < rows; ++ row){
  14. stringList.removeAt(position);
  15. }
  16. endRemoveRows();
  17. }

接下来可以调用一下测试

  1. model.insertRows(3,2);
  2. model.removeRows(0,1);

运行的效果如下
https://cdn.llfc.club/1671256097654.jpg

源码链接

源码链接
https://gitee.com/secondtonone1/qt-learning-notes

热门评论

热门文章

  1. vscode搭建windows C++开发环境

    喜欢(596) 浏览(78714)
  2. 聊天项目(28) 分布式服务通知好友申请

    喜欢(507) 浏览(5691)
  3. 使用hexo搭建个人博客

    喜欢(533) 浏览(11209)
  4. Linux环境搭建和编码

    喜欢(594) 浏览(12860)
  5. Qt环境搭建

    喜欢(517) 浏览(23289)

最新评论

  1. 答疑汇总(thread,async源码分析) Yagus:如果引用计数为0,则会执行 future 的析构进而等待任务执行完成,那么看到的输出将是 这边应该不对吧,std::future析构只在这三种情况都满足的时候才回block: 1.共享状态是std::async 创造的(类型是_Task_async_state) 2.共享状态没有ready 3.这个future是共享状态的最后一个引用 这边共享状态类型是“_Package_state”,引用计数即使为0也不应该block啊
  2. 再谈单例模式 secondtonone1:是的,C++11以后返回局部static变量对象能保证线程安全了。
  3. Qt MVC结构之QItemDelegate介绍 胡歌-此生不换:gpt, google
  4. 聊天项目(7) visualstudio配置grpc diablorrr:cmake文件得改一下 find_package(Boost REQUIRED COMPONENTS system filesystem),要加上filesystem。在target_link_libraries中也同样加上
  5. protobuf配置和使用 熊二:你可以把dll放到系统目录,也可以配置环境变量,还能把dll丢到lib里
  6. boost::asio之socket的创建和连接 项空月:发现一些错别字 :每隔vector存储  是不是是每个. asio::mutable_buffers_1 o或者    是不是多打了个o
  7. string类 WangQi888888:确实错了,应该是!isspace(sind[index]). 否则不进入循环,还是原来的字符串“some string”
  8. Qt 对话框 Spade2077:QDialog w(); //这里是不是不需要带括号
  9. 类和对象 陈宇航:支持!!!!
  10. 构造函数 secondtonone1:构造函数是类的基础知识,要着重掌握
  11. 聊天项目(15) 客户端实现TCP管理者 lkx:已经在&QTcpSocket::readyRead 回调函数中做了处理了的。
  12. interface应用 secondtonone1:interface是万能类型,但是使用时要转换为实际类型来使用。interface丰富了go的多态特性,也降低了传统面向对象语言的耦合性。
  13. 网络编程学习方法和图书推荐 Corleone:啥程度可以找工作
  14. 创建项目和编译 secondtonone1:谢谢支持
  15. 处理网络粘包问题 zyouth: //消息的长度小于头部规定的长度,说明数据未收全,则先将部分消息放到接收节点里 if (bytes_transferred < data_len) { memcpy(_recv_msg_node->_data + _recv_msg_node->_cur_len, _data + copy_len, bytes_transferred); _recv_msg_node->_cur_len += bytes_transferred; ::memset(_data, 0, MAX_LENGTH); _socket.async_read_some(boost::asio::buffer(_data, MAX_LENGTH), std::bind(&CSession::HandleRead, this, std::placeholders::_1, std::placeholders::_2, shared_self)); //头部处理完成 _b_head_parse = true; return; } 把_b_head_parse = true;放在_socket.async_read_some前面是不是更好
  16. 解决博客回复区被脚本注入的问题 secondtonone1:走到现在我忽然明白一个道理,无论工作也好生活也罢,最重要的是开心,即使一份安稳的工作不能给我带来事业上的积累也要合理的舍弃,所以我还是想去做喜欢的方向。
  17. 无锁并发队列 TenThousandOne:_head  和 _tail  替换为原子变量。那里pop的逻辑,val = _data[h] 可以移到循环外面吗
  18. visual studio配置boost库 一giao里我离giaogiao:请问是修改成这样吗:.\b2.exe toolset=MinGW
  19. 聊天项目(9) redis服务搭建 pro_lin:redis线程池的析构函数,除了pop出队列,还要free掉redis连接把
  20. 利用栅栏实现同步 Dzher:作者你好!我觉得 std::thread a(write_x); std::thread b(write_y); std::thread c(read_x_then_y); std::thread d(read_y_then_x); 这个例子中的assert fail并不会发生,原子变量设定了非relaxed内存序后一个线程的原子变量被写入,那么之后的读取一定会被同步的,c和d线程中只可能同时发生一个z++未执行的情况,最终z不是1就是2了,我测试了很多次都没有assert,请问我这个观点有什么错误,谢谢!
  21. 面试题汇总(一) secondtonone1:看到网络上经常提问的go的问题,做了一下汇总,结合自己的经验给出的答案,如有纰漏,望指正批评。
  22. C++ 并发三剑客future, promise和async Yunfei:大佬您好,如果这个线程池中加入的异步任务的形参如果有右值引用,这个commit中的返回类型推导和bind绑定就会出现问题,请问实际工程中,是不是不会用到这种任务,如果用到了,应该怎么解决?
  23. 堆排序 secondtonone1:堆排序非常实用,定时器就是这个原理制作的。

个人公众号

个人微信