2017-01-11 1 views
1

J'ai une classe modèle qui hérite QAbstractListModel:données modèle Accès Qt en dehors ItemDelegate

VehiclesModel.h:

class VehiclesModel : public QAbstractListModel { 
    Q_OBJECT 

    public: 
     enum Roles { 
      ImagePathRole = Qt::UserRole + 1, // QString 
      NameRole       // QString 
     }; 

     virtual int rowCount(const QModelIndex & parent = QModelIndex()) const override { ... } 
     virtual QVariant data(const QModelIndex & index, int role) const override { ... } 
     virtual QHash<int, QByteArray> roleNames() const override { 
      QHash<int, QByteArray> roles = QAbstractListModel::roleNames(); 

      roles[ImagePathRole] = "imagePath"; 
      roles[NameRole] = "name"; 

      return roles; 
     } 
}; 

main.cpp:

#include "VehiclesModel.h" 

int main(int argc, char * argv[]) { 
    QGuiApplication app(argc, argv); 
    VehiclesModel vehiclesModel; 
    QQmlApplicationEngine engine; 

    engine.rootContext()->setContextProperty("vehiclesModel", &vehiclesModel); 
    engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 
    return app.exec(); 
} 

Et ComboBox qui affiche ce modèle: main.qml:

ComboBox { 
    id: control 
    model: vehiclesModel 
    delegate: ItemDelegate { 
     contentItem: RowLayout { 
      Image { 
       source: imagePath 
      } 
      Label { 
       text: name 
      } 
     } 
     highlighted: control.highlightedIndex == index 
    } 
    contentItem: RowLayout { 
     Image { 
      source: ??imagePath?? 
     } 
     Label { 
      text: ??name?? 
     } 
    } 
} 

Je veux personnaliser le ComboBox pour afficher l'image et le nom du véhicule. Je peux accéder aux données de modèle de ItemDelegate mais comment accéder aux données de modèle en dehors du ItemDelegate? Par exemple, je souhaite accéder aux données d'index actuelles (ImagePathRole et NameRole) pour afficher l'image et le nom du véhicule au contentItem.

Est-il possible de le faire sans appeler directement les méthodes QAbstractListModel (à savoir les méthodes et index()data()) et de les rendre Q_INVOKABLE?

Répondre

2

Actuellement, je ne suis pas vraiment intégré, malheureusement, c'est quelque chose qui me manque depuis longtemps, et j'ai envisagé de mettre en place quelque chose pour cela dans les modèles QML. fonctionnalité, mais je n'ai pas encore eu le temps de le faire. Pour l'instant, vous pouvez soit le faire vous-même (comme vous en parlez), au prix de la sécurité de type et ainsi de suite, soit (comme je l'ai déjà fait auparavant), vous pouvez créer une sous-classe QObject pour représenter un seul élément dans le modèle (ItemDataThing ou tout ce que vous choisissez de l'appeler); fournissez-lui un index source, des propriétés, et laissez-le représenter une seule instance de données du modèle &.

Quelque chose comme:

class ImageDataThing : public QObject 
{ 
    Q_OBJECT 
    Q_PROPERTY(QString imagePath READ imagePath NOTIFY imagePathChanged) 
    Q_PROPERTY(QAbstractItemModel* model READ model WRITE setModel NOTIFY modelChanged) 
    Q_PROPERTY(int index READ index WRITE setIndex NOTIFY indexChanged) 

public: 
    QString imagePath() const; 
    QAbstractItemModel *model() const; 
    void setModel(const QAbstractItemModel *newModel); 
    int index() const; 
    void setIndex(int newIndex); 
signals: 
    void imagePathChanged(const QString &imagePath); 
    void modelChanged(QAbstractItemModel *model); 
    void indexChanged(int indexChanged); 
}; 

... et dans votre mise en œuvre, chaque fois que le modèle est fixé, accrochez les signaux de changement (par exemple rowsInserted, rowsRemoved, ...) pour modifier l'index stocké (si elle est fournie) pour le garder à l'endroit correct dans le modèle.

Dans les getters de données du modèle (ici, imagePath par exemple), accédez à l'instance du modèle (en utilisant l'index) pour extraire les données et les renvoyer. Cela a l'inconvénient évident d'être beaucoup de passe-partout, mais d'un autre côté, il est facile d'écrire du code si vous êtes familier avec les modèles, sans danger pour le type, et que vous pouvez facilement le générer facilement.

1

Vous pouvez créer votre propre fonction pour obtenir des données à partir du modèle, comme celui que je suis actuellement en utilisant, VehiclesModel.h:

public slots: 
    int size() const; // to access from QML javascript 
    QVariant getData(int index, int role); // to access from QML javascript 

VehiclesModel.cpp:

int VehiclesModel::size() const { 
    return m_list.size(); 
} 

QVariant VehiclesModel::getData(int index, int role) { 
    if (index < 0 || index >= m_list.count()) 
     return QVariant(); 
    switch (role) { 
    case ImagePathRole: 
     return ... 
     break; 
    default: 
     break; 
    } 
} 
1

Je vous suggère fortement de regarder au Qt QML Tricks bibliothèque faite par Thomas Boutroue:

http://gitlab.unique-conception.org/qt-qml-tricks/

Plus spécifique, le QQmlObjectListModel (du Qt QML Models) pourrait faire l'affaire pour vous.

