Qt Runtime Error when using a derived class - c++

I Simply want to make a program in Qt, where you press one of two buttons and the text of a QLabel changes depending on the button you've changed. I am getting a runtime error when running the script. I made a "custom" window class for this program:
This is the header file:
#ifndef MW_H
#define MW_H
#include <QString>
#include <QPushButton>
#include <QLabel>
#include <QGridLayout>
#include <QDialog>
class MW: public QDialog
{
Q_OBJECT
private:
QPushButton* one;
QPushButton* two;
QLabel* three;
QGridLayout* mainL;
public:
MW();
private slots:
void click_1();
void click_2();
};
#endif // MW_H
This is the .cpp for the header:
#include "MW.h"
MW :: MW()
{
//create needed variables
QGridLayout* mainL = new QGridLayout;
QPushButton* one = new QPushButton("Set1");
QPushButton* two = new QPushButton("Set2");
QLabel* three = new QLabel("This text will be changed");
//connect signals and slots
connect(one, SIGNAL(clicked()), this, SLOT(click_1()));
connect(two, SIGNAL(clicked()), this, SLOT(click_2()));
// create layout
mainL->addWidget(one, 1, 0);
mainL->addWidget(two, 1, 1);
mainL->addWidget(three, 0, 1);
setLayout(mainL);
}
void MW :: click_1()
{
three->setText("One Clicked me!");
}
void MW :: click_2()
{
three->setText("Two Clicked me!");
}
And finally this is the main function:
#include <QApplication>
#include "MW.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MW w;
w.setAttribute(Qt::WA_QuitOnClose);
w.show();
return a.exec();
}
This is the third or so small learning program I am doing and I am getting stuck at the same problem. It is starting to get a bit annoying. Any help will be appreciated.

The error rests within your constructor.
QLabel* three = new QLabel("This text will be changed");
This line stores the new QLabel to a local variable instead of the class variable.
As such, your class variable three remains empty. (As do the other three variables, but that's not the issue here, since you don't access them outside of the constructor)
To make long things short, amend your code like this:
MW :: MW()
{
//create needed variables
mainL = new QGridLayout;
one = new QPushButton("Set1");
two = new QPushButton("Set2");
three = new QLabel("This text will be changed"); //This line, actually.
//connect signals and slots
connect(one, SIGNAL(clicked()), this, SLOT(click_1()));
connect(two, SIGNAL(clicked()), this, SLOT(click_2()));
// create layout
mainL->addWidget(one, 1, 0);
mainL->addWidget(two, 1, 1);
mainL->addWidget(three, 0, 1);
setLayout(mainL);
}
Like this, the variables in the class will be filled and your code should work as expected.

Your problem is this:
QGridLayout* mainL = new QGridLayout;
QPushButton* one = new QPushButton("Set1");
QPushButton* two = new QPushButton("Set2");
QLabel* three = new QLabel("This text will be changed");
You're creating four new variables with the same name as your class members. These new variables hide the class member. So with the above code, you never initialize MW::three in particular. When your slot is called, three->setText(...) dereferences an uninitialized pointer and stuff breaks.
Replace that code with:
mainL = new QGridLayout;
one = new QPushButton("Set1");
two = new QPushButton("Set2");
three = new QLabel("This text will be changed");

Related

Qt: SIGNAL and SLOT don't work in a custom QDialog window

