在该人脸识别系统中,每次识别过程都会从摄像头实时捕获N(暂定为3)张人脸图片,用这N张图片来和证件上面的人脸进行比对。系统会将每次捕获的人脸图片显示在右侧的列表中,我们可以通过QML中的ListView来实现列表功能。
MVC
在说ListView功能前,就不得不提MVC设计模式了,因为ListView是采用这个模式来实现的。
Model-View—Controller(MVC)是源自SmallTalk的一个设计模式,在构建用户界面或前端网页时经常用到。作为一种经典的设计模式,MVC通过将系统分解为模型、视图、控制器三部分,每一部分相互独立,职责单一,在实现过程中只用专注于自身的核心逻辑。模型代表数据,视图代表呈现给用户看的界面部分,而控制器就是一个中间人,从模型拿数据给视图来呈现。
Qt中MVC框架对Controller部分进行了改动,引入了delegate的概念,合起来就是Model-View-Delegate。模型还是负责提供数据,View负责提供舞台进行呈现,Delegate负责控制以什么方式来显示。
Model
ListView的Model(也就是数据)可以在QML中定义,如:
import QtQuick 2.2
import QtQuick.Controls 1.2
import QtQuick.Layouts 1.1
Rectangle {
width: 360;
height: 300;
color: "#EEEEEE";
Component {
id: phoneDelegate;
Item {
id: wrapper;
width: parent.width;
height: 30;
MouseArea {
anchors.fill: parent;
onClicked: wrapper.ListView.view.currentIndex = index;
}
RowLayout {
anchors.left: parent.left;
anchors.verticalCenter: parent.verticalCenter;
spacing: 8;
Text {
id: col1;
text: name;
color: wrapper.ListView.isCurrentItem ? "red" : "black";
font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18;
Layout.preferredWidth: 120;
}
Text {
text: cost;
color: wrapper.ListView.isCurrentItem ? "red" : "black";
font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18;
Layout.preferredWidth: 80;
}
Text {
text: manufacturer;
color: wrapper.ListView.isCurrentItem ? "red" : "black";
font.pixelSize: wrapper.ListView.isCurrentItem ? 22 : 18;
Layout.fillWidth: true;
}
}
}
}
ListView {
id: listView;
anchors.fill: parent;
delegate: phoneDelegate;
model: ListModel {
id: phoneModel;
ListElement{
name: "iPhone 3GS";
cost: "1000";
manufacturer: "Apple";
}
ListElement{
name: "iPhone 4";
cost: "1800";
manufacturer: "Apple";
}
ListElement{
name: "iPhone 4S";
cost: "2300";
manufacturer: "Apple";
}
ListElement{
name: "iPhone 5";
cost: "4900";
manufacturer: "Apple";
}
ListElement{
name: "B199";
cost: "1590";
manufacturer: "HuaWei";
}
ListElement{
name: "MI 2S";
cost: "1999";
manufacturer: "XiaoMi";
}
ListElement{
name: "GALAXY S5";
cost: "4699";
manufacturer: "Samsung";
}
}
focus: true;
highlight: Rectangle{
color: "lightblue";
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
有时候ListView中的Model数据可能是通过某种复杂的方式获取到的(如网络接收、文件读取、硬件解码等),这个就需要通过C++构造Model提供到QML中。我们的人脸识别系统的Model数据就是实时从摄像头捕获的图片数据,所以也是在C++中定义Model。
在Qt中QAbstractItemModel是大部分模型(Model)类的基类,比如QAbstractListModel, QAbstractProxyModel, QAbstractTableModel, QFileSystemModel等。
QAbstractItemView是多部分视图(View)类的基类,如QListView, QTableView, QTreeView等。
QAbstractItemDelegate是所有delegate的基类,它又衍生出了QStyledItemDelegate和QItemDelegate两个分支。如果需要实现自己的delegate,可以从这2个类中选择一个作为基类。
C++中Model类GrapImageListModel
定义:
#pragma once
#include <QAbstractListModel>
#include <QImage>
#include "GrapImageData.h"
class GrapImageListModelPrivate;
// http://doc.qt.io/qt-5/qabstractlistmodel.html
//
class GrapImageListModel : public QAbstractListModel {
Q_OBJECT
public:
GrapImageListModel(QObject* parent = NULL);
virtual ~GrapImageListModel();
// virtual function.
int rowCount(const QModelIndex &parent = QModelIndex() ) const;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole ) const;
QHash<int, QByteArray> roleNames() const;
public slots:
void addGrapImage(const GrapImageData& data);
void clearGrapImage();
signals:
void newImage(const QImage &img) const; // 连接ImageDisplay::setImage槽
protected:
GrapImageListModelPrivate* m_pPrivate;
private:
mutable int m_iIndex;
};
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
该类的关键在重载的三个虚函数rowCount
,data
,roleNames
,Delegate在需要数据时会调用这3个函数来获取数据。
delegate定义在QML中(因为篇幅有删减):
Component {
id: grapImageDelegate;
Rectangle {
id: wrapper;
width: 140;
height: 90;
color: "transparent";
Image {
id: imgGrap;
anchors.fill: parent;
smooth: true;
fillMode: Image.PreserveAspectFit;
source: imgUri;
visible: false;
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
delegate的定义比较简单,在Component中定义一个Image即可。注意,Image的source设置的不是图片实际地址,而是imgUri
。
Delegate通过rowCount
函数获取数据的总行数;通过roleNames
来获取到QHash<int, QByteArray>
,QHash<int, QByteArray>
可以理解为”int - string”的对应集合,比如我们设置0 - imgUri
将0对应到imgUri上,在Delegate需要数据,调用data
时就会在role
参数传0,我们就可以知道这时是需要什么类型的数据了,并且可以根据index
参数知道Delegate要获取哪一行的数据。