Qt中的模型视图设计模式

1 初探Qt中的模型视图设计模式

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

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

模型视图模式的直观理解:
在这里插入图片描述
模型视图模式的工作机制:

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

Qt中的模型类层次结构:
在这里插入图片描述
Qt中视图类的层次结构:
在这里插入图片描述
关键技术问题:模型如何为数据提供统一的访问方式?

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

模型视图编程示例:
在这里插入图片描述
Widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QtGui/QWidget>
#include <QFileSystemModel>
#include <QTreeView>

class Widget : public QWidget
{
    Q_OBJECT
    
    QFileSystemModel m_fsModel;
    QTreeView m_treeView;
public:
    Widget(QWidget *parent = 0);
    ~Widget();
};

#endif // WIDGET_H

Widget.cpp:

#include "Widget.h"
#include <QDir>

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

main.cpp:

#include <QtGui/QApplication>
#include "Widget.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    
    return a.exec();
}

对模型视图设计模式总结一下:

  • 模型定义标准接口(成员函数)对数据进行访问。
  • 视图通过标准接口获取数据并定义显示方式。
  • 模型使用信号与槽的机制通知视图数据变化。
  • 模型的数据都是以层次结构表示的。

2 模型视图中的索引

模型中的索引:

  • 模型索引是数据与视图分离的重要机制。
  • 模型中的数据使用唯一的索引来访问。
  • 索引是访问模型中具体数据的约定方式。
  • QModelIndex是Qt中的模型索引类:
    • 包含具体数据的访问途径。
    • 包含一个指向模型的指针。

索引的意义:
在这里插入图片描述
索引中的行和列:

  • 线性模型可以使用(row,column)作为数据索引。
    在这里插入图片描述
    问题:只用行和列描述数据索引是否足够通用?

思考:

  • 如何索引以属性结构组织的数据?
    在这里插入图片描述

模型中的通用树形结构:在这里插入图片描述
模型中数据索引的通用方式:

  • 三元组:(row、column、parent)。
  • 索引在需要时由模型实时创建。
  • 使用空索引作为父节点表示顶层数据元素。
  • 特殊的模型可以自定义特殊的索引获取方式。
    在这里插入图片描述
    在这里插入图片描述
    编程实验:数据索引深入理解

Widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QtGui/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:
    Widget(QWidget *parent = 0);
    ~Widget();
};

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

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

main.cpp:

#include <QtGui/QApplication>
#include "Widget.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    
    return a.exec();
}

后面需要解决一个问题:QFileSystemModel中是如何组织数据的?如何手动解析QFileSystemModel中的数据?


3 模型中的数据组织方式初探

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

Qt中的标准模型定义:
在这里插入图片描述
MainWindow.h:

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QtGui/QMainWindow>
#include <QTableView>
#include <QStandardItemModel>
class MainWindow : public QMainWindow
{
    Q_OBJECT
private:
    QStandardItemModel m_standerdModel;
    QTableView m_tableView;
    void initStanderdModel();
    void initTableModel();
public:
    MainWindow(QWidget *parent = 0);
    ~MainWindow();
};

#endif // MAINWINDOW_H


MainWindow.cpp:

#include "MainWindow.h"
#include <QStandardItem>

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    initStanderdModel();
    initTableModel();
    m_tableView.setModel(&m_standerdModel);
}
void MainWindow::initStanderdModel()
{
    QStandardItem* root = m_standerdModel.invisibleRootItem();
    QStandardItem* itemA = new QStandardItem();
    QStandardItem* itemB = new QStandardItem();
    QStandardItem* itemC = new QStandardItem();

    itemA->setData("A");//只是往数据项里面放了数据,并没有告诉如何显示
    itemA->setData("Tip A");
    itemA->setData("Help A");
    itemB->setData("B");
    itemB->setData("Tip B");
    itemC->setData("C");
    itemC->setData("Tip B");
    itemC->setData("Help C");

    root->setChild(0, 0, itemA);//itemA、itemB、itemC都是new出来的,这里将数据项
    root->setChild(0, 1, itemB);//加入模型的同时也相当于指定了父组件,不会造成内存泄漏
    root->setChild(1, 0, itemC);

}
void MainWindow::initTableModel()
{
    m_tableView.resize(300,100);
    m_tableView.move(10,10);
    m_tableView.setParent(this);
}
MainWindow::~MainWindow()
{
    
}


main.cpp:

#include <QtGui/QApplication>
#include "MainWindow.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MainWindow w;
    w.show();
    
    return a.exec();
}


运行结果如下:
在这里插入图片描述
我们会发现结果无法正常显示,为了使数据能够正常显示,我们需要引入数据角色的概念:

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

Qt中的数据角色定义:
在这里插入图片描述
数据角色的意义:

  • 定义了数据在特定系统下的标准用途。
  • 不同的视图可以通过相同标准显示数据。

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

更改后的代码如下:
Widget.h:

#ifndef WIDGET_H
#define WIDGET_H

#include <QtGui/QWidget>
#include <QTableView>
#include <QListView>
#include <QTreeView>
#include <QStandardItemModel>

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

    void initModel();
    void initView();
public:
    Widget(QWidget *parent = 0);
    ~Widget();
};

#endif // WIDGET_H

Widget.cpp:

#include "Widget.h"
#include <QStandardItem>

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

main.cpp:

#include <QtGui/QApplication>
#include "Widget.h"

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    Widget w;
    w.show();
    
    return a.exec();
}

运行结果如下:
在这里插入图片描述


参考资料:

  1. QT实验分析教程
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值