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

Registering an Instantiable Object Type



  • Hi,
    Im trying to Register an Instantiable Object just like in this official example
    But when i create the object QML side, my properties are not assigned

    //my class simplified (i keep only one property)
    class PlcCom : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(int port READ port WRITE setPort NOTIFY portChanged)
    
    public:
        explicit PlcCom(QObject *parent = nullptr);
    
        int port() const {  return m_port; }
        void setPort(int port)
        {
            if (m_port == port)
                return;
            m_port = port;
            emit portChanged(m_port);
        }
    //...
    
    private :
    int m_port = 0;
    QString hostAdr = "127.0.0.1";
    ...
    //main.cpp
      qmlRegisterType<PlcCom>("com.not.testComApi", 1, 0, "PlcCom");
    
    //qml
    import com.not.testComApi 1.0
    
    Window {
        PlcCom{
             id:p2
    
             port:62944
    
             hostAdr: "10.81.4.108"
             request : "myRequest"
             Component.onCompleted: {         // i also tryed   
                 //p2.hostAdr = "10.81.4.108"
                 //p2.port = 62944
                 //request =  "myRequest"
             }
         }
    

    the problem is my object is created with default property values, this line in PlcCom ctor

     qDebug()<<"connecting : " << m_hostAdr << ":"<<m_port;
    

    will output :

    connecting : "127.0.0.1" : 0 but i expect 10.81.4.108:62944

    Can someone tell me what i'm missing please ?


  • Moderators

    hi @LeLev

    IIRC your setPort function has to be markt as slot

    public slots:
    void setPort(int port)
        {
            if (m_port == port)
                return;
            m_port = port;
            emit portChanged(m_port);
        }
    

    I think the QML-CPP communication requires Q_OBJECT macro and proper slot macros to work correctly.



  • Thank you
    @J.Hilk said in Registering an Instantiable Object Type:

    has to be markt as slot

    now it is but still not initialized correctly


  • Moderators

    @LeLev mmh,
    do you initialize the QObject parent correctly?



  • @J.Hilk please see the full class code

    class PlcCom : public QObject
    {
        Q_OBJECT
    
        Q_PROPERTY(QString request READ request WRITE setRequest NOTIFY requestChanged)
        Q_PROPERTY(QString stringValue READ stringValue WRITE setStringValue NOTIFY stringValueChanged)
        Q_PROPERTY(QString hostAdr READ hostAdr WRITE setHostAdr NOTIFY hostAdrChanged)
        Q_PROPERTY(int port READ port WRITE setPort NOTIFY portChanged)
    
    public:
        explicit PlcCom(QObject *parent = nullptr);
    
    public slots:
    
        QString request() const {return m_request;}
        void setRequest(QString request)
        {
            if (m_request == request)
                return;
    
            m_request = request;
            emit requestChanged(m_request);
        }
    
        QString stringValue() const{return m_stringValue;}
        void setStringValue(QString stringValue)
        {
            m_stringValue = stringValue;    
            emit stringValueChanged(m_stringValue);
        }
    
        QString hostAdr() const{ return m_hostAdr;}
        void setHostAdr(QString hostAdr)
        {
            if (m_hostAdr == hostAdr)
                return;
    
            m_hostAdr = hostAdr;
            emit hostAdrChanged(m_hostAdr);
        }
    
        int port() const{return m_port;}
    
        void setPort(int port)
        {
            if (m_port == port)
                return;
    
            m_port = port;
            emit portChanged(m_port);
        }
    
    signals:
        void requestChanged(QString request);
        void stringValueChanged(QString stringValue);
        void hostAdrChanged(QString hostAdr);
        void portChanged(int port);
    
    private :
        void socketConnected();
        void socketDisconnected();
        void responseReady();
    
    private:
        QString m_request;
        QString m_stringValue;
        QString m_hostAdr;
        int m_port;
        QTcpSocket *sk;
    };
    
    
    PlcCom::PlcCom(QObject *parent)
        : QObject(parent)
    {
        sk = new QTcpSocket(this);
    
        QObject::connect(sk,&QTcpSocket::connected,this,&PlcCom::socketConnected);
        QObject::connect(sk,&QTcpSocket::disconnected,this,&PlcCom::socketDisconnected);
        QObject::connect(sk,&QTcpSocket::readyRead,this,&PlcCom::responseReady);
        connect(sk, QOverload<QAbstractSocket::SocketError>::of(&QAbstractSocket::error),
                [=](QAbstractSocket::SocketError err){
            qDebug()<<"SocketError";
        });
    
    
        //qDebug()<<"connecting : " << m_hostAdr << ":"<<m_port; // here output default values
    
    
        // BADFIX : TODO   improve , QML properties not set directly ?
        QObject::connect(this,&PlcCom::hostAdrChanged,[&](){ // here everything is initialized with qml values
            qDebug()<<"connecting : " << m_hostAdr << ":"<<m_port;
            sk->connectToHost(QHostAddress(m_hostAdr),static_cast<quint16>(m_port));
    
            if(!sk->waitForConnected(5000))
            {
                qDebug() << "Error: " << sk->errorString();
                return;
            }
        });
    }
    
    void PlcCom::socketConnected()
    {
        qDebug("CONNECTED.");
        QString c = m_request; 
        qDebug()<<"SENDING COMMAND : " << c;
        c.append("\n");
        sk->write(c.toLatin1(),c.length());
    }
    
    void PlcCom::socketDisconnected()
    {
        qDebug("DISCONNECTED...");
    }
    
    void PlcCom::responseReady()
    {
        qDebug() << "reading...";
        setStringValue(sk->readAll());
    }
    
    

  • Moderators

    @LeLev
    I can't reproduce this,
    take this minimal example:

    #ifndef MYCLASS_H
    #define MYCLASS_H
    
    #include <QObject>
    #include <QDebug>
    
    class myClass : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(QString hostAdr READ hostAdr WRITE setHostAdr NOTIFY hostAdrChanged)
    public:
        explicit myClass(QObject *parent = nullptr) :QObject(parent)
        {
            connect(this, &myClass::hostAdrChanged, this, [](QString hostAdr)->void{
                qDebug() <<hostAdr;
            });
        }
    
        const QString &hostAdr() const
        {
            return m_hostAdr;
        }
    
    signals:
        void hostAdrChanged(const QString &hostAdr);
    
    public slots:
    
    void setHostAdr(const QString &hostAdr)
    {
        if (m_hostAdr == hostAdr)
            return;
    
        m_hostAdr = hostAdr;
        emit hostAdrChanged(m_hostAdr);
    }
    
    private:
        QString m_hostAdr;
    };
    
    #endif // MYCLASS_H
    
    
    //main.cpp
    int main(int argc, char *argv[])
    {
        QApplication app(argc, argv);
    
        qmlRegisterType<myClass>("com.not.testComApi", 1, 0, "PlcCom");
    
        QQmlApplicationEngine engine;
    
        engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
        if (engine.rootObjects().isEmpty())
            return -1;
    }
    
    import QtQuick 2.12
    import QtQuick.Controls 2.5
    
    import com.not.testComApi 1.0
    
    ApplicationWindow {
        visible:true
        width:500; height:500
    
        PlcCom{
            hostAdr: "abcd"
        }
    }
    

    the changed signal is emitted fine with the correct string



  • @J.Hilk thank you very much.
    I did the same way (see my previous post), only i wonder why i have to handle that hostAdrChanged signal ? i want to create the object directly with right parameter values, QML side.

    In my constructor

    qDebug()<<"connecting : " << m_hostAdr << ":"<<m_port;
    

    this will output default values,
    but if i do

    QObject::connect(this,&PlcCom::hostAdrChanged,[&](){ 
           qDebug()<<"connecting : " << m_hostAdr << ":"<<m_port;
           
    

    here everything is initialized with qml values


  • Moderators

    @LeLev
    well the c++ part is initialized first. You class gets initiated -> call of the constructor

    then it's passed to the qml engine, then the values from there are taken and set.

    if you want to initialize your values from qml side only, you can do that to.

    IIRC there is QQmlParserStatus Class

    When your class inherits from that as well as from QObject you can override the virtual componentComplete function and do your initialization there.

    That one is called together with the QML Component.onCompleted: ...signal




Log in to reply