I have an application in which I want to prompt a dialog window for further (advanced) editing of apps content. It has to be a modal window, so that's why it can't be a QMainWindow. I just can't seem to make connect() macro work in QDialogwindow.
Previously, I got around this by using the response of the QDialog and a getter function in my dialog class:
A screenshot of a simple query
Dialog_CreateNew mDialog(this);
mDialog.setModal(true);
if (mDialog.exec() == QDialog::Accepted)
{
QString username, password;
mDialog.getData(&username, &password);
}
I used built-in SLOTs of QDialog library accept() and reject():
connect(_cancel, SIGNAL(clicked()), this, SLOT(reject()));
connect(_createNew, SIGNAL(clicked()), this, SLOT(accept()));
But now when I have to create my own slots in the dialog window, idk how to make it work. It will be a complex window, and just accept() and reject() won't do it.
One more thing: when I add Q_OBJECT macro in the dialog class, I get an error:
error: undefined reference to `vtable for Dialog_CreateNew'
These built-in SLOTs accept() and reject() work without Q_OBJECT.
I've seen this work in the Qt Designer, therefore it is possible. I don't want to use the Designer, everything ought to be done in coding.
That's my research and the things I tried.
My question is: How to make a signal-slot mechanism work in a modal child dialog window?
"dialog_createNew.h":
#ifndef DIALOG_CREATENEW_H
#define DIALOG_CREATENEW_H
#include <QtCore>
#include <QDialog>
#include <QIcon>
#include <QWidget>
#include <QLabel>
#include <QLineEdit>
#include <QGridLayout>
#include <QPushButton>
#include <QMessageBox>
#include <QStatusBar>
class Dialog_CreateNew : public QDialog
{
Q_OBJECT
public:
Dialog_CreateNew(QWidget* parent);
void getData(QString* username, QString* password);
void setErrorTip(QString errorTip);
virtual ~Dialog_CreateNew();
private:
QWidget* _parent;
QLabel* _lInstruction;
QLabel* _lUsername;
QLabel* _lPassword;
QLineEdit* _edUsername;
QLineEdit* _edPassword;
QPushButton* _createNew;
QPushButton* _cancel;
QLabel* _lErrorTip;
QString* _errorTip;
QGridLayout* _layout;
void createConnects();
private slots:
void onTextChanged_connect(QString);
};
#endif // DIALOG_CREATENEW_H
"dialog_createNew.cpp":
#include "dialog_createnew.h"
Dialog_CreateNew::Dialog_CreateNew(QWidget* parent)
{
_parent = parent;
this->setWindowTitle("Create New Bot");
this->setWindowIcon(QIcon(":/icons/createNew.svg"));
int nHeight = 150;
int nWidth = 360;
this->setGeometry(parent->x() + parent->width()/2 - nWidth/2,
parent->y() + parent->height()/2 - nHeight/2,
nWidth, nHeight);
this->setFixedSize(QSize(nWidth, nHeight));
_lInstruction = new QLabel(this);
_lInstruction->setText("Enter Your Instagram credentials:");
_lUsername = new QLabel(this);
_lUsername->setText("Username:");
_lPassword = new QLabel(this);
_lPassword->setText("Password:");
_edUsername = new QLineEdit(this);
_edUsername->setPlaceholderText("classybalkan");
_edPassword = new QLineEdit(this);
_edPassword->setEchoMode(QLineEdit::Password);
_edPassword->setPlaceholderText("•••••••••••");
_createNew = new QPushButton(this);
_createNew->setText("Create New Bot");
_cancel = new QPushButton(this);
_cancel->setText("Cancel");
_errorTip = new QString("");
_lErrorTip = new QLabel(this);
_layout = new QGridLayout(this);
_layout->addWidget(_lInstruction, 0, 0, 1, 2);
_layout->addWidget(_lUsername, 1, 0);
_layout->addWidget(_edUsername, 1, 1);
_layout->addWidget(_lPassword, 2, 0);
_layout->addWidget(_edPassword, 2, 1);
_layout->addWidget(_lErrorTip, 3, 1);
_layout->addWidget(_cancel, 4, 0);
_layout->addWidget(_createNew, 4, 1);
this->setLayout(_layout);
createConnects();
}
void Dialog_CreateNew::createConnects()
{
connect(_cancel,
SIGNAL(clicked()),
this,
SLOT(reject()));
connect(_edPassword,
&QLineEdit::textChanged,
this,
&Dialog_CreateNew::onTextChanged_connect);
}
void Dialog_CreateNew::getData(QString* username, QString* password)
{
*username = _edUsername->text();
*password = _edPassword->text();
}
void Dialog_CreateNew::setErrorTip(QString errorTip)
{
*_errorTip = errorTip;
_lErrorTip->setText("<center><font color=""red"">"
+ *_errorTip +
"</font><center>");
}
void Dialog_CreateNew::onTextChanged_connect()
{
QString text = _edPassword->text();
if (text == "")
{
disconnect(_createNew,
&QPushButton::clicked,
this,
&QDialog::accept);
_lErrorTip->setText("Invalid Password");
}
else
{
connect(_createNew,
&QPushButton::clicked,
this,
&QDialog::accept);
}
}
Dialog_CreateNew::~Dialog_CreateNew()
{
}
Solution:
Changing old SIGNAL/SLOT notation with the new function binding:
connect(_edPassword, &QLineEdit::textChanged, this, &Dialog_CreateNew::onTextChanged_connect);
This allowed me to use my own slots, and did the fix.
Three things you should do to fix this:
Split your work to header (.h file) and source (.cpp file)
Define the destructor of your dialog as virtual, e.g.:
virtual ~MyDialog()
{
}
Run qmake again
One or more of these will fix it for you
PS: Please, oh, Please... stop using the outdated SIGNAL() and SLOT(), and start using the new format that uses function binding, like:
connect(_cancel, &QPushButton::clicked, this, &QDialog::reject);
It's faster, more efficient, doesn't use strings, and once compiled, works. Unlike the old format that can fail at runtime.

QT Layout display

I am new to Qt application programming, I have a task to complete i.e
I need to create a paint like application
Based on left side panel objects Right side List must display..
PFA
so now i am stuck with displaying right side panel
To be more clear...
I used left side QGroupBox with Push Buttons.
Right side i used QGridLayout to show specific objects.
Center graphics view ( ignore this point).
now when I am calling the function after push button clicked Button 1 : should display only numbered objects Button 2 : should display only other objects Button 3 : should display empty plane Button N : should display all objects
could some one suggest me with small code for it.... like
ui->gridLayout_1->... ( to hide previous options )
when called second time ui->gridLayout_1->addWidget(label0,0,0); should display relevant
when called for third time display all objects ui->gridLayout_1->addWidget(label0,0,0);
Edit: After seeing a better example of what you were trying to accomplish I threw this together using QStackedWidget. I think the below code mimics the behavior you are looking for. This has replaced my old answer as I didn't want to add any unnecessary confusion.
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QGridLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QMainWindow>
#include <QPushButton>
#include <QStackedWidget>
#include <QWidget>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
QWidget *central_widget = new QWidget(this);
QHBoxLayout *central_layout = new QHBoxLayout();
QVBoxLayout *ctl_btn_layout = new QVBoxLayout();
QPushButton *ctl_btn_1 = new QPushButton("1", central_widget);
QPushButton *ctl_btn_2 = new QPushButton("2", central_widget);
QStackedWidget *widget_stack = new QStackedWidget(central_widget);
QWidget *page_0 = new QWidget();
QWidget *page_1 = new QWidget();
QGridLayout *page_1_layout = new QGridLayout();
QPushButton *r_btn = new QPushButton("R", page_1);
QPushButton *j_btn = new QPushButton("J", page_1);
QPushButton *l_btn = new QPushButton("L", page_1);
QPushButton *o_btn = new QPushButton("O", page_1);
QPushButton *v_btn = new QPushButton("V", page_1);
QPushButton *f_btn = new QPushButton("F", page_1);
QWidget *page_2 = new QWidget();
QVBoxLayout *page_2_layout = new QVBoxLayout();
QPushButton *btn_1 = new QPushButton("1", page_2);
QPushButton *btn_2 = new QPushButton("2", page_2);
QPushButton *btn_3 = new QPushButton("3", page_2);
QPushButton *btn_4 = new QPushButton("4", page_2);
QPushButton *btn_5 = new QPushButton("5", page_2);
QPushButton *btn_6 = new QPushButton("6", page_2);
QLabel *r = new QLabel("R", central_widget);
QLabel *j = new QLabel("J", central_widget);
QLabel *id = new QLabel("ID", central_widget);
QLabel *x = new QLabel("X", central_widget);
QLabel *y = new QLabel("Y", central_widget);
QLineEdit *r_line = new QLineEdit(central_widget);
QLineEdit *j_line = new QLineEdit(central_widget);
QLineEdit *id_line = new QLineEdit(central_widget);
QLineEdit *x_line = new QLineEdit(central_widget);
QLineEdit *y_line = new QLineEdit(central_widget);
QVBoxLayout *lines_layout = new QVBoxLayout();
public slots:
void ctl_btn_1_clicked();
void ctl_btn_2_clicked();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
ctl_btn_layout->addWidget(ctl_btn_1);
connect(ctl_btn_1, SIGNAL(clicked(bool)), this, SLOT(ctl_btn_1_clicked()));
ctl_btn_layout->addWidget(ctl_btn_2);
connect(ctl_btn_2, SIGNAL(clicked(bool)), this, SLOT(ctl_btn_2_clicked()));
central_layout->addLayout(ctl_btn_layout);
page_1_layout->addWidget(r_btn, 0, 0, 1, 1);
page_1_layout->addWidget(j_btn, 1, 1, 1, 1);
page_1_layout->addWidget(l_btn, 2, 0, 1, 1);
page_1_layout->addWidget(o_btn, 3, 1, 1, 1);
page_1_layout->addWidget(v_btn, 4, 2, 1, 1);
page_1_layout->addWidget(f_btn, 5, 3, 1, 1);
page_1->setLayout(page_1_layout);
page_2_layout->addWidget(btn_1);
page_2_layout->addWidget(btn_2);
page_2_layout->addWidget(btn_3);
page_2_layout->addWidget(btn_4);
page_2_layout->addWidget(btn_5);
page_2_layout->addWidget(btn_6);
page_2->setLayout(page_2_layout);
widget_stack->addWidget(page_0); //Empty QWidget.
widget_stack->addWidget(page_1);
widget_stack->addWidget(page_2);
central_layout->addWidget(widget_stack);
lines_layout->addWidget(r);
r->hide();
lines_layout->addWidget(r_line);
r_line->hide();
lines_layout->addWidget(j);
j->hide();
lines_layout->addWidget(j_line);
j_line->hide();
lines_layout->addWidget(id);
id->hide();
lines_layout->addWidget(id_line);
id_line->hide();
lines_layout->addWidget(x);
x->hide();
lines_layout->addWidget(x_line);
x_line->hide();
lines_layout->addWidget(y);
y->hide();
lines_layout->addWidget(y_line);
y_line->hide();
central_layout->addLayout(lines_layout);
central_widget->setLayout(central_layout);
setCentralWidget(central_widget);
}
MainWindow::~MainWindow()
{
}
void MainWindow::ctl_btn_1_clicked()
{
if (j->isVisible())
{
j->hide();
j_line->hide();
}
r->show();
r_line->show();
if (!id->isVisible())
{
id->show();
id_line->show();
x->show();
x_line->show();
y->show();
y_line->show();
}
widget_stack->setCurrentIndex(1);
}
void MainWindow::ctl_btn_2_clicked()
{
if (r->isVisible())
{
r->hide();
r_line->hide();
}
j->show();
j_line->show();
if (!id->isVisible())
{
id->show();
id_line->show();
x->show();
x_line->show();
y->show();
y_line->show();
}
widget_stack->setCurrentIndex(2);
}
This works by using a QStackedWidget, which is populated with 3 QWidgets. The first widget is completely empty to mimic the empty page from your example. The second and third widgets have layouts that hold your different button configurations, and the QStackedWidget will flip between pages through functions connected to the button slots.
Note that I tend not to use the form designer that comes w/ Qt Creator, preferring pure C++ UI design. It doesn't matter how you set it up, with or without the form, I just find it easier this way. Main.cpp was left as default from a new Qt GUI project with the generate form box unchecked.

How to prevent crash after close QMainWindow in Qt?

I have an error, just after closing my application, I get an error message saying that it has stopped unexpectedly. I reached this:
But, after I close the window, I get this error:
The program has unexpectedly finished. The process was ended
forcefully.
My project structure is:
And my code is:
main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <windows/login.h>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Login w;
w.show();
return a.exec();
}
util_window.h
#ifndef UTIL_H
#define UTIL_H
#include <QObject>
#include <QMainWindow>
class UtilWindow : QObject
{
public:
// vars
// methods
util();
void setCenterWindow(QMainWindow* w);
};
#endif // UTIL_H
util_window.cpp
#include "util_window.h"
#include <QDesktopWidget>
UtilWindow::util()
{
}
void UtilWindow::setCenterWindow(QMainWindow *w) {
int width = w->frameGeometry().width();
int height = w->frameGeometry().height();
QDesktopWidget wid;
int screenWidth = wid.screen()->width();
int screenHeight = wid.screen()->height();
w->setGeometry((screenWidth/2)-(width/2),(screenHeight/2)-(height/2),width,height);
w->show();
}
login.h
#ifndef LOGIN_H
#define LOGIN_H
#include <QMainWindow>
#include <QObject>
#include <QWidget>
#include "util/util_window.h"
#include <QGroupBox>
#include <QLabel>
#include <QLineEdit>
#include <QFormLayout>
class Login : public QMainWindow
{
Q_OBJECT
public:
// vars
UtilWindow* util;
// widgets
// labels
QLabel* lblHost;
QLabel* lblPort;
QLabel* lblUser;
QLabel* lblPass;
// textbox
QLineEdit* leHost;
QLineEdit* lePass;
QLineEdit* leUser;
QLineEdit* lePort;
// layouts
QFormLayout* layout;
QWidget* central;
QVBoxLayout* mainLayout;
QGroupBox* gbCredentials;
// methods
void initLabels();
void initTextBoxes();
void initUI();
explicit Login(QWidget *parent = nullptr);
~Login();
signals:
public slots:
};
#endif // LOGIN_H
login.cpp
#include "login.h"
Login::Login(QWidget *parent) : QMainWindow(parent)
{
this->util = new UtilWindow();
this->initUI();
}
Login::~Login() {
delete this->lblHost;
delete this->lblPass;
delete this->lblPort;
delete this->lblUser;
delete this->leHost;
delete this->lePass;
delete this->lePort;
delete this->leUser;
delete this->layout;
delete this->central;
delete this->mainLayout;
delete this->gbCredentials;
delete this->util;
}
void Login::initUI() {
this->setFixedSize(400,400);
this->setWindowTitle(tr("Inicio de sesión"));
this->util->setCenterWindow(this);
this->initLabels();
this->initTextBoxes();
this->layout = new QFormLayout();
this->layout->addRow(this->lblHost, this->leHost);
this->layout->addRow(this->lblPort, this->lePort);
this->layout->addRow(this->lblUser, this->leUser);
this->layout->addRow(this->lblPass, this->lePass);
this->gbCredentials = new QGroupBox();
this->gbCredentials->setTitle(tr("Datos de conexión"));
this->gbCredentials->setLayout(layout);
this->mainLayout = new QVBoxLayout();
this->mainLayout->addWidget(gbCredentials);
this->central = new QWidget();
this->central->setParent(this);
this->central->setLayout(this->mainLayout);
this->setCentralWidget(this->central);
}
void Login::initLabels() {
this->lblHost = new QLabel();
this->lblPass = new QLabel();
this->lblPort = new QLabel();
this->lblUser = new QLabel();
this->lblHost->setText(tr("Host: "));
this->lblPass->setText(tr("Contraseña: "));
this->lblPort->setText(tr("Puerto: "));
this->lblUser->setText(tr("Usuario: "));
}
void Login::initTextBoxes() {
this->leHost = new QLineEdit();
this->lePass = new QLineEdit();
this->lePort = new QLineEdit();
this->leUser = new QLineEdit();
this->leHost->setPlaceholderText(tr("Host de MySQL"));
this->lePass->setPlaceholderText(tr("Ingrese su contraseña"));
this->leUser->setPlaceholderText(tr("Ingrese su nombre de usuario"));
this->lePort->setPlaceholderText(tr("Ingrese el número de puerto"));
this->leHost->setToolTip(this->leHost->placeholderText());
this->leUser->setToolTip(this->leUser->placeholderText());
this->lePass->setToolTip(this->lePass->placeholderText());
this->lePort->setToolTip(this->lePort->placeholderText());
}
Thanks in advance!
When you add widgets to a layout, e.g.
this->layout.addRow(&(this->lblHost), &(this->leHost));
you're parenting them to the layout's widget, in this case your Login main window, and when the parent widget destructor will be called, all children widget will be delete'd. What happens in your code is: the children (line edits and labels) are not instantiated with new, so calling delete on them will crash your application. You should replace the children declarations in your widget header with pointers, this way:
QLabel * lblHost;
// ...
QLineEdit * leHost;
// etc
and instantiate them before adding them to the layout:
this->lblHost = new QLabel();
this->leHost = new QLineEdit();
this->layout.addRow(this->lblHost, this->leHost);
//etc
This applies to all layouts and widgets that have a parent (i.e. central Qwidget).
Also: explicitly calling delete on children is not needed, because the parent destructor will take care of that, and this applies to the central widget as well:
QMainWindow takes ownership of the widget pointer and deletes it at
the appropriate time
As thuga points out in comments, there is nothing inherently wrong in instantiating children on the stack, as long as their destructor is called before their parent's, thus they are automatically removed from the parent's children list and the parent destructor will not call delete on them.
As explained here, this is legit:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget widget;
QLabel label(&widget);
widget.show();
return a.exec();
}
because label will go out of scope before widget.
Changing the two objects creation order will cause the application crash at exit time:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QLabel label;
QWidget widget;
label.setParent(&widget);
widget.show();
return a.exec();
}
You have deleting QMainWindow elements by yourself :
delete this->lblHost;
When your QObject inherited objects are created on heap with parent, you should not delete it by yourselves. because QObject tree will take care of the memory memory management.
So twice deleting would be happen.

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.

Letting QWidget and QVBoxLayout automatically resize if child resizes (Qt4)

I have the following minimal example code given.
main.cpp:
#include <QApplication>
#include "qt.h"
int main(int argc, char** argv)
{
QApplication app(argc, argv);
MyDialog mainWin;
mainWin.show();
return app.exec();
}
qt.cpp:
#include <QLabel>
#include "qt.h"
void MyDialog::setupUi()
{
setCentralWidget(new QWidget);
mainLayout = new QVBoxLayout( centralWidget() );
centralWidget()->setLayout(mainLayout);
// show the add new effect channel button
QPushButton* newKnobBtn = new QPushButton("new", this );
connect( newKnobBtn, SIGNAL(clicked()), this, SLOT(addNewKnob()));
mainLayout->addWidget( newKnobBtn, 0, Qt::AlignRight );
containerWidget = new QWidget(this);
scrollArea = new QScrollArea(containerWidget);
mainLayout->addWidget(containerWidget);
scrollLayout = new QVBoxLayout(scrollArea);
scrollArea->setLayout(scrollLayout);
/*
QSizePolicy pol;
pol.setVerticalPolicy(QSizePolicy::Expanding);
setSizePolicy(pol);
*/
addNewKnob(); // to fit size initially
}
void MyDialog::addNewKnob()
{
scrollLayout->addWidget(new QLabel("Hello World", this));
/*
containerWidget->adjustSize();
adjustSize();
*/
}
qt.h:
#include <QMainWindow>
#include <QVBoxLayout>
#include <QScrollArea>
#include <QPushButton>
class MyDialog : public QMainWindow
{
Q_OBJECT
private slots:
void addNewKnob();
private:
void setupUi();
QVBoxLayout* mainLayout;
QScrollArea* scrollArea;
QVBoxLayout* scrollLayout;
QWidget* containerWidget;
public:
MyDialog( ) { setupUi(); }
};
Compiling: Put all in one directory, type
qmake -project && qmake && make
I have the adjustSize() solution from here, but it does not work: (link). Everything I commented out was things I tried but did not help.
How do I make containerWidget and scrollLayout grow correctly, when a new Label is being added to scrollLayout?
Here's a simplified version that works for me:
qt.cpp:
#include <QLabel>
#include <QPushButton>
#include <QScrollArea>
#include "qt.h"
MyDialog::MyDialog()
{
QWidget * mainWidget = new QWidget;
QBoxLayout * mainLayout = new QVBoxLayout(mainWidget);
setCentralWidget(mainWidget);
// show the add new effect channel button
QPushButton* newKnobBtn = new QPushButton("new");
connect( newKnobBtn, SIGNAL(clicked()), this, SLOT(addNewKnob()));
mainLayout->addWidget( newKnobBtn, 0, Qt::AlignRight );
QScrollArea * scrollArea = new QScrollArea;
scrollArea->setWidgetResizable(true);
mainLayout->addWidget(scrollArea);
QWidget * labelsWidget = new QWidget;
labelsLayout = new QVBoxLayout(labelsWidget);
scrollArea->setWidget(labelsWidget);
addNewKnob(); // to fit size initially
}
void MyDialog::addNewKnob()
{
labelsLayout->addWidget(new QLabel("Hello World"));
}
qt.h:
#include <QMainWindow>
#include <QBoxLayout>
class MyDialog : public QMainWindow
{
Q_OBJECT
public:
MyDialog( );
private slots:
void addNewKnob();
private:
QBoxLayout * labelsLayout;
};
You have containerWidget that contain only one QScrollArea. I don't know why do you need this. But if you need this for some reason, you need to add a layout to this widget in order to make layouts work. Also do not create a layout for QScrollArea. It already have internally implemented layout. You should add scrollLayout to the scroll area's viewport() widget instead.
When you construct a layout and pass a widget to its constructor, the layout is automatically assigned to the passed widget. You should not call setLayout after that. This action will take no effect and produce console warning.