Getting QMainWindow to fit its central widget



  • This question was already asked in various forms, but I cannot find a solution to my particular case.

    I am trying to create a QWidget top window that has an initial size and is resizable. The complication comes from these two facts :

    1. The widget needs a menu bar
    2. The widget cannot have a layout, because parts of it are painted, so its children need to be positioned by absolute x-y coordinates.

    To solve the first point, I have embedded the widget in the center of a QMainWindow, but am now incapable of making the QMainWindow honor the initial size requirement. I would like to avoid calculating an initial size for the QMainWindow, because I'm not sure how portable will these calculations be.

    Setting the central widget to a fixed size solves the initial size problem, but then the QMainWindow can no longer be resized. Setting a minimum size also solves it, but then it cannot be resized below that size.

    After a few hours of wrestling with the problem and trying various combinations, can someone help whose understanding exceeds mine as regarding size policies in this case ?


  • Qt Champions 2016

    hi
    Im not 100% sure about
    "making the QMainWindow honor the initial size requirement"

    Normally it would be the other way around. Making the
    widget follow size of mainwindow.

    So im not sure why you cannot add a layout to mainwindow
    and add a widget to that. so it follow mainwindow
    then in this widget u can use paintevent for drawing and also
    direct x,y pos since it has no layout inside.

    like this:
    https://www.dropbox.com/s/qsqmcyy977kidwu/harry.zip?dl=0

    the green widget would be your own widget doing drawing.



  • @mrjj

    Thanks for answering and for the example.

    I know that QMainWindow has a layout and that is not my problem. The problem is that it resizes the widget. It is the widget that has no layout (or I wouldn't need QMainWindow).

    I would like the widget to start with a size that is determined when creating it - it is not always the same size like in your example, and the painted part is not identical for all invocations. I am forced to embed it in a QMainWindow, because otherwise having a menu is too complicated. But I wish the widget to start initially with a size that is determined at the time of its creation and force the QMainWindow to give the widget that exact size. For future resizes by the user, enlarging or reducing, the QMainWindow may do its stuff in the normal way.


  • Qt Champions 2016

    but its still not clear to me why you cannot let the widget follow mainwindow
    and then just set mainwindow to size that allow the size of the widget you want?
    If u set
    ui->IMSELFPAINTED->setMinimumSize(800,600);
    i get green area of that size. and mainwindow somewhat bigger.
    You cannot do like that?

    then later, maybe in showevent, call
    ui->IMSELFPAINTED->setMinimumSize(0,0);
    to allow any resize.



  • @mrjj

    I currently do use setMinimumSize as a stop-gap measure while developing.

    The setMinimumSize(0,0) solution is certainly much simpler than all my monkeying with size policies.

    Question: Is showEvent for the central widget safe enough, I mean is it done after all size adjustments were already set (at least until some future resize) ?


  • Qt Champions 2016

    @Harry123
    well it would be showEvent for mainwindow.
    Normally its safe yes. All sizes set and layouts resolved.



  • @mrjj

    Many thanks for the idea - I will be trying that later and will report if problems.


  • Qt Champions 2016

    @Harry123
    Good luck.
    Note.
    I just added layout to mainwindow and then a widget. ( the green one)
    So its not directly on centralwidget.


  • Moderators

    You don't need all this toying around. The default size policy is QSizePolicy::Preferred, which means it uses size hint of the widget when it is shown.

    As for the menu bar - the easiest way is add it to the layout. You don't have to use that layout for any other children. It can be there just for the menu bar and you can place other children manually. Here's a demo:

    #include <QApplication>
    #include <QPushButton>
    #include <QMenuBar>
    #include <QVBoxLayout>
    
    struct Foo : public QWidget {
        QSize sizeHint() const override { return QSize(400,400); }
    };
    
    int main(int argc, char *argv[]) {
        QApplication a(argc, argv);
    
        Foo foo;
        foo.setLayout(new QVBoxLayout()); //use a layout for menu bar
        foo.layout()->setMenuBar(new QMenuBar());
        foo.layout()->menuBar()->addAction(new QAction("Hi!", &foo));
    
        QPushButton* p = new QPushButton(&foo); //don't use layout, place it manually
        p->move(100,100);
    
        foo.show();
    
        return a.exec();
    }
    

    If you really don't want the layout for some reason here's the version with main window wrapper, but it's not really necessary:

    #include <QApplication>
    #include <QPushButton>
    #include <QMenuBar>
    
    struct Foo : public QWidget {
        QSize sizeHint() const override { return QSize(400,400); }
    };
    
    int main(int argc, char *argv[])
    {
        QApplication a(argc, argv);
    
        QMainWindow w;
        w.setMenuBar(new QMenuBar()); //use the built-in layout for menu bar
        w.menuBar()->addAction("Hi!");
        w.setCentralWidget(new Foo); //place the widget in built-in layout
    
        QPushButton* p = new QPushButton(w.centralWidget());  //place the child manually
        p->move(100,100);
    
        w.show();
    
        return a.exec();
    }
    


  • @Chris-Kawa

    I tested and this works, but there is a problem with the menubar now obscuring the upper part of the widget.

    First, this would require modifying all paint functions using absolute coordinates to add a vertical offset equal to the height of the menubar, which is not a simple a job.

    Second, it requires manually increasing the height of the widget.

    These entail the kind of assumptions on my part regarding the GUI which I try to avoid. I prefer to let Qt do this kind of calculations, for portability.


  • Moderators

    Ok then, you can use the second variant. QMainWindow will do the calculations for you, but...

    First, this would require modifying all paint functions (...) which is not a simple a job.

    Actually it's a one-liner:

    void Widget:: paintEvent(QPaintEvent* evt) {
        QPainter p(this);
        p.setTransform(QTransform::fromTranslate(0, layout()->menuBar()->height()));
        //paint as usual
    }
    

    Second, it requires manually increasing the height of the widget. These entail the kind of assumptions on my part regarding the GUI which I try to avoid.

    No assumptions needed:

    QSize sizeHint() const override { return QSize(400, 400 + layout()->menuBar()->height()); }
    

    If you're extra paranoid you can also check layout and menubar for nullpointers, but that's not really necessary here.



  • @Chris-Kawa

    I'm learning a lot from your answers, but regarding "No assumptions needed", there is one big assumption here - that the menubar is part of the widget and is displayed on top.

    I never programmed on the Mac or Android, but I'm not too sure that this assumption will hold there or on all other platforms where Qt was or will be ported.


  • Moderators

    @Harry123 said:

    there is one big assumption here

    Right. I'm not an expert on other platforms either. I think the menu can indeed not be a part of the window at least on Mac.
    Ok then. The wrapper option should still be valid in these scenarios.



  • @mrjj

    You solution works, and works beautifully. And what's more, is probably almost guaranteed to work on all future versions of Qt.

    So it's a hack, but who cares, as long as it works so well and is so easy to implement.


Log in to reply
 

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