Qt QHBoxLayout percentage size - c++

How can I maintain an aspect ratio between two QHBoxLayouts?
For instance I want a QHBoxLayout to be one third of the entire window width and the other to be two thirds of the entire window width:
How can I achieve this? I tried messing with the size hints of the controls in them but that didn't work out

void QSizePolicy::setHorizontalStretch(uchar stretchFactor)
Example:
QHBoxLayout* layout = new QHBoxLayout(form);
QWidget* left = new QWidget(form);
QSizePolicy spLeft(QSizePolicy::Preferred, QSizePolicy::Preferred);
spLeft.setHorizontalStretch(1);
left->setSizePolicy(spLeft);
layout->addWidget(left);
QWidget* right = new QWidget(form);
QSizePolicy spRight(QSizePolicy::Preferred, QSizePolicy::Preferred);
spRight.setHorizontalStretch(2);
right->setSizePolicy(spRight);
layout->addWidget(right);

The answer of york.beta is working, but I prefer much less code.
At least the sizePolicy is by default Prefered/Prefered.
The default policy is Preferred/Preferred, which means that the widget can be freely resized, but prefers to be the size sizeHint() returns.
You can simply use the second parameter of addWidget to stretch the widgets.
QHBoxLayout *layout = new QHBoxLayout( this );
layout->setContentsMargins( 0, 0, 0, 0 );
layout->setSpacing( 0 );
QPushButton *left = new QPushButton( "133px", this );
left->setStyleSheet( "QPushButton{border: 1px solid red;}" );
QPushButton *right = new QPushButton( "267px", this );
right->setStyleSheet( "QPushButton{border: 1px solid blue;}" );
layout->addWidget( left, 33 );
layout->addWidget( right, 66 );
this->setLayout( layout );
this->setFixedWidth( 400 );
See http://doc.qt.io/qt-5/qboxlayout.html#addWidget
and http://doc.qt.io/qt-5/qwidget.html#sizePolicy-prop

You can also use the layoutStretch property:
https://doc.qt.io/qt-5/layout.html#stretch-factors
In your case it'd be
<layout class="QHBoxLayout" name="horizontalLayout" stretch="1,2">

You can edit the sizePolicy for the widgets and set a higher horizontalStretch for the widget in the right.

Related

Is possible to have a widget that doesnt scroll in a QScrollArea?

Given this example:
Suppose A and B are QWidgets
Is it possible to keep everything starting from B static when the QScrollArea widget is scrolled?
The QScrollArea occupies the entire GUI, B is inside it to let the vertical ScrollBar closer to the right border of the GUI, or it would look like this:
What I can do in this case?
It is possible. You have to make use of the scrollbar being a widget of its own.
Here is the method:
Change your window in Qt Designer to be: spacer, QScrollArea (that will contain widget A), spacer, widget B, spacer; everything is in a QGridLayout (or QHBoxLayout but please read until the end).It is because widget B is outside the scroll area that it will not move during scrolling.
In your widget constructor, after ui->setupUi(); reparent and move the vertical scrollbar of your QScrollArea with these lines of code:
scrollArea->verticalScrollBar()->setParent(w);
layout->addWidget(sa->verticalScrollBar()); //Adds the scrollbar to the right of the layout.
Note about the margins:Obviously, you can easily push the scrollbar to the very right of your window by setting the layout right margin to 0.
If you also want it to cover the entire height of your window, while keeping some space between the other widgets and the window's border, that is where a QHBoxLayout will not suffice and you need a QGridLayout instead, set its top and bottom margin to 0 and add spacers (fixed size) to obtain the same visual result.
The C++ code for such a window would be:
QWidget* widget = new QWidget();
QGridLayout* layout = new QGridLayout(widget);
layout->setSpacing(0);
layout->setContentsMargins(9, 0, 0, 0);
widget->setLayout(layout);
QScrollArea* scrollArea = new QScrollArea(widget);
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
QWidget* widgetB = new QLabel("Widget B", widget);
//Creates the spacer that will "simulate" the top and bottom margins
QSpacerItem* topSpacer = new QSpacerItem(0, 9, QSizePolicy::Minimum, QSizePolicy::Fixed),
* bottomSpacer = new QSpacerItem(0, 9, QSizePolicy::Minimum, QSizePolicy::Fixed);
layout->addItem(topSpacer, 0, 0);
layout->addWidget(scrollArea, 1, 0);
layout->addItem(bottomSpacer, 2, 0);
layout->addWidget(widgetB, 1, 1);
//Moves the scrollbar outside the scroll area
scrollArea->verticalScrollBar()->setParent(widget);
layout->addWidget(scrollArea->verticalScrollBar(), 0, 2, 3, 1);
QLabel* innerLabel = new QLabel("Some big label to force the scrolling");
scrollArea->setWidget(innerLabel);
innerLabel->setMinimumHeight(500);
widget->show();

