Qt Layout relationship between QLayoutItem and QWidget - c++

this isn't well clarified in the documentation, but what really happens when for instance, I call QBoxLayout::insertWidget. It allocates a QLayoutItem, somehow associates it with the widget and then adds the QLayoutItem to the layout? Why the need for such an indirection? I'm making a custom layout and want to be able to insert widgets at any index of that layout, however I am not fully aware of the mechanics.

Because layouts operate layout items, not widgets. QLayoutItem contains a list of it's own functions that are used to position the layoutitem inside the layout, and properly resize/align it.
Take a simple example: you have a vertical layout that is 300px wide. That means each layout item that you add will also be 300px wide. Now imagine adding a 50x50 widget into it. Since the layout has it's own geometry, sizeHing, and other stuff, you will be able to properly insert the widget (the layoutItem will stay 300px wide, and the widget will stay 50px wide, nothing will break), which would be hard/impossible if you operated widgets directly.

Related

some blank space remains in QVBoxLayout after hiding widgets (Qt)

There are hundreds of widgets in QVBoxLayout. I am hiding/showing them based on option menus. If I hide some of widgets, some blank space remains in QVBoxLayout and I dont want this unnecessary space. Adding spacer at bottom is not solving the issue. Same for setting margin spacing. Its like hidden widgets consume some space. Is there any way to fix this?
Thanks.
Layouts have some default spacing between each child widget, defined by setSpacing(), setHorizontalSpacing(), setVerticalSpacing(). Even if you hide the child widget, the spacing around it remains visible. (Note: I think this is a bad design decision made by Qt developers, but we need to live with it.) You have basically these options:
a) Remove the child widget from the layout instead of hiding it. Remember its original position and if it should be shown again, insert it at that position. This is complicated, the original position may be invalidated if you removed some other widgets meanhile, so this would require some clever algorithm for maintaining the correct visual positions of the hidden child widgets in the layout etc. I would not do this, feels too complicated for me.
b) Use zero-sized default spacing in the layout and add spacings manually by https://doc.qt.io/qt-5/qboxlayout.html#addSpacing and then when hiding the child widgets, set the size of the QSpacerItem next to it to zero. And set it back to non-zero when showing the child widget again.
c) Alternative to a) but do not keep original indexes but have a container of pointers to the child widgets and when a change in the visibility of the child widgets is required, then remove all the items from the layout and put all the child widgets which should be visible to the layout. This means to re-create the content of the layout in each change. Actually this is how I am doing it in my code. I have about 20 widgets and hiding/showing is fast enough. I believe it will be fast enough even for hundreds of widgets.
d) And alternative to c) ... if you have really large number of widgets, then you should consider deleting those which are not supposed to be visible and re-creating them when they are shown. I.e. in c) we keep the widget alive but hidden in a certain container but in d) we delete this widget and create it later again. It depends on your use case whether c) is better than d) or vice versa. My gut feeling is that c) should be fine performance-wise and is simpler.
Note: My reasonging is based on showing and hiding of widgets in grid layout, but I guess that VBox layout has the same principles regarding preserving default spacing even around hidden items.

Lay out an image on a Qt dialog

How to add an image to a dialog in Qt?
I know this has been often asked in the past and most answers come up with a QLabel and its setPixmap member. However, this usually is not what the user (me) intends:
A QLabel with a pixmap set does not participate in the surrounding QLayout. That is, it simply refuses to resize when the dialog is resized, like e.g., a QPushButton would do. Two QPushButtons next to each other in a QHorizontalLayout will (something like) equally divide the available horizontal space between them. A QLabe with a pixmap next to a QPushButton in the same layout will just stay fixed in size.
By default, a naked QLabel won't resize its contents when it's resized.
But when it does (QLabel::setResizeContents) it won't keep aspect ratio.
Is there any native way to have a pixmap shown on a dialog and have it reasonably participate in the layout?
Item resizing can be managed via sizePolicy property. From Qt documentation:
sizePolicy : QSizePolicy
This property holds the default layout behavior of the widget.
If there is a QLayout that manages this widget's children, the size
policy specified by that layout is used. If there is no such QLayout,
the result of this function is used.
The default policy is Preferred/Preferred, which means that the widget
can be freely resized, but prefers to be the size sizeHint() returns.
Button-like widgets set the size policy to specify that they may
stretch horizontally, but are fixed vertically. The same applies to
lineedit controls (such as QLineEdit, QSpinBox or an editable
QComboBox) and other horizontally orientated widgets (such as
QProgressBar). QToolButton's are normally square, so they allow growth
in both directions. Widgets that support different directions (such as
QSlider, QScrollBar or QHeader) specify stretching in the respective
direction only. Widgets that can provide scroll bars (usually
subclasses of QScrollArea) tend to specify that they can use
additional space, and that they can make do with less than sizeHint().
I think you are searching for QSizePolicy::Expanding size policy:
The sizeHint() is a sensible size, but the widget can be shrunk and
still be useful. The widget can make use of extra space, so it should
get as much space as possible (e.g. the horizontal direction of a
horizontal slider).
Set this for your QLabel and check how it will resize. Try other values from QSizePolicy::Policy enum.

