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. Cannot call C++ method when object is created in another QML module
Forum Updated to NodeBB v4.3 + New Features

Cannot call C++ method when object is created in another QML module

Scheduled Pinned Locked Moved Solved QML and Qt Quick
6 Posts 3 Posters 532 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.
  • K Offline
    K Offline
    Kobid
    wrote on 29 Jun 2024, 19:53 last edited by Kobid
    #1

    Hi,
    I have this QObject class:

    class CHPCppInterface : public QObject
    {
        Q_OBJECT
        QML_ELEMENT
    public:
        explicit CHPCppInterface(QObject *parent = nullptr);
    
        //int tescik; const;
        Q_INVOKABLE bool saveAccessToken(QString token) const;
        Q_INVOKABLE QString getAccessgetToken() const;
    signals:
        void tescikUpdated();
    };
    

    When I declare this class in main Window then it works:

    Window {
        id: mainWindow
        width: 640
        height: 480
        CHPCppInterface {
            id: test
        }
       ........
        RoundButton {
            onClicked: {            
                test.saveAccessToken("test");
            }
        }
    

    I like to have main data module where I have common and often used thinks. So I made DataModule.qml and moved this class:

    import QtQuick
    
    Item {
        id: dataModule
        CHPCppInterface {
            id: test
        }
    }
    

    And now when I try to call it:

    RoundButton {
           onClicked: {            
               DataModule.test.saveAccessToken("test");
           }
       }
    

    ... I'm getting TypeError: Cannot call method 'saveAccessToken' of undefined
    How to solve that?

    Another annoying things:

    1. Even in working scenario, Qt Creator is showing red circle on that class with info "unknown component". How to fix that?
    2. Why in module I have to declare this class in Item{} ? Why I can't declare it directly like this:
    import QtQuick
    
    CHPCppInterface {
         id: test
    }
    

    I'm getting syntax error

    R 1 Reply Last reply 30 Jun 2024, 10:13
    0
    • K Kobid
      29 Jun 2024, 19:53

      Hi,
      I have this QObject class:

      class CHPCppInterface : public QObject
      {
          Q_OBJECT
          QML_ELEMENT
      public:
          explicit CHPCppInterface(QObject *parent = nullptr);
      
          //int tescik; const;
          Q_INVOKABLE bool saveAccessToken(QString token) const;
          Q_INVOKABLE QString getAccessgetToken() const;
      signals:
          void tescikUpdated();
      };
      

      When I declare this class in main Window then it works:

      Window {
          id: mainWindow
          width: 640
          height: 480
          CHPCppInterface {
              id: test
          }
         ........
          RoundButton {
              onClicked: {            
                  test.saveAccessToken("test");
              }
          }
      

      I like to have main data module where I have common and often used thinks. So I made DataModule.qml and moved this class:

      import QtQuick
      
      Item {
          id: dataModule
          CHPCppInterface {
              id: test
          }
      }
      

      And now when I try to call it:

      RoundButton {
             onClicked: {            
                 DataModule.test.saveAccessToken("test");
             }
         }
      

      ... I'm getting TypeError: Cannot call method 'saveAccessToken' of undefined
      How to solve that?

      Another annoying things:

      1. Even in working scenario, Qt Creator is showing red circle on that class with info "unknown component". How to fix that?
      2. Why in module I have to declare this class in Item{} ? Why I can't declare it directly like this:
      import QtQuick
      
      CHPCppInterface {
           id: test
      }
      

      I'm getting syntax error

      R Offline
      R Offline
      Ronel_qtmaster
      wrote on 30 Jun 2024, 10:13 last edited by Ronel_qtmaster
      #3

      @Kobid just do:
      public slots:
      bool saveAccessToken(QString token) const;
      QString getAccessgetToken() const;

      in the main.cpp add your object to the root context

      1 Reply Last reply
      1
      • E Offline
        E Offline
        ekkescorner
        Qt Champions 2016
        wrote on 30 Jun 2024, 07:06 last edited by
        #2

        have you tried to use QML_SINGLETON and to create the class from C++ ?
        https://t1p.de/ekkeQML_SINGLETON

        or if you want to control the lifecycle from QML use QT_QML_SINGLETON_TYPE
        https://doc.qt.io/qt-6/qml-singleton.html

        ekke ... Qt Champion 2016 | 2024 ... mobile business apps
        5.15 --> 6.9 https://t1p.de/ekkeChecklist
        QMake --> CMake https://t1p.de/ekkeCMakeMobileApps

        1 Reply Last reply
        1
        • K Kobid
          29 Jun 2024, 19:53

          Hi,
          I have this QObject class:

          class CHPCppInterface : public QObject
          {
              Q_OBJECT
              QML_ELEMENT
          public:
              explicit CHPCppInterface(QObject *parent = nullptr);
          
              //int tescik; const;
              Q_INVOKABLE bool saveAccessToken(QString token) const;
              Q_INVOKABLE QString getAccessgetToken() const;
          signals:
              void tescikUpdated();
          };
          

          When I declare this class in main Window then it works:

          Window {
              id: mainWindow
              width: 640
              height: 480
              CHPCppInterface {
                  id: test
              }
             ........
              RoundButton {
                  onClicked: {            
                      test.saveAccessToken("test");
                  }
              }
          

          I like to have main data module where I have common and often used thinks. So I made DataModule.qml and moved this class:

          import QtQuick
          
          Item {
              id: dataModule
              CHPCppInterface {
                  id: test
              }
          }
          

          And now when I try to call it:

          RoundButton {
                 onClicked: {            
                     DataModule.test.saveAccessToken("test");
                 }
             }
          

          ... I'm getting TypeError: Cannot call method 'saveAccessToken' of undefined
          How to solve that?

          Another annoying things:

          1. Even in working scenario, Qt Creator is showing red circle on that class with info "unknown component". How to fix that?
          2. Why in module I have to declare this class in Item{} ? Why I can't declare it directly like this:
          import QtQuick
          
          CHPCppInterface {
               id: test
          }
          

          I'm getting syntax error

          R Offline
          R Offline
          Ronel_qtmaster
          wrote on 30 Jun 2024, 10:13 last edited by Ronel_qtmaster
          #3

          @Kobid just do:
          public slots:
          bool saveAccessToken(QString token) const;
          QString getAccessgetToken() const;

          in the main.cpp add your object to the root context

          1 Reply Last reply
          1
          • K Offline
            K Offline
            Kobid
            wrote on 1 Jul 2024, 19:26 last edited by
            #4

            I knew both of your solutions but I thought that it is possible to fix that with QML_ELEMENT
            @ekkescorner QML_SINGLETON was to much effort for me since I'm not experienced with C++ macros described on your site. Even Qt doc about QML_ELEMENT is too much complicated with my experience and I think whole idea is form over substance for my simple object
            @Ronel_qtmaster I decided for this solution, works fine so far and "uknown component" marker disappeared from my QML file.

            Thank you both guys

            1 Reply Last reply
            0
            • K Kobid has marked this topic as solved on 1 Jul 2024, 19:27
            • K Kobid has marked this topic as solved on 1 Jul 2024, 19:27
            • K Offline
              K Offline
              Kobid
              wrote on 2 Jul 2024, 19:31 last edited by Kobid 7 Feb 2024, 19:32
              #5

              BTW: My problem was probably not understaing QML objects life cycle and creation. Creating DataModule.qml file and declaring:

              import QtQuick
              
              Item {
                  id: dataModule
                  CHPCppInterface {
                      id: test
                  }
              }
              

              ... doesn't mean that DataModule{} object instance is created. Hence my problem with access to this object. I guess that I must make DataModule as global component, not only as subcomponent of main window

              1 Reply Last reply
              0
              • K Offline
                K Offline
                Kobid
                wrote on 3 Jul 2024, 20:34 last edited by Kobid 7 Mar 2024, 20:57
                #6

                Ok. I finally manged singletons in QML and C++ in Qt 6.7 which could be a bit frustrating for newbies like me especialy when you are reading official Qt documentation which is reffering to another doc and another doc and you finally end with 25 pages opened in your browser. I'm writting it here in case if someone got there via google search or something. Important thing about singletons that you don't call them by id property (id doesn't matter if you set it) but class name and it must start from upper letter
                QML singleton global object
                So. If you want to have global QML object like DataModule.qml which is created just like that and have access to it from anywhere:

                1. Create your DataModule.qml file (first upper character is important). For example:
                pragma Singleton
                import QtQuick
                
                QtObject {
                    property string someString: "foo bar"
                }
                

                Notice: pragma Singleton, it is important
                2. In CMakeLists.txt add this:

                set_source_files_properties(DataModule.qml PROPERTIES QT_QML_SINGLETON_TYPE TRUE)
                qt_add_qml_module(appMyApp
                    URI MyApp
                    VERSION 1.0
                    QML_FILES
                        QML_FILES DataModule.qml
                       .......
                )
                

                Notice: Make sure that set_source_files_properties is called before qt_add_qml_module. Also, not sure if it matter, but add QML_FILES DataModule.qml as first or at least before your Main.qml
                3. Now you can call your someString property from anywhere without declaring class in ApplicationWindow {}. For example, just reffer to class DataModule directly:

                ApplicationWindow {
                    id: mainWindow
                    width: 640
                    height: 480
                    visible: true
                    Component.onCompleted: {
                        console.log(DataModule.someString);
                    }
                }
                

                QObject/C++ singleton global object
                If you want to have global QObject/C++ access from anywhere in your QML without rootContext()->setContextProperty

                1. Create your QObject class and save it and add to project for example
                class MyCppInterface : public QObject
                {
                    Q_OBJECT
                    QML_ELEMENT      <--- important
                    QML_SINGLETON    <--- important
                public:
                    explicit MyCppInterface(QObject *parent = nullptr);
                
                    Q_INVOKABLE void log(QString s) const;
                signals:
                };
                

                Notice: You have to add both, QML_ELEMENT and QML_SINGLETON
                2. Now, you DON'T NEED to declare class in ApplicationWindow to have acces to it by id like you normally did in simple QML_ELEMENT:

                ApplicationWindow {
                    id: mainWindow
                    width: 640
                    height: 480
                    visible: true
                     MyCppInterface {
                         id: myCppIntf
                     }
                    Component.onCompleted: {
                        myCppIntf.log('something');
                    }
                }
                

                Instead. Just call it directly by class (first character is upper!)

                ApplicationWindow {
                    id: mainWindow
                    width: 640
                    height: 480
                    visible: true
                    Component.onCompleted: {
                        MyCppInterface.log('something');
                    }
                }
                

                Mixing both together
                Now. When we know how bot things work, we can make it mixed and have put together in one place. DataModule.qml:

                pragma Singleton
                import QtQuick
                
                QtObject {
                    property string someString: "foo bar"
                    function log(l) {
                        MyCppInterface.log(l);
                    }
                }
                

                And call it from anywhere:

                ApplicationWindow {
                    id: mainWindow
                    width: 640
                    height: 480
                    visible: true
                    Component.onCompleted: {
                        DataModule.log("test log");
                    }
                }
                

                Ideally, I would like to hide global access to MyCppInterface and have it only via DataModule. This could by probably possible via Qt.createQmlObject("MyCppInterface") or Qt.createComponent("MyCppInterface") or something like that inside DataModule {} but I'm very happy what I obtained so far and I like it :)

                1 Reply Last reply
                1

                1/6

                29 Jun 2024, 19:53

                • Login

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