Qt: "Expanding" doesn't work inside a layout - qt

My purpose is to create a scrollable control with a QVBoxLayout inside of it that has various controls (say buttons) on it. That control is put on a *.ui form. In the constructor for that control I write the following code:
MyScrollArea::MyScrollArea(QWidget *parent) :
QScrollArea(parent)
{
// create the scrollable container
this->container = new QWidget(); // container widget member
this->container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
this->container->setContentsMargins(QMargins(0,0,0,0));
this->content = new QVBoxLayout(); // layout member
this->content->setMargin(0);
this->content->setSpacing(0);
for (int i=0; i<100; i++)
{
QPushButton * widget = new QPushButton();
widget->setText("button");
widget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Fixed);
this->content->addWidget(widget);
}
this->container->setLayout(this->content);
this->content->layout();
this->setWidget(this->container);
}
My problem: the buttons have a fixed size and do not expand horizontally. they have a fixed size. i'd like them to expand horizontally to fill the row they're in. How can I get them expanding horizontally across their parent container?

Try calling this->setWidgetResizable(true);

Related

QVBoxLayout push to the top with spacer

I would like to add push buttons to the layout. The newest item would be on the top of the layout.
I also would like position the buttons to the top, thus I am using QSpacerItem.
Here is what I have tried so far.
Constructor:
//frame is a QFrame
lVertical = new QVBoxLayout(frame); //private variable
lVertical->setMargin(0);
lVertical->setSpacing(0);
auto verticalSpacer = new QSpacerItem(10, 20, QSizePolicy::Minimum, QSizePolicy::Expanding);
lVertical->addItem(verticalSpacer);
connect(b, &QPushButton::clicked, this, &MainWindow::addToLayout);
Function:
void MainWindow::addToLayout() {
QPushButton* button = new QPushButton(frameSlider);
button->setText(QString::number(i));
++i; //private variable
layoutVertical->addWidget(button);
}
Currently I add like this:
But I would like to add like this:
The problem is that you have placed a spacer at the beginning so it will stretch all the widgets down.
One possible solution is to add a stretch and then insert an element before, You should not use QSpacerItem:
// constructor
layoutVertical = new QVBoxLayout(frame);
layoutVertical->setMargin(0);
layoutVertical->setSpacing(0);
layoutVertical->addStretch();
void MainWindow::addToLayout() {
QPushButton* button = new QPushButton();
layoutVertical->insertWidget(0, button);
}

Finding QGridLayout elements in Qt

I created labels and added them to the layout. How to get the elements from the layout? I tried to use method children(), but it gives empty list... Is there a way to get them? Some sample code below.
QGridLayout* layout = new QGridLayout();
QLabel* test = new QLabel();
test->setPixmap(m_staticStorage->getFirstImg());
test->setScaledContents(true);
QLabel* test2 = new QLabel();
test2->setMaximumSize(50,50);
test2->setPixmap(m_staticStorage->getSecondImg());
tes2->setScaledContents(true);
layout->addWidget(worker, 0, 0);
layout->addWidget(farmer, 0, 1);
ui->verticalLayout->addLayout(layout);
//layout->children() ->>>> empty
This will iterate over any QLayout subclass to find the items which have been added so far:
for (int i=0; i < layout->count(); ++i) {
QLayoutItem *item = layout->itemAt(i);
if (!item || !item->widget())
continue;
QLabel *label = qobject_cast<QLabel*>(item->widget());
if (label) {
// .... do stuff with label
}
}
One can also iterate in a similar fashion over each row or column of a QGridLayout using QGridLayout::columnCount() or QGridLayout::rowCount() and then QGridLayout::itemAtPosition() to get the actual QLayoutItem.
If you need to uniquely identify the QLabel after finding it, you could for example give each label a unique objectName or do a setProperty() on them with a unique ID when creating them.
QLabel *test1 = new QLabel(this);
test1->setObjectName(QStringLiteral("test1"));
....
if (label) {
if (!label->objectName().compare(QLatin1String("test1")))
// this is "test1" label
}
QLabel *test1 = new QLabel(this);
test1->setProperty("id", 1);
....
if (label) {
if (label->property("id").toInt() == 1)
// this is "test1" label
}
Better to use the function QObject::findChild
Qt is returning all children of a given Type and Objectname. You can decide to get only direct children or also all recursively.
this->findChild<QLabel*>(QString(), Qt::FindDirectChildrenOnly);
This will return all direct children of this (where this is your parent widget, not your layout) with any name and of type QLabel*
Your approach do not work because the layout do not take the ownership of the labels:
From Layout Management:
Tips for Using Layouts
When you use a layout, you do not need to pass a parent when
constructing the child widgets. The layout will automatically
reparent the widgets (using QWidget::setParent()) so that they are
children of the widget on which the layout is installed.
Note: Widgets in a layout are children of the widget on which the
layout is installed, not of the layout itself. Widgets can only have
other widgets as parent, not layouts.
You can nest layouts using addLayout() on a layout; the inner layout
then becomes a child of the layout it is inserted into.
BTW: Don't forget to set a parent for your layout
QGridLayout* layout = new QGridLayout(this);
and for your labels too
QLabel* test2 = new QLabel(this);

