Qt layouts, difference between passing and not passing QWidget as parent - c++

I have created a simple QHBoxLayout (horizontal) that is pushed to the bottom of the QVBoxLayout (Vertical) and it contains two buttons. See code:
QWidget* create_ver_and_horizontal_box() {
QWidget* temp = new QWidget();
// Add buttons to the horizontal box
QHBoxLayout* hbox = new QHBoxLayout();
QPushButton *ok = new QPushButton("OK");
QPushButton *cancel = new QPushButton("Cancel");
hbox->addWidget(ok);
hbox->addWidget(cancel);
// Create a vertical box and add the horizontal box to
// the end of it
QVBoxLayout* vbox = new QVBoxLayout();
vbox->addStretch(1);
vbox->addLayout(hbox);
// set the layout and return
temp->setLayout(vbox);
return temp;
}
and the resulting UI is the following.
But when I add the QWidget temp to be the parent of the QHBoxLayout, like so:
// Add buttons to the horizontal box
QHBoxLayout* hbox = new QHBoxLayout(temp);
This is what I get:
I want to understand what is going on here. And in which cases I want the QWidget to be the parent of a layout or any other QWidget(s) and in which cases I don't the containing QWidget to be the parent of the containing QWidgets. For example, I could've added temp to be the parent of the two Push buttons but I didn't. What is the implication of not adding vs adding.
Thanks,

QHBoxLayout* hbox = new QHBoxLayout(temp);
is equivalent to
QHBoxLayout* hbox = new QHBoxLayout();
temp->setLayout(hbox);
I.e. you are making the horizontal layout responsible for temp.
The call to setLayout(vbox) should have generated a runtime warning message, that temp already has a layout, hinting at that.
Since you want the vertical layout to be responsible for that widget, either keep the temp->setLayout(vbox) or pass temp to the constructor of QVBoxLayout.

Related

Change Background Color specific QSplitter Child

I am using Qt Creator 4.13.1 with Qt 5.15.1 on Windows 10 Pro.
I am new to using Stylesheets in Qt and a bit confused of the possible selectors considering QSplitter and its child. I want to have 2 childs separated by a splitter-handle and draw their backgrounds in different colors.
This is an example szenario
QSplitter* splitter = new QSplitter();
QWidget* widgetA = new QWidget();
// add a layout with some further child widgets
QWidget* widgetB = new QWidget();
// add a layout with some further child widgets
splitter->addWidget(widgetA);
splitter->addWidget(widgetB);
layout()->addWidget(splitter);
where I tried:
widgetA->setStyleSheet("background-color: #ff0000;"); which applies to all child widgets of widgetA (e.g. QLabels, QPushButtons, ...) , but not to their surrounding widgetA itself
splitter->setStyleSheet("background-color: #ff0000;"); applies to the widgetA, widgetB and the handle and all widgets below widgetA and widgetB
naming widgetA->setObjectName("Tim"); and splitter->setStyleSheet("QWidget#Tim {background-color: #ff0000;}"); which has no effect at all.
[EDIT] splitter->setStyleSheet("QSplitter QWidget #Andy {background-color: #ff0000;}"); applies the background only to a certain widget inside either child widget. But using splitter->setStyleSheet("QSplitter #Tim {background-color: #ff0000;}"); has (again) no effect at all.
How would I set the background-color of widgetA separately, so without affecting the childs of widgetA or the other splitter child widgetB?
I don't see any problems with using widget->setStyleSheet() in your case. Here, I have tested your implementation of setObjectName():
Application::Application(QWidget *parent) :
QWidget(parent)
{
//set a base layout for the parent of splitter
QHBoxLayout *boxLayout = new QHBoxLayout(this);
this->setLayout(boxLayout);
//allocate splitter with parent
QSplitter* splitter = new QSplitter(this);
QWidget* widgetA = new QWidget();
widgetA->setObjectName("widgetA");
widgetA->setLayout(new QVBoxLayout());
//example buttons as child for widgetA
QPushButton *p = new QPushButton("0",widgetA);
widgetA->layout()->addWidget(p);
p = new QPushButton("1",widgetA);
widgetA->layout()->addWidget(p);
p = new QPushButton("2",widgetA);
widgetA->layout()->addWidget(p);
p = new QPushButton("3",widgetA);
widgetA->layout()->addWidget(p);
//widgetB, all similar to widgetA
QWidget* widgetB = new QWidget();
widgetB->setObjectName("widgetB");
widgetB->setLayout(new QVBoxLayout());
widgetA->setStyleSheet("QWidget#widgetA{background-color: #ff0000;}");
widgetB->setStyleSheet("QWidget#widgetB{background-color: #00ff00;}");
p = new QPushButton("1",widgetB);
widgetB->layout()->addWidget(p);
p = new QPushButton("2",widgetB);
widgetB->layout()->addWidget(p);
p = new QPushButton("3",widgetB);
widgetB->layout()->addWidget(p);
//add both widgets to splitter
splitter->addWidget(widgetA);
splitter->addWidget(widgetB);
//add splitter to base layout
this->layout()->addWidget(splitter);
}
Output

Why is the QScrollArea restricted in size?

To my custom widget, inherited from QWidget, I have added a QScrollArea like this:
MainWindow::MainWindow(QWidget *parent) :
QWidget(parent)//MainWindow is a QWidget
{
auto *scrollArea = new QScrollArea(this);
auto *widget = new QWidget(this);
widget->setStyleSheet("background-color:green");
scrollArea->setWidget(widget);
scrollArea->setWidgetResizable(true);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
QVBoxLayout *parentLayout = new QVBoxLayout(widget);
this->setStyleSheet("background-color:blue");
for(int i=0;i<12;i++){
QHBoxLayout* labelLineEdit = f1();
parentLayout->addStretch(1);
parentLayout->addLayout(labelLineEdit);
}
parentLayout->setContentsMargins(0,0,40,0);
}
QHBoxLayout* MainWindow::f1()
{
QHBoxLayout *layout = new QHBoxLayout;
QLabel *label = new QLabel("Movie");
label->setStyleSheet("background-color:blue;color:white");
label->setMinimumWidth(300);
label->setMaximumWidth(300);
layout->addWidget(label);
QLineEdit *echoLineEdit = new QLineEdit;
echoLineEdit->setMaximumWidth(120);
echoLineEdit->setMaximumHeight(50);
echoLineEdit->setMinimumHeight(50);
echoLineEdit->setStyleSheet("background-color:white");
layout->addWidget(echoLineEdit);
layout->setSpacing(0);
return layout;
}
This produces a window which looks like this:
The problem is, that I want the scrollArea to occupy the entire window, but it does not. It also doesn't get resized when I resize the window.
How could I fix this?
The problem is, that I want the scrollArea to occupy the entire
window, but it does not. It also doesn't get resized when I resize the window.
The reason is that you have not set any kind of layout to manage the positioning of your QScrollArea widget itself, so it is just being left to its own devices (and therefore it just chooses a default size-and-location for itself and stays at that size-and-location).
A simple fix would be to add these lines to the bottom of your MainWindow constructor:
QBoxLayout * mainLayout = new QVBoxLayout(this);
mainLayout->setMargin(0);
mainLayout->addWidget(scrollArea);

When are widgets deleted in Qt (QVBoxLayout class)?

I have some simple code to create a new widget using Qt:
Dock::Dock() : QDockWidget() {
label = new QLabel(QLatin1String("TEST"));
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(label, 0, Qt::AlignTop);
layout->addStretch(-1);
QWidget* multiWidget = new QWidget();
multiWidget->setLayout(layout);
setWidget(multiWidget);
}
Where label is a private member QLabel* label. My question is: am I responsible for deleting label in Dock's destructor?
I tried changing the code so that label is a std::shared_ptr<QLabel>:
Dock::Dock() : QDockWidget() {
label = std::make_shared<QLabel>(QLatin1String("TEST"));
QVBoxLayout* layout = new QVBoxLayout();
layout->addWidget(label.get(), 0, Qt::AlignTop);
layout->addStretch(-1);
QWidget* multiWidget = new QWidget();
multiWidget->setLayout(layout);
setWidget(multiWidget);
}
So that it is deleted automatically when `Dock' is destroyed but I get an error when I close the program.
In order to properly set up the objects tree you need to do the following:
Create the container widget first,
Create child widget and set it's parent,
Create layout of the container widget,
Add widget to the layout.
Here is the code that demonstrates the mentioned approach:
Dock::Dock()
:
QDockWidget()
{
QWidget* multiWidget = new QWidget;
label = new QLabel(QLatin1String("TEST"), multiWidget); // Set parent
QVBoxLayout* layout = new QVBoxLayout(multiWidget); // Sets layout
layout->addWidget(label, 0, Qt::AlignTop);
layout->addStretch(-1);
setWidget(multiWidget);
}

Center children widget in parent widget(parent widget was added in layout)

I created a layout contain a parent widget. In that parent widget i created another widget.
My code is similarly to this:
QGridLayout *layout = new QGridLayout();
QWidget *parentWidget = new QWidget();
layout->addWidget(parentWidget );
QWidget *childWidget = new QWidget(parentWidget);
How can i center the child widget in parent widget ?
The problem is we cannot get the true size of parent widget because it's in a layout.
Move the child inside the parent's showeEvent. You can use a bool flag to do it only when the parent is shown for the first time.
void Parent::showEvent(QShowEvent *)
{
if(_first_show)
{
_first_show = false;
_child->move(this->rect().center() - _child->rect().center());
}
}
Proof that it works (red is the parent, and blue is the child):
You can do this by setting fixed size of child widget and placing it inside grid layout of parent widget.
QGridLayout *layout = new QGridLayout();
QWidget *parentWidget = new QWidget();
layout->addWidget(parentWidget );
QWidget *childWidget = new QWidget(parentWidget);
QGridLayout *parentLayout = new QGridLayout();
parentWidget->setLayout(parentLayout);
parentLayout->addWidget(childWidget);
childWidget->setFixedSize(/*some fixed size for child widget*/);

QGridLayout with different size of cells

I'm trying to set a QGridLayout with four widget as in the image below:
however what I've managed with QGridLayout as of now is:
I don't see how I can set the size of the row different for column 0 and 1. Maybe QGridLayout is not the right way of doing it but I don't know of any other widget that would do the trick.
Does anyone have any idea how to achieve this?
I would use vertical and horizontal layouts instead of the grid layout. So you need two vertical layouts and horizontal one:
// Left side
QLabel *lbl1 = new QLabel(this);
QTableWidget *t = new QTableWidget(this);
QVBoxLayout *vl1 = new QVBoxLayout;
vl1->addWidget(lbl1);
vl1->addWidget(t);
// Right side
// QImage is not a widget, so it should be a label with image
QLabel *lbl2 = new QLabel(this);
QCustomPlot *pl = new QCustomPlot(this);
QVBoxLayout *vl2 = new QVBoxLayout;
vl2->addWidget(lbl2);
vl2->addWidget(pl);
// Create and set the main layout
QHBoxLayout mainLayout = new QHBoxLayout(this);
mainLayout->addLayout(vl1);
mainLayout->addLayout(vl2);
I don't think grids are the way to go here indeed...
You could try making a horizontal layout of 2 QFrames, in which you set a vertical layout each with the two widgets of that "column"