1

我有一些继承的模型类QAbstractListModel

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

ComboBox显示了这个模型 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??
        }
    }
}

我想自定义ComboBox以显示车辆图像和名称。我可以访问模型数据,ItemDelegate但如何访问外部的模型数据ItemDelegate?例如,我想访问当前索引数据(ImagePathRoleNameRole)以显示车辆图像和名称contentItem

是否可以在不QAbstractListModel直接调用方法(即方法)index()data()制作它们的情况下做到这一点Q_INVOKABLE

4

4 回答 4

3

不幸的是,目前没有任何一种像样的内置方式,这是我发现有一段时间以来一直缺乏的东西,我已经考虑在 QML 模型功能中为此实现一些东西,但我还没有时间这样做。

目前,您可以自己做(就像您正在讨论的那样),以类型安全为代价等等,或者(我之前通常解决这个问题的方式),您可以创建一个 QObject 子类来表示模型中的单个项目(ItemDataThing 或您选择的任何名称);为它提供源模型和索引、属性,并让它代表模型中数据的单个实例。

就像是:

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

...并且在您的实现中,每当设置模型时,挂钩更改信号(例如 rowsInserted、rowsRemoved...)以更改存储的索引(如果提供)以使其映射到模型中的正确位置。

在模型数据获取器(此处为 imagePath 实例)中,访问模型实例(使用索引)以获取数据并将其返回。

这具有作为大量样板的明显缺点,但另一方面,如果您熟悉模型,类型安全,并且可以相当容易地自动生成它,那么它很容易编写代码。

于 2017-01-11T14:05:08.620 回答
1

我强烈建议查看 Thomas Boutroue 制作的Qt QML Tricks库:

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

更具体地说,QQmlObjectListModel(来自Qt QML Models)可以为您解决问题。

使用 扩展Qt Super-Macros,它减少了编写 setter/getter 的开销!这些宏基本上扩展为 Q_PROPERTY,导致 QML 的可访问性,并添加了 setter、getter 和私有变量的定义。

在您的特定情况下的用法可能看起来像这样,快速写下来,未经验证(检查使用正确的索引来引用模型):

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

由于 modelList(以及 imagePath 和 name)被宏扩展为 Q_PROPERTY,因此可以从 QML 端访问它。

对于这个库的详细信息,请务必查看 Thomas Boutroue 在 QtWS2015 上的闪电演讲:https ://www.youtube.com/watch?v=96XAaH97XYo

于 2017-01-11T14:46:26.573 回答
1

您可以创建自己的函数来从模型中获取数据,就像我目前使用的那样 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;
    }
}
于 2017-01-11T14:17:42.120 回答
0

我的SortFilterProxyModel 库的无耻插件。

你问的问题实际上是一个令人头疼的场景。我找到了一种正确的方法,但它有点复杂并且涉及到一个外部库。在我的解决方案中,我们过滤源模型以仅公开与组合框的当前索引对应的元素,并为此元素实例化一个委托并将其用作contentItem.ComboBox

这样做的好处是不必修改模型并与模型更改保持同步。

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
            }
        }
    }
}
于 2017-01-11T15:31:59.080 回答