Unable to find the error in this basic Qt snippet, but I am told that it exists for sure? - c++

I've been recently trying to get into Qt to better understand class hierarchy and OOP. I just fell upon this question in a test bank, asking to "fix" this code to prevent compiler and runtime errors. However, I unfortunately couldn't find the solution and I wasn't able to come up with a solution myself. For me, all QObject-derived classes handle all memory deallocation. So the following code does not pose any problems that I can see, and it compiles.
#include <QLabel>
#include <QWidget>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QLabel label("my string");
QWidget window;
label.setParent(&window);
window.show();
return a.exec();
}

For me, all QObject-derived classes handle all memory deallocation.
Exactly, that's why this sample code contains an error of the kind you'll see when you close window. Qt's parent-children system works in the way that the widget manages it's children's lifetime and deletes them when being deleted itself.
So, when QApplication quits ~QWidget() destructor is called, causing deletion of the label. Thus, ~QLabel() is called.
But on the other hand, when main is finished, local variables are being deleted, label is one of them. It is not a pointer but a value variable, we get another call of ~QLabel().
Double call of the destructor is the error. It can be fixed by creating label on a heap via new.

Qt handles memory management by parent-child relationships. If a parent widget/window is destroyed, it destroys all the children as well. In this instance the label is set as a child of the window, which means that the windows destructor will try to delete it. As it is create on the stack, the memory deallocation will fail. And even if it will not (assuming the memory manger silently ignores such call, which it should not), the label will be destroyed the second time when the execution of main() finishes, leading to double-delete. As others have already pointed out, you could solve it by allocating the label on the heap using QLabel *label = new QLabel("my string"); instead, but there is an even simpler solution: re-order the creation of the window and the label:
#include <QLabel>
#include <QWidget>
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget window;
QLabel label("my string");
label.setParent(&window);
window.show();
return a.exec();
}
This way the label will be destroyed before the window. That will remove the label from the windows children list and allow correct termination.
Finally, the use of the setParent() method is actually relatively rare in Qt. Normally you would have passed the parent to the constructor of the child widget:
QWidget window;
QLabel label("my string", &window); // No need for setParent()
This requires that the window already exists, and is Qt-s way of leading you towards the correct solution.

Related

QTCreator 5.0.2, parallel run of two window, C++

I went throught suggested "questions" about my problem. However neither does not solve it.
I program two windows. The second window is opening from first window. I need active the both windows, however to start the first window(MainWindow) I use:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.setWindowModality(Qt::NonModal);
return a.exec();
}
As was mentioned, the second window is started from pushButton, which is situated in first window(MainWindow) by same way.
void MainWindow::on_pushButton_2_clicked()
{
Graphics gr;
gr.setWindowModality(Qt::NonModal);
gr.exec();
}
I changed modality to NonModal,however the problem is without change. The Non-Modal mean:"The window is not modal and does not block input to other windows." <- from documentation
By documentation is recommended to avoid used .exec(). The alternatives are .show() and open(), which i tried. After the modification, the second window is shut down immediately after opening. after open immediately shut down.
Do you have any idea, how to solve that?
Graphics gr; defines a local variable, so the object is destructed as soon as it goes out of scope (at the end of your function).
In Qt, the typical approach is to work with pointers to Qt widgets (and, more generally, QObjects), but have a parent for each – the parent will clean it up.
Try this instead:
auto gr = new Graphics(this);
gr->setWindowModality(Qt::NonModal); // this is the default, no need for this call
gr->show();
This way the window will survive until your main window is destructed. In Qt there is also a mechanism for widgets/objects to self-destruct:
this->deleteLater();
So for example if you want to close and cleanup gr you can use that mechanism (you could also store gr as a member and call gr->deleteLater().

How to connect two different objects

I have two objects, one will hold the graph, and the other a few buttons. How to use (connect) so that when you press button 1, the inscription is displayed in debag or the schedule is filled with a new one?
For example, I press the button created by the class BtnBox and my graph is displayed. How to use connect()?
main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QThread>
#include "btnbox.h"
#include "plot.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
BtnBox *panel = new BtnBox(&a);
Plot *plot = new Plot();
QObject::connect(panel, SIGNAL(clickedBtn1()), plot, SLOT(slotPrinter()));
// panel->show();
QHBoxLayout *mainLayout = new QHBoxLayout;
mainLayout->addWidget(plot);
mainLayout->addWidget(panel);
QWidget window;
window.setLayout(mainLayout);
window.show();
return a.exec();
}
Maybe you can use the QPushButton::clicked signal and write something like this:
connect(ui->pushButtonObj, &QPushButton::clicked, plot, &Plot::slotPrinter);
But if you want a custom behavior with your class BtnBox you can create on header file of BtnBox a signal.
signals:
void clickedBtn1();
And use: emit clickedBtn1(); whenever you want to emit it, your connect should work.
There is no need for implementation of the signal, you just have to emit it.
The emit keyword is not really necessary, if you want you can simply use clickedBtn();
Ok, I assume you have class BtnBox, and it already has this in the class definition (in .h file usually):
signals:
void clickedBtn1();
Qt moc will generate the implementation of that method, you don't need to do anything more for it here. But you do need to get that signal emitted. In many cases you would add emit clickedBtn1(); in the right places, but in this case you probably want to do something like this in BtnBox::BtnBox constructor:
connect(ui->button1, SIGNAL(clicked()), this, SIGNAL(clickedBtn1()));
Connecting signal to signal will simply do signal forwarding. Replace ui->button1 with the correct pointer to the button, whatever you have in your code.
Note on how to not do it, just to provide a bit of food for thought: Alternative way would be to expose the button 1 from the class, so in your main() you could then do something like this: QObject::connect(panel->getButton1(), SIGNAL(clickedBtn1()), plot, SLOT(slotPrinter()));. But this is generally considered a bit dirty, exposing internals of BtnBox class like that. It's better to expose the signal, and then the code using the class does not need to care how it gets emitted (for example from several different parts of BtnBox), or how internal implementation of BtnBox might change (for example converting it to QML).

