How to do things upon quitting a Qt app - c++

To my understanding, the following will execute an application which will exit when all associated windows are closed.
int main(int argc, char *argv[])
{
QApplication cal(argc, argv); //Application instance
MainWindow window; //Create window in the stack
window.show(); //Show the window
return cal.exec(); //Execute event loop
}
This will also quit said application.
quitButton = new QPushButton("Quit", this);
connect(quitButton, SIGNAL(clicked()), QApplication::instance(), SLOT(quit()));
How should I go about cleaning up (closing hardware connections and libraries etc.) so that I can be sure everything that needs to happen happens no matter how the app it exited, nor at what point in execution it is exited?
I will have a library open throughout the whole app, and a connection to a USB device at most times. I am able to close this connection easily if the process runs its course, but I want the connection to be closed safely if somebody decides to hit quit or close all the windows before it is done.

There is a virtual function called closeEvent for a QWidget (see Documentation, which is called when the widgets close() operation is called.
Alternatively, if you don't want to have your own Base widget, you can hook yourself to the signal QApplication::lastWindowClosed (see here). It is emitted when the last visible window of your application closes.

You can do the following:
int main(int argc, char *argv[])
{
QApplication cal(argc, argv);
// Allocate resources
int ret = cal.exec(); // start the event loop and wait until it quits
// Free the resources
return ret;
}

The proper C++ way of doing it is:
Ensuring that all of your object instances get destructed.
Ensuring that any resources you hold are held via RAII.
Then you don't need to do anything special. As the objects get destructed when main returns, things get cleaned up.
For example:
int main(int argc, char ** argv) {
QApplication app(argc, argv);
QObject foo;
QObject * bar = new QObject(&app);
Widget w;
w.show();
return app.exec();
}
Here, both foo and bar are properly destructed before main returns. Now suppose you hold some non-memory resources, like a file:
class Worker : public QObject {
QFile m_file;
...
void doSomething() {
if (m_file.open()) {
...
}
// No file closing!
}
};
int main(int argc, char ** argv) {
QApplication app(argc, argv);
Worker foo;
Widget w;
w.show();
return app.exec();
}
Again, the file gets closed before main returns.
That's how RAII works, in a nutshell. If you have your own classes that wrap OS or other resources, simply ensure that such resources are freed when the classes get destructed - and that the classes are always destructible (it's always safe to destruct them). My pet peeve is QThread - it breaks this expectation, and you have to implement a wrapper to fix it.
For resources that you need to allocate on the heap, you're supposed to be using smart pointers or QObject memory management.

There is a QApplication signal that you can use for cleaning up before the application quits:
QObject::connect(&app, SIGNAL(aboutToQuit()), &MyCleaner, SLOT(cleanUp()));
Have fun!

Related

QMainWindow doesn't .show() but .showFullScreen() works

I have a desktop application that I'm building which checks a QSetting value and if it isn't set shows one QMainWindow class but if it is, shows a different QMainWindow class. The two classes are literally identical at this point as it's early on and don't really contain anything.
In my main function this is what I've got:
int main (int argc, char *argv[]) {
...
if (userToken == "NO_USER") {
LoginWindow w;
w.show();
} else {
MainWindow w;
w.show();
}
return a.exec();
}
The only difference in this between the default project set up from when I created the project is the addition of the conditional window load. Both MainWindow and LoginWindow don't have anything loading other than the ui file associated with them, they're functionally the same.
The weirdness is if I do w.showFullScreen() for the LoginWindow it will show up and take up the whole screen, if I do w.show() nothing at all happens, no compiler warnings|errors, application runs fine, just no window displays.
If I remove the conditional statements and LoginWindow references, MainWindow shows up fine.
Any idea why a call to .showFullScreen() would work but a call to .show() on the same class wouldn't?
I am not sure if this solves you problem, but there definitely is a bug in your code. You are instanciating the window objects on the stack inside a a tight scope, and as you know those objects will be destructed as soon as they go out of that scope. What you are doing is letting them go out of scope before the application is ever started.
Please try this instead:
int main (int argc, char *argv[]) {
...
if (userToken == "NO_USER") {
LoginWindow w;
w.show();
return a.exec();
} else {
MainWindow w;
w.show();
return a.exec();
}
}

Qt : pass values from a window to another

