Weird behaviour of QWiget after running QPropertyAnimation - c++

I'm running the next animation on two widgets:
QParallelAnimationGroup* animGroup = new QParallelAnimationGroup;
for (int i = 0; i < 2; ++i) {
//widgets is an array of 2 QLabels
QPropertyAnimation* anim = new QPropertyAnimation(widgets[i], "geometry");
anim->setDuration(750);
anim->setStartValue(widgets[i]->geometry());
widgets[i]->setProperty("animating", true);
qDebug() << QString("Animation Start %1: ").arg(widgets[i]->objectName()) << widgets[i]->pos() << " g " << widgets[i]->geometry();
//
anim->setEndValue(widgets[!i]->geometry());
anim->setEasingCurve(QEasingCurve::OutBack);
animGroup->addAnimation(anim);
}
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
So far so good, animation moves both widgets as expected, but as soon as I resize the window, those widgets get automatically moved back to their original position. The current layout holding this both widgets is a QHBoxLayout, and has the following items (same order):
QLabel0 | QSpacerItem0 | QToolTipButton | QSpacerItem1 | QLabel1
after the animation they become:
QLabel1 | QSpacerItem0 | QToolTipButton | QSpacerItem1 | QLabel0
But whenever I change the size of the window the position on the widgets resets to the original layout. I haven't implemented the resizeEvent from my window.
I'm using Windows 10 with Visual Studio | Qt Visual Studio Tools 2.3.1 and Qt 12.0, building with msvc2017_64 version and c++17 enabled.
Complete Test Code (New project):
SwapItemsTest.cpp
#include "SwapItemsTest.h"
#include <array>
#include <QHBoxLayout>
#include <QToolButton>
#include <QSpacerItem>
#include <QLabel>
#include <QDebug>
#include <QParallelAnimationGroup>
#include <QPropertyAnimation>
#include <QTimer>
SwapItemsTest::SwapItemsTest(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
auto layout = new QHBoxLayout;
auto toolButton = new QToolButton;
auto left = new QLabel("Left");
left->setFixedSize(50, size().height());
left->setFrameShape(QFrame::Box);
left->setFrameShadow(QFrame::Plain);
left->setLineWidth(2);
left->setAlignment(Qt::AlignCenter);
auto right = new QLabel("Right");
right->setFixedSize(50, size().height());
right->setAlignment(Qt::AlignCenter);
right->setFrameShape(QFrame::Box);
right->setFrameShadow(QFrame::Plain);
right->setLineWidth(2);
layout->addWidget(left);
layout->addSpacerItem(new QSpacerItem(40, 40));
layout->addWidget(toolButton);
layout->addSpacerItem(new QSpacerItem(40, 40));
layout->addWidget(right);
ui.centralWidget->setLayout(layout);
auto widgets = std::array{left, right};
connect(toolButton, &QToolButton::clicked, [widgets]() {
QParallelAnimationGroup* animGroup = new QParallelAnimationGroup;
for (int i = 0; i < 2; ++i) {
//widgets is an array of 2 QLabels
QPropertyAnimation* anim = new QPropertyAnimation(widgets[i], "geometry");
anim->setDuration(750);
anim->setStartValue(widgets[i]->geometry());
widgets[i]->setProperty("animating", true);
anim->setEndValue(widgets[!i]->geometry());
anim->setEasingCurve(QEasingCurve::OutBack);
animGroup->addAnimation(anim);
}
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
});
}
SwapItemsTest.h
#pragma once
#include <QtWidgets/QMainWindow>
#include "ui_SwapItemsTest.h"
class SwapItemsTest : public QMainWindow
{
Q_OBJECT
public:
SwapItemsTest(QWidget *parent = Q_NULLPTR);
private:
Ui::SwapItemsTestClass ui;
};

