How to expand widgets with window resize? - c++

I have a simple qt application with a QTabWidget inside the main window. I also have a few QPushButton(s) and QRadioButton(s).
What I want is that when I resize the window either manually or by maximizing/minimizing it should resize the containers in the same way.
In other words, what I want is equivalent of DockStyle.Fill in qt C++
How can I do that ?

In Qt you have to use Layouts:
The Qt layout system provides a simple and powerful way of automatically arranging child widgets within a widget to ensure that
they make good use of the available space.
In short, all components in a layout will be relocated to new places after the window, to which the layout belongs, is resized.
If you are using deisgner:
1. Click the empty space of a widget to select itself(or a main Window, I use just a base widget here for demonstration), and the layout option will be hightlighted:
2. Choose a desired layout
Here is what object monitor looks like after a QVBoxLayout is used:
If your widget doesn't use layout, it will look like this:
What we have done here is to make the base widget/mainWindow equip a main layout. You can see that the buttons are automatically aligned, when you resize the widget, those component will be relocated according to the layout:
Perhaps you will find it nettlesome of those expanding space, so the next move is to add a Spacer to the layout; so when layout is resized, only the spacer will stretch.
(Another option is to make your widgets expandable, see ** at the end of this post)
3. Besides, you can add a layout into another to create a nested layout
For example, first I choose A and B (by pressing Ctrl) and use QVBoxLayout. This additional layout is not base layout and hence highlighted by red rectangle.
Then I choose C and the layout which contains A & B, and use QHBoxLayout on them,
Finally I use another QVBoxLayout as my main layout on the base widget, just like what we did previously.
And the object monitor:
If you like the special feeling of hitting keyboard and always handcraft the code:
For the last example:
QWidget *Form = new QWidget;
QPushButton *pushButton_A = new QPushButton("A");
QPushButton *pushButton_B = new QPushButton("B");
QPushButton *pushButton_C = new QPushButton("C");
QVBoxLayout *verticalLayout = new QVBoxLayout;
QHBoxLayout *horizontalLayout = new QHBoxLayout;
QVBoxLayout *mainLayout = new QVBoxLayout;
verticalLayout->addWidget(pushButton_A);
verticalLayout->addWidget(pushButton_B);
horizontalLayout->addWidget(pushButton_C);
horizontalLayout->addLayout(verticalLayout);
mainLayout->addLayout(horizontalLayout);
Form->setLayout(mainLayout);
Form->show();
In your case
Here is an example of layout:
Notice that QMainWidget has a centralwidget as a base widget. Besides, each tab of QTabWidget has it's own base widget (tab and tab_2 in the picture) which adopts another base layout.
*Don't forget to add Spacer in layouts to shape them as you like.
** You can set size policy on each widget (QTabWidget, QPushButton etc) to make them horizontally/vertically expandable or fixed, this cooperates with the layout strategy. For example, in the very begin example if we set
button A to be vertically fixed, horizontally expanding
button B to be vertically expanding, horizontally expanding
button C to be vertically expanding, horizontally fixed
It will look like this when resizing:

you need to look into how to use layouts in your application
http://qt-project.org/doc/qt-4.8/layout.html
As a quick and simple first try, in the Designer you can right-click on the main window, and choose "layout" from the drop-down menu. Here you can pick the grid layout, for instance.

Related

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.

How to manage QSplitter in Qt Designer