How to properly manage text and icon with QToolButton?

I have code snippet looks like this:
nextPageBtn = new QToolButton();
nextPageBtn->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
nextPageBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
nextPageBtn->setIcon(QIcon(":/next.png"));
nextPageBtn->setText("Next");
Currently I have two problems with this.
First:
I want the text is on the left of the icon, but with the code I provide, the text is on the right like this:
Second:
When the window is enlarged, I can not figure out a way to keep the text and icon in the center of the button. It looks like this when the button gets bigger:
Edit:
This is how I manage the layout:
nextPageHLayout = new QHBoxLayout; //This is the layout for QToolButton, it has two spacers and a QToolButton
mainVLayout->addLayout(nextPageHLayout); //mainVLayout is the main layout, and I put the mainVLayout to the central widget, and it also contains a QLabel above the nextPageHLayout
QSpacerItem *leftBtnSpacer = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Fixed);
nextPageHLayout->addSpacerItem(leftBtnSpacer);
nextPageBtn = new QToolButton(mainWidget);
nextPageHLayout->addWidget(nextPageBtn);
nextPageBtn->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
nextPageBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
nextPageBtn->setIcon(QIcon(":/next.png"));
nextPageBtn->setText("Next");
QSpacerItem *rightBtnSpacer = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Fixed);
nextPageHLayout->addSpacerItem(rightBtnSpacer);
You must make the following changes:
You should not change the size policy of the QToolButton, that's why it's expanding.
You must change the layoutDirection to Qt::RightToLeft.
QHBoxLayout * nextPageHLayout = new QHBoxLayout; //This is the layout for QToolButton, it has two spacers and a QToolButton
mainVLayout->addLayout(nextPageHLayout); //mainVLayout is the main layout, and it also contains a QLabel above the nextPageHLayout
QSpacerItem *leftBtnSpacer = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
nextPageHLayout->addSpacerItem(leftBtnSpacer);
nextPageBtn = new QToolButton(mainWidget);
nextPageHLayout->addWidget(nextPageBtn);
nextPageBtn->setToolButtonStyle(Qt::ToolButtonTextBesideIcon);
nextPageBtn->setIcon(QIcon(":/next.png"));
nextPageBtn->setText("Next");
nextPageBtn->setLayoutDirection(Qt::RightToLeft);
QSpacerItem *rightBtnSpacer = new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum);
nextPageHLayout->addSpacerItem(rightBtnSpacer);

How to align children in a QHBoxLayout Left, Center and Right

I have a QHBoxlayout with children set up as follows:
QHBoxlayout h = new QHBoxlayout();
QLLabel leftLabel = new QLLabel("Left");
QLLabel centerLabel = new QLLabel("Center");
QHBoxlayout rightContent = new QHBoxlayout();
QLLabel r1 = new QLLabel("2");
QLLabel r2 = new QLLabel("3");
rightContent.addWidget(r1);
rightContent.addWidget(r2);
h.addWidget(leftLabel);
h.addWidget(centerLabel);
h.addLayout(rightContent);
This will create a QHBoxlayout with all the children floated on the left. I would like leftLabel to be on the left, centerLabel at the center, and rightContent to the extreme right.
How can I achieve this?
Thank you all in advance.
UPDATE
I would like something like left, center, right below:
Just add spacers between "Left", "Center" and "Right":
QHBoxLayout *h = new QHBoxLayout(&parentWidget);
h->addWidget(leftLabel);
h->addStretch()
h->addWidget(centerLabel);
h->addStretch()
h->addLayout(rightLabel);
Might be helpful to practice in Qt Designer.
Depending on what you want, you could also define the alignment while adding the widgets like so:
auto h = new QHBoxLayout();
h->addWidget(leftLabel, 0, Qt::AlignLeft);
h->addWidget(centerLabel, 0, Qt::AlignCenter);
h->addLayout(rightLabel, 0, Qt::AlignRight);
See here for more Information

QScrollArea - scrollbar for a widget with many childs

So, I got some problems with QScrollArea.
I want to put a widget with many children in a QScrollArea, but I just dont get any scrollbars.
Heres the code:
QDialog *dialog = new QDialog();
QVBoxLayout *dialoglayout = new QVBoxLayout( dialog );
QScrollArea *area = new QScrollArea();
dialoglayout->setMargin( 0 );
dialoglayout->addWidget( area );
area->setAlignment( Qt::AlignCenter );
area->setAlignment( Qt::AlignTop );
area->setWidgetResizable( true );
// mainwidget has a lot of children
QWidget *mainwidget = randomclass.getWidget();
QVBoxLayout *mainwidgetlayout = new QVBoxLayout( mainwidget );
dialog->setWindowFlags( Qt::Window );
area->setWidget( mainwidget );
dialog->showMaximized();
if the mainwidget is bigger than the scrollarea, the content just overflows.
Can anyone help me?
Thanks in advance.
Some widgets don't report the area to scroll and that causes the confusion with scroll area. For QScrollarea object to adjust to the content:
myWidget->setMinimumSize(myWidget->sizeHint()); // assume the min size
scrollArea->setWidget( myWidget ); // use that widget in scroll area
Specific scrollers also might need to be enabled depending on the content:
scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
scrollArea->setHorizontalScrollBarPolicy(Qt::ScrollBarAsNeeded);