The problem is that you place the widgets in a layout but then try to manage their geometry yourself. One workaround would be to update the layout once the animation group has finished by swapping the two widgets. Your code then becomes...
SwapItemsTest::SwapItemsTest(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
auto layout = new QHBoxLayout;
auto toolButton = new QToolButton;
auto left = new QLabel("Left");
left->setFixedSize(50, size().height());
left->setFrameShape(QFrame::Box);
left->setFrameShadow(QFrame::Plain);
left->setLineWidth(2);
left->setAlignment(Qt::AlignCenter);
auto right = new QLabel("Right");
right->setFixedSize(50, size().height());
right->setAlignment(Qt::AlignCenter);
right->setFrameShape(QFrame::Box);
right->setFrameShadow(QFrame::Plain);
right->setLineWidth(2);
layout->addWidget(left);
layout->addSpacerItem(new QSpacerItem(40, 40));
layout->addWidget(toolButton);
layout->addSpacerItem(new QSpacerItem(40, 40));
layout->addWidget(right);
ui.centralWidget->setLayout(layout);
auto widgets = std::array{left, right};
connect(toolButton, &QToolButton::clicked,
/*
* Note that layout has been added to the capture list for this lambda.
*/
[layout, widgets]()
{
QParallelAnimationGroup* animGroup = new QParallelAnimationGroup;
for (int i = 0; i < 2; ++i) {
//widgets is an array of 2 QLabels
QPropertyAnimation* anim = new QPropertyAnimation(widgets[i], "geometry");
anim->setDuration(750);
anim->setStartValue(widgets[i]->geometry());
widgets[i]->setProperty("animating", true);
anim->setEndValue(widgets[!i]->geometry());
anim->setEasingCurve(QEasingCurve::OutBack);
animGroup->addAnimation(anim);
}
/*
* This is the lambda that will be invoked when the
* QPropertyAnimation::finished signal is emitted. It simply
* swaps the positions of the two widgets in the layout.
*/
connect(animGroup, &QPropertyAnimation::finished,
[layout, widgets]()
{
auto index0 = layout->indexOf(widgets[0]);
auto index1 = layout->indexOf(widgets[1]);
if (index0 < index1) {
layout->takeAt(index1);
layout->takeAt(index0);
layout->insertWidget(index0, widgets[1]);
layout->insertWidget(index1, widgets[0]);
} else {
layout->takeAt(index0);
layout->takeAt(index1);
layout->insertWidget(index1, widgets[0]);
layout->insertWidget(index0, widgets[1]);
}
});
animGroup->start(QAbstractAnimation::DeleteWhenStopped);
});
}
Note that you might still see some strange behaviour if the parent widget is resized during the animation.

Related

Qt Grid Layout doesn't fit into Scroll Area [duplicate]

This question already has an answer here:
QScrollArea missing Scrollbar
(1 answer)
Closed 1 year ago.
I am trying to put a grid view that will contain some buttons into a scroll area but I don't know why the grid layout only fits into the initial size of the scroll area (it doesn't go downside so I could use the scroll bar to see all elements).
ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
QGridLayout* lay=new QGridLayout(this);
QPushButton *name[100];
for(int i=0;i<10;i++){
QString str1="project"+QString::number(i);
QString str2="3/6task";
QString str3="ALEX";
name[i]=new QPushButton(str1);
name[i]->setObjectName("btn_1");
name[i]->setStyleSheet("QPushButton#btn_1{background:transparent;Text-align:left;font-family:century gothic;font-size:18px;color:red;}"
"QPushButton#btn_1:hover{color:yellow;Font-size:22px;}");
name[i]->setFixedSize(100,40);
lay->addWidget(name[i]);
}
ui->scrollArea->setLayout(lay);
The result is in the next photo:
I fixed it by this way:
ui->scrollArea->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
QGridLayout *lay = new QGridLayout(ui->centralwidget);
lay->addWidget(ui->scrollArea, 0, 0, 1, 1);
auto gridLayout = new QGridLayout(ui->scrollAreaWidgetContents);
gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
auto widget = new QWidget(ui->scrollAreaWidgetContents);
widget->setObjectName(QString::fromUtf8("widget"));
auto gridLayout_2 = new QGridLayout(widget);
gridLayout_2->setObjectName(QString::fromUtf8("gridLayout_2"));
gridLayout->addWidget(widget, 0, 0, 1, 1);
for (int i = 0; i < 10; i++)
{
QString str1 = "project" + QString::number(i);
QString str2 = "3/6task";
QString str3 = "ALEX";
QPushButton *btn = new QPushButton(str1);
btn->setObjectName(QString("btn_%1").arg(i));
btn->setStyleSheet("QPushButton#btn_1{background:transparent;Text-align:left;font-family:century gothic;font-size:18px;color:red;}"
"QPushButton#btn_1:hover{color:yellow;Font-size:22px;}");
btn->setFixedSize(100, 40);
widget->layout()->addWidget(btn);
}
}
You have to use the following code:
This code has been tested.
QScrollArea* scroll = new QScrollArea(this);
scroll->setGeometry(50,0,250,300);
scroll->setWidgetResizable(true);
QWidget *container = new QWidget;
scroll->setWidget(container);
QGridLayout* lay = new QGridLayout(container);
QPushButton *name[100];
for(int i = 0; i < 20; ++i) {
name[i]=new QPushButton(scroll);
lay->addWidget(name[i]);
}
Of cource you can modify the button and the scrollArea as well.
There is no problem of using ui->scrollArea
Your applying the QGridLayout to the QScollArea when you actually need to apply it to a QWidget managed by the scroll area using QScrollArea::setWidget...
#include <QApplication>
#include <QGridLayout>
#include <QPushButton>
#include <QScrollArea>
#include <QString>
#include <QWidget>
int main (int argc, char **argv)
{
QApplication app(argc, argv);
QScrollArea scroll_area;
scroll_area.setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn);
QWidget viewport;
QGridLayout layout(&viewport);
for (int i = 0; i < 10; ++i) {
QString str1 = "project" + QString::number(i);
QString str2 = "3/6task";
QString str3 = "ALEX";
auto *pb = new QPushButton(str1);
pb->setObjectName("btn_1");
pb->setStyleSheet("QPushButton#btn_1{background:transparent;Text-align:left;font-family:century gothic;font-size:18px;color:red;}"
"QPushButton#btn_1:hover{color:yellow;Font-size:22px;}");
pb->setFixedSize(100,40);
layout.addWidget(pb);
}
scroll_area.setWidget(&viewport);
scroll_area.show();
return app.exec();
}

