QVBoxLayout row size - c++

I have a widget, I am using QVBoxLayout with the widget to organise rows of widgets. The first widget is a QLabel followed by multiple QPushButton widgets.
The width of each widget occupies 100% of the width, however the height of the first widget (QLabel) is much larger than the QPushButton widgets that follow it. All the QPushButtons are the same height.
It looks like the first row has been increased in size to pad the layout to fill the parent. If this is the case, is there any way to instruct the layout to use only the height required by the internal widgets and not to pad?

The difference between the behavior of the layout for the QLabel and QPushButton is the sizePolicy, so the solution is to set the policy QSizePolicy::Maximum in the vertical part
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget w;
QVBoxLayout *lay = new QVBoxLayout(&w);
QLabel *label = new QLabel("Stack Overflow");
label->setStyleSheet("background-color:salmon;");
QSizePolicy sp = label->sizePolicy();
sp.setVerticalPolicy(QSizePolicy::Maximum);
label->setSizePolicy(sp);
lay->addWidget(label);
for (int i=0; i<4; i++) {
lay->addWidget(new QPushButton(QString("pushbutton-%1").arg(i)));
}
w.show();
return a.exec();
}

Related

Why QGraphicsView is not shown

I have the following codes in my Qt project with the following main:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
The class Widget is a QWidget object with the following constructor:
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
m_Scene = new QGraphicsScene(this);
QGraphicsLinearLayout* layout = new
QGraphicsLinearLayout(Qt::Orientation::Vertical);
for(int i = 0; i < 10; i++)
{
std::string name = "m_" + std::to_string(i);
GraphicsTextItem* item = new GraphicsTextItem(nullptr, QString(name.c_str()));
layout->addItem(item);
}
QGraphicsWidget* list = new QGraphicsWidget;
list->setPos(0,0);
list->setLayout(layout);
m_Scene->addItem(list);
QGraphicsView* view = new QGraphicsView(this);
view->setScene(m_Scene);
// Why one of these lines must be uncommented?
//m_Scene->setSceneRect(0, 0, 1920, 768);
//QVBoxLayout *ttopLayout = new QVBoxLayout;
//ttopLayout->addWidget(view);
//setLayout(ttopLayout);
}
GraphicsTextItem is just a QGraphicsWidget for displaying text:
class GraphicsTextItem : public QGraphicsWidget
{
public:
QString m_Name;
QColor m_Color;
public:
GraphicsTextItem(QGraphicsItem * parent = nullptr, const QString& name = QString());
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override;
{
Q_UNUSED(option)
Q_UNUSED(widget)
QFont font("Times", 10);
painter->setFont(font);
painter->setPen(m_Color);
painter->drawText(0, 0, m_Name);
}
};
My question is that why my scene is not shown. I must either define a SceneRect or define a layout on my widget?
I made an even shorter MCVE for demonstration:
#include <QtWidgets>
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
QWidget qWinMain;
qWinMain.resize(320, 240);
QFrame qFrm(&qWinMain);
qFrm.setFrameStyle(QFrame::Box | QFrame::Raised);
qFrm.setLineWidth(0);
qFrm.setMidLineWidth(1);
qWinMain.show();
return app.exec();
}
compiled and started in cygwin64. This is how it looks:
There is a main window (with window manager decoration).
There is a child QFrame.
The child QFrame is "pressed" into the upper left corner.
How comes?
What QWidget does ensure: Child widgets are rendered (in front) when QWidget is rendered.
What QWidget is not (directly) responsible for: Layouting child widgets.
For this, a layout manager has to be plugged in:
#include <QtWidgets>
int main(int argc, char **argv)
{
qDebug() << "Qt Version:" << QT_VERSION_STR;
QApplication app(argc, argv);
QWidget qWinMain;
qWinMain.resize(320, 240);
QVBoxLayout qVBox(&qWinMain);
QFrame qFrm(&qWinMain);
qFrm.setFrameStyle(QFrame::Box | QFrame::Raised);
qFrm.setLineWidth(0);
qFrm.setMidLineWidth(1);
qVBox.addWidget(&qFrm);
qWinMain.show();
return app.exec();
}
compiled and started again in cygwin64. This is how it looks:
Now, the QFrame qFrm is filling the QWidget qWinMain nicely. Resize events received in qWinMain will be forwarded to the layout manager qVBox which will re-layout the children of qWinMain (i.e. qFrm) again.
I strongly believe OP's GraphicsView is just not visible because it has no minimal size requirement. (It's just to small to be visible.)
Hence, adding a layout manager ensures that the GraphicsView fills the parent widget client area. Resizing the contents of GraphicsView (by m_Scene->setSceneRect(0, 0, 1920, 768);) is yet another option to fix this, albeit the worse one.
Finally, the link to Qt Doc.: Layout Management.
Layout Management
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.
Introduction
Qt includes a set of layout management classes that are used to describe how widgets are laid out in an application's user interface. These layouts automatically position and resize widgets when the amount of space available for them changes, ensuring that they are consistently arranged and that the user interface as a whole remains usable.
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

