How to get QVBoxLayout to contract QDialog when removing child layouts? - c++

I have a QDialog with a QVBoxLayout controlling its height.
This main QVBoxLayout consists of one or more QVBoxLayout children, followed by some other widgets. As I add additional QVBoxLayout children, (and/or lower widgets) the dialog box expands to accommodate them - as you would expect, and as I want.
However, when I remove QVBoxLayout children from the parent QVBoxLayout, the dialog box is not resized smaller by the amount equal to the size of the QVBoxLayout that was removed - it just remains the same size with unsightly large blank areas.
How do I achieve the correct dynamic behaviour in QVBoxLayout/QDialog, such that the dialog box is resized properly when child components are removed from the QVBoxLayout?

Make sure you call mainLayoutPtr->setSizeConstraint(QLayout::SetFixedSize) (this will also make the dialog not resizable by user, but it will be able to properly resize when widgets hide/show)

If you don't want to make the dialog non-resizable, you can resize it manually after any child is deleted:
QApplication::processEvents();
dialog->resize(dialog->sizeHint());

QWidget::adjustSize() is what you are looking for:
Adjusts the size of the widget to fit its contents.
This function uses sizeHint() if it is valid, i.e., the size hint's width and height are >= 0. Otherwise, it sets the size to the children rectangle that covers all child widgets (the union of all child widget rectangles).
For windows, the screen size is also taken into account. If the sizeHint() is less than (200, 100) and the size policy is expanding, the window will be at least (200, 100). The maximum size of a window is 2/3 of the screen's width and height.
Docs - http://doc.qt.digia.com/4.7/qwidget.html#adjustSize

Related

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.

Get size of QScrollArea viewport before showing

I have a custom QDialog comprised of a QStackedWidget with QScrollArea widgets for each page of the stacked widget.
I want to set the size hint for the QDialog such that the dialog is just large enough that the scroll bars for the scroll area are not visible when the dialog is first shown (i.e. ensure size of QScrollArea viewport = size hint of child widget in scroll area). Currently, the default sizeHint() implementation for the QDialog has insufficient height, which causes the vertical scroll bar to be shown when first loaded.
I thought this could be achieved by re-implementing sizeHint() for the QDialog, whereby the size hint of the dialog would be adjusted by the amount required for the size of QScrollArea viewport to equal the size hint for child widget in the scroll area (for the first page of the stacked layout). Unfortunately, in sizeHint(), the size of the QScrollArea viewport is set to the default size of QStackedWidget (640x480), and only updates to the correct size once the QDialog is shown.
Is there some way to get the correct size of the QScrollArea viewport before it is shown, or another way to achieve the desired effect of adjusting the size hint of the dialog to prevent scroll bars from being shown when it is first displayed (aside from hard-coding the dialog size).
With the composition of your dialog as:
I have a custom QDialog comprised of a QStackedWidget with QScrollArea
widgets for each page of the stacked widget.
The tricky part is to answer:
Is there some way to get the correct size of the QScrollArea viewport
before it is shown?
Well, before switching to certain page you can estimate the scroll area viewport if it is either correctly set or you can just measure the content going inside the scrollarea. I usually force the widget to demand certain height from the scroll area like that:
wdgetInScrollArea->setMinimumSize( widgetInScrollArea->sizeHint() );
wdgetInScrollArea->adjustSize(); // sometimes it is needed
The the scroll area viewport hint is then more 'adequate':
qDebug() << scrollArea->viewPortSizeHint(); // report
I don't see the code but usually it is not even required to do any custom event handling here, just prepare all the nested widgets like that.

Qt Set geometry to a newly created widget

