Prevent widgets stretching in QVBoxLayout and have scrollbar appear in QScrollArea? - qt

Making my way up the Qt learning curve, I've seen many questions about dynamic layouts but the solutions aren't working for me or I don't quite understand them.
Reference questions:: Qt Scroll Area does not add in scroll bars, How can i make widgets overflow to make a scrollbar appear in Qt?
Question:: I want to have a dynamic layout of a set of widgets within a QScrollArea. I've been able to do this manually in Qt Creator and now I am trying to do it through code.
How do I prevent the widgets from stretching/force the area to scroll?
How do I have the added widgets start from the top? I have a vertical spacer in my QVBoxLayout but that pushes everything to the bottom.
Simple test code::
void MainWindow::on_pushButton_clicked()
{
ui->myScroll->setWidgetResizable(true); //making sure this is set
QPushButton *b = new QPushButton(this);
b->setText(QString("Hello Button"));
QHBoxLayout *h = new QHBoxLayout();
h->addWidget(b,0);
ui->myVBoxLayout->addLayout(h,0);
}
Result:: Left side squished (dynamic) – Right side Ok (set up manually)
Qt Creator Setup:: Left side: dynamic – Right side set up manually
Properties::

You can set use setMinimumHeight() on your buttons for preventing squished buttons. The layout can be configured with setContentsMargin() for space between item-border and item-content (QtDesigner has all four directions set to 9 IIRC) and setSpacing() for space between items (QtDesigner uses a default of 6). Also setWidgetResizable(true) allows your scrollarea to resize the view widgeth inside the area (this is where your layout and children are being placed).
This works for me:
In constructor or code set scrollArea->widget() to hold the QVBoxLayout:
v = new QVBoxLayout;
ui->scrollArea->widget()->setLayout(v);
In Button Slot:
void MainWindow::pushButtonPressed()
{
ui->scrollArea->setWidgetResizable(true);
QPushButton *b = new QPushButton(this);
b->setText(QString("Hello Button"));
QHBoxLayout *h = new QHBoxLayout();
h->addWidget(b,0);
v->addLayout(h);
}

Related

How to control widgets position insid QStackedLayout

I've created a QStackedLayout in which I added 2 buttons, but I want to shift one button to the right corner insted of the left corner as you can see in the picture below:
What I've done so far:
QStackedLayout *stackedLayout = new QStackedLayout;
stackedLayout->addWidget(ui->pushButton_2);
stackedLayout->addWidget(ui->pushButton);
stackedLayout->setStackingMode(QStackedLayout::StackAll);
Place the QPushbutton in a container (i.e. QWidget) that allows it to be centered with an additional layout.
If I understand properly, you want a QPushButton above an other. The one above must be smaller and at the top right corner?
QVBoxLayout * vlayout_PB_under = new QVBoxLayout();
QHBoxLayout * hlayout_PB_under = new QHBoxLayout();
QPushButton * PB_x = new QPushButton("X",this);
QPushButton * PB_test = new QPushButton("test",this);
PB_x ->setFixedSize(20,20);
PB_test ->setFixedSize(200,200);
PB_test ->setLayout(vlayout_PB_under );
vlayout_PB_under ->addLayout(hlayout_PB_under);
hlayout_PB_under->addWidget(PB_x );
vlayout_PB_under ->setAlignment(Qt::AlignRight);
hlayout_PB_under->setAlignment(Qt::AlignTop );
The purpose of a QStackLayout is to allow switching easily the visible widget at a same position, not to display at the same time multiple widget...
Read the documentation : "where only one widget is visible at a time"

Image scaling is ignored when adding QLabel with image at runtime

