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 :
- The widget needs a menu bar
- 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 ?
-
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=0the green widget would be your own widget doing drawing.
-
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.
-
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. -
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) ?
-
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(); }
-
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.
-
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.
-
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.
-
@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. -