July 5, 2011

AutoBot AutoBot
Lab Rat
26 posts

QMainWindow::setCentralWidget fails when using my custom QuadSplit widget

 

Here’s my QuadSplit implementation:

QuadSplit.h

  1. #ifndef QUADSPLIT_H
  2. #define QUADSPLIT_H
  3.  
  4. #include <QSplitter>
  5.  
  6. class QuadSplit : public QWidget
  7. {
  8.     Q_OBJECT
  9.  
  10.     QSplitter parentSplit;
  11.     QSplitter childSplit1;
  12.     QSplitter childSplit2;
  13.  
  14.     QWidget *widget1;
  15.     QWidget *widget2;
  16.     QWidget *widget3;
  17.     QWidget *widget4;
  18.  
  19. private slots:
  20.     void sync1()
  21.     {
  22.         childSplit2.setSizes(childSplit1.sizes());
  23.     }
  24.  
  25.     void sync2()
  26.     {
  27.         childSplit1.setSizes(childSplit2.sizes());
  28.     }
  29.  
  30.  
  31. public:
  32.     QuadSplit(QWidget *parent, QWidget *w1, QWidget *w2, QWidget *w3, QWidget *w4);
  33.     void show() {parentSplit.show();}
  34. };
  35.  
  36. #endif // QUADSPLIT_H

QuadSplit.cpp

  1. #include "QuadSplit.h"
  2.  
  3. QuadSplit::QuadSplit(QWidget *parent, QWidget *w1, QWidget *w2, QWidget *w3, QWidget *w4)
  4.     : QWidget(parent),
  5.       widget1(w1),
  6.       widget2(w2),
  7.       widget3(w3),
  8.       widget4(w4)
  9. {
  10.     parentSplit.setOrientation(Qt::Vertical);
  11.  
  12.     childSplit1.addWidget(w1);
  13.     childSplit1.addWidget(w2);
  14.     childSplit2.addWidget(w3);
  15.     childSplit2.addWidget(w4);
  16.  
  17.     parentSplit.addWidget(&childSplit1);
  18.     parentSplit.addWidget(&childSplit2);
  19.  
  20.     connect (&childSplit1,
  21.              SIGNAL(splitterMoved(int,int)),
  22.              this,
  23.              SLOT(sync1()));
  24.  
  25.     connect (&childSplit2,
  26.              SIGNAL(splitterMoved(int,int)),
  27.              this,
  28.              SLOT(sync2()));
  29. }

Whenever I call setCentralWidget with a QuadSplit of four QTextEdits, it only shows a grey window with nothing inside of it. Obviously I haven’t implemented my QuadSplit correctly, since it isn’t working with the rest of the Qt library… But it still works if I call QuadSplit.show() directly. Any advice?

8 replies

July 5, 2011

ludde ludde
Lab Rat
325 posts

Not sure setCentralWidget makes the widget a child of the QMainWindow.
Do you make sure the QuadSplit object is a child of the QMainWindow object?
You might want to add a parent parameter to the constructor that you pass on to the base class QWidget when it is initialized.

July 5, 2011

AutoBot AutoBot
Lab Rat
26 posts

I’ve added the parent parameter as a QWidget pointer and passed that onto the QWidget constructor, as you said, but the result is still the same.

Here’s the main initialization code, if that should help at all:

main.cpp

  1. #include <QApplication>
  2. #include <QMainWindow>
  3. #include <QTextEdit>
  4. #include "QuadSplit.h"
  5.  
  6. int main (int argc, char *argv[])
  7. {
  8.     QApplication app(argc, argv);
  9.  
  10.     QMainWindow *mainWindow = new QMainWindow;
  11.     mainWindow->setCentralWidget (new QuadSplit (mainWindow,
  12.                                                  new QTextEdit,
  13.                                                  new QTextEdit,
  14.                                                  new QTextEdit,
  15.                                                  new QTextEdit));
  16.     mainWindow->show();
  17.  
  18.     return app.exec();
  19. }

I’ve updated the main post with the changes I made to the QuadSplit header/source.

The strange thing is, when I do something like:

  1. mainWindow->setCentralWidget (new QTextEdit);

It works fine! So this seems to be something wrong with the QuadSplit implementation itself, I’m just not sure what. Advice is still greatly appreciated.

July 5, 2011

Lukas Geyer Lukas Geyer
Lab Rat
2074 posts

Your widget has no child widgets which could draw themselves (and produce any visible output).

parentSplit (and so childSplit1 and childSplit2) are no children of your widget, which means they are never drawn. Even though your create them in the widgets constructor, there is no automatic parent / child relationship. You either have to pass the parent explicitly to the constructor or setParent() on your child widget or set the parent implicitly by adding a child widget to the widgets layout (as you do with QSplitter::addWidget() for example).

