Qt——模型视图设计模式

1.模型视图设计模式的核心思想

  • 模型(数据)与视图(显示)相分离
  • 模型对外提供标准接口存取数据(不关心数据如何显示)
  • 视图自定义数据的显示方式(不关心数据如何组织存储)

2.模型视图模式的工作机制

  • 当数据发生改变时,模型发出信号通知视图
  • 当用户与视图进行交互时,视图发出信号提供交互信息

Widget.h

复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QFileSystemModel>
#include <QTreeView>

class Widget : public QWidget
{
    Q_OBJECT

    QFileSystemModel m_fsModel; //定义文件系统模型
    QTreeView m_treeView; //定义树形显示视图

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget() override;
};
#endif // WIDGET_H

Widget.cpp

复制代码
#include "Widget.h"

Widget::Widget(QWidget *parent) : QWidget(parent)
{
    m_treeView.setParent(this);
    m_treeView.move(10, 10);
    m_treeView.resize(500, 300);

    m_fsModel.setRootPath(QDir::currentPath()); //从当前工作目录中取数据

    m_treeView.setModel(&m_fsModel); //连接模型与视图
    m_treeView.setRootIndex(m_fsModel.index(QDir::currentPath())); //设置属性视图的数据索引
}

Widget::~Widget() = default;

运行结果:

(1)模型定义标准接口(成员函数)对数据进行访问

(2)视图通过标准接口获取数据并定义显示方式

(3)模型通过信号与槽的机制通知视图数据变化

(4)模型中的数据都是以层次结果表示的

4.QModelIndex是Qt中的模型索引类

(1)包含具体数据的访问途径

(2)包含一个指向模型的指针

5.索引中的行和列

(1)线性模型可以使用行和列作为数据索引

(2)树形结构可以通过(index, parent)的方式确定节点

所以使用通用的方式------三元组(row, column, parent)

注意:当父节点为虚拟root节点时,可以使用空索引(直接调用QModelIndex()产生)作为父节点参数

Widget.h

复制代码
#ifndef WIDGET_H
#define WIDGET_H

#include <QWidget>
#include <QPlainTextEdit>
#include <QFileSystemModel>

class Widget : public QWidget
{
    Q_OBJECT

    QPlainTextEdit m_edit;
    QFileSystemModel m_fsm;

protected slots:
    void onDirectoryLoaded(const QString path);

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget() override;
};
#endif // WIDGET_H

Widget.cpp

复制代码
#include "Widget.h"
#include <QDir>
#include <QModelIndex>
#include <QByteArray>
#include <QBuffer>
#include <QTextStream>

Widget::Widget(QWidget *parent) : QWidget(parent)
{
    m_edit.setParent(this);
    m_edit.move(10, 10);
    m_edit.resize(500, 300);

    //当文件系统模型加载完目录时,调用onDirectoryLoaded函数
    connect(&m_fsm, SIGNAL(directoryLoaded(QString)), this, SLOT(onDirectoryLoaded(QString)));

    m_fsm.setRootPath(QDir::currentPath()); //设置文件系统模型的根路径为当前工作目录
}
void Widget::onDirectoryLoaded(const QString path)
{
    QModelIndex root = m_fsm.index(path); // 获取指定路径的模型索引
    QByteArray array;
    QBuffer buffer(&array);


    if( buffer.open(QIODevice::WriteOnly))
    {
        QTextStream out(&buffer);

        // 输出各种文件系统模型信息
        out << m_fsm.isDir(root) <<endl; //是否为目录
        out << m_fsm.data(root).toString() << endl; //显示数据(文件名)
        out << root.data().toString() << endl; //索引的数据
        out << &m_fsm << endl; //模型对象地址
        out << root.model() << endl; //索引所属的模型地址
        out << m_fsm.filePath(root) << endl; //完整文件路径
        out << m_fsm.fileName(root) << endl; //文件/文件夹名
        out << endl;

        // 遍历并输出根目录下的所有文件/文件夹名称
        for(int i=0; i<m_fsm.rowCount(root); i++)
        {
            QModelIndex ci = m_fsm.index(i, 0, root);
            out << ci.data().toString() << endl;
        }
        out.flush(); // 刷新缓冲区
        buffer.close();
    }
    if( buffer.open(QIODevice::ReadOnly) )
    {
        QTextStream in(&buffer);
        m_edit.insertPlainText(in.readAll());
        buffer.close();
    }
}

