qtdestructorqgraphicsviewqglwidget

how to release QGLWidget from QGraphicsView


I see many Qt examples about using OpenGL for graphics view. Most of samples look like this:

MyGraphicsView::MyGraphicsView(......)
{
gl = new QGLWidget();
this->setViewport(gl);
....

where QGLWidget* gl is member of MyGraphicsView.

My question is how after that should i delete "gl". Will it be deleted automatically with my class inherited from QGraphicsView? Manual delete for "gl" in MyGraphicsView descructor causes crash.

In Qt really difficult to understand which object will be autodeleted and which not.


Solution

  • In Qt really difficult to understand which object will be autodeleted and which not.

    Not at all. You only need to know two things:

    1. Semantics of C++'s scopes. But you already know these - hopefully.

    2. That QObject::~QObject() deletes its surviving children:

      QObject::~QObject() {
        ...
        for (auto child : children())
          delete child;
        ...
      }
      

      Obviously, if a child has been destroyed prior to entering ~QObject(), then it won't be in the parent's children() list, and won't be doubly-deleted. This somehow seems to throw most people off, but it shouldn't if one doesn't think of Qt as some magic. Qt does what C++ allows it to do. Nothing more, nothing less.

    Thus, an object will be destructed for you if either is true:

    1. You hold it by value in a scope, e.g.

      class MyGraphicsView : public QGraphicsView {
         QOpenGLWidget m_gl;
      public:
         MyGraphicsView(QWidget * parent = nullptr) : QGraphicsView{parent} {
            setViewport(&m_gl); // sets m_gl's parent
            Q_ASSERT(m_gl.parent());
         }
      };
      

      The m_gl member will be destructed after the body of ~MyGraphicsView has returned. Here's the order of destruction:

      this->~MyGraphicsView()
      m_gl.~QOpenGlWidget()  // the viewport widget ceases to exist here
      this->~QGraphicsView()
      this->~QAbstractScrollArea()
      this->~QFrame()
      this->~QWidget()
      this->~QObject()  // has no children to delete
      
    2. It still exists and has a parent by the time the parent's ~QObject() destructor executed. Recall that all widgets are QObjects.

      In the example above, the m_gl object will be destroyed - and thus cease to exist - way before MyGraphicsView::~QObject gets to run, so there's no possibility of double-destruction.

      But you could also have the prematurely-pessimized held-by-pointer approach:

      // Don't code like this. It's silly.
      class MyGraphicsView : public QGraphicsView {
         QOpenGLWidget * m_gl;
      public:
         MyGraphicsView(QWidget * parent = nullptr) :
            QGraphicsView{parent},
            m_gl{new QOpenGLWidget}
         {
            setViewport(m_gl); // sets m_gl's parent
            Q_ASSERT(m_gl->parent());
         }
         ~MyGraphicsView() {
            Q_ASSERT(m_gl->parent()); // make sure ~QObject will delete the child
         }
      };
      

      Here's the order of destruction:

      this->~MyGraphicsView()
      m_gl.~pointer       // a trivial destructor of a pointer value
      this->~QGraphicsView()
      this->~QAbstractScrollArea()
      this->~QFrame()
      this->~QWidget()
      this->~QObject()
        for (auto child : children())
          delete child
            ~QOpenGlWidget()  // m_gl is long gone at this point!
      

      Since the destruction of m_gl member is trivial and doesn't do anything with what the pointer itself points to, the QOpenGlWidget instance persists until the QObject destructor runs, at which point it iterates its child list and deletes every child - thus deleting the QOpenGlWidget instance.

    Manual delete for "gl" in MyGraphicsView descructor causes crash.

    No, it doesn't, unless you're not telling us something. The below works fine in both Qt 4 and Qt 5. The manual deletion is harmless albeit completely unnecessary:

    // https://github.com/KubaO/stackoverflown/tree/master/questions/opengl-viewport-val-39750134
    #include <QtGui>
    #include <QtOpenGL>
    #if QT_VERSION >= QT_VERSION_CHECK(5,0,0)
    #include <QtWidgets>
    #else
    using QOpenGLWidget = QGLWidget;
    #endif
    
    // Don't code like this. It's silly.
    class MyGraphicsView : public QGraphicsView {
       QOpenGLWidget * m_gl = new QOpenGLWidget;
    public:
       MyGraphicsView(QWidget * parent = nullptr) : QGraphicsView{parent}
       {
          setViewport(m_gl); // sets m_gl's parent
          Q_ASSERT(m_gl->parent());
       }
       ~MyGraphicsView() {
          delete m_gl; // completely unnecessary
       }
    };
    
    int main(int argc, char ** argv) {
       QApplication app{argc, argv};
       MyGraphicsView view;
       QGraphicsScene scene;
       scene.addText("Hello World");
       view.setScene(&scene);
       view.show();
       return app.exec();
    }