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
    #1

    I have a class, GraphicsPane3d that inherits from QOpenGLWidget and QOpenGLFunctions_3_0. I hope that usage is reasonable ! My code crashes deleting an instance of GraphicsPane3d. I have isolated the problem to a fairly minimal sequence that crashes::

    GraphicsPane3d *graphics;
    void MainWindow::button1_clicked() {
      graphics = new GraphicsPane3d();
      TopFrame->layout()->addWidget(graphics);
    }
    void MainWindow::button2_clicked() {
        graphics->setParent(NULL);
        delete graphics;
    }
    

    If I do not set the parent to NULL, there is no crash. But I WANT to set the parent to NULL. This effectively removes the object from the screen. In my code the object may subsequently be re-parented in another layout or deleted (actually it has been 'cut' in a design view and then pasted (or not) in a different parent frame). My procedure works for a host of other graphical objects, which makes me think it is specifically an open gl issue.

    My questions are:

    Is there anything I can do to allow me to safely set the parent to NULL ?

    Do I need to free resources or set as current somewhere ?
    (I have seen the are warnings for destructor in derived classes of QOpenGLWidget).

    GraphicsPane3d is defined as follows:

    
    #include <QOpenGLWidget>
    #include <QOpenGLFunctions_3_0>
    #include <GL/gl.h>
    #include <GL/glu.h>
    #include <vector>
    
    class GraphicsPane3d : public QOpenGLWidget, public QOpenGLFunctions_3_0 {
    
      Q_OBJECT
    
    public:
    
      GraphicsPane3d(QWidget *parent=NULL) : QOpenGLWidget(parent) {
        setFocusPolicy(Qt::StrongFocus); //The pane can receive keyboard events.
    
        p = NULL;
      }
    
      ~GraphicsPane3d() {
          delete p;
          p = NULL;
      }
    
      GLUquadricObj *p;
    
      //blah, blah.  A bunch more  member functions....
    };
    
    

    The stack shows:

    1 ?? 0x7ffff3f2bfbb
    2 QOpenGLFunctions_3_0::~QOpenGLFunctions_3_0() 0x7ffff4245b8c
    3 GraphicsPane3d::~GraphicsPane3d GraphicsPane3d.h 30 0x49e0e6
    4 GraphicsPane3d::~GraphicsPane3d GraphicsPane3d.h 33 0x49e134
    5 DynamicDataFrame::holdWidget DynamicDataFrame.cpp 504 0x45a8bd
    6 DynamicDataFrame::contextMenuEvent DynamicDataFrame.cpp 460 0x45a455
    ... <More>

    PS sorry to purists for still using NULL :)

    1 Reply Last reply
    0
    • mrjjM Offline
      mrjjM Offline
      mrjj
      Lifetime Qt Champion
      wrote on last edited by mrjj
      #2

      Hi
      I think its a layout issue ?
      You put the openGl in
      TopFrame->layout()->addWidget(graphics);

      Now layout owns it.
      So you can try to ask layout to give back ownership and then try the reparent/set NULL

      QLayoutItem *child =layout->takeAt(0);
      GraphicsPane3d * minegfx=qobject_cast<GraphicsPane3d * > (child->widget() );
      or just use the other pointer
      Just to show where the widget is.

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

        Thank you very much for helping.

        I think you are totally right that it is to do with layout. I tested idea with this code:

        void MainWindow::button1_clicked() {
          graphics = new GraphicsPane3d();
          graphics->setVisible(true);
          //TopFrame->layout()->addWidget(graphics);
          return;
        }
        
        void MainWindow::button2_clicked() {
          graphics->setParent(0);
          delete graphics;
          return;
        

        The graphics object appears (in its own window). Deleting the pointer does not crash.

        I then did some more testing with the following code:

        void MainWindow::button1_clicked() {
          graphics = new GraphicsPane3d();
          graphics->setVisible(true);
          TopFrame->layout()->addWidget(graphics);
          return;
        }
        
        void MainWindow::button2_clicked() {
          int         index  = TopFrame->layout()->indexOf(graphics);
          QObject     *owner = graphics->parent();
          QLayoutItem *child = TopFrame->layout()->takeAt(index);
        
          owner = graphics->parent();
        
          graphics->setParent(0);
        
          owner = graphics->parent();
        
          delete graphics;
          return;
        

        The sigsegv problem came back. 'owner' does not change throughout the test, and remains as TopFrame.

        I tried another idea:

        void MainWindow::button2_clicked() {
          int         index  = TopFrame->layout()->indexOf(graphics);
          QObject     *owner = graphics->parent();
          TopFrame->layout()->removeWidget(graphics);
        
          owner = graphics->parent();
        
          graphics->setParent(0);
        
          owner = graphics->parent();
        
          delete graphics;
          return;
        

        The crash remained and 'owner' still remains as TopFrame.

        Finally I tried the above tests with another type of widget (not inheriting from QOpenGLWidget or QOpenGLFunctions_3_0). 'owner' is treated identically - it remains as TopFrame, but there is no crash on deletion.

        I still have the feeling that the warning about using makeCurrent in destructors of classes derived from QOpenGLWidget may be a clue.

        1 Reply Last reply
        0
        • 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 Online
              Chris KawaC Online
              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 Online
                  Chris KawaC Online
                  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 Online
                          Chris KawaC Online
                          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