When I press a button, I bring up a dialog where user select things and press 'Ok' at the end. I want a splitter in this dialog. Left pane will show tree and right will show something else. How do I do that right?
From Qt example itself:
QSplitter *splitter = new QSplitter(parent);
QListView *listview = new QListView;
QTreeView *treeview = new QTreeView;
QTextEdit *textedit = new QTextEdit;
splitter->addWidget(listview);
splitter->addWidget(treeview);
splitter->addWidget(textedit);
So in this example, splitter is created without any dialog resource. If I have to create this way, that would mean I have to create all my controls in the code as well rather than Qt Creator.
What is the right way to do this when I need other controls on the screen?
You can simply create splitter containing items in Qt Designer :
First place your widgets on your dialog or widget in designer (They should not be in a layout)
Select the widgets that you want to be in a splitter (By holding CTL and clicking on them)
Right click on a selected widget and from Layout menu select Lay Out Horizontally in Splitter or Lay Out Vertically in Splitter.
Now apply a grid layout to the dialog and everything should be OK. You would see something like this in Object Inspector View :
Okay, I know this is ancient, but here's the complete answer.
First, within some sort of widget container, plop your pieces in. For the window I just did, I have a Widget as my window. I put two widgets inside that labeled something like topContainer and bottomContainer. I then put all the widgets they each need into them, and gave them their own layouts.
Then do NOT select the main container. Select the two widgets you want to split. You're in effect putting a splitter on them, not on the main container. So I went to the widget list window and selected both together, then right-click for the dialog window, scroll down to the Layout option, and "Lay Out Vertically in a Splitter" is NOT greyed out. Select it.
You still need a layout on the main container. A splitter is not a layout. So at that point, I just put a vertical layout on the main container.
To repeat: you are NOT setting a layout on the container holding the pieces you're trying to split. You are selecting the two widgets to split and adding a QSplitter around them. That's the trick to get it to work.
You can still create your controls in a .ui file using Qt Designer (integrated in Qt Creator). Within Qt Designer, add a QWidget object to your dialog. Then, from QDialog derived class you'll write, directly in your constructor, create your QSplitter using the QWidget object as a parent.
This way, you can create all but the splitter object from Qt Designer.
I think it's also possible to create the QSplitter (as you can create a QButton, QCheckBox...) item directly from Qt Designer.

QSpacerItem In QFormLayout - Vertical Expand

