qt — создание экземпляра QQuickItem из C ++ и установка головоломки (не разрешается передавать что-либо конструктору)

Я нахожусь в затруднительном положении, пытаясь динамически создать пользовательские элементы QML из C ++.

Итак, для решения проблемы:

  • У меня легкий вес Node структура данных объекта, абстрагированная от любого пользовательского интерфейса.
  • Из-за тяжелой памяти QObject и производные, я должен создавать элементы пользовательского интерфейса по требованию.
  • каждый Node имеет _ui* член, и каждый UI имеет _node* член.
  • каждый Node имеет уникальный _idкаждый UI имеет ID PROPERTYдоставлено из _node*, Свойство ID доступно только для чтения, так как идентификатор устанавливается на Node инстанцирование и не должно быть изменяемым.
  • Элементы QML должны иметь только конструкторы с параметрами по умолчанию, например, не может ничего передать конструктору.
  • Когда элемент QML создан, _node* участник NULL, поэтому любой дочерний элемент QML UI пытаться получить доступ к свойству ID не может, потому что _node* не устанавливается при создании экземпляра, его можно установить только методом aux, но, поскольку свойство фактически является постоянной только для чтения, оно больше не обновляется после создания экземпляра, когда _node* член UI на самом деле установлен.

Таким образом, в принципе, мне нужно иметь возможность установить ссылку на соответствующий Node каждого UI после создания, чтобы он был доступен для UI Элементы QML, но я не могу передать его конструктору, и поскольку свойство доступно только для чтения, оно читается только один раз, после UI экземпляр, когда _node* UI участник все еще NULL, поэтому значение идентификатора не может быть доступно.

Одно быстрое и грязное решение, которое приходит на ум, это добавить NOTIFY idChanged() сигнал, излучаемый при настройке _node* постинстанцирование члена, даже если свойство идентификатора никогда не изменяется, не может и не должно, и добавляет проверку для метода получения идентификатора — возвращает поддельное фиктивное произвольное значение, если _node* является NULLиначе получите значение идентификатора из _node* член. Излишне говорить, что это не элегантно и добавляет некоторые накладные расходы, а значение фиктивного идентификатора является потенциальным источником червей, поэтому любые идеи о том, как победить плохой дизайн внутренних компонентов QML, приветствуются.

2

Решение

Я просто пришел с решением, и довольно простым. Учитывая, что 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);

Кроме того, другой метод, который применим, когда вам не нужен объект, заданный еще в конструкторе, — это создание QQmlComponents beginCreate()установите необходимые свойства и завершите completeCreate()

1

Другие решения

Поскольку компоненты 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.

0

Добавьте 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'
0
По вопросам рекламы [email protected]