QT. Add new layout to QLayout

I create interface dynamically when Application is run.
1) I have QTabWidget with 4 predefined tabs. But i must show only 1 or 2 tabs, in case of user shoice. On StackOverflow i learned, that i must keep all tabs in collection to add and destroit it.
I have QHash: twInputMethodsTabs = new QHash< int, QPair<QWidget*, QString> >();
First argument = index; Second = Tab Widget; Third = Tab Widget Caption Text;
2) I fill the collection like this:
for(int i = 0; i < ui->twInputMethods->children().length(); i++)
{
twInputMethodsTabs->insert(i, QPair<QWidget*, QString>(ui->twInputMethods->widget(i), ui->twInputMethods->tabText(i)));
}
3) I add new widget in the tab like this:
twInputMethodsTabs->value(1).first->layout()->addWidget(cmbbCommands);
4) How can i add new layout to this widget? I want to do like this:
QHBoxLayout *hblParams =new QHBoxLayout();
twInputMethodsTabs->value(1).first->layout()->addLayout(hblParams);
But it does not work, because layout() returns QLayout which havent addLayout() function. How i can do this?
Or how can i should change architecture of code to do this?
In this following code you get a widget (.first) and then select that widget's layout ->layout() and then add a Widget to that layout ->addWidget().
twInputMethodsTabs->value(1).first->layout()->addWidget(cmbbCommands);
In the following code you get a widget (.first) and then select that widget's layout ->layout() and try to set the layout on the layout.
twInputMethodsTabs->value(1).first->layout()->addLayout(hblParams);
Replacing the QLayout
To set the layout on the parent widget, you need to drop the ->layout():
twInputMethodsTabs->value(1).first->addLayout(hblParams);
Note that since you are now adding an empty layout to the widget, any widgets current in the previous layout will be lost, so you may need to re-add the widgets to the layout.
Adding new QLayout inside existing QLayout
If you want to add a layout into the existing layout, you cannot do this directly. QLayout can only accept QWidget via .addWidget(). However, you can apply a layout to an empty QWidget() and then add that to the layout. For example:
QWidget *w = new QWidget();
w.addLayout(hblParams);
twInputMethodsTabs->value(1).first->layout()->addWidget(w);
An alternative is to set the layout on the QWidget to a layout that does support .addLayout() such as QHBoxLayout or QVBoxLayout. For example:
QVBoxLayout *l = new QVBoxLayout();
cmbbCommands.setLayout(l); // Now has a layout that supports .addLayout
twInputMethodsTabs->value(1).first->layout()->addWidget(cmbbCommands);
Now the following should work because ->layout() returns a QVBoxLayout:
QHBoxLayout *hblParams =new QHBoxLayout();
twInputMethodsTabs->value(1).first->layout()->addLayout(hblParams);
I Hope, I get what you want to do:
twInputMethodsTabs->value(1).first->layout()->addWidget(cmbbCommands);
QHBoxLayout *hblParams =new QHBoxLayout();
QWidget *w = new QWidget(twInputMethodsTabs->value(1).first);
twInputMethodsTabs->value(1).first->layout()->addWidget(w);
w->addLayout(hblParams);
I just wrote the code here, so it is untested. However it should give you an idea what I tried to explain in my comment.
Cutted from "working" application:
WidgetA::WidgetA(QWidget *parent) : QWidget(parent)
{
QVBoxLayout *pLayout = new QVBoxLayout();
buildWidget(pLayout);
this->setLayout(pLayout);
}
void WidgetA::buildWidget(QVBoxLayout *layout){
for(int i=0; i<4; ++i){
this->buildSegments(layout);
}
}
void WidgetA::buildSegments(QVBoxLayout *layout){
QHBoxLayout *pHbl = new QHBoxLayout();
QLabel *pSegmentSize = new QLabel(this);
pSegmentSize->setText(tr("Segment Size(1,N)"));
QSpinBox *pSegments = new QSpinBox(this);
pHbl->addWidget(pSegmentSize);
pHbl->addWidget(pSegments);
layout->addItem(pHbl);
}
Read this one: Widgets Tutorial - Nested Layouts

