如何从 QGraphicsView 释放 QGLWidget

how to release QGLWidget from QGraphicsView

我看到很多关于使用 OpenGL 进行图形视图的 Qt 示例。 大多数示例如下所示:

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

其中 QGLWidget* gl 是 MyGraphicsView 的成员。

我的问题是我应该如何删除 "gl"。 它会随着我从 QGraphicsView 继承的 class 自动删除吗? 在 MyGraphicsView descructor 中手动删除 "gl" 会导致崩溃。

在 Qt 中真的很难理解哪些对象会被自动删除,哪些不会。

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

完全没有。你只需要知道两件事:

  1. C++ 作用域的语义。但您已经知道这些 - 希望如此。

  2. QObject::~QObject()删除其幸存的children:

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

    显然,如果 child 在 进入 ~QObject() 之前被销毁 ,那么它不会出现在 parent' s children() 列表,不会是 doubly-deleted。这在某种程度上似乎让大多数人望而却步,但如果有人不认为 Qt 是一种魔法,它不应该。 Qt 做 C++ 允许它做的事情。仅此而已。

因此,如果任一条件为真,object 将为您销毁:

  1. 你在范围内按值保存它,例如

    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());
       }
    };
    

    m_gl 成员 ~MyGraphicsView 的主体返回后被销毁。以下是销毁顺序:

    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. 它仍然存在并且在 parent 的 ~QObject() 析构函数执行时有一个 parent。回想一下,所有小部件都是 QObjects.

    在上面的示例中,m_gl object 将在 MyGraphicsView::~QObject 到达 运行 之前被销毁并因此不复存在,因此不可能double-destruction.

    但您也可以使用 prematurely-pessimized held-by-pointer 方法:

    // 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
       }
    };
    

    破坏顺序如下:

    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!
    

    由于 m_gl 成员的销毁是微不足道的,并且不会对指针本身指向的内容做任何事情,因此 QOpenGlWidget 实例一直存在,直到 QObject 析构函数 运行s,此时它迭代其 child 列表和每个 child 的 deletes - 从而删除 QOpenGlWidget 实例。

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

不,它不会,除非你没有告诉我们什么。以下内容在 Qt 4 和 Qt 5 中都可以正常工作。手动删除是无害的,尽管完全没有必要:

// https://github.com/KubaO/Whosebugn/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();
}