How to have many layouts in one window?

I want to have some sequential actions; for example press a QPushButton and then delete the layout that is running and run another layout in the "SAME WINDOW"
In fact, I don't know what exactly layouts and widgets are!
Are they an object? an instance of object or what?
I found the bellow code in the Internet, I don't know how to change it to make it useful for me
#include <QApplication>
#include <QPushButton>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QWidget *window = new Qwidget;
QPushButton *button1 = new QPushButton("One");
QPushButton *button2 = new QPushButton("Two"); QPushButton *button3 = new QpushButton("Three");
QHBoxLayout *layout = new QHBoxLayout;
layout->addWidget(button1);
layout->addWidget(button2);
layout->addWidget(button3);
window->setLayout(layout);
window->show();
return app.exec();
}
A better way than deleting layout and setting a new one would be to use a QStackedWidget (docs) and the concept of pages. Using QStackedWidget you can show and hide pages as you wish.

QT Display a QWidget above QGraphicsScene

Currently I have a QGraphicsScene that is put inside a QGraphicsView and is shown on the display. I add all my elements to my scene that I set as the active scene.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QGraphicsView w;
GameScene *gameScene = new GameScene(); // GameScene extends QGraphicsScene, adds tons of elements to the scene
w.setScene(gameScene);
w.show();
return a.exec();
}
Above this scene I want a bar that contains several layout elements, like several QProgressBar.
For what I have found so far, QWidget's can be positioned easily. I've made already a widget of what I need to be displayed above the scene:
QWidget *dummyWidget = new QWidget();
QFormLayout *formLayout = new QFormLayout;
QProgressBar *bar1 = new QProgressBar();
QProgressBar *bar2 = new QProgressBar();
bar1->setValue(20);
bar2->setValue(100);
formLayout->addRow("&Health:", bar1);
formLayout->addRow("&Energy:", bar2);
dummyWidget->setLayout(formLayout);
dummyWidget->show();
But how do I get this to be displayed above my QGraphicsScene?
If you want to display your widget above the view you can have a layout similar to the one for dummyWidget and add the widget and view in it :
QGraphicsView w;
QWidget *widget = new QWidget();
QFormLayout *formLayout2 = new QFormLayout(widget);
QWidget *dummyWidget = new QWidget();
QFormLayout *formLayout = new QFormLayout;
QProgressBar *bar1 = new QProgressBar();
QProgressBar *bar2 = new QProgressBar();
bar1->setValue(20);
bar2->setValue(100);
formLayout->addRow("&Health:", bar1);
formLayout->addRow("&Energy:", bar2);
dummyWidget->setLayout(formLayout);
formLayout2->addRow("", dynamic_cast<QWidget*>(dummyWidget));
formLayout2->addRow("", dynamic_cast<QWidget*>(&w));
widget->show();
If you want to add the widget in the scene, you can use QGraphicsScene::addWidget which creates a new QGraphicsProxyWidget for widget, adds it to the scene, and returns a pointer to the proxy :
QGraphicsProxyWidget * item = gameScene->addWidget(dummyWidget);
item->setPos(100,100);
item->setZValue(1);
You can also add it to an item :
item->setParentItem(anOtherItem);

QLabel takes complete space

I have a QWidget which contains a QVBoxLayout and that layout contains a QLabel and QToolButtons. My problem is, that the QLabel takes all the space. The only solution I found is to set maximumHeight to the QLabel, but if I do that, the Qt::AlignTop doesn't work anymore.
main.cpp:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget window_main;
QWidget *widget_steps = new QWidget(&window_main);
widget_steps->setFixedWidth(75);
widget_steps->move(QPoint(0, 0));
widget_steps->setStyleSheet("background-color: red;");
QVBoxLayout *layout_steps = new QVBoxLayout(widget_steps);
layout_steps->setContentsMargins(0, 0, 0, 0);
layout_steps->setSpacing(0);
QLabel *label_steps_start = new QLabel("steps:");
label_steps_start->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
label_steps_start->setStyleSheet("background-color: blue;");
layout_steps->addWidget(label_steps_start);
QToolButton *tbutton_step1 = new QToolButton();
layout_steps->addWidget(tbutton_step1);
QToolButton *tbutton_step2 = new QToolButton();
layout_steps->addWidget(tbutton_step2);
QToolButton *tbutton_step3 = new QToolButton();
layout_steps->addWidget(tbutton_step3);
window_main.showMaximized();
return a.exec();
}
Here a picture that shows how much space the QLable takes(the blue space):
So please help to minimize the space the QLable takes :)
Your problem is that the tool buttons have a fixed size, and therefore when resizing, the label is the only type that can grow: Therefore:
After adding the label, add stretch to the layout:
layout_steps->addWidget(label_steps_start);
layout_steps->addStretch();
Modified code - adds stretch at the bottom. Label size remains fixed, and buttons remain under it. I've removed the whole main window around the outside for the sake of testing.
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget *widget_steps = new QWidget;
widget_steps->setFixedWidth(75);
widget_steps->move(QPoint(0, 0));
widget_steps->setStyleSheet("background-color: red;");
QVBoxLayout *layout_steps = new QVBoxLayout(widget_steps);
layout_steps->setContentsMargins(0, 0, 0, 0);
layout_steps->setSpacing(0);
QLabel *label_steps_start = new QLabel("steps:");
label_steps_start->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
label_steps_start->setStyleSheet("background-color: blue;");
layout_steps->addWidget(label_steps_start);
//--- Removed.... layout_steps->addStretch();
QToolButton *tbutton_step1 = new QToolButton();
layout_steps->addWidget(tbutton_step1);
QToolButton *tbutton_step2 = new QToolButton();
layout_steps->addWidget(tbutton_step2);
QToolButton *tbutton_step3 = new QToolButton();
layout_steps->addWidget(tbutton_step3);
layout_steps->addStretch(); //<----- Added!
widget_steps->show();
return a.exec();
}
One way you could do is to set the stretch factor for that particular widget inside the QVBoxLayout. You can find the documentation for that in here.
Basically, when you add a widget you can set that, for instance:
#include <QtWidgets/QApplication>
#include <QtWidgets/QLabel>
#include <QtWidgets/QToolButton>
#include <QtWidgets/QVBoxLayout>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget window_main;
QWidget *widget_steps = new QWidget(&window_main);
widget_steps->setFixedWidth(75);
widget_steps->move(QPoint(0, 0));
widget_steps->setStyleSheet("background-color: red;");
QVBoxLayout *layout_steps = new QVBoxLayout(widget_steps);
layout_steps->setContentsMargins(0, 0, 0, 0);
layout_steps->setSpacing(0);
QLabel *label_steps_start = new QLabel("steps:");
label_steps_start->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
label_steps_start->setStyleSheet("background-color: blue;");
layout_steps->addWidget(label_steps_start, 3, Qt::AlignTop);
layout_steps->addStretch();
QToolButton *tbutton_step1 = new QToolButton();
layout_steps->addWidget(tbutton_step1, 1);
QToolButton *tbutton_step2 = new QToolButton();
layout_steps->addWidget(tbutton_step2, 1);
QToolButton *tbutton_step3 = new QToolButton();
layout_steps->addWidget(tbutton_step3, 1);
window_main.showMaximized();
return a.exec();
}

How to make Qt subwidget height equal?

I have some QDockWidgets (not floating, only closable) inside a single QWidget.
I have some widgets inside each QDockWidget - their heights should be equal.
These inner widgets can be hidden through the context menu.
My inner widgets should have equal height. I done it this way:
void MyDocksPanel::redistributeSpace()
{
QBoxLayout * lay = (QBoxLayout *)layout();
for (int i = 0; i < lay->count(); i++)
{
QWidget * dock = lay->itemAt(i)->widget();
if (dock == NULL)
continue;
int size = 0;
foreach(QWidget * subWidget, dock->findChildren<QWidget*>())
size += subWidget->isVisible() ? 1 : 0;
if (dock->isVisible() && (size == 0))
dock->hide();
lay->setStretch(i, size);
}
}
All works fine until I add some const elements to each QDockWidget: some horizontal scrollbars and some Labels... Now my inner widgets have different sizes. But it is necessary for me to set their heights strongly equal.
QLayout lays out widget sizes on one level of a widget's hierarchy. How can I make height-equal subwidgets?
3 subwidgets vs 2 subwidgets
My first strategy to set stretches 3 and 2:
But, when i have added scroll bars:
Heights of my 5 widgets are equals to 37,37,37,28,28 ... and thats the problem
You're on the right track with the stretch factors, but think in terms of pixel values rather than small proportions. Try setting the stretch factor of each dock widget to this:
dockWidgetStretch = numChildWidgets * childWidgetMinimumHeight + scrollBarHeight;
where childWidgetMinimumHeight and scrollBarHeight are both expressed in pixels, and are both constants.
EDIT: Here is a working example. You might have to experiment a bit to get it to work with your program, but this should be a good start.
header.h
#include <QtGui>
class WidgetWith3Children : public QWidget
{
public:
WidgetWith3Children()
{
QTextEdit *edit1 = new QTextEdit;
QTextEdit *edit2 = new QTextEdit;
QTextEdit *edit3 = new QTextEdit;
QScrollBar *scrollBar = new QScrollBar(Qt::Horizontal);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(edit1);
layout->addWidget(edit2);
layout->addWidget(edit3);
layout->addWidget(scrollBar);
setLayout(layout);
}
};
class WidgetWith2Children : public QWidget
{
public:
WidgetWith2Children()
{
QTextEdit *edit1 = new QTextEdit;
QTextEdit *edit2 = new QTextEdit;
QScrollBar *scrollBar = new QScrollBar(Qt::Horizontal);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(edit1);
layout->addWidget(edit2);
layout->addWidget(scrollBar);
setLayout(layout);
}
};
class OuterWidget : public QWidget
{
public:
OuterWidget()
{
QDockWidget *dockWidget1 = new QDockWidget;
QDockWidget *dockWidget2 = new QDockWidget;
dockWidget1->setWidget(new WidgetWith3Children);
dockWidget2->setWidget(new WidgetWith2Children);
QVBoxLayout *layout = new QVBoxLayout;
// 71 is the height of the minimum size hint for QTextEdit
// 30 is the height of a horizontal scrollbar (on my system)
layout->addWidget(dockWidget1, 71 * 3 + 30);
layout->addWidget(dockWidget2, 71 * 2 + 30);
layout->setMargin(0);
setLayout(layout);
}
};
main.cpp
#include <QtGui/QApplication>
#include "header.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
OuterWidget w;
w.show();
return a.exec();
}
Just to check I understand: you have a QDockWidget which contains multiple children, as well as a label and a horizontal scrollbar. The label and scrollbar should be of fixed height, and the remaining vertical space should be divided between the child widgets.
If that's correct, all you need to do is add a QVBoxLayout to each QDockWidget. Add your widgets as I've done below:
QDockWidget DockWidget;
QVBoxLayout Layout = new QVBoxLayout(DockWidget);
FixedHeightWidget.setFixedHeight(10)
Layout.addWidget(FixedHeightWidget, 0);
Layout.addWidget(FirstVariableHeightWidget, 1);
Layout.addWidget(SecondVariableHeightWidget, 1);
Layout.addWidget(ThirdVariableHeightWidget, 1);
If you were to hide any of the widgets you've added to the layout, the layout will handle resizing the remaining visible children.