I have a Qt App running two windows, a login window and a data window. I need to be able to pass a couple of values from the first to the second, but i can't figure out a way to do it, since - on my main.cpp file - i have :
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
LoginWindow loginWindow;
loginWindow.show();
return a.exec();
}
What i'd want would be :
LoginWindow loginWindow;
loginWindow.who();
// wait for user to input data in LoginWindow.......
// fetch datas from loginWindow
SecondWindow secondWindow;
secondWindow.show();
So, as soon as my LoginWindow is closed (after the login), the whole app is terminated. Plus, i can't extract datas drom LoginWindow to the main function, since i don't know when they'll be available.
How can i make this work ?
Thanks.
I would declare a local variable in Main() and pass it by-reference to the login window's constructor, i.e.:
string username;
LoginWindow loginwindow(username);
loginwindow.show();
Where the constructor is declared like this:
LoginWindow::LoginWindow(string& username);
Then LoginWindow can modify the string however it needs to, and when the call to show() returns, the user name will be stored in the local variable username. You can then pass that to the constructor of secondWindow in the same way.
That's a proper place for Qt's signals and slots. The simplest way to get going - just add one signal to LoginWindow class and one slot to DataWindow class:
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
LoginWindow loginWindow;
DataWindow dataWindow;
QObject::connect(&loginWindow, SIGNAL(loginAcquired(LoginData)),
&dataWindow, SLOT(getLoginData(LoginData)));
QObject::connect(&loginWindow, SIGNAL(loginAcquired(LoginData)),
&dataWindow, SLOT(show()));
loginWindow.show();
return a.exec();
}

C++ application does not kill all processes on exit

This is my main.cpp which starts the mainwindow:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TabWindow w;
w.show();
return a.exec();
}
Even with the a.connect(...) I do not understand why myApplication.exe still runs after I close the mainwindow. Any suggestions on how I can fully end all processes after the quit button is clicked?
EDIT: The Qt Documentation says this:
We recommend that you connect clean-up code to the aboutToQuit() signal, instead of putting it in your application's main() function. This is because, on some platforms, the QApplication::exec() call may not return.
There's nothing wrong with your code. And your connect doesn't do anything.
Unless you call QGuiApplication::setQuitOnLastWindowClosed(true) somewhere, application should exit when the last window is closed. Probably you block event loop somewhere in your window code.
Thanks to the comment posted by #ratchetfreak in my question, I figured out where the problem was.
In my MainWindow, I started a worker thread which was not terminated and thus still persisted as a process after the application was closed. In order to fix this, I registered the close event and kept track of the existence of the thread - i.e. basically, ignored the closeEvent until the thread was deleted as well.
void MainWindow::closeEvent(QCloseEvent *event)
{
if (workerThreadExists) {
// Gracefully exiting all tasks inside the worker thread
while (workerThreadExists){
event->ignore();
}
event->accept();
}
}
...and for me workerThreadExists is just a BOOLEAN that is set to true once the thread is created and then it is set to false when the thread is deleted. Hope this helps!
You should have something like this:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
TabWindow w;
w.show();
return a.exec();
}
// Do quit in your TabWindow:
TabWindow::TabWindow()
: public QWidget(NULL)
{
connect( ui->myQuitButton, SIGNAL( clicked() ), qApp, SLOT( quit() ) );
}
just replace
return a.exec();
with
return 0;
That should end all processes which is associated with that program.

Qt 5.3 Signals and Slots, Simple Function and Lambda Expression

I tried to Write a program using Qt 5.3 and I try to using signals and slots as practice. I wrote the following code (part of the code) :
void exitToWin()
{
exit(0);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
QMessageBox EndBox;
QObject::connect((EndBox.button(QMessageBox::Ok)),SIGNAL(clicked()),exitToWin);
w.show();
EndBox.show();
return a.exec();
}
I even change the declaration of the function to static and I checked the expression with parentheses and without them while I am writing the connect command. but although what Qt documented and what its IDE guided to. also I read here and I tested it.
Moreover I tried with lambda expression as below:
QObject::connect((EndBox.button(QMessageBox::Ok)),SIGNAL(clicked()),[=](){
exit(0);
});
but still I receive errors indicate "No matching function call".
And after all I have to say that I am using Microsoft Windows 7.
This works on Qt 5.3:
#include <QtWidgets>
void exitToWin()
{
exit(0);
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QMainWindow w;
QMessageBox endBox;
endBox.addButton(QMessageBox::Ok); // (2)
endBox.connect(endBox.button(QMessageBox::Ok),
&QAbstractButton::clicked, exitToWin); // (1)
/* This works, too:
endBox.connect(endBox.button(QMessageBox::Ok),
&QAbstractButton::clicked,
[] () { exit(0); });
*/
w.show();
endBox.show();
return a.exec();
}
Here's why:
(1) You can use endBox's QObject to do the connection between the QAbstractButton's clicked signal and your exitToWin simple function. You also can't connect a SIGNAL to a simple function or a lambda, so we use the member function variety, instead.
(2) endBox doesn't actually get an OK button by default. When you mention it on line (1) in your code, it creates it, but not in time (apparently) to pass the pointer back to connect, so we create it first here.
Your code won't work for two reasons:
Firstly, QMessageBox does not have such a signal. See the documentation for the signals it does have.
Secondly, when making connections from a signal to a slot (or lambda function), you must define the function signatures, not specific values.
If a signal can pass a variety of values and you only want your slot to perform a certain function on a selection of those values (in this case, only if the value QMessageBox::Ok is passed) it is up to the slot to interrogate the values, not the connect statement.
Since the connect() method is from QObject it has to be called from a QObject child containing the Q_OBJECT macro in its declaration. Running qmake prepare the class to send signals and receive slots.

