Qml 2.0 TableView avec QAbstractItemModel et menu contextuel

J’ai TableView de Qml 2.0 contrôles qui est connecté à QAbstractItemModel . Je souhaite créer un menu contextuel pouvant modifier les propriétés ou simplement appeler des méthodes d’un object concret à partir du modèle.

Exemple:

QAbstractItemModel a un std::vector . Person a une méthode alter() qui apporte des modifications (toutes les modifications, peu importe lesquelles sont exactement des modifications, le fait est que nous pouvons appeler la méthode).

Quand il y a un clic droit sur la ligne, le menu apparaît avec un élément Alter .

Tout ce que j’ai pu trouver, c’est comment faire le menu.

  rowDelegate: Item { Menu { id: myContextMenu MenuItem {text: "Alter"; onTriggered: {} } } MouseArea { id: longPressArea anchors.fill: parent acceptedButtons: Qt.LeftButton | Qt.RightButton onClicked: { if (mouse.button == Qt.RightButton) myContextMenu.popup() } } } 

Mais je ne sais toujours pas comment connecter le menu avec un object exact de la ligne.

Dans le délégué, vous pouvez faire référence à l’élément en utilisant la convention role.property . Le rôle par défaut serait display . Bien entendu, la Person doit dériver de QObject et doit être enregistrée auprès du moteur QML.

Le code ci-dessous montre comment:

  1. Créez un ObjectListModel comportement ObjectListModel pour le stockage de QObjects , utilisable à partir de QML.

  2. Créez une classe QObject QObject qui conserve vos données.

  3. Accédez aux propriétés et aux méthodes invocables des objects de données à partir d’un menu contextuel affiché sur le délégué.

Le modèle peut être configuré pour notifier automatiquement les modifications apscopes aux propriétés des QObjects contenus. Ces notifications, si elles résultent de modifications dataChanged (faites par exemple dans une boucle), sont fusionnées et envoyées en tant dataChanged unique.

Malheureusement, la propriété utilisateur d’un QObject n’acquiert aucune signification particulière – vous devez toujours utiliser le sélecteur .property pour y accéder.

Le comportement correct du modèle peut être observé directement, car il existe deux listes reliées au même modèle – elles ont mieux montré la même chose.

ObjectListModel peut également implémenter un mappage entre les rôles et les propriétés. Actuellement, les rôles d’affichage et d’édition sélectionnent l’object entier, pas une propriété particulière de celui-ci.

Si le stockage de QObject s est trop important, une implémentation de modèle alternative pourrait créer des adaptateurs QObject pour les types de POD à la volée.

capture d'écran

