Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

[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 use qmlRegisterType() 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 it

    Thanks,
    Nicola


  • Moderators

    @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(). But qmlRegisterType() 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 using qmlRegisterSingletonType<SettingsDialogCtrl>

    For this I think using anonymous qml type declaration,
    Qt5.12 qmlRegisterType<Type>()
    Qt5.14 qmlRegisterAnonymousType<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 link https://doc.qt.io/qt-5.12/qtqml-cppintegration-data.html

    Looks like I'm losing some information on why I should use qRegisterMetaType...


  • Qt Champions 2016

    @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


  • Moderators

    @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 qmlRegisterType

    This 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 case int 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 with MenuModel * class.


  • Qt Champions 2016

    @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 do

    This 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();
    

Log in to reply