I have a class which displays QLabel with some text in QGridLayout. When QLabel is a class member, everything is ok, but when it is not, it isn't displayed in grid.
QLabel is not a member and is created in the constructor.
class Account : public QWidget
{
private:
//QLabel lab;
QGridLayout * grid;
public:
Account(QWidget * parent=0);
public slots:
void spend(int);
void update();
};
.cpp
#include <QLabel>
#include <QLineEdit>
#include <QTextStream>
#include <QFileInfo>
#include <vector>
#include <QGridLayout>
#include <iostream>
Account::Account(QWidget * parent) : QWidget(parent)
{
grid=new QGridLayout(this);
QLabel lab;
lab.setText("RFD");
grid->addWidget(&lab,0,0); //is not displayed
}
QLabel is a member:
class Account : public QWidget
{
private:
QLabel lab;
QGridLayout * grid;
public:
Account(QWidget * parent=0);
public slots:
void spend(int);
void update();
};
.cpp
#include <QLabel>
#include <QLineEdit>
#include <QTextStream>
#include <QFileInfo>
#include <vector>
#include <QGridLayout>
#include <iostream>
Account::Account(QWidget * parent) : QWidget(parent)
{
grid=new QGridLayout(this);
lab.setText("RFD");
grid->addWidget(&lab,0,0); //is displayed
}
WHy is it so?
Because when you declare QLabel in your constructor it is only a local variable, therefore it falls out-of-scope at the end of the function (see my comments below):
{
grid=new QGridLayout(this);
QLabel lab; // created here
lab.setText("RFD");
grid->addWidget(&lab,0,0); //is not displayed
} // destroyed here as the variable is out of scope
note
If you want to create it in your constructor then make it a pointer and new it - you will still need to keep track of this pointer, either as a member variable or pass the pointer to some other function / class to take care of it... (i.e. to delete it when you are finished with it...
Further - addWidget() takes a pointer, so it might handle the destruction of it itself - so you could probably do:
{
grid=new QGridLayout(this);
QLabel *pLab = new QLabel(this);
lab.setText("RFD");
grid->addWidget(pLab,0,0); //is not displayed
}
QLabel lab; is a local variable that lives until the end of the function and then gets automatically destroyed, so the label you want displayed doesn't actually exist anymore when you want to display it.
Furthermore Qt has a broken unconventional memory model that requires you to new up the QLabel and then pass an owning raw pointer to the function which Qt will automatically delete later when the parent gets removed. If you delete it manually you get a double delete (one from you and one from Qt) which usually results in a crash. Manually deleteing an object will automatically detach it from the parent, so you don't get a double delete.
The way Qt wants you to do it is
QLabel *lab = new QLabel;
lab->setText("RFD");
grid->addWidget(lab,0,0);
While this looks like a memory leak, it isn't, due to Qt's memory model.
Related
I am writing Qt/C++ project and I created my mainwindow.ui in QtDesigner. I placed in mainwindow.ui an empty widget which later I want to extend by putting there my widget written in code. This is my code:
class which extends QWidget
#pragma once
#include <QtGui>
#include <QWidget>
using namespace QtDataVisualization;
class GraphDataCreator : public QWidget
{
Q_OBJECT
public:
GraphDataCreator(QWidget* parent = 0);
~GraphDataCreator();
};
cpp of this class:
#include "GraphDataCreator.h"
GraphDataCreator::GraphDataCreator(QWidget* parent)
: QWidget(parent)
{
this->setStyleSheet("background-color:green;");
}
and the mainwindow class:
#pragma once
#include <QMainWindow>
#include "ui_MainWindow.h"
#include "GraphDataCreator.h"
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QMainWindow*parent = Q_NULLPTR);
~MainWindow();
private:
Ui::MainWindow ui;
};
cpp of main window class
#include "MainWindow.h"
#include <string>
#include <QtGui>
#include "GraphDataCreator.h"
MainWindow::MainWindow(QMainWindow*parent)
: QMainWindow(parent)
{
ui.setupUi(this);
QGridLayout* layout = new QGridLayout(ui.Chart3DWidget);
GraphDataCreator* chart3D = new GraphDataCreator(ui.Chart3DWidget);
layout->addWidget(chart3D);
QWidget* test_widget = new QWidget;
test_widget->setStyleSheet("background-color: red;");
layout->addWidget(test_widget);
ui.Chart3DWidget->setLayout(layout);
}
I wanted to be the whole widget in ui green, but it didn't appear so I put there test_widget with red background and the result is that it is half nothing/half red. So the green widget excists there but it is not visible. And my question is why the green part is not visible??? And how to solve it?
Of course names of classes are weird because I tried to do something else and this explanation simplifies that problem.
EDIT:
thx #sajas for help! Actually I used that link Why do stylesheets not work when subclassing QWidget and using Q_OBJECT? and I used case 2 written there so I deleted Q_OBJECT macro in GraphDataCreator class and it worked without any other changes. The function for background color of widget I left the same and everything else. The result is as expected half green / half red. Anyway I think I should include macro Q_Object as written in link over there because it is a Qt class, but it does work without. Maybe there is a small bug in Qt??? To sum up if you delete Q_Object macro it works.
Before we start things, this could be a possible duplicate of Qt Scroll Area does not add in scroll bars, however the answer provided by the user and which seemed to work for the OP, does not work for me. I fear it may have to do with versions, perhaps? Or am I missing something? (it is possible!)
Alright, for those who know Qt, I am a beginner and I have what seem to be a pretty silly problem, but is giving me a lot of headaches:
I want to press a push button and add items to a container, then it is supposed to be possible to scroll it down. As simple as that.
So I thought that perhaps setting a layout to scrollArea would do the job. It indeed adds my items as I wanted, but it doesn't load the scrollBar. I have checked the ScrollPolicy already, but nothing satisfied my issue. It is like the GVBoxLayout doesn't increase size and doesn`t let the scroll area to scroll.
Does anybody have a clue on how to fix it?
Code below:
saleWindow.h
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QListWidget>
#include<QString>
namespace Ui {
class SaleWindow;
}
class SaleWindow : public QMainWindow
{
Q_OBJECT
public:
explicit SaleWindow(QWidget *parent = 0);
~SaleWindow();
private slots:
void on_pushButton_clicked();
private:
Ui::SaleWindow *ui;
QVBoxLayout *gBoxLayout;
QMap<QString, QListWidget *> m_mappings;
};
#endif // SALEWINDOW_H
saleWindow.cpp
#include "salewindow.h"
#include "ui_salewindow.h"
#include <iostream>
#include <QGroupBox>
#include <QLabel>
//#include <QtSql/QSqlDatabase>
//#include <QtSql>
#include <QtWidgets/QPushButton>
SaleWindow::SaleWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::SaleWindow),
gBoxLayout(new QVBoxLayout())
{
ui->setupUi(this);
// Adding layout to scrollbar
{
ui->scrollArea_sales->setWidgetResizable(true);
ui->scrollArea_sales->setLayout(gBoxLayout);
QWidget *central = new QWidget;
ui->scrollArea_sales->setWidget(central);
}
}
SaleWindow::~SaleWindow()
{
delete ui;
delete gBoxLayout;
}
void SaleWindow::on_pushButton_clicked()
{
QGroupBox *sale = new QGroupBox();
sale->setTitle("minha venda");
gBoxLayout->addWidget(sale);
ui->scrollArea_sales->setLayout(gBoxLayout);
}
If you are using a QScrollArea for the scrollArea_sales object from the ui editor, you can see in the object inspector(qtdesigner) a default widget in the QScrollArea, so you not need to add this. Try this code:
// Adding layout to scrollbar(on the contrctor, replace yor scope by)
{
ui->scrollArea_sales->widget()->setLayout(gBoxLayout);
}
void MainWindow::on_pushButton_clicked() {// on the slot replace all by:
QGroupBox *sale = new QGroupBox();
sale->setTitle("minha venda");
ui->scrollArea_sales->widget()->layout()->addWidget(sale);
}
There is one QPushButton in a QWidget, click the button should open another QWidget, as coded below:
project.pro
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = untitled
TEMPLATE = app
CONFIG(debug, debug|release) {
DESTDIR = debug
} else {
DESTDIR = release
}
INCLUDEPATH += ..
SOURCES += ../main.cpp\
../widget.cpp \
../secondwidget.cpp
HEADERS += ../widget.h \
../secondwidget.h
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = 0);
~Widget();
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include <QVBoxLayout>
#include <QPushButton>
#include "secondwidget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
{
resize(400, 300);
QVBoxLayout *vLayout = new QVBoxLayout(this);
QPushButton *bt = new QPushButton("Open 2nd Widget");
vLayout->addWidget(bt, 1, Qt::AlignRight);
SecondWidget *secondWidget = new SecondWidget();
// SecondWidget *secondWidget = new SecondWidget(this);
connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
}
Widget::~Widget()
{
}
secondwidget.h
#ifndef SECONDWIDGET_H
#define SECONDWIDGET_H
#include <QWidget>
class SecondWidget : public QWidget
{
Q_OBJECT
public:
explicit SecondWidget(QWidget *parent = 0);
signals:
public slots:
};
#endif // SECONDWIDGET_H
secondwidget.cpp
#include "secondwidget.h"
SecondWidget::SecondWidget(QWidget *parent) : QWidget(parent)
{
setAttribute( Qt::WA_QuitOnClose, false );
}
Passing this for the SecondWidget constructor (as in the commented line) will brake my logic at some point. So the SecondWidget doesn't show anymore, when the button is clicked.
What's going on?
In addition to the problems with your destructor and constructor (and the memory leak you have because of the second), and the project file here are a few things that you may want to know in order to understand the whole parent situation:
You don't need to pass this. The purpose of assigning a parent is to be able to simplify the cleanup procedure for QObject instances (including all classes inheriting from the QObject class). Qt parent-child model follows the C++ standard that is destructors are invoked in the reversed order of their constructors. In simple terms this means the following:
Imagine you create widget A, B and C. Using the parent property you assign B and C to be children of A. So here we have the order of creation:
A (parent)
B,C (children)
Now at some point you want to destroy your widgets (example: closing the application). The standard C++ way is to destroy them in the following (reversed to the construction) order:
B, C (children)
A (parent)
And this is indeed what happens if we go for the parent first. However we are dealing with Qt here so we have to consider one additional feature the library provides - slots and signals.
Whenever a QObject gets destroyed it emits a signal. Based on the role the object plays in the parent-child relationship the outcome is one of the following:
only the QObject that is destroyed gets destroyed - this is what happens when a child gets destroyed before we destroy its parent
all children of that QObject get destroyed first followed by the destruction of the QObject itself - this is what happens when a parent gets destroyed. The emitted signal is caught by all its children which in return also get eliminated.
However assigning a parent is not mandatory. You can do the cleanup manually yourself. Read more about the parent-child model in the Qt documentation.
In addition to all that ownership transfer (a widget becomes a child of another widget) often happens automatically so it is not necessary to explicitly specify a widget's parent. Again taking things out of the Qt documentation an exception is made here. If you add a widget to a layout, the ownership is NOT transferred to the layout itself but to the QWidget it is part of.
Finally there is one important case when not assigning a parent makes things very, very different. Here I shall quote the Qt documentation on QObject:
Setting parent to 0 constructs an object with no parent. If the object is a widget, it will become a top-level window.
So if you have a QWidget and don't add it to some layout (and indirectly to the QWidget that has that layout) for example it will automatically become a separate window.
EDIT:
Check your constructor and in particular the way you work with your secondWidget object. As I've mentioned above in case you don't want to assign it to a parent widget you need to take care of the cleaning.
You dynamically allocate the memory for it
SecondWidget *secondWidget = new SecondWidget();
and even connect it to your button
connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
however you never release the allocated memory using delete or assigning it to a parent widget, which will take care of it automatically. Hence you create a memory leak. Connecting a QObject via signals and slots doesn't have anything to do with transfer of ownership.
I personally assume that you actually want a secondWidget to be an extra window shown on the screen. In this case you need to create a class member of type SecondWidget
SecondWidget *secondWidget;
then allocate it inside your constructor and connect whichever slots and signals you want
Widget::Widget(QWidget *parent) : QWidget(parent)
{
//...
secondWidget = new SecondWidget();
connect(bt, &QPushButton::clicked, secondWidget, &SecondWidget::show);
}
and finally release the memory inside your constructor:
Widget::~Widget()
{
delete secondWidget;
}
Otherwise as I said you are basically creating a reference to a memory block and right after you leave your constructor that reference gets destroyed since it runs out of scope.
EDIT 2:
Here is a small example how to do it if you want secondWidget as a child to your main widget:
main.cpp
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
widget.hpp
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "secondwidget.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
SecondWidget *secondWidget = new SecondWidget(this); # secondWidget is now officially adopted by Widget
# If you skip the parent assignment inside SecondWidget you can call secondWidget->setParent(this) here
connect(ui->pushButton, SIGNAL(clicked(bool)), secondWidget, SLOT(show()));
}
Widget::~Widget()
{
delete ui;
}
secondwidget.hpp
#ifndef SECONDWIDGET_H
#define SECONDWIDGET_H
#include <QObject>
#include <QDialog>
class SecondWidget : public QDialog
{
Q_OBJECT
public:
explicit SecondWidget(QWidget *parent = 0);
~SecondWidget();
};
#endif // SECONDWIDGET_H
secondwidget.cpp
#include "secondwidget.h"
#include <QFormLayout>
#include <QLabel>
SecondWidget::SecondWidget(QWidget *parent) : QDialog(parent)
{
# If you don't call the constructor of your superclass you can still assign a parent by invoking setParent(parent) here
QFormLayout *layout = new QFormLayout();
QLabel *label = new QLabel("SecondWidget here");
layout->addWidget(label); # Transfer ownership of label to SecondWidget
setLayout(layout);
}
SecondWidget::~SecondWidget()
{
}
Note the setParent(parent) inside the SecondWidget's constructor. You either have to invoke the superclass constructor (as you have done) or manually call setParent(parent). If you don't do that yoursecondWidgetwill not be assigned as a child to your main widget where you create it and thus you will produce a memory leak. You can also invokesecondWidget->setParent(this)` from within the constructor of your main widget in order to set the parent.
Here is how you can check if the parent-child hierarchy is okay:
To each QObject that you have (QWidget, QLayout etc. are all subclasses of QObject) assign an object name using QObject::setObjectName("some name")
At the end of both of your constructors add:
for(int i = 0; i < this->children().count(); i++)
std::cout << this->children()[i]->objectName().toStdString() << std::endl; // Add #include <iostream> to get the output
which basically traverses all the children of this (Widget or SecondWidget) and displays its children. In my case I got
label // Printing out children of SecondWidget
formLayout // Printing out children of SecondWidget
gridLayout // Printing out children of Widget
pushButton // Printing out children of Widget
main second widget // Printing out children of Widget
once I launched my application.
EDIT 3: Ah, I didn't notice that you are calling the QWidget(parent) in you SecondWidget constructor. This also does the trick so you don't need setParent(parent). I have altered my second EDIT.
This usually happens if
classes are linked
destructor calls aren't properly being called
Makefile needs some changes.
Actual Error?
Makefile needs to be remade using the cmd prompt or Terminal
Make sure that your destructor doesn't call itself so this change the remedy of execution.
I'm trying to write a simple Qt program which takes text inside a QLineEdit and appends it into a QTextEdit object when the return key is pressed.
Here is the code for my program:
#include <QApplication>
#include <QtGui>
#define WIDTH 640
#define HEIGHT 480
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
QTextEdit textArea;
textArea.setReadOnly(true);
QLineEdit lineEdit;
QPushButton quit("Quit");
QObject::connect(&quit, SIGNAL(clicked()), qApp, SLOT(quit()));
QHBoxLayout hLayout;
hLayout.addWidget(&lineEdit);
hLayout.addWidget(&quit);
QVBoxLayout vLayout;
vLayout.addWidget(&textArea);
vLayout.addLayout(&hLayout);
QWidget window;
window.setBaseSize(WIDTH, HEIGHT);
window.setLayout(&vLayout);
window.show();
//This is the line I can not get to work
QObject::connect(&lineEdit, SIGNAL(returnPressed()), &textArea, SLOT(append(lineEdit.text())));
return app.exec();
}
Essentially, the problem is connecting the QLineEdit returnPressed() SIGNAL to the QTextEdit append() SLOT. I am hoping someone can point out what is wrong with my code.
Thank you very much in advance for your time.
When you run your program, you should notice on the console the following Qt error output..
Object::connect: No such slot QTextEdit::append(lineEdit.text()) in ..
You would need to qualify the append reference in your call to connect with the QTextEdit variable name textArea.
But that's not going to help much because you can only specify signal and slot method names and parameter types when calling connect so you can't specify lineEdit.text() in there.
Since the append() slot expects a QString, ideally you would want to connect a signal that includes a QString but there is no such signal for QLineEdits.
You pretty much have to write a slot yourself that you can connect to returnPressed() and call textArea.append(lineEdit.text()) from there. You will need to subclass a QObject of some kind to write a slot which would usually mean subclassing QWidget and putting all of your UI building code in its constructor.
You might also notice that your program crashes when you close it. Since Qt likes to manage the destruction of most QObjects itself, it is usually best to allocate all QObject instances on the heap with new. This isn't technically necessary all the time but it is much easier :)
QObject::connect(&lineEdit, SIGNAL(returnPressed()), &textArea, SLOT(append(lineEdit.text())));
returnPressed() doesn't take any arguments, but append(QString) does take one argument; a QString. Thus, if this would work, you would theoretically call append(""), meaning you wouldn't append anything. Using lineEdit.text() wouldn't work either at this place.
I would recommend you to create a class for the widget:
class Widget : public QWidget
{
public:
Widget(QWidget parent = 0);
//Other public functions
private:
//Private functions and variables
public slots:
void boom();
};
Then you can just use
Widget w(0);
w.show();
in your main function.
void boom() would be called by returnPressed(), and it would take lineEdit.text() and append it to the QTextEdit.
I hope this helps.
here is the code it might be helpful.....
#include "hwidget.h"
Hwidget::Hwidget(QWidget *parent) :
QWidget(parent)
{
}
void Hwidget::mainform_init(void)
{
lineeditp = new QLineEdit;
quitp = new QPushButton("&Exit");
hboxlayoutp = new QHBoxLayout;
hboxlayoutp->addWidget(lineeditp);
hboxlayoutp->addWidget(quitp,0,0);
vboxlayoutp = new QVBoxLayout;
texteditp = new QTextEdit;
texteditp->setReadOnly(true);
vboxlayoutp->addWidget(texteditp,0,0);
vboxlayoutp->addLayout(hboxlayoutp);
QWidget *mywin = new QWidget;
mywin->setLayout(vboxlayoutp);
mywin->setWindowTitle("My Sig and Slot");
mywin->show();
lineeditp->setFocus();
}
void Hwidget::mcopy(void)
{
qDebug() <<"i am your copy slot";
texteditp->setText(lineeditp->text());
lineeditp->clear();
}
#include <QApplication>
#include "hwidget.h"
int main (int argc, char *argv[])
{
QApplication app(argc,argv);
Hwidget *hwin = new Hwidget;
hwin->mainform_init();
hwin->connect(hwin->quitp,SIGNAL(pressed()),
qApp,SLOT(quit()));
hwin->connect(hwin->lineeditp,SIGNAL(returnPressed()),
hwin,SLOT(mcopy()));
return app.exec();
return 0;
}
#ifndef HWIDGET_H
#define HWIDGET_H
#include <QWidget>
#include <QTextEdit>
#include <QLineEdit>
#include <QHBoxLayout>
#include <QVBoxLayout>
#include <QPushButton>
#include <QObject>
#include <QString>
#include <QDebug>
class Hwidget : public QWidget
{
Q_OBJECT
public:
explicit Hwidget(QWidget *parent = 0);
void mainform_init(void);
signals:
public slots:
void mcopy(void);
private:
QHBoxLayout *hboxlayoutp;
QVBoxLayout *vboxlayoutp;
public:
QPushButton *quitp;
QLineEdit *lineeditp;
QTextEdit *texteditp;
};
#endif // HWIDGET_H
here is my code:
//MainWindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtGui>
class MainWindow : public QWidget
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
private:
QTextEdit *textEdit;
};
#endif // MAINWINDOW_H
// MainWindow.cpp
#include "mainwindow.h"
#include <QMessageBox>
MainWindow::MainWindow(QWidget *parent)
{
textEdit = new QTextEdit();
}
MainWindow::~MainWindow()
{
delete textEdit;
}
//main.cpp
#include <QtGui>
#include "mainwindow.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
Is it more efficient (here's the "Q[Objects] VS using Pointers?" part of the question) to:
1) Use pointers as I am actually doing or
2) Use objects (removing * + delete statement)
Thank you!
MainWindow::MainWindow(QWidget *parent)
{
textEdit = new QTextEdit(this);
}
MainWindow::~MainWindow()
{
}
For QObject members as pointers, you shouldn't use delete, the QTextEdit will probably be a child of MainWindow, so it will be deleted automatically.
It would, of course, be theoretically faster to use non-pointer QObject members, with one less level of indirection. Like this (for those who didn't understand the question):
class MainWindow : public QMainWindow {
...
private:
QTextEdit textEdit;
};
and there is also less code to type, because you don't have to retype the class name of the members to initialize them in the constructor.
But since QObject are themselves already heavily using indirection (with their d-pointer), the gain will probably be negligible. And the extra code you type with pointer members allows you to have a lower coupling between your header files and Qt, because you can use forward declarations instead of header inclusion, which means faster compilations (especially if you are not using precompiled headers yet) and recompilations.
Also,
manually deleting QObject pointer members, or
declaring QObject as non-pointers members
can causes double deletion, if you don't respectively delete/declare them in the right order (children then parents for deletion, or parents then children for declaration).
For example:
class MainWindow : public QMainWindow {
...
private:
QTextEdit textEdit;
QScrollArea scrollArea;
};
MainWindow::MainWindow() {
setCentralWidget(&scrollArea);
QWidget *scrolledWidget = new QWidget(&scrollArea);
QVBoxLayout *lay = new QVBoxLayout(scrolledWidget);
lay->addWidget(...);
lay->addWidget(&textEdit); // textEdit becomes a grand-child of scrollArea
scrollArea.setWidget(scrolledWidget);
}
When MainWindow is deleted, its non-static class members are deleted in the reverse order of their declaration in the class. So scrollArea is destroyed first then textEdit, but scrollArea also destroys its children including textEdit, so textEdit is effectively destroyed twice causing a crash.
So it is less error prone to use QObject members as pointers, and to not delete the QObject which have a parent yourself.
Try creating QLineEdit on the stack and then put it into layout... Quit your application... What do you see? HINT: launch your application in debugger.
ksming is right about reading documentation. It is not language specific issue. If you are asking what is faster: heap or stack allocation then form your question properly.