I am trying to add a QLabel with an image to my GUI at runtime, but the scaling is ignored and the image expands to its full size (which is larger than the screen), ignoring the size constraints and not scaling the contents correctly.
The image should be fit into the bottom, left side of the window, as my GridLayout describes here:
headerPnl= new HeaderPnl();
buttonPnl = new ButttonPnl;
mainContentPnl = new QStackedWidget;
mainLayout = new QGridLayout;
mainLayout->setMargin(0);
mainLayout->setSpacing(0);
mainLayout->addWidget(headerPnl, 0, 0, 1, 7);
mainLayout->addWidget(mainContentPnl, 1, 0, 10, 6);
mainLayout->addWidget(buttonPnl, 1, 6, 10, 1);
mainLayout->setRowStretch(0,1);
mainLayout->setRowStretch(1,2);
mainLayout->setRowStretch(2,2);
mainLayout->setRowStretch(3,2);
mainLayout->setRowStretch(4,2);
mainLayout->setRowStretch(5,2);
mainLayout->setRowStretch(6,2);
mainLayout->setColumnStretch(0,1);
mainLayout->setColumnStretch(1,1);
mainLayout->setColumnStretch(2,1);
mainLayout->setColumnStretch(3,1);
mainLayout->setColumnStretch(4,1);
mainLayout->setColumnStretch(5,1);
mainLayout->setColumnStretch(6,1);
this->setLayout(mainLayout);
The header goes across the top, the button panel goes along the right side, and the rest of the screen is changing depending on the workflow of the application (ie what buttons are pressed, etc).
When necessary, my GUI replaces the widgets and updates the GUI like this:
void MainWindow::setContentPane(QWidget *content){
mainLayout->replaceWidget(contentPnl, content);
contentPnl = content;
}
void MainWindow::setButtonPanel(QWidget *buttonPanel){
mainLayout->replaceWidget(buttonPnl, buttonPanel);
buttonPnl = buttonPanel;
}
void MainWindow::configureWelcome(){
QLabel *welcomeLbl = new QLabel;
welcomeLbl->setObjectName("welcomeLbl");
welcomeLbl->setPixmap(QPixmap(":/images/welcome.jpg"));
welcomeLbl->setScaledContents(true);
CustomWidget *welcomeWidget = new CustomWidget;
QHBoxLayout *welcomeLayout = new QHBoxLayout;
welcomeLayout->addWidget(welcomeLbl);
welcomeWidget->setLayout(welcomeLayout);
setContentPane(welcomeWidget);
CustomWidget *buttonPnl = createWelcomeButtonPanel();
setButtonPanel(buttonPnl);
}
How can I make this image fit inside the GridLayout properly? It seems like when adding widgets to a layout that has already been set, the GUI doesn't know how to handle the size constraints from the GridLayout. Replacing the buttons works fine, but adding an image does not.
Side question: I have been trying to stay away from a QStackedWidget, as this application is designed for a lower power system, and it doesn't make sense to me to create all the possible screens and add them all to a QStackedWidget when the application starts. Instead I would rather use the resources when necessary, and only create all the GUI elements when I need to (ie, when the right buttons are clicked). Does that make sense?
Did you had a QSizePolicy to the widget containing the QGridLayout? I suggest an horizontal and vertical QSizePolicy::Fixed.
In your first code segment add :
this->setSizePolicy(QSizePolicy::Fixed,QSizePolicy::Fixed);

Allignment of a QLabel and a QCheckBox in a BoxLayout gives unexpected result

