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. How to add a property to a QML object after creation but before binding evaluation?
Forum Updated to NodeBB v4.3 + New Features

How to add a property to a QML object after creation but before binding evaluation?

Scheduled Pinned Locked Moved Unsolved QML and Qt Quick
8 Posts 2 Posters 1.9k Views
  • 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.
  • R Offline
    R Offline
    rweickelt
    wrote on last edited by rweickelt
    #1

    Let's assume the following QML document:

    Project {
        Testcase {
            name: "test1"
            Exports {
                property string result
            }
    
            run: {
                result = "yes"
            }
        }
    
        Testcase {
            name: "test2"
            Depends { name: "test1" }
            property someProperty: test1.result
    
            /* ... */
    }
    

    I am trying to attach the Exports item from the first Testcase object to the second Testcase object under the name test1 so that the binding of someProperty becomes valid. Note: I am explicitly NOT using id:test1 because in my DSL the Testcase objects could be defined in separate documents. The idea is a bit similar to Product items in Qbs, but the meaning and implementation is very different. And again: This is not a normal QML application. This document is analysed and heavily manipulated before it is executed by QQmlEngine.

    I thought I'd have the possibility to do add properties to objects after Component::beginCreate(), but that doesn't work:

    • Calling QObject::setProperty("test1", someObject) on test2 has no effect, even before
      QQmlComponent::completeCreate().
    • Using QQmlProperty on test2 has no effect, probably because the property
      doesn't physically exist.
    • Setting test1 as context property of qmlContext(test2) fails because the context is private. Setting it on the parent context of test2 would bring it into the root context which gives it wrong scope. I want test1 to point to my object only within the scope of test2.

    So I wonder:

    1. Is there any possibility to add a property to an object after QQmlComponent::beginCreate() and before bindings are evaluated in QQmlComponent::completeCreate() ?

    2. Why does every QML object have a private context that is not writable? I've asked this question already here, but didn't get any response.

    I am looking forward for hints and answers.
    Richard

    DiracsbracketD 1 Reply Last reply
    0
    • R rweickelt

      Let's assume the following QML document:

      Project {
          Testcase {
              name: "test1"
              Exports {
                  property string result
              }
      
              run: {
                  result = "yes"
              }
          }
      
          Testcase {
              name: "test2"
              Depends { name: "test1" }
              property someProperty: test1.result
      
              /* ... */
      }
      

      I am trying to attach the Exports item from the first Testcase object to the second Testcase object under the name test1 so that the binding of someProperty becomes valid. Note: I am explicitly NOT using id:test1 because in my DSL the Testcase objects could be defined in separate documents. The idea is a bit similar to Product items in Qbs, but the meaning and implementation is very different. And again: This is not a normal QML application. This document is analysed and heavily manipulated before it is executed by QQmlEngine.

      I thought I'd have the possibility to do add properties to objects after Component::beginCreate(), but that doesn't work:

      • Calling QObject::setProperty("test1", someObject) on test2 has no effect, even before
        QQmlComponent::completeCreate().
      • Using QQmlProperty on test2 has no effect, probably because the property
        doesn't physically exist.
      • Setting test1 as context property of qmlContext(test2) fails because the context is private. Setting it on the parent context of test2 would bring it into the root context which gives it wrong scope. I want test1 to point to my object only within the scope of test2.

      So I wonder:

      1. Is there any possibility to add a property to an object after QQmlComponent::beginCreate() and before bindings are evaluated in QQmlComponent::completeCreate() ?

      2. Why does every QML object have a private context that is not writable? I've asked this question already here, but didn't get any response.

      I am looking forward for hints and answers.
      Richard

      DiracsbracketD Offline
      DiracsbracketD Offline
      Diracsbracket
      wrote on last edited by Diracsbracket
      #2

      @rweickelt said in How to add a property to a QML object after creation but before binding evaluation?:

      test1.result

      Where is test1 defined? If you think it is this:

      Testcase {
          name: "test1"
          ...
      }
      

      then you are mistaken. If you declare it like:

      Testcase {
          id: test1
          name: "test1"
          property string someStringProperty
          ...
      }
      

      Then you can use

      test1.someStringProperty
      
      1 Reply Last reply
      0
      • R Offline
        R Offline
        rweickelt
        wrote on last edited by
        #3

        Thank you for your reply. I think my question was not clear enough. This is not a normal QML application. Instead, the document is analysed and manipulated before the QML engine executes it.

        The reason why I cannot use id:test1 is that Testcase objects might be defined in separate documents. My backend analyses all documents and based on the Depends items and the Exports items, it creates relationships.

        So if any test case contains an item Depends { name: "bla" } AND the test case "bla" contains an Exports { } item, then this Exports item of "bla" is attached to the dependent test case using the name "bla".

        Does that make it clear?

        DiracsbracketD 1 Reply Last reply
        0
        • R rweickelt

          Thank you for your reply. I think my question was not clear enough. This is not a normal QML application. Instead, the document is analysed and manipulated before the QML engine executes it.

          The reason why I cannot use id:test1 is that Testcase objects might be defined in separate documents. My backend analyses all documents and based on the Depends items and the Exports items, it creates relationships.

          So if any test case contains an item Depends { name: "bla" } AND the test case "bla" contains an Exports { } item, then this Exports item of "bla" is attached to the dependent test case using the name "bla".

          Does that make it clear?

          DiracsbracketD Offline
          DiracsbracketD Offline
          Diracsbracket
          wrote on last edited by
          #4

          @rweickelt
          OK... my bad. I never used TestCases and the Qbs framework, hence my confusion. Yet another thing for me to learn...

          1 Reply Last reply
          0
          • R Offline
            R Offline
            rweickelt
            wrote on last edited by
            #5

            Never mind. It's still QML after all and in difference to Qbs I am using a vanilla QQmlEngine. I am just analyzing and manipulating the object tree between QQmlComponent::beginCreate() and QQmlComponent::completeCreate().

            Still looking for a solution to my problem.

            1 Reply Last reply
            0
            • R Offline
              R Offline
              rweickelt
              wrote on last edited by
              #6

              Here is a minimal almost working example that shows the same issue:

              file.qml:

              import QtQml 2.0
              
              QtObject {
                  property var test1: QtObject {
                      objectName: "child1"
                      property string color: environment.color
                  }
              
                  property var test2: QtObject {
                      objectName: "child2"
                      property string color: environment.color
                  }
              }
              

              main.cpp:

              #include <QtCore>
              #include <QtQml>
              
              int main(int argc, char* argv[]) {
                  QCoreApplication app(argc, argv);
                  QQmlEngine engine;
                  QQmlComponent component(&engine,QUrl::fromLocalFile("file.qml"));
                  QObject* object = component.beginCreate(engine.rootContext());
                  QObject* child1 = object->findChild<QObject*>("child1", Qt::FindChildrenRecursively);
                  QObject* child2 = object->findChild<QObject*>("child2", Qt::FindChildrenRecursively);
              
                  QQmlPropertyMap env1;
                  QQmlPropertyMap env2;
                  env1["color"] = "green";
                  env2["color"] = "blue";
              
                  // Now I want to make object1 available for child1
                  // and object2 available for child2.
                  // But I can't do this:
                  //    qmlContext(test1)->setContextProperty("environment", env1);
                  //    qmlContext(test2)->setContextProperty("environment", env2);
              
                  component.completeCreate();
              
                  if (!component.errors().isEmpty()) {
                      qDebug() << component.errorString();
                  }
              
                  Q_ASSERT(child1->property("color") == QString("green"));
                  Q_ASSERT(child2->property("color") == QString("blue"));
              
                  qDebug() << "It works";
              }
              

              That might be more similar to a normal QML application.

              1 Reply Last reply
              0
              • DiracsbracketD Offline
                DiracsbracketD Offline
                Diracsbracket
                wrote on last edited by Diracsbracket
                #7

                What about something like:

                #include <QtCore>
                #include <QtQml>
                #include <QQmlContext>
                #include <QVariant>
                #include <QStringList>
                
                int main(int argc, char* argv[]) {
                    QCoreApplication app(argc, argv);
                    QQmlEngine engine;
                    QQmlComponent component(&engine,QUrl::fromLocalFile("G:/UMAGI_Source/Qt/test6/file.qml"));
                    QObject* object = component.beginCreate(engine.rootContext());
                    QObject* child1 = object->findChild<QObject*>("child1", Qt::FindChildrenRecursively);
                    QObject* child2 = object->findChild<QObject*>("child2", Qt::FindChildrenRecursively);
                
                    QQmlPropertyMap env;
                    env["colors"] = QVariant(QStringList({"green", "blue"}));
                
                    QQmlContext *ctxt = engine.rootContext();
                    ctxt->setContextProperty("environment", &env);
                
                    component.completeCreate();
                
                    if (!component.errors().isEmpty()) {
                        qDebug() << component.errorString();
                    }
                
                    Q_ASSERT(child1->property("color") == QString("green"));
                    Q_ASSERT(child2->property("color") == QString("blue"));
                
                    qDebug() << "It works";
                }
                

                and

                import QtQml 2.11
                
                QtObject {
                    property var test1: QtObject {
                        objectName: "child1"
                        property string color: environment.colors[0]
                    }
                
                    property var test2: QtObject {
                        objectName: "child2"
                        property string color: environment.colors[1]
                    }
                }
                
                

                This gives:

                04:08:38: Starting G:\Source\Qt\build-test6-Desktop_Qt_5_11_0_MSVC2017_64bit3-Debug\debug\test6...
                QML debugging is enabled. Only use this in a safe environment.
                It works

                1 Reply Last reply
                1
                • R Offline
                  R Offline
                  rweickelt
                  wrote on last edited by rweickelt
                  #8

                  Indeed. Thank you for taking the time. But the person who writes the documents is not supposed to know the index of the correct element. The env objects in the above example are not randomly assigned.

                  What I now ended up with, is a QQmlPropertyMap property in my Testcase item under the name "dependencies". Speaking in the terms of my original post: For each Depends item, I am now inserting a QVariantMap that represents the Exports item. That gives me unnecessary verbose QML code, but at least, it solves the problem:

                  Project {
                      Testcase {
                          name: "test1"
                          Exports {
                              property string result
                          }
                  
                          run: {
                              result = "yes"
                          }
                      }
                  
                      Testcase {
                          name: "test2"
                          Depends { name: "test1" }
                          property someProperty: dependencies.test1.result
                  
                          /* ... */
                  }
                  

                  I might also experiment with sub-classing Testcase from QQmlPropertyMap so that I can skip the intermediate dependencies.

                  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