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;

运行结果:

相关推荐
玖玥拾17 小时前
C/C++ 基础笔记(十一)类的进阶
c语言·c++·设计模式·
geovindu21 小时前
go: Broadcast Pattern
开发语言·后端·设计模式·golang·广播模式
我爱cope21 小时前
【Agent智能体23 | 规划-规划工作流】
人工智能·设计模式·语言模型·职场和发展
lengjingzju1 天前
符·形·音·意(SFEM):一种面向通用智能的四维认知架构
设计模式·ai·学习方法
贵慜_Derek1 天前
《从零实现 Agent 系统》连载 23|Skill 体系与 Skill Creator:能力打包与迭代
人工智能·设计模式·架构
张小姐的猫1 天前
【Linux】多线程 —— 线程池 | 单例模式 | 常见锁
linux·运维·服务器·c++·单例模式·设计模式·策略模式
老码观察1 天前
设计模式实战解读(十二):状态模式——干掉状态机里的 if-else 地狱
设计模式·状态模式
我爱cope1 天前
【Agent智能体24 | 规划-创建和执行LLM计划】
人工智能·设计模式·语言模型·职场和发展
Hillain1 天前
软件设计师设计模式
java·开发语言·经验分享·笔记·算法·设计模式·软考