Qt 学习之路 2笔记4

model/view 架构

MVC 是 Model-View-Controller 的简写,即模型-视图-控制器。在 MVC 中,模型负责获取需要显示的数据,并且存储这些数据的修改。视图用于将模型数据显示给用户。对于数量很大的数据,或许只显示一小部分,这样就能很好的提高性能。控制器是模型和视图之间的媒介,将用户的动作解析成对数据的操作,比如查找数据或者修改数据,然后转发给模型执行,最后再将模型中需要被显示的数据直接转发给视图进行显示。MVC 的核心思想是分层,不同的层应用不同的功能。

当 MVC 的 V 和 C 结合在一起,我们就得到了 model/view 架构。这种架构依然将数据和界面分离,但是框架更为简单。同样,这种架构也允许使用不同界面显示同一数据,也能够在不改变数据的情况下添加新的显示界面。为了处理用户输入,我们还引入了委托(delegate)。引入委托的好处是,我们能够自定义数据项的渲染和编辑。

模型与数据源进行交互,为框架中其它组件提供接口。这种交互的本质在于数据源的类型以及模型的实现方式。视图从模型获取模型索引,这种索引就是数据项的引用。通过将这个模型索引反向传给模型,视图又可以从数据源获取数据。在标准视图中,委托渲染数据项;在需要编辑数据时,委托使用直接模型索引直接与模型进行交互。

总的来说,model/view 架构将传统的 MV 模型分为三部分:模型、视图和委托。每一个组件都由一个抽象类定义,这个抽象类提供了基本的公共接口以及一些默认实现。模型、视图和委托则使用信号槽进行交互:

  • 来自模型的信号通知视图,其底层维护的数据发生了改变;
  • 来自视图的信号提供了有关用户与界面进行交互的信息;
  • 来自委托的信号在用户编辑数据项时使用,用于告知模型和视图编辑器的状态。

所有的模型都是QAbstractItemModel的子类。这个类定义了供视图和委托访问数据的接口。模型并不存储数据本身。这意味着,你可以将数据存储在一个数据结构中、另外的类中、文件中、数据库中,或者其他你所能想到的东西中。

QAbstractItemModel提供的接口足够灵活,足以应付以表格、列表和树的形式显示的数据。但是,如果你需要为列表或者表格设计另外的模型,直接继承QAbstractListModel和QAbstractTableModel类可能更好一些,因为这两个类已经实现了很多通用函数。

Qt 内置了许多标准模型:

  • QStringListModel:存储简单的字符串列表。
  • QStandardItemModel:可以用于树结构的存储,提供了层次数据。
  • QFileSystemModel:本地系统的文件和目录信息。
  • QSqlQueryModel、QSqlTableModel和QSqlRelationalTableModel:存取数据库数据。

如果这些标准模型不能满足你的需要,就必须继承QAbstractItemModel、QAbstractListModel或者QAbstractTableModel,创建自己的模型类。

Qt 还提供了一系列预定义好的视图:QListView用于显示列表,QTableView用于显示表格,QTreeView用于显示层次数据。这些类都是QAbstractItemView的子类。这意味着,如果你要创建新的视图类,则可以继承QAbstractItemView。

QAbstractItemDelegate则是所有委托的抽象基类。自 Qt 4.4 以来,默认的委托实现是QStyledItemDelegate。但是,QStyledItemDelegate和QItemDelegate都可以作为视图的编辑器,二者的区别在于,QStyledItemDelegate使用当前样式进行绘制。在实现自定义委托时,推荐使用QStyledItemDelegate作为基类,或者结合 Qt style sheets。

如果你觉得 model/view 模型过于复杂,或者有很多功能是用不到的,Qt 还有一系列方便使用的类。这些类都是继承自标准的视图类,并且继承了标准模型。这些类并不是为其他类继承而准备的,只是为了使用方便。它们包括QListWidget、QTreeWidget和QTableWidget。这些类远不如视图类灵活,不能使用另外的模型,因此只适用于简单的情形。

QListWidget、QTreeWidget 和 QTableWidget

QListWidget

先来看下面的代码示例:

label = new QLabel(this);
label->setFixedWidth(70);

listWidget = new QListWidget(this);

new QListWidgetItem(QIcon(":/Chrome.png"), tr("Chrome"), listWidget);
new QListWidgetItem(QIcon(":/Firefox.png"), tr("Firefox"), listWidget);