QMainWindow : Set widgets size with respect to screen size

I have a Qt class which inherits from QMainWindow. The constructor of the class creates two widgets which are added to a horizontal layout object as follows:
MyWindow::MyWindow()
{
resize(QDesktopWidget().availableGeometry(this).size());
display = new MyWidget(this);
display->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding);
current = new MyWidget(this);
current->setSizePolicy(QSizePolicy::Expanding,
QSizePolicy::Expanding);
QHBoxLayout *layout = new QHBoxLayout();
layout->addWidget(display);
layout->addWidget(current);
QFrame* frame = new QFrame();
frame->setFrameShape(QFrame::StyledPanel);
frame->setLayout(layout);
setCentralWidget(frame);
show();
}
This currently shows the widget side of side of each other. However, what I would like to do is have one of the widgets take 30% of the horizontal space while the other one occupies the other 70%. I would also like the widgets to expand or contract if one resizes the main window but keeping these ratios.
When you place a widget into a layout you can specify its stretch factor:
QHBoxLayout *layout = new QHBoxLayout();
layout->addWidget(display, 3);
layout->addWidget(current, 7);
http://doc.qt.io/qt-5/qboxlayout.html#addWidget

Setting initial size of QTabWidget

I am creating a QTabWidget that has a number of tabs and each tab contains a different number of push buttons. Because some of these tabs may include many push buttons, I would like to make each tab scrollable.
I have an implementation that sort of works except no matter what I try I can't get the darn QTabWidget to be of a particular vertical size - it always wants to size itself based on the maximum height used by one of its pages. I've tried changing size policies, layout strategies and nothing works.
Here is exact widget structure I am using:
QFrame
QSplitter
QTabWidget
QFrame
I populate the QTabWidget with QWidget instances that serve as pages. These instances are wrapped around with a QScrollArea and use a QGridLayout so that I can populate the tab with push buttons in a grid.
Here is the actual code that populates the tab widget:
for (const std::string &tabName : tabs) {
// Grid layout for each tab
QGridLayout *gridLayout = new QGridLayout();
// The tab widget itself
QWidget *page = new QWidget(tabWidget);
page->setLayout(gridLayout);
// Wrap tab with a scroll area so that it's scrollable when the tab widget
// is resized to be smaller than the page size
QScrollArea *scrollArea = new QScrollArea(tabWidget);
scrollArea->setWidgetResizable(true);
scrollArea->setWidget(page);
tabWidget->addTab(scrollArea, tabName.c_str());
// Populate tab with push buttons
const std::vector<std::string> &buttons = GetButtons(tabName);
for (const std::string &buttonName : buttons) {
QPushButton *button = new QPushButton(buttonName.c_str());
button->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
QSize buttonSize(130, 20);
button->setFixedSize(buttonSize); // <=== this does work
int row = i / numColumns;
int col = i % numColumns;
gridLayout->addWidget(button, row, col, Qt::AlignLeft | Qt::AlignTop);
}
}
// This does not work
const int fixedVerticalSize = 120;
tabWidget->resize(tabWidget->size().width(), fixedVerticalSize);
BTW, I am using Qt 5.0.2 on Mac OS X 10.8.2.

Resources