Доступ к QLists С++ из QML
Если у меня есть список вещей на С++, как я могу представить это в QML (в Qt5/QtQuick 2)? Похоже, что QML может понимать только классы QObject
-derived, что является проблемой, потому что QObject
нельзя поместить в QList
или скопировать. Как это сделать:
struct Thing
{
int size;
QString name;
};
class ThingManager : public QObject
{
Q_OBJECT
// These macros support QtQuick, in case we one day want to use it to make a slick
// interface (when QML desktop components are released).
Q_PROPERTY(QList<Thing> things READ things NOTIFY thingssChanged)
public:
// ...
QList<Thing> things() const;
// ...
};
Итак, я могу сделать что-то подобное в QML:?
var a = thingManager.things[0].name;
Ответы
Ответ 1
После большего опыта работы с QML я нашел лучший способ иметь списки вещей с QAbstractListModel
.
Вы делаете свой Thing
из QObject
, поэтому его можно сохранить в QVariant
(после его регистрации). Затем вы можете вернуть фактический Thing
в качестве элемента модели. Вы можете получить доступ к нему в Repeater
как model.display.a_property_of_thing
. Длина списка доступна как model.count
.
Это имеет следующие плюсы и минусы:
- Fast - он не копирует весь список для доступа к одному элементу.
- Вы можете легко получить анимацию для внесения изменений в список (добавление, перегруппировка и удаление элементов).
- Он прост в использовании из QML.
- Чтобы активировать анимацию, всякий раз, когда вы меняете список, вам нужно немного облегчить бухгалтерию (
beginInsertRows()
и т.д.).
...
class Things : public QObject
{
...
};
Q_DECLARE_METATYPE(Thing*)
class ThingList : public QAbstractListModel
{
Q_OBJECT
public:
explicit ThingList(QObject *parent = 0);
~ThingList();
int rowCount(const QModelIndex& parent = QModelIndex()) const override;
QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;
public slots:
// Extra function to get the thing easily from outside Repeaters.
Thing* thing(int idx);
private:
QList<Thing*> mThings;
};
int ThingList::rowCount(const QModelIndex& parent) const
{
return mThings.size();
}
QVariant ThingList::data(const QModelIndex& index, int role) const
{
int i = index.row();
if (i < 0 || i >= mPorts.size())
return QVariant(QVariant::Invalid);
return QVariant::fromValue(mThings[i]);
}
Thing* ThingList::thing(int idx)
{
if (idx < 0 || idx >= mThings.size())
return nullptr;
return mThings[idx];
}
Ответ 2
Я столкнулся с этим вопросом, пытаясь исправить аналогичную проблему, где я хотел использовать код С++ в качестве источника модели в QML. Ответ, данный TheBootroo, указал мне в правильном направлении, но не работал полностью для меня. У меня недостаточно репутации, чтобы отвечать на него напрямую (но я ответил на его ответ).
Я использую Qt 5.0.0
Я нашел эту ссылку очень полезно
Определение ThingManager должно быть изменено следующим образом
class ThingManager : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<QObject*> things READ getThings NOTIFY thingsChanged)
public:
QList<QObject*> getThings () const { return m_things; }
signals:
void thingsChanged ();
private:
QList<QObject*> m_things;
};
Обратите внимание, что я изменил возвращаемый тип getThings на QList < QObject * > . Без этого изменения Qt предупреждает, что это "Невозможно обрабатывать незарегистрированный тип данных" QList < Thing * > ".
В QML-коде свойства Thing можно получить через модель как model.modelData.size и model.modelData.name.
Ответ 3
В качестве альтернативы вы можете использовать QVariantList
(QList<QVariant>
), он автоматически изменится на массив JavaScript при передаче в QML и будет доступен для чтения и записи из С++ и QML
Ответ 4
А я нашел ответ (я думаю, не тестировался): QQmlListProperty
В примерах несколько примеров, например. при qtdeclarative/examples/quick/tutorials/gettingStartedQml/filedialog/directory.*
:
К сожалению, на данный момент вы можете иметь только списки только для чтения.
Ответ 5
вы совершенно ошибаетесь в QObject, их можно присвоить QList, просто в форме указателя, так как следующее работает отлично:
class Thing : public QObject
{
Q_OBJECT
Q_PROPERTY (int size READ getSize CONSTANT)
Q_PROPERTY (QString name READ getName CONSTANT)
public:
Thing(QObject * parent = NULL) : QObject(parent) {}
int getSize () const { return m_size; }
QString getName () const { return m_name; }
private:
int m_size;
QString m_name;
};
class ThingManager : public QObject
{
Q_OBJECT
Q_PROPERTY(QList<Thing*> things READ getThings NOTIFY thingsChanged)
public:
QList<Thing*> getThings () const { return m_things; }
signals:
void thingsChanged ();
private:
QList<Things*> m_things;
};
Ответ 6
Ответ, заданный eatyourgreens
, правильный. Внедряя свой класс таким образом, вы можете получить доступ к как можно большему количеству потомков. Еще один полезный совет, который я нашел полезным, - создать псевдоним для нашей модели внутри элемента делегата qml.
ListView {
anchors.fill: parent
model: thing_manager.things
delegate: ItemDelagate {}
clip: true
spacing: 10
}
И затем в ItemDelegate.qml вы можете создать псевдоним для модели, чтобы не использовать все время model.modelDatap >
Item{
width: 600
height: 200
property var thing: model.modelData
Rectangle {
anchors.fill: parent
color: "red"
Text {
text: thing.name // or any other field
}
}
}
Ответ 7
Одним из весьма косвенных способов достижения этого является следующее:
i.) Сделайте модель в qml
ListModel
{
id: thingModel
ListElement
{
size: 10
name: "Apple"
}
}
ii.) Затем предоставьте пару функций javascript, чтобы изменить этот список, например.
function jAppendThing( newSize, newName )
{
thingModel.append({"size": nameSize, "name": newName })
}
function jClearThing()
{
thingModel.clear()
}
аналогично jDeleteThing и т.д.
iii.) Вы можете прочитать как вызвать qml-функции из С++
iv.) Запустите цикл в списке С++ и вызовите функцию добавления qml, чтобы добавить все эти данные в список qml.
v.) В любом обновлении в боковом списке С++ также измените данные qml, используя указанную выше функцию, чтобы обновить ее.
Ответ 8
лучший способ использовать QQmlListProperty
. Простите этот простой образец, я надеюсь вам помочь.
Примеры типов объектов и списков
Ответ 9
Используйте хорошее, но не упомянутое решение:
class ThingManager : public QObject
{
Q_OBJECT
// These macros support QtQuick, in case we one day want to use it to make a slick
// interface (when QML desktop components are released).
Q_PROPERTY(QList<Thing> things MEMBER m_things NOTIFY thingssChanged)
// ...
private:
// ...
QList<Thing> m_things;
// ...
};
Чтение и запись применимы. Нет дорогостоящего вызова функции и копирования данных. Просто прямой доступ к членам класса в QML:
var a = thingManager.things[0].name;
Для получения дополнительной информации см. документ: https://doc-snapshots.qt.io/qt5-dev/properties.html