I. L'article original▲
Cet article est une adaptation en langue française de How to use a C++ list model in QML.
II. Première étape : écrire un modèle de liste en C++ qui dérive de QAbstractListModel▲
Pour l'exemple, on a écrit une classe ListModel qui hérite de QAbstractListModel et fournit les méthodes les plus couramment utilisées. Elle utilise un conteneur QList pour stocker les items et définit une classe abstraite ListItem pour aider à définir des classes d'items qui conviennent.
class
ListItem: public
QObject
{
Q_OBJECT
public
:
ListItem(QObject
*
parent =
0
) : QObject
(parent) {}
virtual
~
ListItem() {}
virtual
QString
id() const
=
0
;
virtual
QVariant
data(int
role) const
=
0
;
virtual
QHash
<
int
, QByteArray
>
roleNames() const
=
0
;
signals
:
void
dataChanged();
}
;
class
ListModel : public
QAbstractListModel
{
Q_OBJECT
public
:
explicit
ListModel(ListItem*
prototype, QObject
*
parent =
0
);
~
ListModel();
int
rowCount(const
QModelIndex
&
parent =
QModelIndex
()) const
;
QVariant
data(const
QModelIndex
&
index, int
role =
Qt
::
DisplayRole) const
;
void
appendRow(ListItem*
item);
void
appendRows(const
QList
<
ListItem*>
&
items);
void
insertRow(int
row, ListItem*
item);
bool
removeRow(int
row, const
QModelIndex
&
parent =
QModelIndex
());
bool
removeRows(int
row, int
count, const
QModelIndex
&
parent =
QModelIndex
());
ListItem*
takeRow(int
row);
ListItem*
find(const
QString
&
id);
QModelIndex
indexFromItem( const
ListItem*
item) const
;
void
clear();
private
slots
:
void
handleItemChange();
private
:
ListItem*
m_prototype;
QList
<
ListItem*>
m_list;
}
;
On peut réutiliser facilement ces fichiers pour créer son propre modèle.
III. Deuxième étape : créer une classe d'item pour le modèle (ListItem)▲
On montre ici comment hériter de la classe ListItem pour créer des items. On considère l'exemple d'un fruit ayant les propriétés de nom, taille (petit, moyen, grand) et prix.
class
FruitItem : public
ListItem
{
Q_OBJECT
public
:
enum
Roles {
NameRole =
Qt
::
UserRole+
1
,
SizeRole,
PriceRole
}
;
public
:
FruitItem(QObject
*
parent =
0
): ListItem(parent) {}
explicit
FruitItem(const
QString
&
name, const
QString
&
size, QObject
*
parent =
0
);
QVariant
data(int
role) const
;
QHash
<
int
, QByteArray
>
roleNames() const
;
void
setPrice(qreal
price);
inline
QString
id() const
{
return
m_name; }
inline
QString
name() const
{
return
m_name; }
inline
QString
size() const
{
return
m_size; }
inline
qreal
price() const
{
return
m_price; }
private
:
QString
m_name;
QString
m_size;
qreal
m_price;
}
;
FruitItem::
FruitItem(const
QString
&
name, const
QString
&
size, QObject
*
parent) :
ListItem(parent), m_name(name), m_size(size), m_price(-
1
)
{
}
void
FruitItem::
setPrice(qreal
price)
{
if
(m_price !=
price) {
m_price =
price;
emit
dataChanged();
}
}
QHash
<
int
, QByteArray
>
FruitItem::
roleNames() const
{
QHash
<
int
, QByteArray
>
names;
names[NameRole] =
"name"
;
names[SizeRole] =
"size"
;
names[PriceRole] =
"price"
;
return
names;
}
QVariant
FruitItem::
data(int
role) const
{
switch
(role) {
case
NameRole:
return
name();
case
SizeRole:
return
size();
case
PriceRole:
return
price();
default
:
return
QVariant
();
}
}
Il est important de comprendre la fonction roleNames(). Elle sera utilisée par le ListModel pour retourner ses noms de rôle (voir QAbstractItemModel::roleNames()). Ainsi, le délégué QML aura accès aux différentes propriétés de l'item en utilisant les noms de rôle assignés.
De même, le prix peut être changé en utilisant la méthode setPrice(). Pour cette raison, on s'assure que setPrice() émet le signal dataChanged() pour que le modèle transmette l'information à la vue, qui sera alors mise à jour.
IV. Exposer le modèle C++ à QML▲
Une manière facile d'intégrer C++ et QML est d'utiliser la classe QDeclarativeView. Elle fournit un widget pour afficher une interface graphique déclarative. On peut alors utiliser QDeclarativeView::rootContext()::setContextProperty(QString name, QObject* value) pour exposer une instance du modèle à QML et lui assigner un nom de variable utilisable depuis QML.
Voici un exemple :
ListModel*
createModel() {
ListModel *
model =
new
ListModel(new
FruitItem, qApp
);
model->
appendRow(new
FruitItem("Apple"
, "medium"
, model));
model->
appendRow(new
FruitItem("PineApple"
, "big"
, model));
model->
appendRow(new
FruitItem("Grape"
, "small"
, model));
return
model;
}
int
main(int
argc, char
*
argv[])
{
QApplication
app(argc, argv);
QDeclarativeView
view;
view.rootContext()->
setContextProperty("fruitsModel"
, createModel());
view.setSource(QUrl
::
fromLocalFile("qml/QMLCPP/main.qml"
));
view.show();
return
app.exec();
}
La fonction createModel() crée une instance de ListModel en utilisant la classe FruitItem, que l'on vient de définir, comme prototype. Elle remplit aussi le modèle avec quelques items avant de le retourner à l'appelant.
V. Créer une vue QML qui utilise le modèle C++▲
Utiliser le modèle C++ en QML est maintenant aussi facile que lui assigner son nom de variable (défini lors de l'exposition du modèle avec setContextPropery()) à la propriété de modèle de la vue.
L'exemple suivant utilise une vue ListView de QML pour afficher les données du modèle de fruits :
import
QtQuick 1.0
Rectangle
{
width
:
360
height
:
360
ListView
{
id
:
fruitsView
anchors.fill
:
parent
model
:
fruitsModel
delegate
:
DummyDelegate{}
}
}
Voici le code du délégué qui accède aux propriétés des items du modèle en utilisant les noms de rôle assignés (ici, name et size) :
import
QtQuick 1.0
Item
{
id
:
delegate
height
:
30
width
:
delegate.ListView.view.width
Text
{
anchors.fill
:
parent
;
text
:
name
+
" ("
+
size
+
")"
}
}
VI. Remerciements▲
Merci à Guillaume Belz et Claude Leloup pour leur relecture attentive !