Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. How can I re-parent a QOpenGLWidget ?
Forum Updated to NodeBB v4.3 + New Features

How can I re-parent a QOpenGLWidget ?

Scheduled Pinned Locked Moved Solved General and Desktop
13 Posts 3 Posters 4.6k 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.
  • E Offline
    E Offline
    elpidiovaldez5
    wrote on last edited by elpidiovaldez5
    #4

    I have tracked the bug down to a really minimal piece of code.

    #include <QOpenGLWidget>
    #include <QOpenGLFunctions_3_0>
    
    class TestGraphicsPane : public QOpenGLWidget, public QOpenGLFunctions_3_0 {
    
      Q_OBJECT
    
    public:
    
      TestGraphicsPane(QWidget *parent=NULL) : QOpenGLWidget(parent) {
      }
    
      void initializeGL() {
        initializeOpenGLFunctions();
      }
    
    };
    
    TestGraphicsPane *graphics;
    
    void MainWindow::on_addObjectButton_clicked() {
      graphics = new TestGraphicsPane();
      TopFrame->layout()->addWidget(graphics);
    }
    
    void MainWindow::on_loadLayoutButton_clicked() {
      graphics->setParent(0);
      delete graphics;
    }
    
    

    If I comment out the call to InitializeOpenGLFunctions in TestGraphicsPane:: initializeGL, then the crash no longer occurs.

    I conclude that it is the destruction of the OpenGL functions that is the root of the problem (to do with calling order of destructors and the current open GL context ?)

    I tried a possible to solution - using the alternative (annoying) usage of QOpenGLFunctions which requires all OpenGL calls to be prefixed with 'glf->'. It seems to me that this approach uses a shared object to contain the OpenGL functions and would avoid the need to for the destructor to be called.

    I changed the test class as follows:

    class TestGraphicsPane : public QOpenGLWidget {
    
      Q_OBJECT
    
    public:
    
      TestGraphicsPane(QWidget *parent=NULL) : QOpenGLWidget(parent) {
      }
    
      void initializeGL() {
        QOpenGLFunctions_3_0 *glf = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_0>();
        glf->glClearColor(1.0, 0.0, 0.0, 0.0);
      }
    
      QOpenGLFunctions_3_0 *glf;
    };
    
    

    Wonder of wonders ! It no longer crashes on deleting the pointer.

    I believe that inheriting from QOpenGLFunctions_3_0 is acceptable usage as is setting 'parent' to NULL. So is this crash a Qt bug ?

    Is there some way to safely deconstruct the inherited OpenGL functions base class without crashing ?

    I got a compile error when I tried:

      ~TestGraphicsPane() {
        makeCurrent();
        QOpenGLFunctions_3_0::~QOpenGLFunctions_3_0();
    
        doneCurrent();
      }
    
    
    1 Reply Last reply
    0
    • mrjjM Offline
      mrjjM Offline
      mrjj
      Lifetime Qt Champion
      wrote on last edited by
      #5

      Hi
      Good digging :)

      So if we forget the layout for a moment which should be fixed using TakeAt.

      You are saying that
      class TestGraphicsPane : public QOpenGLWidget, public QOpenGLFunctions_3_0

      can crash on delete.

      Can we make it do it small small?

      The "OpenGL Window Example"
      does it the same way.

      Can we get this to crash the same way?

      1 Reply Last reply
      0
      • Chris KawaC Offline
        Chris KawaC Offline
        Chris Kawa
        Lifetime Qt Champion
        wrote on last edited by Chris Kawa
        #6

        After you switch parents you need to show the widget again so that it has a surface to re-create the context on:

        void MainWindow::on_loadLayoutButton_clicked() {
          graphics->setParent(0);
          graphics->show(); //or showMinimized() if you want to hide it immediately after and avoid the blink
          delete graphics;
        }
        

        If you want to just delete the widget don't switch the parent at all to avoid costly context re-creation:

        void MainWindow::on_loadLayoutButton_clicked() {
          delete graphics;
        }
        

        Btw. You don't need to take a widget out of a layout when you switch parents. The layout is aware of the situation and stops managing the widget automatically.

        mrjjM 1 Reply Last reply
        4
        • Chris KawaC Chris Kawa

          After you switch parents you need to show the widget again so that it has a surface to re-create the context on:

          void MainWindow::on_loadLayoutButton_clicked() {
            graphics->setParent(0);
            graphics->show(); //or showMinimized() if you want to hide it immediately after and avoid the blink
            delete graphics;
          }
          

          If you want to just delete the widget don't switch the parent at all to avoid costly context re-creation:

          void MainWindow::on_loadLayoutButton_clicked() {
            delete graphics;
          }
          

          Btw. You don't need to take a widget out of a layout when you switch parents. The layout is aware of the situation and stops managing the widget automatically.

          mrjjM Offline
          mrjjM Offline
          mrjj
          Lifetime Qt Champion
          wrote on last edited by
          #7

          @Chris-Kawa
          Thank you for the knowledge.
          so if it is inserted into layout, you will need the extra show() to avoid
          context conflict ? Its seems that TakeAt do not alter parent so its
          the actual graphics->setParent(0); that create the issue?

          1 Reply Last reply
          0
          • Chris KawaC Offline
            Chris KawaC Offline
            Chris Kawa
            Lifetime Qt Champion
            wrote on last edited by
            #8

            so its the actual graphics->setParent(0); that create the issue?

            Depends how you look at it. Setting parent to null hides the widget. To create a context you need a surface. To have a surface a widget needs to be shown. I'd say no single line creates the issue. It's the combination ;)

            I haven't looked at the internals, so I speculate here, but It seems that entire context switching (so also de-initializing the old one) is deferred to the time the widget is shown next time, which is kinda... dangerous? So if you delete it without showing it first again it thinks it is still attached to the old surface. I'd say it should de-init properly when it is hidden on parent switch, but apparently that's not happening.

            This looks like a bug, but again, I'm just speculating here.

            mrjjM 1 Reply Last reply
            2
            • Chris KawaC Chris Kawa

              so its the actual graphics->setParent(0); that create the issue?

              Depends how you look at it. Setting parent to null hides the widget. To create a context you need a surface. To have a surface a widget needs to be shown. I'd say no single line creates the issue. It's the combination ;)

              I haven't looked at the internals, so I speculate here, but It seems that entire context switching (so also de-initializing the old one) is deferred to the time the widget is shown next time, which is kinda... dangerous? So if you delete it without showing it first again it thinks it is still attached to the old surface. I'd say it should de-init properly when it is hidden on parent switch, but apparently that's not happening.

              This looks like a bug, but again, I'm just speculating here.

              mrjjM Offline
              mrjjM Offline
              mrjj
              Lifetime Qt Champion
              wrote on last edited by
              #9

              @Chris-Kawa

              • I'd say no single line creates the issue. It's the combination ;)
                That would certainly explain testing it gave surprising results.

              Your speculating is far more valid than my random guesses ;) and using your show() trick should
              solve the OPs issue. \o/

              I dont know enough openGl to say its a bug or the use case of reparenting is not very common and
              careful managing of surfaces is always needed. However this also explains some cases with docks where we had
              crash re-inserting the openglwidget into a dock when it went from full screen back to windowed.

              I didn't see this mentions in the Docs even plenty chance its there as did not fine read it yet. :)

              Thx for saving my ass :) we could have posted many tests here before we reached the Surface so to speak :)

              1 Reply Last reply
              1
              • E Offline
                E Offline
                elpidiovaldez5
                wrote on last edited by
                #10

                @Chris-Kawa

                You are right. I just tested and it is true that adding graphics->show() or showMinimized() after setParent(0) stops delete graphics crashing. That solves my problem !

                I do not want to just delete the widget without switching parent so the costly context re-creation is something I will have to put up with. Actually it is not an issue because this code is executed as part of user interaction and the delay is imperceptible.

                Just to explain my use case: I have a design view of a user interface. The user has a sort-of 'cut and paste' functionality at the widget level. The problem occurred when an open gl graphics pane was 'cut' and held without a parent in a clipboard type thingummy. Pasting would cause the pane to be re-parented somewhere else in the ui, but if the graphics pane was not subesequently pasted, it would be deleted the next time something was put in the clipboard. I will add code to test if the object being deleted is an open gl graphics pane and, if so, call showMinimized.

                @Chris-Kawa
                @mrjj

                Thank you both for helping me with this problem. I have actually been bothered by it for months and am very glad to have a solution.

                1 Reply Last reply
                1
                • E Offline
                  E Offline
                  elpidiovaldez5
                  wrote on last edited by
                  #11

                  I now have a super-simple solution. - I ust put showMinimized at the start of the graphics pane destructor. It is executed in time to avoid the crash.

                  Chris KawaC 1 Reply Last reply
                  0
                  • E elpidiovaldez5

                    I now have a super-simple solution. - I ust put showMinimized at the start of the graphics pane destructor. It is executed in time to avoid the crash.

                    Chris KawaC Offline
                    Chris KawaC Offline
                    Chris Kawa
                    Lifetime Qt Champion
                    wrote on last edited by
                    #12

                    @elpidiovaldez5 said:

                    I ust put showMinimized at the start of the graphics pane destructor

                    Might work here and now but that's generally a very bad idea. Destructors should well.. destruct, not initialize stuff. Doing something as heavy as OpenGL context initialization in a destructor smells trouble.
                    For example later down the line you might subclass that widget and showXXX() is likely calling some virtual functions. If you happen to override one of those then it's gonna be one hell of a debugging day for you or some other poor soul working with that code.

                    1 Reply Last reply
                    1
                    • E Offline
                      E Offline
                      elpidiovaldez5
                      wrote on last edited by
                      #13

                      Initially I started to put a test in the code before the object is deleted. Something like:

                      QOpenGLWidget *glObj = dynamic_cast<QOpenGLWidget *>(obj);
                      if (glObj) {
                        glObj->showMinimized();
                      }
                      delete obj;
                      

                      I decided I really did not like this, because I had to include the header for QOpenGLWidget in the header for a class that really should not need it. So putting the call in the destructor seemed to keep everything together in the class that was causing the problem.

                      Taking your objection into consideration I have come up with an alternative which takes the fix into a separate file and leaves everything else relatively unsullied. Cheers.

                      extern FixQtProblem();
                      FixQtProblem();
                      delete obj;
                      

                      And the provide a self contained .cpp file:

                      #include <QOpenGLWidget.h>
                      
                      void FixQtProblem() {
                        QOpenGLWidget *glObj = dynamic_cast<QOpenGLWidget *>(obj);
                        if (glObj) {
                            glObj->showMinimized();   
                        }
                      }
                      
                      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