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();
}
运行结果如下:

参考资料:
6729

被折叠的 条评论
为什么被折叠?



