Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. QML and Qt Quick
  4. exposing C++ attributes (missing a step)
Forum Updated to NodeBB v4.3 + New Features

exposing C++ attributes (missing a step)

Scheduled Pinned Locked Moved Solved QML and Qt Quick
8 Posts 4 Posters 495 Views 2 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • mzimmersM Offline
    mzimmersM Offline
    mzimmers
    wrote on last edited by
    #1

    Hi all -

    After an 18-month hiatus, I'm returning to QML-land...currently trying scrape the rust off my memory.

    I'm trying to connect a C++ object to a QML display. I'm roughly following the instructions here with minor modifications (I have a QQmlApplicationEngine instead of a QQuickView), but I must be missing a step, because I'm getting a runtime error.

    Here's a snippet from my main.cpp code:

        QQmlApplicationEngine engine;
        Clock clock;
        const QUrl url(u"qrc:/nga/main.qml"_qs);
    
        engine.rootContext()->setContextProperty("clock", &clock);
        engine.load(url);
    

    Clock.h:

    class Clock : public QObject
    {
        Q_OBJECT
        Q_PROPERTY(QString m_timeStr READ time WRITE setTime NOTIFY timeChanged)
    public:
        void setTime(const QString &time);
        QString time() {
            return m_timeStr;
        }
    private:
        QString m_timeStr;
    ...
    

    and my main.qml:

     import (a bunch of stuff)
     ApplicationWindow {
        Text {
            text: clock.time
        }
    ...
    

    I'm getting a runtime error "TypeError: Cannot read property 'time' of null." Could someone please tell me what I left out?

    Thanks...

    1 Reply Last reply
    0
    • fcarneyF Offline
      fcarneyF Offline
      fcarney
      wrote on last edited by
      #2

      @mzimmers said in exposing C++ attributes (missing a step):

      Q_PROPERTY(QString m_timeStr READ time WRITE setTime NOTIFY timeChanged)

      I think this should be:
      Q_PROPERTY(QString time READ time WRITE setTime NOTIFY timeChanged)

      But it does not explain why "clock" is null.

      C++ is a perfectly valid school of magic.

      mzimmersM 1 Reply Last reply
      0
      • fcarneyF fcarney

        @mzimmers said in exposing C++ attributes (missing a step):

        Q_PROPERTY(QString m_timeStr READ time WRITE setTime NOTIFY timeChanged)

        I think this should be:
        Q_PROPERTY(QString time READ time WRITE setTime NOTIFY timeChanged)

        But it does not explain why "clock" is null.

        mzimmersM Offline
        mzimmersM Offline
        mzimmers
        wrote on last edited by
        #3

        @fcarney I just changed that...the only effect seems to be when the error messages are displayed...heh.

        1 Reply Last reply
        0
        • C Offline
          C Offline
          Creaperdown
          wrote on last edited by
          #4

          Hey, I tried your setup in a sample project (Qt 5.15) and it works just fine:

          // Clock.hpp
          #pragma once
          #include <QObject>
          
          class Clock : public QObject
          {
              Q_OBJECT
              Q_PROPERTY(QString m_timeStr READ time WRITE setTime CONSTANT)
              
          public:
              void setTime(const QString &time)
              {
                  m_timeStr = time;
              }
              QString time() {
                  return m_timeStr;
              }
              
          private:
              QString m_timeStr = "test";
          };
          
          // main.cpp
          int main(int argc, char *argv[])
          {
              QGuiApplication app(argc, argv);
              QQmlApplicationEngine engine;
              
              Clock clock;
              
              const QUrl url(QStringLiteral("qrc:/main.qml"));
              
              engine.rootContext()->setContextProperty("clock", &clock);
              
              QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                               &app, [url](QObject *obj, const QUrl &objUrl) {
                  if (!obj && url == objUrl)
                      QCoreApplication::exit(-1);
              }, Qt::QueuedConnection);
              engine.load(url);
              
              return app.exec();
          }
          
          // main.qml
          ApplicationWindow
          {
              id: root
              visible: true
              width: 1200
              height: 800
              
              Component.onCompleted: console.log(clock.m_timeStr)
          }
          

          (This prints "test")

          I am sure we could help you better, if you'd provide a reproducible example

          mzimmersM 1 Reply Last reply
          1
          • C Creaperdown

            Hey, I tried your setup in a sample project (Qt 5.15) and it works just fine:

            // Clock.hpp
            #pragma once
            #include <QObject>
            
            class Clock : public QObject
            {
                Q_OBJECT
                Q_PROPERTY(QString m_timeStr READ time WRITE setTime CONSTANT)
                
            public:
                void setTime(const QString &time)
                {
                    m_timeStr = time;
                }
                QString time() {
                    return m_timeStr;
                }
                
            private:
                QString m_timeStr = "test";
            };
            
            // main.cpp
            int main(int argc, char *argv[])
            {
                QGuiApplication app(argc, argv);
                QQmlApplicationEngine engine;
                
                Clock clock;
                
                const QUrl url(QStringLiteral("qrc:/main.qml"));
                
                engine.rootContext()->setContextProperty("clock", &clock);
                
                QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                                 &app, [url](QObject *obj, const QUrl &objUrl) {
                    if (!obj && url == objUrl)
                        QCoreApplication::exit(-1);
                }, Qt::QueuedConnection);
                engine.load(url);
                
                return app.exec();
            }
            
            // main.qml
            ApplicationWindow
            {
                id: root
                visible: true
                width: 1200
                height: 800
                
                Component.onCompleted: console.log(clock.m_timeStr)
            }
            

            (This prints "test")

            I am sure we could help you better, if you'd provide a reproducible example

            mzimmersM Offline
            mzimmersM Offline
            mzimmers
            wrote on last edited by
            #5

            @Creaperdown normally my questions are easy enough for this audience that snippets reveal my error, but evidently not so in this case. So, here's the whole thing:

            main.cpp

            #include <QGuiApplication>
            #include <QQmlApplicationEngine>
            #include <QQmlContext>
            
            #include "clock.h"
            
            int main(int argc, char *argv[])
            {
                QGuiApplication app(argc, argv);
                QQmlApplicationEngine engine;
                const QUrl url(u"qrc:/nga/main.qml"_qs);
                Clock clock;
            
                engine.rootContext()->setContextProperty("clock", &clock);
                QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                                 &app, [url](QObject *obj, const QUrl &objUrl) {
                    if (!obj && url == objUrl)
                        QCoreApplication::exit(-1);
                }, Qt::QueuedConnection);
            
                engine.load(url);
            
                return app.exec();
            }
            

            clock.h

            #ifndef CLOCK_H
            #define CLOCK_H
            
            #include <QObject>
            #include <QTime>
            #include <QTimer>
            
            class Clock : public QObject
            {
                Q_OBJECT
                Q_PROPERTY(QString time MEMBER m_timeStr READ time WRITE setTime NOTIFY timeChanged)
            public:
                explicit Clock(QObject *parent = nullptr);
                void setTime(const QString &time);
                QString time() {
                    return m_timeStr;
                }
            
            signals:
                void timeChanged(QString time);
            public slots:
                void update();
            private:
                QString m_timeStr;
                QTime m_time;
                QTimer m_timer;
            };
            
            #endif // CLOCK_H
            

            clock.cpp

            #include <QDebug>
            
            #include "clock.h"
            
            Clock::Clock(QObject *parent)
                : QObject{parent}
            {
                connect(&m_timer, &QTimer::timeout, this, &Clock::update);
                m_timer.start(1000);
                m_timeStr = "uninitialized";
            }
            void Clock::setTime(const QString &time) {
                if (time != m_timeStr) {
                    m_timeStr = time;
                    emit timeChanged(m_timeStr);
                }
            }
            void Clock::update() {
                QString l_timeStr;
            
                m_time = QTime::currentTime();
                l_timeStr = m_time.toString("hh:mm:ss");
                if (l_timeStr != m_timeStr) {
                    m_timeStr = l_timeStr;
                    qDebug() << "m_timeStr updated to" << m_timeStr;
                }
            }
            

            and main.qml:

            import QtQml 2.2
            import QtQuick 2.15
            import QtQuick.Controls 2.15
            import QtQuick.Controls.Material 2.15
            import QtQuick.Layouts 1.15
            
             ApplicationWindow {
            
                visible: true
                width: 800
                height: 480
            
               Mytoolbar { // ignore this
                    id: toolBar
                }
                Text {
                    anchors {
                        top: toolBar.bottom
                    }
                    text: clock.time
                }
            }
            

            Thanks...

            J.HilkJ 1 Reply Last reply
            0
            • mzimmersM mzimmers

              @Creaperdown normally my questions are easy enough for this audience that snippets reveal my error, but evidently not so in this case. So, here's the whole thing:

              main.cpp

              #include <QGuiApplication>
              #include <QQmlApplicationEngine>
              #include <QQmlContext>
              
              #include "clock.h"
              
              int main(int argc, char *argv[])
              {
                  QGuiApplication app(argc, argv);
                  QQmlApplicationEngine engine;
                  const QUrl url(u"qrc:/nga/main.qml"_qs);
                  Clock clock;
              
                  engine.rootContext()->setContextProperty("clock", &clock);
                  QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                                   &app, [url](QObject *obj, const QUrl &objUrl) {
                      if (!obj && url == objUrl)
                          QCoreApplication::exit(-1);
                  }, Qt::QueuedConnection);
              
                  engine.load(url);
              
                  return app.exec();
              }
              

              clock.h

              #ifndef CLOCK_H
              #define CLOCK_H
              
              #include <QObject>
              #include <QTime>
              #include <QTimer>
              
              class Clock : public QObject
              {
                  Q_OBJECT
                  Q_PROPERTY(QString time MEMBER m_timeStr READ time WRITE setTime NOTIFY timeChanged)
              public:
                  explicit Clock(QObject *parent = nullptr);
                  void setTime(const QString &time);
                  QString time() {
                      return m_timeStr;
                  }
              
              signals:
                  void timeChanged(QString time);
              public slots:
                  void update();
              private:
                  QString m_timeStr;
                  QTime m_time;
                  QTimer m_timer;
              };
              
              #endif // CLOCK_H
              

              clock.cpp

              #include <QDebug>
              
              #include "clock.h"
              
              Clock::Clock(QObject *parent)
                  : QObject{parent}
              {
                  connect(&m_timer, &QTimer::timeout, this, &Clock::update);
                  m_timer.start(1000);
                  m_timeStr = "uninitialized";
              }
              void Clock::setTime(const QString &time) {
                  if (time != m_timeStr) {
                      m_timeStr = time;
                      emit timeChanged(m_timeStr);
                  }
              }
              void Clock::update() {
                  QString l_timeStr;
              
                  m_time = QTime::currentTime();
                  l_timeStr = m_time.toString("hh:mm:ss");
                  if (l_timeStr != m_timeStr) {
                      m_timeStr = l_timeStr;
                      qDebug() << "m_timeStr updated to" << m_timeStr;
                  }
              }
              

              and main.qml:

              import QtQml 2.2
              import QtQuick 2.15
              import QtQuick.Controls 2.15
              import QtQuick.Controls.Material 2.15
              import QtQuick.Layouts 1.15
              
               ApplicationWindow {
              
                  visible: true
                  width: 800
                  height: 480
              
                 Mytoolbar { // ignore this
                      id: toolBar
                  }
                  Text {
                      anchors {
                          top: toolBar.bottom
                      }
                      text: clock.time
                  }
              }
              

              Thanks...

              J.HilkJ Offline
              J.HilkJ Offline
              J.Hilk
              Moderators
              wrote on last edited by
              #6

              @mzimmers said in exposing C++ attributes (missing a step):

              timeChanged

              you never call setTime, so the changed signal is never emitted -> ui never updates.

              the Cannot read property 'time' of null is because of the stack FIFO, create the Clock instance before the GuiApplication instance and that too will go away :D


              Be aware of the Qt Code of Conduct, when posting : https://forum.qt.io/topic/113070/qt-code-of-conduct


              Q: What's that?
              A: It's blue light.
              Q: What does it do?
              A: It turns blue.

              mzimmersM 1 Reply Last reply
              3
              • C Offline
                C Offline
                Creaperdown
                wrote on last edited by
                #7
                This post is deleted!
                1 Reply Last reply
                0
                • J.HilkJ J.Hilk

                  @mzimmers said in exposing C++ attributes (missing a step):

                  timeChanged

                  you never call setTime, so the changed signal is never emitted -> ui never updates.

                  the Cannot read property 'time' of null is because of the stack FIFO, create the Clock instance before the GuiApplication instance and that too will go away :D

                  mzimmersM Offline
                  mzimmersM Offline
                  mzimmers
                  wrote on last edited by
                  #8

                  @J-Hilk that was it (sometimes the most obvious omissions are the hardest to find).

                  clock.cpp:

                  Clock::Clock(QObject *parent)
                      : QObject{parent}
                  {
                      connect(&m_timer, &QTimer::timeout, this, &Clock::update);
                      m_timeStr = "00:00 AM";
                  }
                  void Clock::setTime(const QString &time) {
                      if (time != m_timeStr) {
                          m_timeStr = time;
                          emit timeChanged(m_timeStr);
                      }
                  }
                  void Clock::update() {
                      QString l_timeStr;
                  
                      m_time = QTime::currentTime();
                      l_timeStr = m_time.toString("hh:mm AP");
                      setTime(l_timeStr);
                  }
                  

                  I had to move the m_timer.start() out of the c'tor, because I got this runtime error:

                  QObject::startTimer: Timers can only be used with threads started with QThread
                  

                  So I created a start() function and I call it from main.cpp. Works like a charm now. Thanks!

                  1 Reply Last reply
                  1

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved