Qt Gridlayout doesn't realign GUI elements - c++

I have the following code in the ctor of my main window widget, in my Qt App. No matter how I align the buttons added to the QGridLayout, they always stay in the upper left corner, on top of each other.
Can anybody tell me what I've done wrong, I can't find it.
btn_File= new QPushButton("&File", this);
btn_Close = new QPushButton("&Close", this);
btn_File->setAutoFillBackground(true);
btn_Close->setAutoFillBackground(true);
QGridLayout * layout = new QGridLayout(this);
layout->setContentsMargins(20,20,10,10);
layout->setSpacing(5);
layout->addWidget(btn_File,2,2, Qt::AlignRight);
layout->addWidget(btn_Close,1,1);
this->setLayout(layout);
EDIT: It seems that only the btn_Close is being drawn. I just tried to add a QComboBox to the grid, and it doesn't show up.

The problem was that my main window was derived from QMainWindow, in which you need to add a CentralWidget before adding GUI elements. I changed my main window to derive from QWidget instead, and now it works.

tried calling this->adjustSize() at the end?

qt layouts really suck!
alignment on qgridlayout depends on the size of the objects, how many cols an object needs, and the size of the biggest object inserted.. so it is very difficult to put objects as you want...
i suggest to use setGeometry or move instead!

Related

How to expand widgets with window resize?

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.

How to add a widget to a pre-existing QLayout?

I'm looking for a super simple example, and can't seem to find one. I have a MainWindow. When a button gets pressed I want to create a new window that gets opened up in the layout of MainWindow, to become a part of the mainwindow.
I have the code that sets up when a button is pushed to call this slot...when it gets called my QLabel shows up, but my QWidget does not
QWidget *test = new QWidget();
test->setGeometry(QRect(100,100,100,100));
layout->addWidget(test,0,0)
//Operation Mode
QLabel *operationalModeLabel1 = new QLabel("TEST");
layout->addWidget(operationalModeLabel1,2,1);
The reason for "lack of examples" is that you think of it wrong. What you describe is done all the time, by every single Qt example that uses layouts! I mean it. It doesn't matter when you add a widget to a layout. There's nothing magical about adding widgets "now" vs. adding them "later".
Just think of the title of the question: it makes no sense. All widgets must be added to layouts that already exist! By definition, no less. If there's no layout, how could you add a widget to it?
Your code is wrong, that's all. It's always pointless to set a geometry on a widget that is to be managed by a layout. As soon as you add it to the layout, the layout will change the geometry.
Since you're adding an empty widget into the layout, you most likely won't be able to see it. That's why the label shows up - it's not an empty widget.
If you want a widget that has a fixed size, to make it easier to notice, just set the fixed size on it. Even better, make it red so that it stands out.
QWidget * test = new QWidget();
test->setStyleSheet("QWidget { background-color: red }");
test->setFixedSize(100, 100);
layout->addWidget(test, 0, 0);

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.

GtkVBox Qt equivalent

In GTK, I used to have a window to which I gtk_container_add()'d a GtkVBox. Then I could pack widgets I wanted to the GtkVBox to have them appear in the window.
Now I've decided to try out Qt, but I can't seem to figure out how to do this in Qt.
Right now what I've done is create a QMainWindow, but I found that you can only pack one main widget into it, which is obviously quite limiting. So I wanted to create something like the GtkVBox and use that as the main widget and then add other widgets to this box.
What I've found by Googling are however only the Q3VBox widget, which seems to be what I want, but is deprecated, and the QVBoxLayout.
I tried to use the QVBoxLayout, but it tells me that I cannot change the layout of my QMainWindow since it already has a layout.
Edit: Here is how I do it (this is in the constructor):
box = new QVBoxLayout;
setLayout(box)
It compiles fine, but during runtime, it prints on the console:
QWidget::setLayout: Attempting to set QLayout "" on HCGWindow "", which already has a layout
(HCGWindow is my app's window, which is a subclass of QMainWindow)
So, how can I create something similar to a GtkVBox in Qt, and if the solution is the Q3VBox, why is it deprecated and what other thing should I use?
Thanks
In fact, here is the recommended solution provided by the Qt documentation.
You should create a QVBoxLayout and add the widgets you want in it. After that, you set the layout on another empty widget and then you set this widget as the central widget of the QMainWindow subclass. Here is an example in code:
QWidget* widget1 = new QWidget(); // This could be anything subclassing QWidget.
QWidget* widget2 = new QWidget();
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(widget1);
layout->addWidget(widget2);
QWidget* central = new QWidget(); // Only a containing QWidget.
central->setLayout(layout);
this->setCentralWidget(central);
Now, you QMainWindow subclass should have the two QWidgets in it, arranged in a QVBoxLayout.
Note here that I did not give any parent to anyone. You could have done it, but when you call addWidget or setCentralWidget, the ownership of the widget (and the layout) is given to the containing class.
If you read a bit about Qt, you'll know that this allows the parent to destroy his children when he is about to be destroyed itself.
Finally, note that QMainWindow is an exception and, from what I know, is the only class with setCentralWidget as a method. If you attempt to create a QWidget subclass, you will be able to use setLayout (as shown in the example above).
Hope this helps.
Try to create QVBoxLayout instance, add your widgets to it and add (not replace) this layout to main window's layout.
Note, QLayout class has no member addLayout, but subclasses has one.
Firstly you must get and remember classname of main window's layout:
qDebug(this.layout()->objectName);
Then add your QVBoxLayout to window's layout:
dynamic_cast<YourWindowLayoutClass>(
this.layout())->addLayout(your_qvboxlayout_object);
I hope it will work.

qt - widget - positioning

I want to place some widgets in a parent widget in some random places, like one button at Point (10,10) and another at (15,40), etc. How to achieve this?. QGridLayout is pushing everything into row column style. But I want to put the widgets whereever I want,Can anybody help me?
If you really want to set absolute positions, I would ignore using a layout altogether. You can manually set the positions of elements by using the move() function or the setGeometry() function.
QWidget *parent = new QWidget();
parent->resize(400, 400);
QPushButton *buttonA = new QPushButton(parent);
buttonA->setText("First Button");
buttonA->move(10, 10);
QPushButton *buttonB = new QPushButton(parent);
buttonB->setText("Second Button");
buttonB->move(15, 40);
Side note: I would avoid setting absolute positions of elements in Qt. Why? Well, Qt tries to be a platform-independent GUI library. On different platforms, a lot of display things can change (i.e. font size of text in push buttons) so the size of your actual push buttons can vary to accommodate large or smaller font sizes. This can throw off your meticulously spaced push buttons is you use absolute positions as in the example above.
If you use layouts, overlapping buttons or buttons falling off the edge of your window can be avoided.
You can see my answer for overlay button in QT: Qt Widget Overlays. This may help you to achieve what you want.