listWidget->addItem(new QListWidgetItem(QIcon(":/IE.png"), tr("IE")));
listWidget->addItem(new QListWidgetItem(QIcon(":/Netscape.png"), tr("Netscape")));
listWidget->addItem(new QListWidgetItem(QIcon(":/Opera.png"), tr("Opera")));
listWidget->addItem(new QListWidgetItem(QIcon(":/Safari.png"), tr("Safari")));
listWidget->addItem(new QListWidgetItem(QIcon(":/TheWorld.png"), tr("TheWorld")));
listWidget->addItem(new QListWidgetItem(QIcon(":/Traveler.png"), tr("Traveler")));

QListWidgetItem *newItem = new QListWidgetItem;
newItem->setIcon(QIcon(":/Maxthon.png"));
newItem->setText(tr("Maxthon"));
listWidget->insertItem(3, newItem);

QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(label);
layout->addWidget(listWidget);

setLayout(layout);

connect(listWidget, SIGNAL(currentTextChanged(QString)),
        label, SLOT(setText(QString)));

QListWidget是简单的列表组件。当我们不需要复杂的列表时,可以选择QListWidget。QListWidget中可以添加QListWidgetItem类型作为列表项,QListWidgetItem即可以有文本,也可以有图标。上面的代码显示了三种向列表中添加列表项的方法(实际是两种,后两种其实是一样的),我们的列表组件是listWidget,那么,向listWidget添加列表项可以:第一,使用下面的语句

new QListWidgetItem(QIcon(":/Chrome.png"), tr("Chrome"), listWidget);

第二,使用

listWidget->addItem(new QListWidgetItem(QIcon(":/IE.png"), tr("IE")));
// 或者
QListWidgetItem *newItem = new QListWidgetItem;
newItem->setIcon(QIcon(":/Maxthon.png"));
newItem->setText(tr("Maxthon"));
listWidget->insertItem(3, newItem);

我们可以利用QListWidget发出的各种信号来判断是哪个列表项被选择,具体细节可以参考文档。另外,我们也可以改变列表的显示方式。前面的列表是小图标显示,我们也可以更改为图标显示,只要添加一行语句:

listWidget->setViewMode(QListView::IconMode);

QTreeWidget

这个类需要同另外一个辅助类QTreeWidgetItem一起使用。不过,既然是提供方面的封装类,即便是看上去很复杂的树,在使用这个类的时候也是显得比较简单的。当不需要使用复杂的QTreeView特性的时候,我们可以直接使用QTreeWidget代替。

QTreeWidget treeWidget;
treeWidget.setColumnCount(1);

QTreeWidgetItem *root = new QTreeWidgetItem(&treeWidget,
                                            QStringList(QString("Root")));
new QTreeWidgetItem(root, QStringList(QString("Leaf 1")));
QTreeWidgetItem *leaf2 = new QTreeWidgetItem(root, QStringList(QString("Leaf 2")));
leaf2->setCheckState(0, Qt::Checked);

QList<QTreeWidgetItem *> rootList;
rootList << root;
treeWidget.insertTopLevelItems(0, rootList);

treeWidget.show();

首先,我们创建了一个QTreeWidget实例。然后我们调用setColumnCount()函数设定栏数。最后,我们向QTreeWidget添加QTreeWidgetItem。

QTreeWidgetItem(QTreeWidget *parent, const QStringList &strings, int type = Type);

这里有 3 个参数,第一个参数用于指定这个项属于哪一个树,类似前面的QListWidgetItem,如果指定了这个值,则意味着该项被直接添加到树中;第二个参数指定显示的文字;第三个参数指定其类型,同QListWidgetItem的type参数十分类似。值得注意的是,第二个参数是QStringList类型的,而不是QString类型。

QTreeWidget treeWidget;

QStringList headers;
headers << "Name" << "Number";
treeWidget.setHeaderLabels(headers);

QStringList rootTextList;
rootTextList << "Root" << "0";
QTreeWidgetItem *root = new QTreeWidgetItem(&treeWidget, rootTextList);

new QTreeWidgetItem(root, QStringList() << QString("Leaf 1") << "1");
QTreeWidgetItem *leaf2 = new QTreeWidgetItem(root,
                                   QStringList() << QString("Leaf 2") << "2");
