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. QQmlContext::setContextProperty and deleting objects
Forum Updated to NodeBB v4.3 + New Features

QQmlContext::setContextProperty and deleting objects

Scheduled Pinned Locked Moved Solved QML and Qt Quick
4 Posts 2 Posters 2.0k Views 1 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.
  • fcarneyF Offline
    fcarneyF Offline
    fcarney
    wrote on last edited by fcarney
    #1

    We have a bunch of code that follows this pattern:

    • create QObject based object
    • setContextProperty in root context for this object: root->setContextProperty("somename", object)
    • open QML item/dialog that uses this context property
    • delete object then item/dialog closes

    I cannot find anything in the docs for setContextProperty that says this is okay or not okay. I do know that accessing a context property that has been deleted is an immediate seg fault.

    Why am I asking questions about this? Because I have an object that is being set to a context property that is somehow causing unrelated code to segfault. When I look at the trace it looks like all internal QML and jvm interpreter calls in the call stack. I cannot trace it back to any C++ code. If I don't delete the object (creates a memory leak as is) then it no longer crashes. So I suspect that deleting an object that has been set as a context property of the root context somehow corrupts the lookup for context properties or other things in general.

    I plan on creating a separate project to test this and see if I can recreate. One thing that complicates this is that other objects created in exactly the same way do NOT seem to be causing this issue. So I am not completely sold on the idea that setting a context property on the root context causes this. However, I cannot find any other causes.

    C++ is a perfectly valid school of magic.

    1 Reply Last reply
    0
    • kenneth.fernandezK Offline
      kenneth.fernandezK Offline
      kenneth.fernandez
      wrote on last edited by
      #2

      Hi fcarney,

      My first guess about this problem is the way you are using the contextProperty or the assignment. As I do not have code is kind of hard to help you deeply, but some recommendations to take advantage of the context property without leaking or seg faults:

      1 - Use objects that are going to be there from the beginning to the end. For example: Controllers or Utility classes.

      2 - If you want to use properties to expose objects that are not permanent, the first thing to have in mind is a QMLContextProperty is not the way to communicate QML and C++ or Python. It Is the way you are able to use an object as reference on QML for commodity, if you need data for dialog/items is better use models. (Sorry if this is obvious, but is only the good practice.)

      3 - If there is no option, please use a pointer instead of a concrete object:

      pQMLContext->setContextProperty("contextProperty", *pObject);
      

      pObject will point to the object you need to access at that moment, if you want other dialog only delete the memory and move the pointer to point to your other object and if you have no object please use a pattern of null object, using an interface as reference. Use the architecture in our favor is the best option in this scenario.

      Sorry if this was not helpful, normally the memory leaks and segmentation fault errors are complex.

      Regards,
      Kenneth

      --
      Lic-Ing. Kenneth Fernández Esquivel
      Embedded Software Engineer
      RidgeRun Engineering Ltd.
      www.ridgerun.com
      Email: kenneth.fernandez@ridgerun.com
       San Juan, La Unión. Cartago. Costa Rica

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

        Okay, this is a non-issue. The culprit was someone had used this property object in another inappropriate place. It will not crash if set to null (does not exist). So I may implement a strategy to set context properties to nullptr before delete so we can see QML errors saying the object is undefined rather than seg faulting.

        C++ is a perfectly valid school of magic.

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

          For future reference my test setup to try and break context properties.

          main.cpp:

          #include <QGuiApplication>
          #include <QQmlApplicationEngine>
          #include <QQmlContext>
          #include <QHash>
          #include <QRandomGenerator>
          
          #include <QObject>
          #include <QDebug>
          
          class ContextSetter : public QObject
          {
              Q_OBJECT
          
              // test multiple context properties in one object
              Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
              QString m_name;
          
          public:
              ContextSetter(QObject* parent=nullptr)
                  : QObject(parent)
              {}
              QString name() const
              {
                  return m_name;
              }
          public slots:
              void setName(QString name)
              {
                  if (m_name == name)
                      return;
          
                  m_name = name;
                  emit nameChanged(m_name);
              }
          signals:
              void nameChanged(QString name);
          };
          
          class ContextHolder : public QObject
          {
              Q_OBJECT
          public:
              ContextHolder(QObject* parent=nullptr)
                  : QObject(parent)
                  , m_rootcontext(nullptr)
              {
                  m_seededgen = QRandomGenerator::securelySeeded();
              }
          
              void setrootcontext(QQmlContext* context){
                  m_rootcontext = context;
              }
          
              QQmlContext* rootcontext(){
                  return m_rootcontext;
              }
          
          public slots:
              QString createContextPropertyObject(QString name = QString()){
                  if(m_rootcontext){
                      char set = std::round(m_seededgen.generateDouble()) ? 'a' : 'A';
                      char head = set + std::floor(m_seededgen.generateDouble() * 26); // a to Z
                      int body = std::round(m_seededgen.generateDouble() * 100000);
          
                      QString propname;
                      if(name.length())
                          propname = name;
                      else
                          propname = QString("%1%2").arg(head).arg(body);
          
                      auto obj = new ContextSetter(this);
                      obj->setName(propname);
                      m_propobjects[propname] = obj;
          
                      m_rootcontext->setContextProperty(propname, obj);
          
                      qDebug() << "Created:" << propname;
          
                      return propname;
                  }
          
                  return QString();
              }
              void deleteContextPropertyObject(QString propname){
                  if(m_propobjects.contains(propname)){
                      auto obj = m_propobjects[propname];
                      m_propobjects.remove(propname);
                      obj->deleteLater();
          
                      m_rootcontext->setContextProperty(propname, nullptr);
          
                      qDebug() << "Deleted:" << propname;
                  }
              }
          
          private:
              QQmlContext* m_rootcontext;
          
              QHash<QString, QObject*> m_propobjects;
          
              QRandomGenerator m_seededgen;
          };
          
          int main(int argc, char *argv[])
          {
              QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
          
              QGuiApplication app(argc, argv);
          
              QQmlApplicationEngine engine;
          
              auto rootcontext = engine.rootContext();
          
              ContextHolder cholder;
              cholder.setrootcontext(rootcontext);
              rootcontext->setContextProperty("contextholder", &cholder);
          
              const QUrl url(QStringLiteral("qrc:/main.qml"));
              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();
          }
          
          #include "main.moc"
          

          main.qml:

          import QtQuick 2.12
          import QtQuick.Controls 2.12
          import QtQuick.Window 2.12
          
          Window {
              id: mainwin
          
              width: 640
              height: 480
              visible: true
              title: qsTr("setContextProperty object deletion testing")
          
              property var proplist: []
          
              Timer {
                  id: createtimer
          
                  interval: 1000
                  repeat: true
                  running: true
          
                  onTriggered: {
                      var propname = contextholder.createContextPropertyObject()
                      proplist.push(propname)
                  }
              }
          
              Timer {
                  id: deletetimer
          
                  interval: 1000
                  repeat: true
                  running: true
          
                  onTriggered: {
                      var propname = proplist.pop()
                      contextholder.deleteContextPropertyObject(propname)
                  }
              }
          
              Timer {
                  id: cycledialog
          
                  interval: 500
                  repeat: true
                  running: true
          
                  property string propname: "KillMe"
          
                  Component.onCompleted: {
                      contextholder.createContextPropertyObject(propname)
                      contextholder.deleteContextPropertyObject(propname)
                  }
          
                  onTriggered: {
                      contextholder.createContextPropertyObject("Atestobject")
                      contextholder.createContextPropertyObject("atestobject")
                      contextholder.createContextPropertyObject("Ztestobject")
                      contextholder.createContextPropertyObject("ztestobject")
                      contextholder.createContextPropertyObject("Qtestobject")
                      contextholder.createContextPropertyObject("qtestobject")
          
                      testdialogloader.active = true
                  }
              }
          
              Loader {
                  id: testdialogloader
          
                  active: false
                  sourceComponent: testdialog
              }
          
              Component {
                  id:testdialog
          
                  Rectangle {
                      width: 200
                      height: 200
          
                      color: "red"
          
                      Column {
                          Text {
                              text: Atestobject.name
                          }
                          Text {
                              text: atestobject.name
                          }
                          Text {
                              text: Ztestobject.name
                          }
                          Text {
                              text: atestobject.name
                          }
                          Text {
                              text: Qtestobject.name
                          }
                          Text {
                              text: qtestobject.name
                          }
                          Text {
                              text: "propname: " + KillMe
                          }
                      }
          
                      Timer {
                          id: disabletimer
          
                          interval: 250
                          repeat: false
                          running: true
          
                          onTriggered: {
                              testdialogloader.active = false
          
                              contextholder.deleteContextPropertyObject("Atestobject")
                              contextholder.deleteContextPropertyObject("atestobject")
                              contextholder.deleteContextPropertyObject("Ztestobject")
                              contextholder.deleteContextPropertyObject("ztestobject")
                              contextholder.deleteContextPropertyObject("Qtestobject")
                              contextholder.deleteContextPropertyObject("qtestobject")
                          }
                      }
                  }
              }
          }
          

          Notice if you comment out:

          m_rootcontext->setContextProperty(propname, nullptr);
          

          Then it will crash on accessing the KillMe context property. So setting a previously used context property to nullptr is a way to protect yourself from seg faults.

          C++ is a perfectly valid school of magic.

          1 Reply Last reply
          0

          • Login

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