Я нахожусь в затруднительном положении, пытаясь динамически создать пользовательские элементы QML из C ++.
Итак, для решения проблемы:
Node
структура данных объекта, абстрагированная от любого пользовательского интерфейса.QObject
и производные, я должен создавать элементы пользовательского интерфейса по требованию.Node
имеет _ui*
член, и каждый UI
имеет _node*
член.Node
имеет уникальный _id
каждый UI
имеет ID
PROPERTY
доставлено из _node*
, Свойство ID доступно только для чтения, так как идентификатор устанавливается на Node
инстанцирование и не должно быть изменяемым._node*
участник NULL
, поэтому любой дочерний элемент QML UI
пытаться получить доступ к свойству ID не может, потому что _node*
не устанавливается при создании экземпляра, его можно установить только методом aux, но, поскольку свойство фактически является постоянной только для чтения, оно больше не обновляется после создания экземпляра, когда _node*
член UI
на самом деле установлен.Таким образом, в принципе, мне нужно иметь возможность установить ссылку на соответствующий Node
каждого UI
после создания, чтобы он был доступен для UI
Элементы QML, но я не могу передать его конструктору, и поскольку свойство доступно только для чтения, оно читается только один раз, после UI
экземпляр, когда _node*
UI
участник все еще NULL
, поэтому значение идентификатора не может быть доступно.
Одно быстрое и грязное решение, которое приходит на ум, это добавить NOTIFY
idChanged()
сигнал, излучаемый при настройке _node*
постинстанцирование члена, даже если свойство идентификатора никогда не изменяется, не может и не должно, и добавляет проверку для метода получения идентификатора — возвращает поддельное фиктивное произвольное значение, если _node*
является NULL
иначе получите значение идентификатора из _node*
член. Излишне говорить, что это не элегантно и добавляет некоторые накладные расходы, а значение фиктивного идентификатора является потенциальным источником червей, поэтому любые идеи о том, как победить плохой дизайн внутренних компонентов QML, приветствуются.
Я просто пришел с решением, и довольно простым. Учитывая, что QtQuick разработан таким образом, что не позволяет указывать параметры конструктора, и учитывая, что подход «создать и установить» имеет огромное значение в моем конкретном сценарии использования, я решил просто сделать значение доступным для / в UI
конструктор, даже если не передан туда в качестве параметра.
Итак, вместо «создать и установить» я просто делаю «установить и создать» и использую статический член класса, который устанавливается перед созданием каждого элемента и используется в конструкторе элементов для установки данных во времени.
class UI : public QQuickItem
{
Q_OBJECT
public:
explicit UI(QQuickItem * parent = 0);
...
private:
Object * object;
static UI * rootUI;
};
Object * UI::protoObject = 0;
UI::UI(QQuickItem * parent) : QQuickItem(parent), object(protoObject) {
if (!protoObject) qDebug() << "ERROR: prototype object is 0, unexpected";
...
}
и фактическое создание объекта:
UI::setProtoObject(obj);
// create QQmlComponent with obj set being set in the constructor
UI::setProtoObject(0);
Кроме того, другой метод, который применим, когда вам не нужен объект, заданный еще в конструкторе, — это создание QQmlComponent
s beginCreate()
установите необходимые свойства и завершите completeCreate()
Поскольку компоненты QML всегда являются генетической структурой без конкретных данных, вы не можете избежать двух шагов: создать экземпляр и заполнить.
ui.qml
import MyModules 1.0
MyUIRoot {
id: root
// all the visual stuff
Rectangle {
Text {
text: root.nodeId // binding should be set up automatically
}
}
}
Затем вы строите MyUIRoot
класс в с ++ в myuiroot.h
а также myuiroot.cpp
что наследует QQuickItem
с функциями
public:
Q_PROPERTY(int nodeId READ nodeId NOTIFY nodeIdChanged);
int nodeId();
void setNode(Node* node);
signals:
void nodeIdChanged();
private:
Node* _node;
В myuiroot.cpp
у тебя есть
MyUIRoot::MyUIRoot(QQuickItem *parent) :
QQuickItem(parent)
{
}
int nodeId()
{
if (_node)
// make sure that every valid ID is > 0
return _node.id();
else
return -1;
}
void setNode(Node* node)
{
if (_node != node)
{
_node = node;
emit nodeIdChanged();
}
}
Зарегистрируйте свой компонент в main.cpp
qmlRegisterType<MyUIRoot>("MyModules", 1, 0, "MyUIRoot");
и создать с
QQmlComponent c(_view->engine(), path);
QObject *o = c.create();
MyUIRoot *item = qobject_cast<MyUIRoot*>(o);
item.setNode(....);
Единственный другой способ создания визуальных элементов Qt Quick из C ++ — это использовать «Элементы пользовательского графа сцены», где вам не нужен QML для создания визуального элемента. Увидеть http://qt-project.org/doc/qt-5.0/qtquick/qquickitem.html#custom-scene-graph-items.
Добавьте 2-й конструктор и создайте элемент из C ++
ui.h
#include <QQuickItem>
#include "node.h"
class UI : public QQuickItem
{
Q_OBJECT
public:
explicit UI(QQuickItem *parent = 0);
explicit UI(QQuickItem *parent = 0, Node *node = 0);
signals:
public slots:
private:
Node* _node;
};
ui.cpp
#include "ui.h"
UI::UI(QQuickItem *parent) :
QQuickItem(parent)
{
}
UI::UI(QQuickItem *parent, Node *node) :
QQuickItem(parent)
, _node(node)
{
qDebug() << "My ID is" << node->id();
}
Создать с
Node *node = new Node();
UI ui(0, node); // or parent QQuickItem instead of '0'