leaf2->setCheckState(0, Qt::Checked);

QList<QTreeWidgetItem *> rootList;
rootList << root;
treeWidget.insertTopLevelItems(0, rootList);

treeWidget.show();

如果你不需要显示这个表头,可以调用setHeaderHidden()函数将其隐藏。

QTableWidget

QTableWidget tableWidget;
tableWidget.setColumnCount(3);
tableWidget.setRowCount(5);

QStringList headers;
headers << "ID" << "Name" << "Age" << "Sex";
tableWidget.setHorizontalHeaderLabels(headers);

tableWidget.setItem(0, 0, new QTableWidgetItem(QString("0001")));
tableWidget.setItem(1, 0, new QTableWidgetItem(QString("0002")));
tableWidget.setItem(2, 0, new QTableWidgetItem(QString("0003")));
tableWidget.setItem(3, 0, new QTableWidgetItem(QString("0004")));
tableWidget.setItem(4, 0, new QTableWidgetItem(QString("0005")));
tableWidget.setItem(0, 1, new QTableWidgetItem(QString("20100112")));

tableWidget.show();

QStringListModel

QStringListModel是最简单的模型类,具备向视图提供字符串数据的能力。QStringListModel是一个可编辑的模型,可以为组件提供一系列字符串作为数据。我们可以将其看作是封装了QStringList的模型。

MyListView::MyListView()
{
    QStringList data;
    data << "Letter A" << "Letter B" << "Letter C";
    model = new QStringListModel(this);
    model->setStringList(data);

    listView = new QListView(this);
    listView->setModel(model);

    QHBoxLayout *btnLayout = new QHBoxLayout;
    QPushButton *insertBtn = new QPushButton(tr("insert"), this);
    connect(insertBtn, SIGNAL(clicked()), this, SLOT(insertData()));
    QPushButton *delBtn = new QPushButton(tr("Delete"), this);
    connect(delBtn, SIGNAL(clicked()), this, SLOT(deleteData()));
    QPushButton *showBtn = new QPushButton(tr("Show"), this);
    connect(showBtn, SIGNAL(clicked()), this, SLOT(showData()));
    btnLayout->addWidget(insertBtn);
    btnLayout->addWidget(delBtn);
    btnLayout->addWidget(showBtn);

    QVBoxLayout *mainLayout = new QVBoxLayout(this);
    mainLayout->addWidget(listView);
    mainLayout->addLayout(btnLayout);
    setLayout(mainLayout);
}

void MyListView::insertData()
{
    bool isOK;
    QString text = QInputDialog::getText(this, "Insert",
                                         "Please input new data:",
                                         QLineEdit::Normal,
                                         "You are inserting new data.",
                                         &isOK);
    if (isOK) {
        QModelIndex currIndex = listView->currentIndex();
        model->insertRows(currIndex.row(), 1);
        model->setData(currIndex, text);
        listView->edit(currIndex);
    }
}
void MyListView::deleteData()
{
    if (model->rowCount() > 1) {
        model->removeRows(listView->currentIndex().row(), 1);
    }
}
void MyListView::showData()
{
    QStringList data = model->stringList();
    QString str;
    foreach(QString s, data) {
        str += s + "\n";
    }
    QMessageBox::information(this, "Data", str);
}

模型

各种 model 的组织示意图:

时间: 2024-08-27 13:24:55

Qt 学习之路 2笔记4的相关文章

Qt 学习之路 2笔记2

事件过滤器 QObject有一个eventFilter()函数,用于建立事件过滤器.这个函数的签名如下: virtual bool QObject::eventFilter ( QObject * watched, QEvent * event ); 事件过滤器:会检查接收到的事件.如果这个事件是我们感兴趣的类型,就进行我们自己的处理:如果不是,就继续转发.这个函数返回一个 bool 类型,如果你想将参数 event 过滤出来,比如,不想让它继续转发,就返回 true,否则返回 false.事件

Qt 学习之路 2笔记3

