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 !