I would like to force an expanding space in my QFormLayout, but no matter what QFormLayout only uses the QSpaceItem::sizeHint(). Does anyone know a way around this, or the proper way to handle this?
MyWidget::MyWidget(QWidget *parent) : QWidget(parent)
{
SetupLayout();
}
void MyWidget::SetupLayout()
{
QFormLayout * layout = new QFormLayout();
layout->addRow("Something1", new QComboBox());
layout->addRow("Something2", new QSpinBox());
//Spacer
layout->addItem(new QSpacerItem(0,10, QSizePolicy::Expanding, QSizePolicy::Expanding));
layout->addRow(QPushButton("Something3"));
setLayout(layout);
}
So there were a few different issues:
QFormLayout do not expand like other layouts. My widgets (a few of them) were being placed into a QFormLayout. This prevented them from expanding. I switched my main parent layout from QFormLayout to QVBoxLayout. This made me have to use QLayout::setAlignment(Qt::AlignTop)
This fixed a few problems with a few of my other widgets not expanding. However these widgets used QVBoxLayout. The widget above uses a QFormLayout. To get this expand, I had to use the following line in my QSpacerItem:
QSpacerItem * my_spacer = new QSpacerItem(0,1000, QSizePolicy::Expanding, QSizePolicy::Expanding);
I am supplying some example code. The goal is to show the hierarchy, and where QFormLayout would cause trouble.
Example code:
//A main Widget class
void SetupLayout()
{
QHBoxLayout * main_layout = new QHBoxLayout();
main_layout->addWidget(Some_Widget);
//Create a control widget
control_widget = new QWidget(); // IMPORTANT control_widget is a member
QVBoxLayout * layout = new QVBoxLayout(); //IMPORTANT!!!! - Here it was QFormLayout
layout->setAlignment(Qt::AlignTop); //IMPORTANT - Needed this after switching to QVBoxLayout
layout->addWidget(new QComboBox("stuff")); //Some combo box
control_widget->setLayout(layout);
main_layout->addWidget(control_widget);
}
//Later on, we have a "Put a new widget in the control area" task
void NewControlArea()
{
if(current_control)
control_widget->removeWidget(current_control); //current_control is a member variable
current_control = new MyWidget(); //FROM ABOVE
control_widget->addWidget(current_control);
}
If MyWidget uses a QFormLayout, things are not expanded unless I add spacers with size hints. However, if MyWidget uses a QVBoxLayout, any QWidgets inside it are expanded correctly.
After lot of time with manual as well as lots of tries i guess it's impossible to do what you want using QFormLayout.
This layout is desinged for windows with lot of fields to fill, but only for that.
If you want to add bigger spacing between sections of your form you can use QVBoxLayout with a couple of QFormLayout's inside it separated by spacings.
Notice that in this case each section will have own width of first and second column so maybe that is not the best solution.
The other solution (if we are talking about grouping some options) is to use a couple of QGroupBoxes with QFormLayouts in it. The groups will not be separated by growing spacing, but it will be very readable and you can name your groups. If grouping options is what you want to do - this is probably the most common and user friendly way to do this.
If you only want visual effect you pointed - columns with same width in every section and growing spacing between sections, you can use QGridLayout with 2 columns and add spacers in rows between sections. In this case you have to create QLabel to put into first column by yourself.
Just for posterity, I was having a similar problem. I found that having an organization like the following would cause anything in the inner layout (QHBoxLayout) to not expand vertically as they would if I had dropped them into the QFormLayout by themselves:
QFormLayout
-QHBoxLayout
--QListWidget
However, if I added a layer of indirection by putting the HBoxLayout into a QWidget, then sizing worked correctly:
QFormLayout
-QWidget
--QHBoxLayout
---QListWidget
So you might try adding a QWidget in there and putting your spacer inside of it.
What I did is probably a bit of hacking and is not very elegant, yet it did the trick in just one row:
addRow(" ", (QWidget*)nullptr);
This doubles the space between rows added before and after this call.
I suspect that MyWidget has the wrong size. The layout will use the widget's size to work out how best to layout its own items. Just because one of those items is set to an expanding policy does not mean that the layout will force the size of MyWidget to expand. If the size of MyWidget is fixed there is nothing the internal layout can do.
If you make MyWidget bigger you should see the layout working as you hope. Perhaps you need to put MyWidget in a vertical layout with its policy set to expanding? Without knowing how MyWidget gets its size it's hard to be sure.

Qt: making QHBoxLayout scrollable

I have a QHBoxLayout horizontal layout with a lot of list widgets added to it.
And although I call setMaximumWidth(300) and setMinimumWidth(300) for the list widgets, once they don't fit on the window, they start to shrink.
I would like to have a scroll bar instead. Is this possible?
Yes, if you put the Layout inside a parent widget, and that parent widget inside a QScrollArea.
QScrollArea Documentation

resize problem in scroll area

Hello everyone, here is my code:
myplot *p = new myplot(gao.structpayloadgraph,
gao1.structpayloadgraph,
gao.structcol-2, "payload");
ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
ui->scrollArea->setWidgetResizable(false);
p->resize(ui->scrollArea->size().width(), ui->scrollArea->size().height());
ui->scrollArea->setWidget(p);
I want p to take up the full available space of the scrollbar area and fit itself. However, the appearance looks 'squeezed' even though I called the resize function. What should I do to achieve the desired effect?
You have to treat the scroll area content widget as a normal QWidget. If you want automatic resize and you must use layouts in Qt. Try the following :
QVBoxLayout layout = new QVBoxLayout( ui->scrollAreaContent);
layout->setMargin(0);
layout->setContentsMargins(0,0,0,0);
layout->setSpacing(0);
ui->scrollAreaContent->setLayout( layout);
layout->addWidget(p);
NOTE: ui->scrollAreaContent is a guess, but I think you are using ui files and default content widget is named like that ...
Go to the top right of the QT creator designer screen (Object, Class), right click on the QScrollArea row and select the "Lay Out" menu item, choose a layout (eg vertical or horizontal layout), make sure that your QWidget has a minimum or above size policy. Your scroll widget should now resize with the layout.