模型视图设计模式

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

模型 (数据) 与视图 (显示) 相分离

模型对外提供标准接口存取数据 (不关心数据如何显示)

视图自定义数据的显示方式 (不关心数据如何组织存储)

模型视图的直观理解

模型视图模式的工作机制

当数据发生改变时

  • 模型发出信号通知视图

当用户与视图进行交互时

  • 视图发出信号提供交互信息

Qt 中的模型类层次结构

 Qt 中的视图类层次结构

关键技术问题:

模型如何为数据提供统一的访问方式?

深入理解:

在 Qt 中,无论模型以什么结构组织数据,都必须为每一个数据提供独一无二的索引;视图通过索引访问模型中的具体数据。

模型视图编程示例

模型视图结构的初探

Widget.h

class Widget : public QWidget
{
    Q_OBJECT
private:
    QFileSystemModel m_fsModel;
    QTreeView m_treeView;

public:
    Widget(QWidget* parent = nullptr);
    ~Widget();
};

Widget.cpp

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

    m_fsModel.setRootPath(QDir::currentPath());
    m_treeView.setModel(&m_fsModel);
    m_treeView.setRootIndex(m_fsModel.index(QDir::currentPath()));
}

Widget::~Widget()
{
}

程序运行结果如下所示:

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

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

模型使用信号与槽的机制通知视图数据变化

模型中的数据都是以层次结构表示的

模型中的索引

模型索引是数据与视图分离的重要机制

模型中的数据使用唯一的索引来访问

QModelIndex 是 Qt 中的模型索引类

  • 包含具体数据的访问途径
  • 包含一个指向模型的指针

索引的意义

索引中的行和列

线性模型可以使用 (row,column) 作为数据索引

问题

 只用行和列描述数据索引是否足够通用?

不足够通用。

思考:

如何索引以树形结构组织的数据?

模型中的通用树形结构 

模型中数据索引的通用方式

三元组:(row,column,parent)

数据索引深入理解

Widget.h

class Widget : public QWidget
{
    Q_OBJECT
private:
    QPlainTextEdit m_edit;
    QFileSystemModel m_fsModel;

protected slots:
    void onDirectoryLoaded(const QString& path);

public:
    Widget(QWidget* parent = nullptr);
    ~Widget();
};

Widget.cpp

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

    connect(&m_fsModel, &QFileSystemModel::directoryLoaded, this, &Widget::onDirectoryLoaded);

    m_fsModel.setRootPath(QDir::currentPath());
}

void Widget::onDirectoryLoaded(const QString &path)
{
    QModelIndex root = m_fsModel.index(path);
    QByteArray array;
    QBuffer buf(&array);

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

        out << m_fsModel.isDir(root) << endl;
        out << m_fsModel.data(root).toString() << endl;
        out << root.data().toString() << endl;
        out << &m_fsModel << endl;
        out << root.model() << endl;
        out << m_fsModel.filePath(root) << endl;
        out << m_fsModel.fileName(root) << endl;

        for(int i = 0; i < m_fsModel.rowCount(root); i++)
        {
            QModelIndex c = m_fsModel.index(i, 0, root);

            out << c.data().toString() << endl;
        }

        out.flush();

        buf.close();
    }

    if(buf.open(QIODevice::ReadOnly))
    {
        m_edit.insertPlainText(buf.readAll());

        buf.close();
    }
}

Widget::~Widget()
{
}

程序运行结果如下所示:

问题

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

Qt 中标准模型定义

数据角色的概念

模型中的数据在视图中的用途 (显示方式可能不同)

模型必须为数据设置特定数据角色 (数据属性)

数据角色用于提示视图数据的作用

数据角色是不同视图以统一风格显示数据的标准

Qt 中的数据角色定义

数据角色的意义

定义了数据在特点系统下的标准用途

不同的视图可以通过相同标准显示数据

注意:

  • 数据角色只是一个附加的属性,这个属性代表推荐的数据显示方式。不同的视图完全可以自由解析或忽略数据的角色信息。

数据模型中的角色

Widget.h

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

    void initModel();
    void initView();

public:
    Widget(QWidget *parent = nullptr);
    ~Widget();
};

Widget.cpp

Widget::Widget(QWidget* parent)
    : QWidget(parent, Qt::WindowContextHelpButtonHint | Qt::WindowCloseButtonHint)
{
    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();

    itemChild->setData("child", Qt::DisplayRole);
    itemChild->setData("Tip child", Qt::ToolTipRole);

    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);

    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()
{
}

程序运行结果如下所示:

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值