Can you change the queue size of a Qt connection? [duplicate]

This question already has answers here:
How to Compress Slot Calls When Using Queued Connection in Qt?
(8 answers)
Closed 8 years ago.
In Qt you can connect two objects by setting up a signal in one object a slot in the other and then connecting them using "connect()".
Now by emitting a signal in one object it is sent to the second object. I have a system that takes user inputs and if there are too many user inputs I want my "queue" to fill up and not accept any more inputs.
I could implement a reply mechanism on the receiving object, but I want to know if we can make a queue size of (for example) 1. So only one message will be handled, and any new emittions are simply chucked away until the "pipe" is has space.
Is this possible in Qt?
In my case the two objects are in different threads and have a queued connection (if that makes any difference)...
MainWindow::MainWindow()
{
// Make object 1, stick it in another thread
MyObjType1 *obj1 = new MyObjType1();
anotherThread = new QThread; // anotherThread is type QThread *
obj1->moveToThread(anotherThread);
anotherThread->start();
// Make object 2, connect a signal to obj1
MyObjType2 *obj2 = new MyObjType2();
connect(obj2, SIGNAL(obj2Signal(int), obj1, SLOT(obj1Slot(int), Qt::QueuedConnection);
// Hammer obj1 with signals to its queue
for (int i = 0; i < 100000; i++)
{
emit obj2->obj2Signal(i);
}
}
So the idea would be that obj1 gets lots of signals, it handles the first one, and somehow throws the others away until it finishes, then takes on the next one that is emitted.
With a queued connection, for each slot connected to a signal there is a QMetaCallEvent posted to the connected slot object's event queue. The events are delivered when the event loop runs. The code below outputs:
about to emit
done emitting
in aSlot()
class MyObject {
Q_OBJECT
Q_SIGNAL void aSignal();
Q_SLOT void aSlot() { qDebug() << "in aSlot()"; }
public:
MyObject(Qt::ConnectionType conn = Qt::AutoConnection) {
// QObject::connect() defaults the connection type to Qt::AutoConnection,
// we merely duplicate this behavior.
connect(this, SIGNAL(aSignal()), SLOT(aSlot()), conn);
qDebug() << "about to emit";
emit aSignal();
qDebug() << "done emitting";
}
};
int main(int argc, char ** argv) {
QCoreApplication app(argc, argv);
MyObject obj(Qt::QueuedConnection);
QCoreApplication::processEvents();
return 0;
}
The problem can now be reformulated to: How to force removal of duplicate QMetaCallEvent events from the event queue? This is known as event compression. I have already provided a canonical answer to that question. For user input, you want the most recently emitted signal to be retained, not the oldest one, but I've implemented both behaviors in the answer code.
Using the code from my answer, your example merely needs the following in the main() function:
int main(int argc, char ** argv) {
CompressorApplication<QApplication> app(argc, argv);
app.addCompressedSignal(MyObjType2::staticMetaObject.method(MyObjType2::staticMetaObject.indexOfSignal("obj2Signal(int)")));
MainWindow w;
w.show();
return app.exec();
}
Note: If one were connecting objects with the default Qt::AutoConnection and the objects were in the same thread, then the concept of a queue wouldn't apply at all. The slot is called before the signal function returns and nothing needs to be queued! The code below will output:
about to emit
in aSlot()
done emitting
// MyObject as above
int main(int argc, char ** argv) {
QCoreApplication app(argc, argv);
MyObject obj;
return 0;
}