main.cpp

 #include  #include  #include  #include  #include  #include  #include  #include  #include  class Person : public QObject { Q_OBJECT Q_PROPERTY(QSsortingng name NOTIFY nameChanged MEMBER m_name) QSsortingng m_name; public: Q_INVOKABLE Person(QObject * parent = 0) : QObject(parent) { setRandomName(); } Q_INVOKABLE Person(QSsortingng name, QObject * parent = 0) : QObject(parent), m_name(name) {} Q_SIGNAL void nameChanged(const QSsortingng &); Q_INVOKABLE void setRandomName() { static const QSsortingng names = "Badger,Shopkeeper,Pepperpots,Gumbys,Colonel"; static const QSsortingngList nameList = names.split(','); QSsortingng newName = nameList.at(qrand() % nameList.length()); if (newName != m_name) { m_name = newName; emit nameChanged(m_name); } } }; class ObjectListModel : public QAbstractListModel { Q_OBJECT Q_DISABLE_COPY(ObjectListModel) //! Whether changes to underlying objects are exposed via `dataChanged` signals Q_PROPERTY(bool elementChangeTracking READ elementChangeTracking WRITE setElementChangeTracking NOTIFY elementChangeTrackingChanged) QObjectList m_data; std::function m_factory; bool m_tracking; QBasicTimer m_notifyTimer; QMap m_notifyIndexes; //! Updates the property tracking connections on given object. void updateTracking(QObject* obj) { const int nIndex = metaObject()->indexOfSlot("propertyNotification()"); QMetaMethod const nSlot = metaObject()->method(nIndex); const int props = obj->metaObject()->propertyCount(); if (m_tracking) for (int i = 0; i < props; ++i) { const QMetaProperty prop = obj->metaObject()->property(i); if (prop.hasNotifySignal()) connect(obj, prop.notifySignal(), this, nSlot); } else { disconnect(obj, 0, this, 0); } } //! Receives property notification changes Q_SLOT void propertyNotification() { int i = m_data.indexOf(sender()); if (i >= 0) m_notifyIndexes.insert(i, 0); // All of the notifications will be sent as a single signal from the event loop. if (!m_notifyTimer.isActive()) m_notifyTimer.start(0, this); } protected: //! Emits the notifications of changes done on the underlying QObject properties void timerEvent(QTimerEvent * ev) { if (ev->timerId() != m_notifyTimer.timerId()) return; emit dataChanged(index(m_notifyIndexes.begin().key()), index((m_notifyIndexes.end()-1).key()), QVector(1, Qt::DisplayRole)); m_notifyTimer.stop(); m_notifyIndexes.clear(); } public: //! A model that creates instances via a given metaobject ObjectListModel(const QMetaObject * mo, QObject * parent = 0) : QAbstractListModel(parent), m_factory([mo, this](){ return mo->newInstance(Q_ARG(QObject*, this)); }), m_tracking(false) {} //! A model that creates instances using a factory function ObjectListModel(const std::function & factory, QObject * parent = 0) : QAbstractListModel(parent), m_factory(factory), m_tracking(false) {} ~ObjectListModel() { qDeleteAll(m_data); } bool elementChangeTracking() const { return m_tracking; } void setElementChangeTracking(bool tracking) { if (m_tracking == tracking) return; for (QObject* obj : m_data) updateTracking(obj); emit elementChangeTrackingChanged(m_tracking = tracking); } Q_SIGNAL void elementChangeTrackingChanged(bool); int rowCount(const QModelIndex &) const Q_DECL_OVERRIDE { return m_data.count(); } QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE { if (role == Qt::DisplayRole || role == Qt::EditRole) { return QVariant::fromValue(m_data.at(index.row())); } return QVariant(); } bool setData(const QModelIndex &index, const QVariant &value, int role) Q_DECL_OVERRIDE { Q_UNUSED(role); QObject* object = value.value(); if (!object) return false; if (object == m_data.at(index.row())) return true; delete m_data.at(index.row()); m_data[index.row()] = object; emit dataChanged(index, index, QVector(1, role)); return true; } Q_INVOKABLE bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE { Q_UNUSED(parent); beginInsertRows(QModelIndex(), row, row + count - 1); for (int i = row; i < row + count; ++ i) { QObject * object = m_factory(); Q_ASSERT(object); m_data.insert(i, object); updateTracking(object); QQmlEngine::setObjectOwnership(object, QQmlEngine::CppOwnership); } endInsertRows(); return true; } Q_INVOKABLE bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) Q_DECL_OVERRIDE { Q_UNUSED(parent); beginRemoveRows(QModelIndex(), row, row + count - 1); while (count--) delete m_data.takeAt(row); endRemoveRows(); return true; } }; int main(int argc, char *argv[]) { QGuiApplication app(argc, argv); QQmlApplicationEngine engine; qmlRegisterType(); ObjectListModel model1(&Person::staticMetaObject); model1.setElementChangeTracking(true); model1.insertRows(0, 1); engine.rootContext()->setContextProperty("model1", &model1); engine.load(QUrl("qrc:/main.qml")); QObject *topLevel = engine.rootObjects().value(0); QQuickWindow *window = qobject_cast(topLevel); window->show(); return app.exec(); } #include "main.moc" 

main.qrc

   main.qml   

main.qml

 import QtQuick 2.0 import QtQml.Models 2.1 import QtQuick.Controls 1.0 ApplicationWindow { width: 300; height: 300 Row { width: parent.width anchors.top: parent.top anchors.bottom: row2.top Component { id: commonDelegate Rectangle { width: view.width implicitHeight: editor.implicitHeight + 10 border.color: "red" border.width: 2 radius: 5 TextInput { id: editor anchors.margins: 1.5 * parent.border.width anchors.fill: parent text: edit.name // "edit" role of the model, to break the binding loop onTextChanged: { display.name = text; // set the name property of the data object } } Menu { id: myContextMenu MenuItem { text: "Randomize"; onTriggered: display.setRandomName() } MenuItem { text: "Remove"; onTriggered: model1.removeRows(index, 1) } } MouseArea { id: longPressArea anchors.fill: parent acceptedButtons: Qt.RightButton onClicked: myContextMenu.popup() } } } spacing: 2 ListView { id: view width: (parent.width - parent.spacing)/2 height: parent.height model: DelegateModel { id: delegateModel1 model: model1 delegate: commonDelegate } spacing: 2 } ListView { width: (parent.width - parent.spacing)/2 height: parent.height model: DelegateModel { model: model1 delegate: commonDelegate } spacing: 2 } } Row { id: row2 anchors.bottom: parent.bottom Button { text: "Add Page"; onClicked: model1.insertRows(delegateModel1.count, 1) } } } 

J’ai créé un nouveau projet basé sur la réponse Kuba Ober. J’ai également créé une classe de produits appelée QQuickList, qui hérite de QList et intègre un QObjectListModel. Prendre plaisir.

https://github.com/Murazaki/QObjectListModel-QQuickList