Graphics View Framework Graphics View 提供了一种接口,用于管理大量自定义的 2D 图形元素,并与之进行交互:还提供了用于将这些元素进行可视化显示的观察组件,并支持缩放和旋转. Graphics View 框架包含了一套完整的事件体系,可以用于与场景中的元素进行双精度的交互.这些元素同样支持键盘事件.鼠标事件等.Graphics View 使用了 BSP 树(Binary Space Partitioning tree,这是一种被广泛应用于图形学方面的数据结构

Qt 学习之路 2 --- 读书笔记

一.文章来由 来自豆子老师非常好的一本Qt教程,但是只有网络版,所以用这个做笔记了,不动笔墨不读书嘛~~ 二.读书笔记 1.Qt 学习之路 2(2):Qt 简介 1.1 关于 Qt 的一站式解决 Qt 是一个著名的 C++ 应用程序框架.但并不只是一个 GUI 库,因为 Qt 十分庞大,并不仅仅是 GUI 组件.使用 Qt,在一定程度上你获得的是一个"一站式"的解决方案:不再需要研究 STL,不再需要 C++ 的,不再需要到处去找解析 XML.连接数据库.访问网络的各种第三方库,因为

Qt学习之路

  Qt学习之路_14(简易音乐播放器) Qt学习之路_13(简易俄罗斯方块) Qt学习之路_12(简易数据管理系统) Qt学习之路_11(简易多文档编辑器) Qt学习之路_10(Qt中statusBar,MessageBox和Timer的简单处理) Qt学习之路_9(Qt中Item Widget初步探索) Qt学习之路_8(Qt中与文件目录相关操作) Qt学习之路_7(线性布局和网格布局初步探索) Qt学习之路_6(Qt局域网聊天软件) Qt学习之路_5(Qt TCP的初步使用) Qt学习之路

QT学习之路(1):彩票绝对不中模拟器

//============================================//绝对不中,彩票开奖模拟器#include "mainwindow.h"#include "ui_mainwindow.h"#include <QHash>#include <QDebug>MainWindow::MainWindow(QWidget *parent) :    QMainWindow(parent),    ui(new Ui::M

QT学习之路--创建一个对话框

Q_OBJECT:这是一个宏,凡是定义信号槽的类都必须声明这个宏. 函数tr()全名是QObject::tr(),被他处理过的字符串可以使用工具提取出来翻译成其他语言,也就是做国际化使用. 对于QT学习之路:Qt学习之路(7):创建一个对话框(上)这个程序.编译出现 invalid use of incomplete type ‘class QPushButton’ findButton->setEnabled(!text.isEmpty()); ^ In file included from

Qt学习之路1---软件下载安装及工程简介

1.下载安装目前最新版的qt,官网链接:https://www.qt.io/qt5-8/: 和qt4不同,qt5在线安装,轻巧快速,而且不用配置一些繁琐的东西,安装之后会出现Qt creator这就是我们之后使用的IDE. 2.Qt creator工程包含不同类型的文件 _ .pro项目描述文件 _ .pro.user 用户配置描述文件  _ .ui 界面描述文件  _ 资源文件(图片,音频等) 2.1 .pro项目描述文件的基本组成 _ .#  注释符 _ QT 模块声明 _ TARGET  

Qt学习之路(24): QPainter(改写paintEvent)

多些大家对我的支持啊!有朋友也提出,前面的几节有关event的教程缺少例子.因为event比较难做例子,也就没有去写,只是把大概写了一下.今天带来的是新的部分,有关Qt的2D绘图.这部分不像前面的内容,还是比较好理解的啦!所以,例子也会增加出来. 有人问豆子拿Qt做什么,其实,豆子就是在做一个Qt的画图程序,努力朝着Photoshop和GIMP的方向发展.但这终究要经过很长的时间.很困难的路程的,所以也放在网上开源,有兴趣的朋友可以来试试的呀… 好了,闲话少说,来继续我们的学习吧! Qt的绘图系

Qt 学习之路:Canvas

在 QML 刚刚被引入到 Qt 4 的那段时间,人们往往在讨论 Qt Quick 是不是需要一个椭圆组件.由此,人们又联想到,是不是还需要其它的形状?这种没玩没了的联想导致了一个最直接的结果:除了圆角矩形,Qt Quick 什么都没有提供,包括椭圆.如果你需要一个椭圆,那就找个图片,或者干脆自己用 C++ 写一个吧(反正 Qt Quick 是可以扩展的,不是么)! 为了使用脚本化的绘图机制,Qt 5 引入的Canvas元素.Canvas元素提供了一种与分辨率无关的位图绘制机制.通过Canvas,