Having a QScrollArea dynamically fit its parent but does not expand for its children

I seem to be having an issue with getting the behavior I want from a QScrollArea. As it stands, whenever I add something to the layout of the widget set as the target of the scroll area it will rather opt to expand the entire window rather than fit to scroll.
Here's my current setup:
QSplitter * mainArea = new QSplitter( Qt::Vertical );
QWidget * containment = new QWidget;
containment->setLayout( new QVBoxLayout );
currentStructures = new QWidget;
currentStructures->setLayout( new QVBoxLayout );
currentStructures->layout()->setAlignment( Qt::AlignTop );
QScrollArea * scroll = new QScrollArea();
scroll->setWidget( currentStructures );
containment->layout()->addWidget( currentStructures );
mainArea->addWidget( containment );
mainArea->addWidget( new QWidget ); //TODO: create preview bar
this->layout()->addWidget( mainArea );
This makes it so that the scroll area only ever expands and never shows the scroll bars.
By inserting this line:
containment->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Ignored );
I can get the area to ignore the size of its children but it also doesn't take up the space needed nor does it show scroll bars -- it just crunches the widget inside of it.
I'm a bit of a greenhorn to using Qt, but I was wondering how I would achieve the behavior that I need: I would like the scroll area to greedily take up the area it has available from its parent layout but not expand the the containing layouts vertically when adding but instead actually shows scroll bars. I'm planning on allowing a lot of resizing, so it needs to actually scale to the parent instead of just being a fixed size. I am at a loss as to how I should proceed in solving this aspect of my GUI. Thank you for your time.
If you would like to tackle this problem visually, here's a harness that you can use. I greatly appreciate your help.
#include <QtWidgets\qapplication.h>
#include <QtWidgets\qsplitter.h>
#include <QtWidgets\qlayout.h>
#include <QtWidgets\qscrollarea.h>
#include <QtWidgets\qpushbutton.h>
#include <QtWidgets\qlabel.h>
#include <QtWidgets\qsizepolicy.h>
int main( int argc, char * argv[] )
{
QApplication app( argc, argv );
QWidget * testWidget = new QWidget;
testWidget->setLayout( new QVBoxLayout );
//////////////////CODE IN QUESTIION//////////////////////
QSplitter * mainArea = new QSplitter( Qt::Vertical );
QWidget * containment = new QWidget;
containment->setLayout( new QVBoxLayout );
//containment->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Ignored );
QWidget * currentStructures = new QWidget;
currentStructures->setLayout( new QVBoxLayout );
currentStructures->layout()->setAlignment( Qt::AlignTop );
QScrollArea * scroll = new QScrollArea();
scroll->setWidget( currentStructures );
containment->layout()->addWidget( currentStructures );
mainArea->addWidget( containment );
///////////////////////////////////////////////////////////
QPushButton * pushIntoLayout = new QPushButton( "Add Element to Widget" );
QWidget::connect( pushIntoLayout, &QPushButton::clicked, [currentStructures](){ currentStructures->layout()->addWidget( new QLabel( "A generated label" ) ); } );
mainArea->addWidget( pushIntoLayout );
currentStructures->setStyleSheet(
"QWidget {"
"background-color: #FAA;"
"}"
);
testWidget->layout()->addWidget( mainArea );
testWidget->show();
return app.exec();
}
Even after copying and pasting my code to make a test harness I had not noticed my grievous error. I had mistakenly pushed the currentStructures widget to the container's layout rather than scroll, the scrolling area after giving it its child.
An excerpt from the Qt docs on void QScrollArea::setWidget( QWidget * widget ) for those unfamiliar:
The widget becomes a child of the scroll area, and will be destroyed when the scroll area is deleted or when a new widget is set.
Thanks to everyone who looked it over.
For those wondering, the fixed code would look like the following:
QSplitter * mainArea = new QSplitter( Qt::Vertical );
QWidget * containment = new QWidget;
containment->setLayout( new QVBoxLayout );
QWidget * currentStructures = new QWidget;
currentStructures->setLayout( new QVBoxLayout );
currentStructures->layout()->setAlignment( Qt::AlignTop );
QScrollArea * scroll = new QScrollArea();
scroll->setWidget( currentStructures );
scroll->setWidgetResizable( true );
containment->layout()->addWidget( scroll );
mainArea->addWidget( containment );
mainArea->addWidget( new QWidget ); //TODO: create preview bar
this->layout()->addWidget( mainArea );
Cheers!