2

我发现自己在尝试从 C++ 动态实例化自定义 QML 元素。

所以,要布局问题:

  • Node我有一个从任何 UI 中抽象出来的轻量级对象数据结构。
  • 由于内存繁重QObject且派生,我必须按需实例化 UI 元素。
  • 每个Node人都有一个_ui*成员,每个UI人都有一个_node*成员。
  • 每个Node都有一个独特的_id,每个UI有一个ID PROPERTY,交付自_node*。ID 属性是只读的,因为 ID 是在Node实例化时设置的,不应修改。
  • QML 元素必须只有带有默认参数的构造函数,例如不能向构造函数传递任何东西。
  • 当 QML 元素创建时,_node*成员是,因此任何试图访问 ID 属性的NULLQML 子元素都不能,因为实例化时没有设置,只能通过 aux 方法设置,但由于该属性实际上是一个只读常量,实例化后,当实际设置的成员时不再更新。UI_node*_node*UI

所以基本上,我需要能够在创建时将链接设置Node为每个对应的链接UI,以便UIQML 元素可以访问它,但我不能将它传递给构造函数,并且由于属性是只读的,因此它被读取只有一次,在UI实例化时,当_node* UI成员仍然是时NULL,因此无法访问 ID 值。

想到的一个快速而肮脏的解决方案是NOTIFY idChanged()在设置成员后实例化时添加一个要发出的信号,_node*即使 ID 属性从未真正改变,不能也不应该,并添加对 ID getter 的检查 - 返回一个假虚拟如果_node*是任意值,否则从成员中NULL获取 ID 值。_node*不用说,这并不优雅,并且增加了一些开销,并且虚拟 ID 值是潜在的蠕虫罐头,因此欢迎任何关于如何击败 QML 内部不良设计的想法。

4

3 回答 3

1

I just came with a solution, and a rather simple one. Considering that QtQuick is designed in a way that does not allow to specify constructor parameters and considering that the "create and set" approach has huge implications in my particular usage scenario, I decided to simply make the value available to/in the UI constructor even if not passed there as a parameter.

So, instead of "create and set" I just do a "set and create" and use a static class member that is set before creating each item and used in the item constructor to set the data in time.

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";
    ...
}

and the actual object creation:

UI::setProtoObject(obj);
// create QQmlComponent with obj set being set in the constructor
UI::setProtoObject(0);

Also, another method, which is applicable when you don't need the object set as early as in the constructor is to create the QQmlComponents beginCreate(), set the needed properties, and finalize with completeCreate()

于 2014-01-11T10:40:19.370 回答
0

添加第二个构造函数并从 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;

};

用户界面.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'
于 2014-01-11T10:31:54.607 回答
0

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

然后你在 C++ 中构建一个MyUIRoot类,myuiroot.hmyuiroot.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(....);

我看到从 C++ 创建可视 Qt Quick 元素的唯一其他方法是使用“自定义场景图项”,您不需要 QML 来创建可视项。请参阅http://qt-project.org/doc/qt-5.0/qtquick/qquickitem.html#custom-scene-graph-items

于 2014-01-11T01:43:35.383 回答