1

我有TableView从 Qml 2.0 连接到QAbstractItemModel. 我想制作一个上下文菜单,可以更改属性或简单地从模型中调用具体对象的方法。

例子:

QAbstractItemModel有一个std::vector<Person>Person有一个alter()进行一些更改的方法(任何更改,哪些更改并不重要,关键是我们能够调用该方法)。

右键单击该行时,将出现带有 item 的菜单Alter

我所能找到的只是如何制作菜单。

  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()
      }
    }
  }

但我仍然不知道如何将菜单与行的确切对象联系起来。

4

2 回答 2

5

在委托中,您可以使用role.property约定来引用项目。默认角色是display. 当然,必须Person从 派生QObject,并且必须在 QML 引擎中注册。

下面的代码演示了如何:

  1. 创建一个明智的行为ObjectListModel来存储QObjects, 可从 QML 使用。

  2. 创建一个QObject保留数据的派生类。

  3. 从委托上显示的弹出菜单访问数据对象的属性和可调用方法。

该模型可以设置为自动通知包含的 QObjects 的属性的更改。此类通知,如果是由批量更改(比如在循环中完成)导致的,则会合并并作为单个dataChanged事件发送。

不幸的是, a 的 user 属性QObject没有特殊含义——您仍然需要使用.property选择器来访问它。

可以直接观察到模型的正确行为,因为有两个列表与同一个模型挂钩——它们最好显示相同的东西。

ObjectListModel还可以实现角色和属性之间的映射。目前,显示和编辑角色都选择整个对象,而不是它的任何特定属性。

如果QObjects 的存储开销太大,另一种模型实现可以动态创建QObjectPOD 类型的适配器。

截屏

主文件

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQuickWindow>
#include <QAbstractListModel>
#include <QQmlContext>
#include <QtQml>
#include <QSet>
#include <QBasicTimer>
#include <functional>

class Person : public QObject {
    Q_OBJECT
    Q_PROPERTY(QString name NOTIFY nameChanged MEMBER m_name)
    QString m_name;
public:
    Q_INVOKABLE Person(QObject * parent = 0) : QObject(parent) { setRandomName(); }
    Q_INVOKABLE Person(QString name, QObject * parent = 0) :
                       QObject(parent), m_name(name) {}
    Q_SIGNAL void nameChanged(const QString &);
    Q_INVOKABLE void setRandomName() {
        static const QString names = "Badger,Shopkeeper,Pepperpots,Gumbys,Colonel";
        static const QStringList nameList = names.split(',');
        QString 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<QObject*()> m_factory;
    bool m_tracking;
    QBasicTimer m_notifyTimer;
    QMap<int, char> 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<int>(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<QObject*()> & 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<QObject*>();
        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<int>(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<Person>();
    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<QQuickWindow *>(topLevel);
    window->show();
    return app.exec();
}

#include "main.moc"

主.qrc

<RCC>
    <qresource prefix="/">
        <file>main.qml</file>
    </qresource>
</RCC>

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)
        }

    }
}
于 2013-10-11T18:36:13.033 回答
0

我根据 Kuba Ober 的回答创建了一个新项目。我还创建了一个名为 QQuickList 的商品类,它继承了 QList 并集成了一个 QObjectListModel。享受。

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

于 2014-04-17T09:03:36.443 回答