Qt - Child widgets are not shown

I'm building a multi-window app and so far creates and shows MainWidget with 8 buttons. My next step is to make each button open a new window which in Qt terms is a child of QWidget. I keep all my buttons and new windows (which should be opened upon a button is clicked) in QVectors. It all compiles with no warnings or errors, however, when I click a button, the corresponding QWidget window is not shown.
mainwidget.h
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
#include <QWidget>
#include <QPushButton>
#include <QGridLayout>
#include <QSignalMapper>
#include "examwindow.h"
namespace Ui { class MainWidget; }
class MainWidget : public QWidget {
Q_OBJECT
public:
explicit MainWidget(QWidget *parent = 0);
~MainWidget();
private:
Ui::MainWidget *ui;
int nExams;
QVector<QString> titles;
QVector<QPushButton*> examButtons;
QGridLayout* mainWidgetLayout;
QVector<ExamWindow*> examWindows;
public slots:
void clickedExamW ();
};
#endif // MAINWIDGET_H
mainwidget.cpp
#include "mainwidget.h"
#include "ui_mainwidget.h"
#include <QDesktopWidget>
#include <iostream>
MainWidget::MainWidget(QWidget *parent) : QWidget(parent), ui(new Ui::MainWidget)
{
ui->setupUi(this);
/**
* #brief Resize the main window size in proportion to the desktop size
*/
double ratio = 0.7;
resize(QDesktopWidget().availableGeometry(this).size() * ratio);
/**
* #brief Set the main window position in the desktop center
*/
QDesktopWidget dw;
int width = this->frameGeometry().width();
int height = this->frameGeometry().height();
int screenWidth = dw.screen() -> width();
int screenHeight = dw.screen() -> height();
this->setGeometry((screenWidth / 2) - (width / 2), (screenHeight / 2) - (height / 2), width, height);
/**
* Set the button titles
*/
titles.push_back("FCE - 2008");
titles.push_back("CAE - 2008");
titles.push_back("CPE - 2008");
titles.push_back("ЕГЭ");
titles.push_back("FCE - 2015");
titles.push_back("CAE - 2015");
titles.push_back("CPE - 2015");
titles.push_back("User's Format");
/**
* Create buttons
*/
nExams = 8; // Number of exams
examButtons.resize(nExams);
for(int i = 0; i < nExams; i++) {
examButtons[i] = new QPushButton(titles[i]);
examButtons[i]->setMinimumSize(QSize(150, 150));
examButtons[i]->setMaximumSize(QSize(500, 500));
examButtons[i]->setObjectName(titles[i]);
connect(examButtons[i], SIGNAL(clicked()), this, SLOT(clickedExamW()));
}
/**
* Add exam buttons to the main widget layout
*/
mainWidgetLayout = new QGridLayout(this);
for(int i = 0; i < nExams; i++)
if (i < nExams / 2)
mainWidgetLayout -> addWidget(examButtons[i], i, 0);
else
mainWidgetLayout -> addWidget(examButtons[i], i - nExams / 2, 1);
/**
* Create exam windows
*/
examWindows.resize(nExams);
for(int i = 0; i < nExams; i++) {
examWindows[i] = new ExamWindow(this);
examWindows[i]->setWindowTitle(titles[i]);
}
}
void MainWidget::clickedExamW() {
QObject *senderObj = sender();
QString senderObjName = senderObj->objectName();
for(int i = 0; i < nExams; i++)
if (senderObjName == titles[i]) {
this->setWindowTitle(titles[i]); // WORKS - it changes the title
examWindows[i]->show(); // DOES NOT WORK - no win shown
}
}
MainWidget::~MainWidget()
{
delete ui;
}
examwindow.h
#ifndef EXAMWINDOW_H
#define EXAMWINDOW_H
#include <QWidget>
class ExamWindow : public QWidget
{
Q_OBJECT
public:
explicit ExamWindow(QWidget *parent = 0);
signals:
public slots:
};
#endif // EXAMWINDOW_H
examwindow.cpp
#include "examwindow.h"
ExamWindow::ExamWindow(QWidget *parent) : QWidget(parent)
{
}
The way, how do you create widgets is ok (examWindows[i] = new ExamWindow(this);). But:
ExamWindow::ExamWindow(QWidget *parent)
: QWidget(parent, Qt::Window)
{}
You need to directly specify a flag that you need a "window" widget.
Or, if you don't want to set parent widget, you may set Qt::WA_DeleteOnClose attribute for automatic releasing memory. Note, that in this case you will have an invalid pointer inside your examWindows vector. It may be resolved if you will use next declaration: QVector<QPointer<ExamWindow>> examWindows;
ANSWER: it turned out that the problem was in this line:
examWindows[i] = new ExamWindow(this);
If I remove 'this', making my windows parentless, it works as intended. I'm puzzled why and I guess I have to delete them now manually.

