9

Dynamically instantiating a QML object from C++ is well documented, but what I can't find is how to instantiate it with pre-specified values for it's properties.

For example, I am creating a slightly modified SplitView from C++ like this:

QQmlEngine* engine = QtQml::qmlEngine( this );
QQmlComponent splitComp( engine, QUrl( "qrc:/qml/Sy_splitView.qml" ) );
QObject* splitter = splitComp.create();

splitter->setProperty( "orientation", QVariant::fromValue( orientation ) );

The problem I have is that specifying the orientation of the SplitView after it is instantiated causes it's internal layout to break. So, is there a way of creating the SplitView with the orientation already specified?

Alternatively I can create both a horizontal and vertical version of SplitView in separate files and instantiate the appropriate one at runtime - but this is less elegant.

Update

I found QQmlComponent::beginCreate(QQmlContext* publicContext):

QQmlEngine* engine = QtQml::qmlEngine( this );
QQmlComponent splitComp( engine, QUrl( "qrc:/qml/Sy_splitView.qml" ) );
QObject* splitter = splitComp.beginCreate( engine->contextForObject( this ) );

splitter->setProperty( "orientation", QVariant::fromValue( orientation ) );
splitter->setParent( parent() );
splitter->setProperty( "parent", QVariant::fromValue( parent() ) );
splitComp.completeCreate();

But it had no effect surprisingly.

4
  • I bet the thing is in how you attempt to assign the enum via QVariant (enums are somewhat buggy in QML). I'd try to first register a simple QObject based type and a custom enum and check if the whole thing works at all. [also note, that you apparently attempt to set parent twice, but that's minor] Commented Oct 31, 2013 at 7:53
  • I didn't know that about enums, so thanks I'll try it. And I'm not setting the parent twice, I set the QObject parent first, and then QML visual parent second (if I could set the QObject parent via QML, I wouldn't have bothered with C++ at all for this). Commented Oct 31, 2013 at 8:24
  • Right, indeed; instead of setting the QObject parent though, you could set memory ownership (or however it is called) to QmlOwnership, I believe (so that the freshly created object will be garbage-colleced / ref-counted by the QML runtime as necessary). Btw, is it that you specifically need to set the QObject parent for some reasons other than memory management? Commented Oct 31, 2013 at 11:11
  • 1
    It is for memory management. Each SplitView contains two custom OSG viewports, each of which in turn can be split ad infinitum (like Qt Creator's text editor panes), forming a tree. So when I delete a particular splitter, it deletes all of the children appropriately. I'll take a look at having ownership on QML side, it might simplify things - thanks! Commented Oct 31, 2013 at 11:56

3 Answers 3

4

For anyone still interested in this problem, in Qt 5 (and so Qt 6), you can also use a custom QQmlContext with QQmlContext::setContextProperty() to setup external property (orientation in your case):

QQmlEngine engine;

QQmlContext *context = new QQmlContext(engine.rootContext());
context->setContextProperty("myCustomOrientation", QVariant::fromValue(orientation));

// you can use a 'myCustomOrientation' property inside Sy_splitView.qml, e.g.
// `orientation: myCustomOrientation`
QQmlComponent splitComp(&engine, QUrl("qrc:/qml/Sy_splitView.qml"));
QObject* splitter = splitComp.create(context);

This should allow you to not fiddle with beginCreate and completeCreate.

Update:

There is also QQmlComponent::createWithInitialProperties() (from 5.14 onwards) that allows you to create an object and initialize its properties before the creation process finishes.

(And then QQmlApplicationEngine::setInitialProperties() is the application engine's version of the same functionality)

Sign up to request clarification or add additional context in comments.

Comments

0

I think you should be able to use a custom QQmlIncubator and the QQmlComponent::create(QQmlIncubator & incubator, QQmlContext * context = 0, QQmlContext * forContext = 0) factory method.

In particular, quoting from the QQmlIncubator documentation:

void QQmlIncubator::setInitialState(QObject * object) [virtual protected]

Called after the object is first created, but before property bindings are evaluated and, if applicable, QQmlParserStatus::componentComplete() is called. This is equivalent to the point between QQmlComponent::beginCreate() and QQmlComponent::endCreate(), and can be used to assign initial values to the object's properties.

The default implementation does nothing.

Comments

0

I have had similar situation for my own QML component. Just wanted to init some props before running some bindings. In pure QML I did it that way:

var some = component.createObject(this, {'modelClass': my_model});

From C++ I tried that way:

// create ui object
auto uiObject = qobject_cast<QQuickItem*>(component.beginCreate(ctx));
// place on ui
uiObject->setParentItem(cont);

// set model properties
classInstance->setView(QVariant::fromValue(uiObject));
classInstance->setPosition(QPointF(x, y));

// set ui object properties
uiObject->setProperty("modelClass", QVariant::fromValue(classInstance.get()));

// finish
component.completeCreate();

but I had binding errors because modelClass remains null! After digging for a while I found the cause. And it looks reasonable for me. I've changed my QML class!

Item {
    id: root
    // property var modelClass: null
    property var modelClass // just remove : null!
}

Properties with initial values right after invoking beginCreate are not visible from C++, until I call completeCreate(). But if I remove initial value property becomes visible and I can initialize it in C++ code

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.