QMainWindow::setCentralWidget fails when using my custom QuadSplit widget



  • Here's my QuadSplit implementation:

    QuadSplit.h
    @#ifndef QUADSPLIT_H
    #define QUADSPLIT_H

    #include <QSplitter>

    class QuadSplit : public QWidget
    {
    Q_OBJECT

    QSplitter parentSplit;
    QSplitter childSplit1;
    QSplitter childSplit2;
    
    QWidget *widget1;
    QWidget *widget2;
    QWidget *widget3;
    QWidget *widget4;
    

    private slots:
    void sync1()
    {
    childSplit2.setSizes(childSplit1.sizes());
    }

    void sync2()
    {
        childSplit1.setSizes(childSplit2.sizes());
    }
    

    public:
    QuadSplit(QWidget *parent, QWidget *w1, QWidget *w2, QWidget *w3, QWidget *w4);
    void show() {parentSplit.show();}
    };

    #endif // QUADSPLIT_H
    @

    QuadSplit.cpp

    @#include "QuadSplit.h"

    QuadSplit::QuadSplit(QWidget *parent, QWidget *w1, QWidget *w2, QWidget *w3, QWidget *w4)
    : QWidget(parent),
    widget1(w1),
    widget2(w2),
    widget3(w3),
    widget4(w4)
    {
    parentSplit.setOrientation(Qt::Vertical);

    childSplit1.addWidget(w1);
    childSplit1.addWidget(w2);
    childSplit2.addWidget(w3);
    childSplit2.addWidget(w4);
    
    parentSplit.addWidget(&childSplit1);
    parentSplit.addWidget(&childSplit2);
    
    connect (&childSplit1,
             SIGNAL(splitterMoved(int,int)),
             this,
             SLOT(sync1()));
    
    connect (&childSplit2,
             SIGNAL(splitterMoved(int,int)),
             this,
             SLOT(sync2()));
    

    }
    @

    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?



  • 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.



  • 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
    @#include <QApplication>
    #include <QMainWindow>
    #include <QTextEdit>
    #include "QuadSplit.h"

    int main (int argc, char *argv[])
    {
    QApplication app(argc, argv);

    QMainWindow *mainWindow = new QMainWindow;
    mainWindow->setCentralWidget (new QuadSplit (mainWindow,
                                                 new QTextEdit,
                                                 new QTextEdit,
                                                 new QTextEdit,
                                                 new QTextEdit));
    mainWindow->show();
    
    return app.exec(&#41;;
    

    }
    @

    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:
    @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.



  • 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.
    @
    class QuadSplit : public QSplitter
    {
    Q_OBJECT

    QSplitter* childSplit1;
    QSplitter* childSplit2;
    
    QWidget *widget1;
    QWidget *widget2;
    QWidget *widget3;
    QWidget *widget4;
    

    private slots:
    void sync1()
    {
    childSplit2->setSizes(childSplit1->sizes());
    }

    void sync2()
    {
        childSplit1->setSizes(childSplit2->sizes());
    }
    

    public:
    QuadSplit(QWidget *parent, QWidget *w1, QWidget *w2, QWidget *w3, QWidget *w4);
    };@

    @
    QuadSplit::QuadSplit(QWidget *parent, QWidget *w1, QWidget *w2, QWidget *w3, QWidget *w4)
    : QSplitter(Qt::Vertical, parent),
    widget1(w1),
    widget2(w2),
    widget3(w3),
    widget4(w4)
    {
    childSplit1 = new QSplitter;
    childSplit1->addWidget(w1);
    childSplit1->addWidget(w2);

    childSplit2 = new QSplitter;
    childSplit2->addWidget(w3);
    childSplit2->addWidget(w4);
    
    addWidget(childSplit1);
    addWidget(childSplit2);
    
    // ...
    

    }
    @



  • 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.



  • 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.



  • 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!



  • [quote author="AutoBot" date="1309894013"]I've made it a child of QSplitter and implemented it as you've shown, and it finally seems to work![/quote] Of course it does ;-)

    [quote author="AutoBot" date="1309894013"]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.[/quote]

    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 @ QuadSplit* quadSplit = new QuadSplit;
    quadSplit->show(); // QuadSplit::show() is called here @
    works and @ QWidget* quadSplit = new QuadSplit;
    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.



  • bq. 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!


Log in to reply
 

Looks like your connection to Qt Forum was lost, please wait while we try to reconnect.