Widget::~Widget() = default;

运行结果:

6.不同的视图如何显示同一个模型中的数据?

数据角色的概念

  • 模型中的数据在视图中的用途(显示方式)可能不同
  • 模型必须为数据设置特定数据角色(数据属性)
  • 数据角色用于提示视图数据的作用
  • 数据角色是不同视图以统一风格显示数据的标准

Widget.h

复制代码
#ifndef WIDGET_H
#define WIDGET_H
#include <QTableView>
#include <QStandardItemModel> //通用数据模型
#include <QTreeView>
#include <QListView>
#include <QWidget>

class Widget : public QWidget
{
    Q_OBJECT
    QStandardItemModel m_model;
    QTableView m_tableView;
    QListView m_listView;
    QTreeView m_treeView;

    void initModel();
    void initView();

public:
    explicit Widget(QWidget *parent = nullptr);
    ~Widget() override;
};
#endif // WIDGET_H

Widget.cpp

复制代码
#include "Widget.h"

Widget::Widget(QWidget *parent)
    : QWidget(parent, Qt::WindowContextHelpButtonHint)
{
    initModel();
    initView();

    m_tableView.setModel(&m_model);
    m_listView.setModel(&m_model);
    m_treeView.setModel(&m_model);
}

void Widget::initModel()
{
    QStandardItem* root = m_model.invisibleRootItem(); //返回模型的虚拟根节点
    QStandardItem* itemA = new QStandardItem();
    QStandardItem* itemB = new QStandardItem();
    QStandardItem* itemC = new QStandardItem();
    QStandardItem* itemChild = new QStandardItem();

    itemA->setData("A", Qt::DisplayRole); //直接展示
    itemA->setData("Tip A", Qt::ToolTipRole); //鼠标悬停显示
    itemA->setData("Help A", Qt::WhatsThisRole); //当用户点击"这是什么"按钮后点击界面元素时显示帮助文本。

    itemB->setData("B", Qt::DisplayRole);
    itemB->setData("Tip B", Qt::ToolTipRole);

    itemC->setData("C", Qt::DisplayRole);
    itemC->setData("Tip C", Qt::ToolTipRole);
    itemC->setData("Help C", Qt::WhatsThisRole);

    itemChild->setData("Child", Qt::DisplayRole);
    itemChild->setData("Tip Child", Qt::ToolTipRole);
    itemChild->setData("Help Child", Qt::WhatsThisRole);

    itemC->setChild(0, 0, itemChild);

    root->setChild(0, 0, itemA);
    root->setChild(0, 1, itemB);
    root->setChild(1, 0, itemC);

}
void Widget::initView()
{
    m_tableView.setParent(this);
    m_tableView.move(10, 10);
    m_tableView.resize(300, 100);

    m_listView.setParent(this);
    m_listView.move(10, 120);
    m_listView.resize(300, 100);

    m_treeView.setParent(this);
    m_treeView.move(10, 230);
    m_treeView.resize(300, 100);
}
Widget::~Widget() = default;

运行结果:

相关推荐
咖啡八杯1 小时前
GoF设计模式——解释器模式
java·后端·spring·设计模式
大数据新鸟4 小时前
设计模式四大基本原则
设计模式
南一Nanyi9 小时前
依赖注入和控制反转
前端·设计模式·nestjs
tiankong121312 小时前
如何拓展多态下的子类
设计模式·c#
jctech15 小时前
让「小程序」跑起真正的原生代码:ComboNative 多进程原生小程序运行时 · Alpha 首发
android·设计模式·架构
Kel1 天前
Node.js 单线程高并发:从内核中断到 JS 回调的完整技术栈
设计模式·架构·node.js
解局易否结局2 天前
ArkUI MVI 架构实战:单向数据流设计模式在 HarmonyOS NEXT 中的深度实践
华为·设计模式·架构·harmonyos
解局易否结局3 天前
ArkTS 设计模式与架构进阶:构建可维护的 HarmonyOS 应用
华为·设计模式·架构·harmonyos
happyness443 天前
AI编程与设计模式
设计模式·ai编程
就是小王同学啊3 天前
Spring小技巧之设计模式
sql·spring·设计模式