How to force layout recalculation after one of its children is resized using CSS?

I have a dialog box with two widgets (which are actually vertical layouts themselves), and a spacer in the middle, arranged as a horizontal layout, like so (layout "lines" are red):
I also have a global stylesheet that's loaded and parsed at runtime. The trouble is, this stylesheet changes the height of some widgets in the left layout, but the main layout doesn't seem to take that into consideration, and so when the application is running it looks like this:
My understanding is that the main layout is resized to compensate for the extra space needed to accommodate the widgets in the left layout, but the right layout is then not resized to fit that extra space in its parent, which looks ugly. I'm using the default size constraint in all layouts.
What can I do to force the right layout to resize itself?

Widget same size as it's children?

In Qt, how can I have a widget which automatically sizes itself according to the size of it's children?
For example, if I have a QGroupBox which contains a QHBoxLayout which contains some QPushButtons, I would like the QGroupBox to automatically calculate it's size so that it is no bigger and no smaller than necessary to fit all of the QPushButtons.
Ideally I would like to be able to do this in Qt Designer so that I can create a .ui file which already knows how to size the QGroupBox, however I am also opening to deriving from a class inside a .ui file and doing the resizing manually.
I have tried placing the QGroupBox inside it's own layout (with and without a spacer) but this just resizes the QGroupBox to the smallest possible size so that none of the children are visible.
There are two things to pay attention to:
Set the size policies appropriately on the children in the groupbox. You literally need to think what the buttons can do - most likely, you do not want the buttons to either grow or shrink, so setting both of their size policies to Fixed is the right thing to do. You could, possibly, let the buttons expand horizontally, so the horizontal policy of MinimumExpanding is an option.
Set the size constraint on the layout in the groupbox to act according to your objective:
ui->groupbox->layout()->setConstraint(QLayout::SetMinAndMaxSize);
Of course, the groupbox will be inside of some layout in its parent window, but that doesn't matter.
You'll probably have the most luck by sub classing QGroupBox and overriding sizeHint or other sizing functions to loop through children and calculate the minimum bounding rectangle. Depending on how dynamic the group box is, managing connections to new widgets might be a small challenge.

Enlarge a Qt widget so it might cover other widgets

I have a complex layout of widgets in widgets in widgets in a QMainWindow. In one of them I have an image, it sits in the corner. What I would like to achieve is following: if the image is activated (e.g. clicked upon), it should be enlarged, so it might overlap other widgets, or parts of other widgets. The problem is, I still would like it to remain in the layout, but in a way that everything else remains in its original size and position.
I was thinking about having an empty but similar size widget as a "placeholder", and have the actual resizable widget float on top of it. My problem is, that it does not guarantee that it stays in its position if the main window is resized, maximized, etc. Is there a better or more efficient way to do it?
One way to do it, if the widgets to be overlapped are in the same layout than the one you want to enlarge, and the policies for that widget allow it, is just .setVisible(false) in the other widgets. The widget that remains visible should resize to cover all the available area!
If I can't find a better solution, I think I'll do the following:
The MainWindow will have no layout, just two QWidgets on top of each other. The bottom one will contain all the layouts and everything else, while the upper one will have a transparent background and the resizable widget, maybe supported with a number of spacers.