S'étendant avec l'utilisation du Qt Super-Macros, il réduit les setters/getters d'écriture! Ces macros se développent fondamentalement en un Q_PROPERTY, résultant en l'accessibilité de QML, et ajoutent la définition d'un setter, getter et une variable privée.

Utilisation dans votre cas cela pourrait ressembler à quelque chose comme ça, rapidement écrit, non validé (contrôle avec l'index correct pour le référencement du modèle):

VehicleItem.h:

#include <QObject> 
#include "QQmlVarPropertyHelpers.h" // Include library Qt Super-Macros 

class VehicleItem : public QObject { 
    Q_OBJECT 

    QML_WRITABLE_VAR_PROPERTY(QString, imagePath) 
    QML_WRITABLE_VAR_PROPERTY(QString, name) 

public: 
    explicit VehicleItem(QString imagePath, QString name, QObject* parent=0) 
    : QObject (parent) 
    , m_imagePath (imagePath) 
    , m_name  (name) 
    {} 

}; 

VehiclesModel.h:

#include <QObject> 
#include "QQmlObjectListModel.h" // Include library Qt QML Models 
#include "VehicleItem.h" 

class VehiclesModel : public QObject { 
    Q_OBJECT 

    QML_OBJMODEL_PROPERTY(VehicleItem, modelList) 

public: 
    explicit VehiclesModel(QObject *parent = 0); 

}; 

VehiclesModel.c:

#include "VehiclesModel.h" 

VehiclesModel::VehiclesModel(QObject *parent) : 
    QObject(parent), m_modelList(new QQmlObjectListModel<VehicleItem>()) 
{} 

main.c (remains the same):

#include "VehiclesModel.h" 

int main(int argc, char * argv[]) { 
    QGuiApplication app(argc, argv); 
    VehiclesModel vehiclesModel; 
    QQmlApplicationEngine engine; 

    engine.rootContext()->setContextProperty("vehiclesModel", &vehiclesModel); 
    engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); 
    return app.exec(); 
} 

main.qml:

ComboBox { 
    id: control 
    model: vehiclesModel 
    delegate: ItemDelegate { 
     contentItem: RowLayout { 
      Image { 
       source: imagePath 
      } 
      Label { 
       text: name 
      } 
     } 
     highlighted: control.highlightedIndex == index 
    } 
    contentItem: RowLayout { 
     Image { 
      source: vehiclesModel.modelList.get(index).imagePath 
     } 
     Label { 
      text: vehiclesModel.modelList.get(index).name 
     } 
    } 
} 

Comme modelList (et imagePath et le nom) est dilatée par la macro à un Q_PROPERTY, il est accessible d'un côté QML.

Pour les tenants et les aboutissants de cette bibliothèque, assurez-vous de vérifier la présentation éclair de Thomas Boutroue au QtWS2015: https://www.youtube.com/watch?v=96XAaH97XYo

+0

Comment est-ce "exactement ce que OP recherche"? Pouvez-vous l'expliquer dans votre réponse?Que voulez-vous dire par "syntaxe facile et agréable avec un minimum de frais généraux d'écriture de vos setters/getters"? Comment cela est-il pertinent à la question? Je ne vois pas non plus le point de votre dernière phrase (les Q_PROPERTY sont utilisés pour définir les rôles du modèle, pour ne pas pouvoir accéder au modèle en QML) – GrecKo

+0

Voir la mise à jour de la réponse. – Bartel

+0

Merci de mettre à jour votre réponse et de montrer comment la bibliothèque peut être utilisée. Mes questions restent toujours sans réponse, attendues les 2 premières. Par syntaxe facile et sympa, je crois que vous parlez de son qt-supermacros lib, ce qui n'est pas clair que vous utilisez dans votre réponse. Votre phrase sur Q_PROPERTY n'est toujours pas claire et plutôt vague. – GrecKo

0

Shameless plug pour mon SortFilterProxyModel library.

Le problème que vous posez est en réalité un scénario de tête-grattage. J'ai trouvé un moyen de le faire un peu correctement mais c'est un peu compliqué et implique une bibliothèque externe. Dans ma solution, nous filtrons le modèle source pour exposer uniquement l'élément correspondant à l'index actuel de la zone de liste déroulante et instancier un délégué pour cet élément et l'utiliser comme contentItem du ComboBox.

Ceci a l'avantage de ne pas avoir à modifier votre modèle et de rester synchronisé avec les changements de votre modèle.

import SortFilterProxyModel 0.2 // from https://github.com/oKcerG/SortFilterProxyModel 
import QtQml 2.2 

/* 
... 
*/ 

ComboBox { 
    id: control 
    model: vehiclesModel 
    delegate: ItemDelegate { 
     contentItem: RowLayout { 
      Image { 
       source: imagePath 
      } 
      Label { 
       text: name 
      } 
     } 
     highlighted: control.highlightedIndex == index 
    } 
    contentItem: { currentIndex; return selectedInstantiator.object; } // use currentIndex to force the binding reevaluation. When the model changes, the instantiator doesn't notify object has changed 
    Instantiator { 
     id: selectedInstantiator 
     model: SortFilterProxyModel { 
      sourceModel: control.model 
      filters: IndexFilter { 
       minimumIndex: control.currentIndex 
       maximumIndex: control.currentIndex 
      } 
     } 
     delegate: RowLayout { 
      Image { 
       source: imagePath 
      } 
      Label { 
       text: name 
      } 
     } 
    } 
}