Can't refesh pixmap of QLabel

I have been stuck on this issue for 2 days now. I'm using the Qt plugin for Visual Studio 2013 on Window 7-64 bit.
I have been trying to display a pair of images in QLabels. I need to manipulate the pixel data regularly, so I store them in QImages, and every time I want to refresh the display I set the QPixmap of a QLabel. The problem is, it only seems to refresh if I change/move the window in some way.
This problems goes away if I just make the QLabels children of my QWidget, but never set a layout. If I then add repaint() or update(), the problem comes back.
(this is a very similar post to one I posted using QGraphicsScene, but the problem seems to be more fundamental than that, so I am reposting)
Here is my code. First the .h
#ifndef DISPLAYWIDGET_H
#define DISPLAYWIDGET_H
#include <QtWidgets/QWidget>
#include <QPixmap>
#include <QGraphicsScene>
#include <QGraphicsView>
#include <QGraphicsPixmapItem>
#include <QPushButton>
#include <QHBoxLayout>
#include <QTimer>
#include <QLabel>
#define FULLSCALE 255
#define IM_X_MIN -5.0
#define IM_X_MAX 5.0
#define IM_Z_MIN 0.0
#define IM_Z_MAX 15.0
#define IM_PIXEL_WIDTH 200
#define IM_PIXEL_HEIGHT IM_PIXEL_WIDTH * (IM_Z_MAX-IM_Z_MIN)/(IM_X_MAX - IM_X_MIN)
#define BORDER_WIDTH 10
#define RAND_SEED 7
class DisplayWidget : public QWidget
{
Q_OBJECT
public:
DisplayWidget(int width, int height, QWidget *parent = 0);
~DisplayWidget();
private:
QLabel* bimageLabel;
QLabel* dimageLabel;
QImage* bImage;
QImage* dImage;
QTimer* frameGrab;
QPushButton* debugButton;
void CreateWidgets();
void SetupGui();
int w, h;
public slots:
void GenerateNewData();
};
#endif // DISPLAYWIDGET_H
and the .cpp.
#include "displaywidget.h"
DisplayWidget::DisplayWidget(int width, int height, QWidget *parent): QWidget(parent)
{
//ui.setupUi(this);
w = width;
h = height;
CreateWidgets();
SetupGui();
// seed the random number generator
srand(RAND_SEED);
GenerateNewData();
}
DisplayWidget::~DisplayWidget()
{
}
void DisplayWidget::CreateWidgets()
{
bImage = new QImage(w, h, QImage::Format_ARGB32);
dImage = new QImage(w, h, QImage::Format_ARGB32);
bimageLabel = new QLabel(this);
dimageLabel = new QLabel(this);
debugButton = new QPushButton("DEBUG", this);
bimageLabel->setStyleSheet("QLabel {background-color: black};");
dimageLabel->setStyleSheet("QLabel {background-color: white};");
frameGrab = new QTimer(this);
}
void DisplayWidget::SetupGui()
{
QHBoxLayout * layout = new QHBoxLayout();
setLayout(layout); // commenting this line out makes it refresh
layout->addWidget(bimageLabel);
layout->addWidget(dimageLabel);
layout->addWidget(debugButton);
connect(frameGrab, SIGNAL(timeout()),this, SLOT(GenerateNewData()));
connect(debugButton, SIGNAL(clicked()), this, SLOT(GenerateNewData()));
frameGrab->start(50);
}
void DisplayWidget::GenerateNewData()
{
QRgb * bImageData = (QRgb *)bImage->scanLine(0);
QRgb * dImageData = (QRgb *)dImage->scanLine(0);
for (int i; i < w * h; i++)
{
bImageData[i] = qRgba(rand() % FULLSCALE, 0, 0, FULLSCALE);
dImageData[i] = qRgba(0, 0, rand() % FULLSCALE, FULLSCALE);
}
bimageLabel->setPixmap(QPixmap::fromImage(*bImage).scaled(QSize(IM_PIXEL_WIDTH, IM_PIXEL_HEIGHT)));
dimageLabel->setPixmap(QPixmap::fromImage(*dImage).scaled(QSize(IM_PIXEL_WIDTH, IM_PIXEL_HEIGHT)));
//this->update(); // this breaks it again
}
Losing my mind here. I have very limited experience with Qt, but I believe I have the right approach.
Please help!
I've tested your code on Ubuntu. I can unfortunately not comment on windows. I've modified the code slightly (See comments marked by //#w:):
#include "displaywidget.h"
DisplayWidget::DisplayWidget(int width, int height, QWidget *parent): QWidget(parent)
{
w = width;
h = height;
CreateWidgets();
SetupGui();
// seed the random number generator
srand(RAND_SEED);
GenerateNewData();
}
DisplayWidget::~DisplayWidget()
{
}
void DisplayWidget::CreateWidgets()
{
bImage = new QImage(w, h, QImage::Format_ARGB32);
dImage = new QImage(w, h, QImage::Format_ARGB32);
bimageLabel = new QLabel(this);
bimageLabel->setScaledContents(true);
dimageLabel = new QLabel(this);
dimageLabel->setScaledContents(true);
debugButton = new QPushButton("DEBUG", this);
bimageLabel->setStyleSheet("QLabel {background-color: black};");
dimageLabel->setStyleSheet("QLabel {background-color: grey};");
frameGrab = new QTimer(this);
}
void DisplayWidget::SetupGui()
{
//#w: Adding vertical layout as button below seems like better usage of space...
QVBoxLayout * vlay = new QVBoxLayout();
QHBoxLayout * hlay = new QHBoxLayout();
hlay->addWidget(bimageLabel);
hlay->addWidget(dimageLabel);
vlay->addLayout(hlay);
vlay->addWidget(debugButton);
//#w: Removing size constraints on top layout allows me to resize window and see effect.
vlay->setSizeConstraint(QLayout::SetNoConstraint);
setLayout(vlay); // commenting this line out makes it refresh
connect(frameGrab, SIGNAL(timeout()),this, SLOT(GenerateNewData()));
//#w: I suppose we can chuck the button.... Currently it serves no purpose
connect(debugButton, SIGNAL(clicked()), this, SLOT(GenerateNewData()));
//#w: Timer slower initially, then increase to see where performance degrades.
frameGrab->start(200);
}
void DisplayWidget::GenerateNewData()
{
QRgb * bImageData = (QRgb *)bImage->scanLine(0);
QRgb * dImageData = (QRgb *)dImage->scanLine(0);
//#w: This code just varies the contents by having a b and d selector that
// alternates colour... Simple stuff...
int bSelect = rand() % 3,
dSelect = (bSelect==2) ? 0 : bSelect+1;
for (int i = 0; i < w * h; i++)
{
//#w: 3 colours, only two being selected - can be improved, I suppose.
QRgb rgb[3] =
{
(bSelect == 0) || (dSelect==0) ? qRgba(rand() % FULLSCALE, 0, 0, FULLSCALE) : 0,
(bSelect == 1) || (dSelect==1) ? qRgba(0, rand() % FULLSCALE, 0, FULLSCALE) : 0,
(bSelect == 2) || (dSelect==2) ? qRgba(0, 0, rand() % FULLSCALE, FULLSCALE) : 0,
};
bImageData[i] = rgb[bSelect];
dImageData[i] = rgb[dSelect];
}
//#w: Removed scaling, as it depends on layout...
bimageLabel->setPixmap(QPixmap::fromImage(*bImage));
dimageLabel->setPixmap(QPixmap::fromImage(*dImage));
}
Initially the labels take its size from the pixmaps. Thereafter the labels adhere to the layout, which adheres to resizing of the form (main/parent widget)
QtForum solved this one for me, and boy is it embarassing.
I forgot to initialize the loop counter. Fixed everything.

Creating and laying out widgets using a for loop in Qt

So i wanted to create 5 buttons in Qt but instead, create just one button and put it in a for loop so i don't have to create each of the 5 buttons manually. I tried different ways but all proved futile. I'm new to C++ and Qt.
Here are the codes;
show.h
#ifndef SHOW_H
#define SHOW_H
#include <QDialog>
#include <QPushButton>
#include <QVBoxLayout>
class Show : public QDialog {
Q_OBJECT
public:
explicit Show(QWidget *parent = 0);
~Show();
private:
QPushButton *button;
};
#endif // SHOW_H
show.cpp
#include "show.h"
#include "ui_show.h"
Show::Show(QWidget *parent) : QDialog(parent) {
int a = 5;
button = new QPushButton[a];
button->setText("Ok");
QVBoxLayout *layout = new QVBoxLayout[a];
for (int i = 0; i < sizeof(button)/4; i++) {
/*here, i wanted to do something like this;
'layout[i].addWidget(button[i]);' but didn't work*/
layout[i].addWidget(button);
}
setLayout(layout);
}
Show::~Show() {
}
main.cpp
#include "show.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
Show *dialog = new Show;
dialog->show();
return a.exec();
}
After i run the code, i only see one button.
Your help is deeply appreciated. Thank you!!
QPushButton* pButton = new QPushButton("Ok");
This creates a single instance of a QPushButton.
You can add the button to a layout with a call to Layout::addWidget, which internally calls addItem.
As the documentation states for addItem: -
Note: The ownership of item is transferred to the layout, and it's the layout's responsibility to delete it
So your current code creates a single button and as it gets added to each successive layout, it is removed from the layout in which it was previously added.
You're creating one button and adding the same button 5 times. If you want 5 buttons in 5 layouts using a loop, then you need 5 separate instances of the button: -
for (int i=0; i<5; ++i)
{
QPushButton* pButton = new QPushButton("Ok");
layout[i].addWidget(pButton);
}
In your show.cpp change the lines
Show::Show(QWidget *parent) : QDialog(parent) {
int a = 5;
// You only need one layout for all buttons, not one per button.
QVBoxLayout *layout = new QVBoxLayout( this );
for (int i = 0; i < a; i++) {
QPushButton * newButton = new QPushButton( this );
newButton ->setText( "Ok" );
layout->addWidget( newButton );
}
setLayout(layout);
}
Show::~Show() {
}
The layout expects a pointer and you only need one layout instead one per button.

