[QML / C++] Exposing Pointer Type attribute to QML
-
Dear All,
as written in the title I have some doubts on exposing a C++ pointer object to QML.
What I need is making a pointer object (so no instantiable) to QML ( in details, I expose an instance of an AbstractItemModel's subclass).
From QML doc (https://doc.qt.io/qt-5/qtqml-cppintegration-overview.html#choosing-the-correct-integration-method-between-c-and-qml), I try to useqmlRegisterType()
without success.However from the an old Qt post (https://www.qtcentre.org/threads/14835-How-to-use-Q_DECLARE_METATYPE?p=76295#post76295), it suggests to use this declaration:
qRegisterMetaType<Type*>("Type*");
And in this way it seems working.
My question is: why do I need to use this function?
I know that it is needed for signal/slot connections, but for QML I don't find any indication to use itThanks,
Nicola -
@nico88desmo said in [QML / C++] Exposing Pointer Type attribute to QML:
I try to use qmlRegisterType() without success.
What are the symptoms of failure? Any warnings or errors?
From your description I'd say you rather need
qRegisterUncreatableType()
. ButqmlRegisterType()
should work fine, too.qRegisterMetaType<Type*>("Type*");
There is no need to register types for subclasses of QObject, you should not need to use it.
-
Here are more code details (I write only necessary parts of code...)
The failure appears during runtime and is:
QMetaProperty::read: Unable to handle unregistered datatype 'MenuModel*' for property 'dv::life::SettingsDialogCtrl::menuModel'
MenuModel is the follow:
class MenuModel : public QAbstractListModel { Q_OBJECT ... }
used inside QML using the code:
ListView { ... model: SettingsDialogCtrl.menuModel ...
and finally SettingsDialogCtrl is a "Controller" class that handle events from UI; it is declare in this way:
class SettingsDialogCtrl : public BaseController { Q_OBJECT Q_PROPERTY(MenuModel* menuModel READ getMenuModel) private: SettingsDialogCtrlP* dPtr; // PIMPL idiom public: MenuModel* getMenuModel() { return dPtr->menuModel.data(); } public slots: void testAdd(); ... };
I can't use
qmlRegisterUncreatableType
because I don't need to access to the object using a qmlName, instead I need to use it in qml as property of SettingsDialogCtrl, which is a singleton declare usingqmlRegisterSingletonType<SettingsDialogCtrl>
For this I think using anonymous qml type declaration,
Qt5.12qmlRegisterType<Type>()
Qt5.14qmlRegisterAnonymousType<Type>(const char *uri, int versionMajor)
But it seems not working for pointer (even if my case is similar to example https://doc.qt.io/qt-5.12/qqmlengine.html#qmlRegisterType)
-
I found also this reply of an 3-years ago answer:
https://www.qtcentre.org/threads/68270-C-pointer-to-Qml-is-this-possible-!?p=299124#post299124
where it's indicated to register the type using
qRegisterMetaType<Type*>("Type *")
because the type (pointer type) should be understandable to QVariant (why??)Is there any implicit conversion?
But I don't find any information at this linkhttps://doc.qt.io/qt-5.12/qtqml-cppintegration-data.html
Looks like I'm losing some information on why I should use
qRegisterMetaType
... -
@nico88desmo I'm exposing all my (QObject*) Entities and dtos in this way:
qmlRegisterType<Person>("org.ekkescorner.data", 1, 0, "Person");
then in QML
import org.ekkescorner.data 1.0
now QML "knows" my Objects and I can easy access the properties or use it as parameters in Q_INVOKABLE methods
here's a snippet from my Person Class:... #include <QObject> ... class Person: public QObject { Q_OBJECT Q_PROPERTY(QString uuid READ uuid WRITE setUuid NOTIFY uuidChanged FINAL) Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL) Q_PROPERTY(QString phone READ phone WRITE setPhone NOTIFY phoneChanged FINAL) ... public: Person(QObject *parent = nullptr); ...
never had a problem with qmlRegisterType
-
@nico88desmo said in [QML / C++] Exposing Pointer Type attribute to QML:
qRegisterMetaType<Type*>("Type *")
There is no need to provide anything in the round brackets
()
in most cases. Remove the string and try. Anyway, like Ekke and me said - there is no need to call it at all.because the type (pointer type) should be understandable to QVariant (why??)
You return a pointer in the property, so Qt Meta Object system needs to know about it. But Q_OBJECT macro handles this automatically.
Is there any implicit conversion?
Yes, all communication between C++ and QML is based on QVariants.
But it seems not working for pointer (even if my case is similar to example
You are calling the qmlRegister* methods before instantiating the QML engine, right? You should.
-
Thanks both for the support, I'm going to answer to each one:
@ekkescorner said in [QML / C++] Exposing Pointer Type attribute to QML:
@nico88desmo I'm exposing all my (QObject*) Entities and dtos in this way:
...
cut
...
never had a problem with qmlRegisterTypeThis solution works me too for other objects, but if you declare:
qmlRegisterType<Person>("org.ekkescorner.data", 1, 0, "Person");
your're telling to QML Engine that this type is instantable from QML; in my case, I don't want QML is able to create any instance of it.
For this I declare:qmlRegisterType<MenuModel>();
as documented here: This template function registers the C++ type in the QML system. Instances of this type cannot be created from the QML system
@sierdzio said in [QML / C++] Exposing Pointer Type attribute to QML:
@nico88desmo said in [QML / C++] Exposing Pointer Type attribute to QML:
qRegisterMetaType<Type*>("Type *")
There is no need to provide anything in the round brackets
()
in most cases. Remove the string and try. Anyway, like Ekke and me said - there is no need to call it at all.because the type (pointer type) should be understandable to QVariant (why??)
I try removing it, but without the string "MenuModel*", QML ListView isn't working.
It works only in this caseint qmlTypeId = qRegisterMetaType<namespace::MenuModel*>("MenuModel*");
You return a pointer in the property, so Qt Meta Object system needs to know about it. But Q_OBJECT macro handles this automatically.
Is there any implicit conversion?
Yes, all communication between C++ and QML is based on QVariants.
Good to know this.
But it seems not working for pointer (even if my case is similar to example
You are calling the qmlRegister* methods before instantiating the QML engine, right? You should.
Yes, before starting program, I call the necessary functions to allow QML to see C++ objects.
The others one work correctly, the only small problem is withMenuModel *
class. -
@nico88desmo said in [QML / C++] Exposing Pointer Type attribute to QML:
your're telling to QML Engine that this type is instantable from QML; in my case, I don't want QML is able to create any instance of it.
ah - understand
BTW: in my projects I never create QObject* from QML, always a C++ class is parent and manages lifecycle - but this is only a project-rule and not something I'm telling the QML engine as you want to do -
@ekkescorner said in [QML / C++] Exposing Pointer Type attribute to QML:
ah - understand
BTW: in my projects I never create QObject* from QML, always a C++ class is parent and manages lifecycle - but this is only a project-rule and not something I'm telling the QML engine as you want to doThis is the same policy I have with my current project; in any case, since QML Engine give the possibility to prevent instantiation of some object, I was investigating on why in this case everything works iff I register my type using
qRegisterMetatype<Type*>("Type*")
I'm quite new on QML architecture, that's why I'm trying to go deeper to understand better how it works ;)
-
Hi,
You can try to use setContextProperty("QML_POINTER_NAME",pointer);
For example:
C++: MyClass * myPointer; QQuickView view; view.engine()->rootContext()->setContextProperty("myPointerInQML",myPointer); QML: myPointerInQML.myMethodOrProperty();
-
there is no need to call it at all.
@sierdzio @ekkescorner I can confirm @nico88desmo 's observation that it is indeed needed to register the pointer type with
qRegisterMetaType
or you would receive the error they mentioned:QMetaProperty::read: Unable to handle unregistered datatype ... for property ...
And trying to register the pointer type results in a compiler error:
error: type ... cannot be used prior to '::' because it has no members
To reproduce register a type as a singleton. Then have a property that is a pointer type or an invokable that returns a pointer. (therefore the pointer is passed to QML not the object type itself) Then try registering with either of the methods you propose. Only option that seems to work is using
qRegisterMetaType
. -
Hi @nico88desmo
I am using exactly the same pattern as you:I have a Qml Singleton type registered as:
qmlRegisterSingletonType<QmlInterface>(UM_URI_DOMAIN, 1, 0, "UMP", &QmlInterface::singletonProvider);
and in there, a property declared as follows:
Q_PROPERTY(UmFolderListModel* file_model READ file_model CONSTANT)
The only other registration that is required is:
qmlRegisterUncreatableType<UmFolderListModel>(UM_URI_DOMAIN, 1, 0, "UmFolderListModel", UM_UNCREATABLE_REASON);
And I use it as follows in QML:
property UmFolderListModel fileModel: UMP.file_model
As stated above, the
qmlRegisterUncreatableType
registration is needed in my case.