Qt must construct QApplication before a QWidget

Qt recently started crashing without having a reason for it. The most recent one which is currently grinding my nerves down to a pulp is crashing due to starting another form programmatically. The "must construct QApplication before a QWidget" apparently is a common issue with Qt 5.7.* versions and the solutions I have found so far in StackOverflow haven't helped me.
This is a screenshot of the error message I got after the application crashed:
And here is the bit of the code that I remove which allows me to restart the application without any noticeable problems:
#include "operations.h"
Operations o;
void mainWindow::on_thisButton_clicked()
{
o.show();
this->hide();
}
----
The main.cpp as requested :)
#include "mainWindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
mainWindow w;
w.show();
return a.exec();
}
Try this:
#include "operations.h"
void mainWindow::on_thisButton_clicked()
{
Operations *o = new Operations();
o->show();
this->hide();
}
You might want to declare Operations *o as a member of mainWindow and initialize it the the constructor if you don't want to create a new one each time the button is clicked.
"must construct QApplication before a QWidget" is the standard type of error you get with Qt applications, when linking something incompatible ( like mixing debug/release ).
So in most use cases this indicates a build problem and has nothing to with the code itself.
Don't create Operations object as a global variable, as it will be created as a static BEFORE running main(), therefore the error message is simply correct and relevant. This is a C++ issue, not a Qt one.
All other suggestions work because you now create the object at the right time, after the QApplication...
Okay, I have managed to find a solution, however, it is borderline idiotic as it does not make any sense why it does not work in its prior state. Technically, all you need to do in order to have the error not appearing is to stick the declaration of the form class you are referring within the function itself(ie Operations o;).
Here is the code solution itself:
#include "operations.h"
void mainWindow::on_thisButton_clicked()
{
Operations o;
o.show();
this->hide();
}
Bare in mind that this is not the end of all problems as I currently have the problem of the new form closing in the very same 1 second period it opens. If I manage to solve it I will update my solution.

Program crash after setting QApplication::style twice

#include <QApplication>
int main() {
QApplication::setStyle("windows");
QApplication::setStyle("windows");
}
This program produces Segmentation fault (core dumped). My qmake version is 4.7.2. Is this a Qt bug or my version is too old?
You must create an instance of QApplication before you set it's style. From the documentation
Ownership of the style object is transferred to QApplication, so
QApplication will delete the style object on application exit or when
a new style is set and the old style is still the parent of the
application object.
I'm assuming it's crashing because there is no QApplication to take ownership of the style. In general, creating the QApplication is one of the first things you should do.
#include <QApplication>
int main() {
QApplication a(argc, argv);
QApplication::setStyle("windows");
QApplication::setStyle("windows");
}

Can't update Text Edit Text QT

I'm having an issue. My textEditBox doesn't seem to be updating when my Addtext function is called.
Here's my Addtext:
void CTextBox::AddText(QString string, QString spriteString)
{
textBrowser->setText(string + spriteString);
update();
}
Another class then calls the function and it should add text to the textbox but it doesn't.
How do you call CTextBox::AddText()? update() only schedules a paintEvent() for later, when the program returns to the event loop. That means that
you actually need to have an event loop, ie. at some point you need to call qApp->exec();
you need to allow the programm some time to qApp->processEvents() (insert that after update()), if you want any paining done within a blocking while() {...} or something like that.
Edit: Come to think of it, you shouldn't even need to call update() nor processEvents() if your program returns to the event loop some time after AddText, so there really seems to be an issue with the event loop. Post your main.cpp, will you?
Here is a trivial example of what it sounds like you are trying to do. Maybe you can see where your design differs?
Notice: no explicit update() is needed. I think that's a red herring. I think it far more likely that (1) you are somehow calling your AddText method with empty strings, or (2) your real text edit is a different variable, and have somehow created two of them and are updating one that just exists in memory and was never added to a layout. Perhaps the code of your FileLoaderQT would help? (You can edit your question rather than posting in comments.)
#include <QtGui>
int main(int argc, char **argv) {
QApplication app(argc, argv);
QMainWindow w;
QTextEdit *edit = new QTextEdit;
w.setCentralWidget(edit);
edit->setText("Hello world!");
w.show();
edit->append("Hello world again!");
return app.exec();
}