when i add a QLabel and QCheckBoxs to either a QVBoxLayout or a QHBoxLayout i would expect them to be evenly distributed but the checkboxes will allign tight at the very bottom (in the above example) and the label will be centered on the resulting free space in the widget. How can i change this behaviour to distribute all 3 widgets evenly?
Many thanks.
This is the example code:
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
QLabel* l = new QLabel("Hi");
QCheckBox* c = new QCheckBox("Label");
QCheckBox* c2 = new QCheckBox("Label");
l->setText("Hi");
QVBoxLayout* v = new QVBoxLayout;
v->addWidget(l);
v->addWidget(c);
v->addWidget(c2);
setLayout(v);
ui->setupUi(this);
}
And this is the result:
Take a look at QSizePolicy. You need to setSizePolicy for your QLabel and QCheckBoxes to be QSizePolicy::Preferred, from the docs:
The default policy is Preferred/Preferred, which means that the widget
can be freely resized, but prefers to be the size sizeHint() returns.
Button-like widgets set the size policy to specify that they may
stretch horizontally, but are fixed vertically. The same applies to
lineedit controls (such as QLineEdit, QSpinBox or an editable
QComboBox) and other horizontally orientated widgets (such as
QProgressBar).
Currently, your QLabel has preferred height, while both of the QCheckBoxes has fixed height. That means that the QLabel will be expanded automatically to take up any additional vertical space (that can't be taken by the QCheckBoxes.
So, In order to set all your widgets to Preferred height, you need to add the following to your constructor:
l->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
c->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
c2->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
Another option, would be to add spacers around each one of the widgets, like this:
v->addStretch();
v->addWidget(l);
v->addStretch();
v->addWidget(c);
v->addStretch();
v->addWidget(c2);
v->addStretch();
setLayout(v);
This way QSpacerItems will take up all the additional space.

Need to display the widgets inside my QListWidget with an offset, basically shifted a bit to the right

I have a panel.ui file done using QTDesigner. It's a QFrame class, rectangular shape with few labels on it. And I have a QListWidget class where I insert 3 instances of the panel.ui.
I create a QListWidgetItem and then use List->SetItemWidget(..) to populate my list.
The Result is a list filled with three panels. I was also able to move the panels inside the list using dragDropMode internalMove.
I also tested the ability to shift the panels a bit to the right when I click on them and that worked:
in procedure List::mousePressEvent(QMouseEvent *event)
Panel *child = static_cast<Panel*>(childAt(event->pos()))
...
int y= child->pos().y();
int x = child->pos().x();
child->move (x +10, y); `
Problem: When I run the app and display the list, I want all the panels to be displayed with that 10 offset to the right. So in the List constructor and inside the loop after this->setItemWidget(myPanelItem, myPanel); I try using myPanel->move() like above but it doesn't seem to work.
I run the app, the panels are displayed without my offset ( not sure why?) but when I click on one, it shifts.
move() won't work reliably since the widgets are in a layout. (Well, not a layout as in a QLayout, but the effect is comparable: When any metric in your application changes, e.g. you resize or scroll the list, the widgets are repositioned by the list widget.)
What you can do is wrap your actual widget in a container widget with a layout margin:
QWidget* wrapIntoContainerForOffset(QWidget* widget, int offset /*in pixels*/) {
QWidget* container = new QWidget;
QHBoxLayout* layout = new QLayout(container);
layout->setContentsMargins(/*left=*/ offset, /*others=*/ 0, 0, 0);
layout->addWidget(widget);
return container;
}
Then you add these containers to the listwidget instead.
Have You tried StyleSheets. The QListWidget supports Box model( http://doc.qt.digia.com/qt/stylesheet-customizing.html#box-model ). So You may want to try playing around with margins in the stylesheets.
Style sheet reference: http://doc.qt.digia.com/qt/stylesheet-reference.html

Create Qt layout with fixed height

I want to create a Qt window that contains two layouts, one fixed height that contains a list of buttons at the top and one that fills the remaning space with a layout that centers a widget vertically and horizontally as per the image below.
How should i be laying out my layouts/widgets. Ive tried a few options with nested horizontal and vertical layouts to no avail
Try making the pink box a QWidget with a QHBoxLayout (rather than just making it a layout). The reason is that QLayouts don't provide functionality to make fixed sizes, but QWidgets do.
// first create the four widgets at the top left,
// and use QWidget::setFixedWidth() on each of them.
// then set up the top widget (composed of the four smaller widgets):
QWidget *topWidget = new QWidget;
QHBoxLayout *topWidgetLayout = new QHBoxLayout(topWidget);
topWidgetLayout->addWidget(widget1);
topWidgetLayout->addWidget(widget2);
topWidgetLayout->addWidget(widget3);
topWidgetLayout->addWidget(widget4);
topWidgetLayout->addStretch(1); // add the stretch
topWidget->setFixedHeight(50);
// now put the bottom (centered) widget into its own QHBoxLayout
QHBoxLayout *hLayout = new QHBoxLayout;
hLayout->addStretch(1);
hLayout->addWidget(bottomWidget);
hLayout->addStretch(1);
bottomWidget->setFixedSize(QSize(50, 50));
// now use a QVBoxLayout to lay everything out
QVBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(topWidget);
mainLayout->addStretch(1);
mainLayout->addLayout(hLayout);
mainLayout->addStretch(1);
If you really want to have two separate layouts--one for the pink box and one for the blue box--the idea is basically the same except you'd make the blue box into its own QVBoxLayout, and then use:
mainLayout->addWidget(topWidget);
mainLayout->addLayout(bottomLayout);

Resources