Using non-creatable type in QML
-
I played with QML/C++ interfacing this week. In a test projet I wanted to declare a QML property using a type that is registered via qmlRegisterUncreatableType(). The property is initialized with 'null' in QML. To my surprise I get the error message "Element is not creatable." on application start although there is no attempt to create in instance of this class in QML. Does someone have an idea what I'm doing wrong?
-
You cannot declare properties of an uncreatable type in an object declaration in QML. You can declare properties of an uncreatable type in a C++ type declaration only.
For example, the following works:
@
class MyQmlType : public QObject
{
Q_OBJECT
Q_PROPERTY(MyUncreatableType *someProp READ accessor CONSTANT)public:
MyQmlType();
~MyQmlType();
MyUncreatableType *someProp() const;
}
@But the following does not:
@
Item {
property MyUncreatableType someProp
}
@In practice, this does make uncreatable types somewhat less useful from QML, I agree.
Cheers,
Chris. -
Maybe I was not explicit enough. What i did was: declare a type in C++ and register it with qmlRegisterUncreatableType() for QML interop. Then I used it in QML like this
@
Item {
property MyUncreatableType someProp = null
}
@I intended to set the property in a QML function later on. But I immediatly received the error mentioned above when I started the app. If you were right, then the only use for uncreatable types would be for declaring globals via QmlContext::setContextProperty.
My expectation was this uncreatable registration would simply prevent creating objects in QML - not using them in QML. Maybe it's a bug?
-
I understood you. As I said, you cannot declare a property whose type is uncreatable in a QML object declaration.
No, you're not correct is stating that the only use for uncreatable types is for declaring globals via context properties. They are also useful as properties of C++-defined types, as I explained in my original response.
Your expectation is basically correct, but due to the way that property declaration occurs in a QML object instance, this also means that you cannot declare properties of such uncreatable types in QML. It's not a bug. It's expected (although I agree suboptimal) behaviour.
Cheers,
Chris. -
Thank you for the details.
But if the observed behavior is expected then the current documentation is obviously incomplete as it seems to match my expectations.
bq.
This template function registers the C++ type in the QML system with the name qmlName, in the library imported from uri having the version number composed from versionMajor and versionMinor.
While the type has a name and a type, it cannot be created, and the given error message will result if creation is attempted.
This is useful where the type is only intended for providing attached properties or enum values. -
Hi,
You're right - it should be expanded to explicitly mention that properties of that type cannot be declared. Please file a bug report about this issue.
Thanks,
Chris. -
So this is still a bug (documentation or otherwise) two years later.
After reading the Qt 5.4 documentation, I fully expected to be able to use a type registered in this way, and this thread is the only comment I've found anywhere that say you can't.
Quote from the Qt 5.4 docs:
This is useful if a type's enums or attached properties should be accessible from QML but the type itself should not be instantiable.
It is in fact strictly necessary to do this in many cases, as it is common for QObjects to be impossible to create without additional parameters.
I just want a place to put a pointer to my QObject. Is that really impossible?
QML still has incredibly poor documentation throughout. There are a huge number of circular links, and a lot of "magic" functions that appear to exist yet are totally undocumented - pretty much everything to do with Models, for example.
-
I'm pretty new to Qt and QML but as I'm currently researching this topic myself, I thought I'd let you know what I've found. You can gain access to instances of uncreatable types via the setContextProperty call.
C++
QQuickView view; qmlRegisterUncreatableType<MyType>("myUri", 1, 0, "Don't try to add to a qml definition"); view->rootContext()->setContextProperty("myInstance", myInstance);
QML
import myUri 1.0 Item { property int something: myInstance.something // binding a C++ property to QML property int somethingElse: 0 function doSomething () { myInstance.doSomething(); // calling a Q_INVOKABLE } Connections { target: myInstance onSomethingChanged: { // property changed signal handler } }
as referenced here: http://doc.qt.io/qt-5/qtqml-cppintegration-contextproperties.html#setting-an-object-as-a-context-property .
Another way you can pass uncreatable types to QML is through a Q_INVOKABLE method called from a creatable type or another context property. The downsides are that the Connections don't work despite everything else seemingly functioning properly. You have to use
connect
in the Javascript. Also, because these are loaded dynamically you need to verify their existence first which is kind of hacky. Assuming you have the above class defined and is accessible from QML using setContextProperty:C++
qmlRegisterUncreatableType<MyOtherType>("myUri", 1, 0, "Don't try to add to a qml definition");
QML
import myUri 1.0 Item { property var myOtherInstance property int myProperty: myOtherInstance !== null ? myOtherInstance.something : 0 // property depending on existence of the instance Component.onCompleted: { myOtherInstance = myInstance.getOtherInstance(); } onMyOtherInstanceChanged: { myOtherInstance.doSomething() // Q_INVOKABLE myOtherInstance.onSomethingChanged.connect( function () { console.debug("something changed!") } ); // signal handler for Q_PROPERTY }
edit: There is another major component to this. By passing the Q_OBJECT to the QML, it takes ownership of that object and will garbage collect it. For objects intended to contain persistent data, as you might imagine, this is not ideal. You will need to give explicit ownership to the C++.
QQmlEngine::setObjectOwnership(myInstance, QQMLEngine::CppOwnership);