I have a MainWindow in qt with a Tab Widget attached, containing a tab called "tab_upload".
On this tab i got a label with the text "Genres" (it's a library application) with a "plus" button attached. I want to be able to get a new QLineEdit every time I click this button , positioned inline with the other ones. To get the right coordonates is easy but I'm not able to properly set the geometry of the new QLineEdit. It doesn't matter what I type in the setGeometry function, the QLineEdit will always appear in the center.
And also, if I press the button a second time I get an error saying
QWidget::setLayout: Attempting to set QLayout "" on QWidget "tab_upload", which already has a layout.
if(nr_genres < 4)
{
QLineEdit *newgen = new QLineEdit(ui->tab_upload);
int x = 5 + nr_genres * 90;
newgen->setGeometry(x,187,90,25);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(newgen);
ui->tab_upload->setLayout(layout);
}
Layouts are designed to have control over the positioning of widgets:
All QWidget subclasses can use layouts to manage their children. The QWidget::setLayout() function applies a layout to a widget. When a layout is set on a widget in this way, it takes charge of the following tasks:
Positioning of child widgets.
Sensible default sizes for windows.
Sensible minimum sizes for windows.
Resize handling.
Automatic updates when contents change:
Font size, text or other contents of
child widgets.
Hiding or showing a child widget.
Removal of child widgets.
You should read this documentation for information about adding widgets to layouts:
All the widgets will initially be allocated an amount of space in accordance with their QWidget::sizePolicy() and QWidget::sizeHint().
If any of the widgets have stretch factors set, with a value greater than zero, then they are allocated space in proportion to their stretch factor (explained below).
If any of the widgets have stretch factors set to zero they will only get more space if no other widgets want the space. Of these, space is allocated to widgets with an Expanding size policy first.
Any widgets that are allocated less space than their minimum size (or minimum size hint if no minimum size is specified) are allocated this minimum size they require. (Widgets don't have to have a minimum size or minimum size hint in which case the stretch factor is their determining factor.)
Any widgets that are allocated more space than their maximum size are allocated the maximum size space they require. (Widgets do not have to have a maximum size in which case the stretch factor is their determining factor.)
I think that the easiest way to manage widgets in layouts is to use the Design mode in Qt Creator, and specify a minimumSize and/or maximumSize for each widget, along with a sizePolicy. This way, you can see what happens and experiment with different values.
With regards to the error you're receiving, it is mentioned in the documentation for setLayout():
If there already is a layout manager installed on this widget, QWidget won't let you install another. You must first delete the existing layout manager (returned by layout()) before you can call setLayout() with the new layout.
QWidget::setLayout: Attempting to set QLayout "" on QWidget
"tab_upload", which already has a layout.
You set new layout on widget that already contains layout from previous click on "plus" button. This problem can be solve by next way:
QLineEdit *newgen = new QLineEdit(ui->tab_upload);
QVBoxLayout *layout = static_cast<QVBoxLayout*>(ui->tab_upload->layout());
if(!layout) {
layout = new QVBoxLayout(ui->tab_upload);
ui->tab_upload->setLayout(layout);
}
layout->addWidget(newgen);
But you cannot directly set geometry for your QLineEdit because geometry was set by layout. If I understand the problem, this code must help you. It creates new QLineEdit after each click on "plus" button and append QLineEdit to existing layout.

Preventing a QScrollArea from displaying a horizontal Scroll bar

I currently have the following structure in my form
I have a QFrame (Brown) that has a QScrollArea . Now Multiple QFrames are dynamically added to the QScrollArea (gray).The dynamically added QFrame are composed of a QLabel.
Now here is the problem I have disabled the horizontal scrollbar in the QScrollArea . Thus the horizontal scrollbar does not show up. The problem is that when the dynamically added QFrame (gray) is added to the the QScrollArea. Half of the frame is cut off. This is because I have no way to scroll horizontally. What I want is to have the dynamically added Qframe expand vertically instead of horizontally. Any suggestions ?
Update :
I have a QVBoxLayout inside the QScrollArea
Set the proper horizontal size policies for your dynamically created frames while creating them. One option is fixed size (QSizePolicy::Fixed), the other is QSizePolicy::Maximum (it's not very intuitive, but actually maximum means that the frame won't be bigger than the size specified by sizeHint() function). If you want the widget to expand vertically, set the vertical size policy to QSizePolicy::MinimumExpanding or QSizePolicy::Expanding - whatever works for you.

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.