Fortunately QSplitter is already a widget, so you can subclass QuadSplit directly from QSplitter. In addition, your child widgets have to be created on the heap, as they are automatically disposed when the parent is destroyed.

  1. class QuadSplit : public QSplitter
  2. {
  3.     Q_OBJECT
  4.  
  5.     QSplitter* childSplit1;
  6.     QSplitter* childSplit2;
  7.  
  8.     QWidget *widget1;
  9.     QWidget *widget2;
  10.     QWidget *widget3;
  11.     QWidget *widget4;
  12.  
  13. private slots:
  14.     void sync1()
  15.     {
  16.         childSplit2->setSizes(childSplit1->sizes());
  17.     }
  18.  
  19.     void sync2()
  20.     {
  21.         childSplit1->setSizes(childSplit2->sizes());
  22.     }
  23.  
  24.  
  25. public:
  26.     QuadSplit(QWidget *parent, QWidget *w1, QWidget *w2, QWidget *w3, QWidget *w4);
  27. };

  1. QuadSplit::QuadSplit(QWidget *parent, QWidget *w1, QWidget *w2, QWidget *w3, QWidget *w4)
  2.     : QSplitter(Qt::Vertical, parent),
  3.       widget1(w1),
  4.       widget2(w2),
  5.       widget3(w3),
  6.       widget4(w4)
  7. {
  8.     childSplit1 = new QSplitter;
  9.     childSplit1->addWidget(w1);
  10.     childSplit1->addWidget(w2);
  11.  
  12.     childSplit2 = new QSplitter;
  13.     childSplit2->addWidget(w3);
  14.     childSplit2->addWidget(w4);
  15.  
  16.     addWidget(childSplit1);
  17.     addWidget(childSplit2);
  18.  
  19.     // ...
  20. }

July 5, 2011

AutoBot AutoBot
Lab Rat
26 posts

I’ve made it a child of QSplitter and implemented it as you’ve shown, and it finally seems to work!

But I am a bit confused as to why it works now. Before I had implemented a show function by which the QMainWindow would be able to draw the child widgets, but does that not suffice? I can understand that inheriting QuadSplit from QSplitter would be a much better approach. But what more does that add to the QMainWindow’s ability to draw the QuadSplitter, besides the show() function I implemented in the first place? I ask this mostly because I’m curious as to how Qt handles custom widgets in general, and that’ll help me avoid these kinds of mistakes in the future.

Thanks to the help and any response you can give.

July 5, 2011

wongk wongk
Lab Rat
3 posts

As mentioned previously, your 3 QSplitter widgets, which where members of the QuadSplit, where never given a parent widget. In addition, your override of QWidget::show was never being called, because show is not a virtual function. Thus, your splitters where never being shown. Even if they had been, their geometries would not have been handled correctly.

July 5, 2011

AutoBot AutoBot
Lab Rat
26 posts

Ah, I see now, I suppose I never realized having a parent object was crucial to integrating with Qt’s other components.

Thanks for the help everybody!

July 6, 2011

Lukas Geyer Lukas Geyer
Lab Rat
2074 posts

AutoBot wrote:
I’ve made it a child of QSplitter and implemented it as you’ve shown, and it finally seems to work!
Of course it does ;-)

AutoBot wrote:
But I am a bit confused as to why it works now. Before I had implemented a show function by which the QMainWindow would be able to draw the child widgets, but does that not suffice? I can understand that inheriting QuadSplit from QSplitter would be a much better approach. But what more does that add to the QMainWindow’s ability to draw the QuadSplitter, besides the show() function I implemented in the first place? I ask this mostly because I’m curious as to how Qt handles custom widgets in general, and that’ll help me avoid these kinds of mistakes in the future.

In fact this is not a Qt specific behaviour, this is C++.

If you take a look at the QWidget class, you will see that the show() method is not virtual, which means that polymorphism won’t work with such methods. If you then call a non-virtual method of a derived class through a base class pointer, the base class’ method is called (and not the derived one).

You cannot override non-virtual methods in derived classes, you just can hide them (and lose polymorphism).

This is why

  1.  QuadSplit* quadSplit = new QuadSplit;
  2. quadSplit->show(); // QuadSplit::show() is called here

works and
  1.  QWidget* quadSplit = new QuadSplit;
  2. quadSplit->show(); // QWidget::show() is called here, not QuadSplit()::show()!

does not work.

Fortunately you do not need to override the show() method at all, as QWidget::show() will automatically “draw” all its child widgets. For this, of course, all widgets which should be drawn have to be children of your QWidget-based class. As stated above, this is either done explicitly or implicitly – but never automatically.

July 6, 2011

AutoBot AutoBot
Lab Rat
26 posts

If you then call a non-virtual method of a derived class through a base class pointer, the base class’ method is called (and not the derived one).
all widgets which should be drawn have to be children of your QWidget-based class. As stated above, this is either done explicitly or implicitly – but never automatically.

Ahh, I forgot about this. I suppose I need to be a bit more assertive with Qt’s inheritance system… Then again, it’s all about learning the library, and I’m a bit rusty in C++ atm. Thanks!

 
  ‹‹ Connecting a signal to another thread’s slot      [SOLVED] Reload contents of QTableView from textfile ››

You must log in to post a reply. Not a member yet? Register here!