Qt 实战(10)MVD | 10.1、MVD机制详解


前言:

在Qt框架中,MVD(Model-View-Delegate)机制是一种用于实现数据与用户界面分离的重要设计模式。它源于经典的MVC(Model-View-Controller)设计模式,但在Qt中进行了适当的调整和优化,将Controller的角色融入到了Delegate中,使得整个框架更加简洁高效。本文将详细介绍MVD机制的原理、实现机制以及使用场景。

一、MVD机制

1、MVC设计模式

1.1、简介

介绍MVD之前,先介绍下MVC设计模式。MVC是一种与用户界面相关的设计模式,通过使用该设计模式,可以有效的分离数据与用户界面。MVC设计模式包含三个元素:表示数据的模型(Model)、表示用户界面的视图(View) 和定义了用户在界面上操作的控制器(Controller),这三者的主要职责,如下:

  • 视图层(View): 用户看到并与之交互的界面(例如:网页界面或软件的客户端界面),负责显示模型中的数据。
  • 模型层(Model): 指从现实世界中抽象出来的对象模型,它封装了数据和对数据的操作,是实际进行数据处理的地方。
  • 控制器(Controller): 处理用户交互操作(鼠标事件、键盘事件、用户输入),并调用模型和视图去完成用户的需求。控制器本身不输出任何东西和做任何处理,它只是接收用户交互操作并决定调用哪个模型构件去处理,然后再确定用哪个视图来显示返回的数据。

1.2、优缺点分析

下面是对MVC的优缺点进行分析,如下:

优点:

  • 多视图共享一个模型,大大提高了代码的可重用性
  • MVC 三个模块相互独立,松耦合架构
  • 控制器提高了应用程序的灵活性和可配置性

缺点:

  • 原理复杂
  • 增加了系统结构和实现的复杂性
  • 视图对模型数据的低效率访问

通过 MVC 设计模式最终可以打造出一个松耦合+高可重用性+高可适用性的完美架构。但是、MVC 并不适合小型甚至中型规模的项目,花费大量时间将 MVC 应用到规模并不是很大的应用程序,通常得不偿失,所以对于 MVC 设计模式的使用要根据具体的应用场景来决定。

2、什么是MVD?

2.1、简介

与MVC设计模式类似、Qt引入了模型/视图结构用于完成界面与数据分离。但是不同的是,Qt的模型/视图结构把视图与控制器部件结合在一起,使得框架更加的简洁。为了灵活的处理用户输入,Qt的模型/视图框架引入了代理(Delegate),通过使用代理,能够自定义数据条目(item)的显示和编辑方式,如下:

在这里插入图片描述

2.2、核心角色

MVD机制由三部分组成:Model(模型)、View(视图)和Delegate(代理)。这三部分各司其职,共同实现了数据与界面的分离,提高了应用程序的可维护性和扩展性。

  • Model(模型): 负责存储和管理数据。模型是数据的核心,它独立于视图和代理存在,可以通过接口提供数据的存取服务。在Qt中,自定义模型通常通过继承QAbstractItemModelQAbstractListModelQAbstractTableModel等抽象基类来实现。
  • View(视图): 负责数据的显示。视图从模型中获取数据,并通过界面将其展示给用户。在Qt中,视图类如QListViewQTreeViewQTableView等,都内置了对模型数据的展示逻辑。视图通过模型索引(ModelIndex)来引用模型中的数据项。
  • Delegate(代理): 负责数据的显示和编辑逻辑。在MVD机制中,代理不仅仅是简单的数据渲染器,它还负责处理数据的编辑逻辑。通过代理,用户可以自定义数据的显示方式和编辑行为。在Qt中,代理通常通过继承QStyledItemDelegateQItemDelegate等类来实现。
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
以下是一个简单的示例代码,演示了QTMVD机制的原理: ``` // 定义一个数据模型类 class MyModel : public QAbstractItemModel { public: // 实现父类中的纯虚函数 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex &child) const override; int rowCount(const QModelIndex &parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; private: // 存储数据的变量 QVector<QString> m_data; }; // 实现数据模型类中的函数 QModelIndex MyModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); // 计算索引值 int index = row * columnCount() + column; // 返回对应索引的QModelIndex对象 return createIndex(row, column, static_cast<void*>(&m_data[index])); } QModelIndex MyModel::parent(const QModelIndex &child) const { return QModelIndex(); } int MyModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return m_data.size() / columnCount(); } int MyModel::columnCount(const QModelIndex &parent) const { if (parent.isValid()) return 0; return 3; } QVariant MyModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole || role == Qt::EditRole) return QVariant(*static_cast<QString*>(index.internalPointer())); return QVariant(); } bool MyModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || role != Qt::EditRole) return false; *static_cast<QString*>(index.internalPointer()) = value.toString(); emit dataChanged(index, index, {role}); return true; } // 定义一个委托类 class MyDelegate : public QStyledItemDelegate { public: // 实现父类中的纯虚函数 QWidget* createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; void setEditorData(QWidget *editor, const QModelIndex &index) const override; void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override; }; // 实现委托类中的函数 QWidget* MyDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const { QLineEdit *editor = new QLineEdit(parent); return editor; } void MyDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const { QString value = index.model()->data(index, Qt::EditRole).toString(); QLineEdit *lineEdit = static_cast<QLineEdit*>(editor); lineEdit->setText(value); } void MyDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QLineEdit *lineEdit = static_cast<QLineEdit*>(editor); QString value = lineEdit->text(); model->setData(index, value, Qt::EditRole); } // 在MainWindow的构造函数中设置MVD机制 MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) { MyModel *model = new MyModel(this); model->m_data = {"a", "b", "c"}; QTreeView *view = new QTreeView(this); view->setModel(model); MyDelegate *delegate = new MyDelegate(this); view->setItemDelegate(delegate); setCentralWidget(view); } ``` 这段代码创建了一个名为MyModel的数据模型类,并实现了QAbstractItemModel中的纯虚函数。数据模型类中存储了一个包含字符串的向量,可以通过索引获取其中的元素。在MainWindow的构造函数中,创建了一个包含MyModel的QTreeView,并使用MyDelegate作为其委托类,实现了对数据项的定制化显示和编辑。在运行时,用户可以通过双击某个数据项来编辑其内容,此时会调用委托类中的函数,将编辑后的数据传递给数据模型类,并更新UI以反映更改。这个示例演示了QTMVD机制的原理,使得应用程序更易于维护和扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值