QPushButton does not honor horizontal expanding size policy

I'm trying to put several QPushButton entities inside a QVBoxLayout such that they are centered and expanding. The expanding tag works fine until I tell the QVBoxLayout to use AlignHCenter, after which the QPushButton's all jump to the minimum size and stay there. What am I doing wrong?
QVBoxLayout *vBoxLayout = new QVBoxLayout(this);
setLayout(vBoxLayout);
//Create title and add to layout
QLabel *titleLabel = new QLabel(this);
titleLabel->setText(menuTitle);
titleLabel->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
titleLabel->setMaximumHeight(35);
titleLabel->setStyleSheet(QString("QLabel { font-size: 16pt; }"));
vBoxLayout->addWidget(titleLabel);
vBoxLayout->setStretchFactor(titleLabel, 1);
//Create buttons and add to layout
QMap<int, QString>::const_iterator it;
for (it = m_buttonMapping.cbegin(); it != m_buttonMapping.cend(); ++it)
{
QPushButton *button = new QPushButton(it.value(), this);
connect(button, SIGNAL(clicked()), sigMapper, SLOT(map()));
sigMapper->setMapping(button, it.key());
button->setMinimumHeight(40);
button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
button->setMaximumWidth(800);
button->setMinimumWidth(300);
vBoxLayout->addWidget(button);
vBoxLayout->setAlignment(button, Qt::AlignHCenter); //<-- without this, expanding works fine!
vBoxLayout->setStretchFactor(button, 1);
}
vBoxLayout->setContentsMargins(10, 0, 10, 0);
By specifying the alignment on the layout, you keep your QPushButtons from being able to expand. Available new space will be used to keep the QPushButtons centered, instead of allowing them to resize and for an amount of space around them to be utilized for centering. Stretch factors fulfill your requirement for a proportional resizing and centering of a layout's contents.
To get around this, create a wrapper widget and layout (or just a layout), and add the widget that is laid out by your vBoxLayout to the wrapper layout with a stretch factor applied. Before and after adding your widget, you'll add QSpacerItems to the wrapper layout with QHBoxLayout::addStretch. You can then adjust the stretch factors of your widget and the spacers to get the effect you want.
Here's some sample code that should solve your problem:
MainWindow.cpp
#include "MainWindow.hpp"
#include <QPushButton>
#include <QLabel>
#include <QBoxLayout>
MainWindow::MainWindow(QWidget* parent)
: QMainWindow(parent) {
QWidget* centralWidget = new QWidget(this);
QVBoxLayout* layout = new QVBoxLayout(centralWidget);
// Create a wrapper widget that will align horizontally
QWidget* alignHorizontalWrapper = new QWidget(centralWidget);
layout->addWidget(alignHorizontalWrapper);
// Layout for wrapper widget
QHBoxLayout* wrapperLayout = new QHBoxLayout(alignHorizontalWrapper);
// Set its contents margins to 0 so it won't interfere with your layout
wrapperLayout->setContentsMargins(0, 0, 0, 0);
wrapperLayout->addStretch(1);
QWidget* widget = new QWidget(alignHorizontalWrapper);
wrapperLayout->addWidget(widget, 3);
wrapperLayout->addStretch(1);
QVBoxLayout* vBoxLayout = new QVBoxLayout(widget);
QLabel* titleLabel = new QLabel(this);
titleLabel->setText(QStringLiteral("Menu"));
titleLabel->setAlignment(Qt::AlignHCenter | Qt::AlignTop);
titleLabel->setMaximumHeight(35);
titleLabel->setStyleSheet(QStringLiteral("QLabel { font-size: 16pt; }"));
vBoxLayout->addWidget(titleLabel);
vBoxLayout->setStretchFactor(titleLabel, 1);
for (int i = 0; i < 3; ++i) {
const QString& value = QStringLiteral("Button ") + QString::number(i);
QPushButton* button = new QPushButton(value, this);
button->setMinimumHeight(40);
button->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
//button->setMaximumWidth(800);
button->setMinimumWidth(300);
vBoxLayout->addWidget(button);
//vBoxLayout->setAlignment(button, Qt::AlignHCenter); // without this, expanding works fine!
vBoxLayout->setStretchFactor(button, 3);
}
vBoxLayout->setContentsMargins(10, 0, 10, 0);
this->setCentralWidget(centralWidget);
}
MainWindow.hpp
#ifndef MAINWINDOW_HPP
#define MAINWINDOW_HPP
#include <QMainWindow>
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget* parent = nullptr);
};
#endif // MAINWINDOW_HPP
main.cpp
#include "MainWindow.hpp"
#include <QApplication>
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
MainWindow window;
window.show();
return app.exec();
}