I find myself in a pickle trying to dynamically instantiate custom QML elements from C++.
So, to layout the problem:
Node
object data structure that is abstracted from any UI.QObject
and derived, I must instantiate the UI elements on demand.Node
has a _ui*
member, and each UI
has a _node*
member.Node
has a unique _id
, each UI
has an ID
PROPERTY
, delivered from the _node*
. The ID property is read only, since the ID is set upon Node
instantiation and should not be modifiable._node*
member is NULL
, so any QML child element of UI
trying to access the ID property cannot, because the _node*
is not set upon instantiation, it can only be set by an aux method, but since the property is effectively a read-only constant, it is not updated anymore after the instantiation when the _node*
member of the UI
is actually set.So basically, I need to be able to set the link to the corresponding Node
of each UI
upon creation, so that it is accessible to the UI
QML elements, but I cannot pass it to a constructor, and since the property is read-only it is read only once, upon the UI
instantiation, when the _node*
UI
member is still NULL
, so the ID value cannot be accessed.
One quick and dirty solution that comes to mind is to add a NOTIFY
idChanged()
signal to emit when setting the _node*
member post-instantiation even though the ID property never really changes, cannot and should not, and add a check for the ID getter - return a fake dummy arbitrary value if _node*
is NULL
, otherwise fetch the ID value from the _node*
member. Needless to say, this is not elegant, and adds some overhead and the dummy ID value is a potential can of worms, so any ideas how to defeat the ill design of QML internals are welcome.
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 QQmlComponent
s beginCreate()
, set the needed properties, and finalize with completeCreate()