I have a Qt application that uses a QMainWindow-derived class for the main UI. On startup I want to make some security checks and, if they fail, display a message to the user and close the main window. Currently I make these checks in the QMainWindow constructor, but if I call the close method, nothing happens and the application continues to run. For example:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
{
setupUi(this);
...
if (checkFails())
{
QMessageBox::warning(this, tr("Error"), tr("You cannot run this app"));
// This has no effect
close();
}
}
Alternatively I could make the checks in the main function but then I lose the ability to display a language-specific message box (the tr macro only works in a QObject-derived class by the looks of things.)
Any ideas on how to close the main window on startup or make the tr macro work outside of a QObject derived class?
The event loop needs to be running before you can successfully close the main window. Since you probably first construct a window, and then start the event loop the close() call has no effect. Try the following solution instead:
QTimer::singleShot(0, this, SLOT(close()));
The QTimer::singleShot() will fire as soon as an event loop has been started, and subsequently calls the close() method on your application main window.
The above solution will probably cause your main application window to be visible for a short period of time, causing unwanted flickering.
A cleaner solution should perform the security checks prior to constructing the main window. Since tr() is also available as a static method on QObject, this can be done from the main function.
Most applications start up in three steps: 1) construct the window; 2) show the window; 3) start the event loop. You can make steps 2 and 3 conditional on the success of step 1 by adding a flag, whose value is set by the window constructor, to the window class:
Window class:
class myMainWindowClass : public QMainWindow
{
Q_OBJECT
public:
myMainWindowClass()
: isFinished_(false) { if (error) isFinished_ = true; } // constructor
bool isFinished() const { return isFinished_; }
private:
bool isFinished_;
}
Application code:
int main()
{
myMainWindowClass main_window(); // Step 1
// Finish early if isFinished flag is set
if (main_window.isFinished())
return 0;
main_window.show(); // Step 2
return a.exec(); // Step 3
}
This should also avoid any flicker as the application will end before the window is show()n.
tr is a public static member of QObject. You should be able to call QObject::tr("Error") in your main function.
Have you tried first hide()ing the window (this should occur anyway when close() is called) to see if this then allows close() to destroy the window.
If this does not work, you could always try destroy(true, true)ing the window along with any sub-windows.
Related
Here I am explaining my problem statement in detail and the efforts I have put so far
A) Problem Statement : During printing if 'Stop Printing' pushbutton is pressed, the printing should stop at that moment!
B) My Work :
1. StartPrinitng_Pressed :
void MainWindow :: on_StartPrinitng_Pressed()
{QSqlquery studentList;
studentList("SELECT Name, address FROM class WHERE Roll No = some variable")
while(studentList.next())
{
Name=studentList.value(0).toString();
address=studentList.value(1).toString();
QTimer:: singleShot(1000,this,SLOT(StopNow())); //calling stopNow function
if(StopPrintingNow==0)
{ //** I am printing the fetched data (in a string) by setting GPIO pins HIGH **// }
}
}
2. StopPrinting_Pressed :
void MainWindow::on_StopPrinting_Pressed()
{StopPrintingNow=1;}
3. StopNow Function Declaration :
void MainWindow::StopNow()
{
if(StopPrintingNow==1)
{ //** I have reset all serials ports; Break; **// }
else if(StopPrintingNow==0)
{ QTimer::singleShot(1000,this,SLOT(on_startPrinting_pressed())); }
}
C) Flow of program execution : As and when "StartPrinting" pushbutton is pressed, the query shown in my question executes which fetches data from database and perform simultaneous printing.
D)Problem Faced -
1.GUI is getting hanged while printing, hence StopPrinting button doesn't respond.
Qtimer is not calling "StopNow function " while printing (though I have called it at correct position)enter image description here
Handling of timers and button presses is both covered by the Qt event loop -- which is blocked while you are looping over that SQL query. You have two options:
1) Periodically dispatch events in your while loop.
This is as simple as
qApp->processEvents();
But you have to be careful, however: any events you trigger due to user interaction (or a timer) will block and your while loop will not run until the event is finished. In your case especially, you could end up running a second copy of your on_StartPrinitng_Pressed function.
2) Do the printing on a separate thread.
This involves some more code, but the gist of it is that you create a SqlPrinter object with a startPrinting slot and stopPrinting slot. You then create a QThread and change its owner thread to that thread. Slot invocations will happen across the thread boundary and all will be fine.
class SqlPrinter : public QObject {
Q_OBJECT
public:
SqlPrinter(QObject* parent = nullptr) : QObject(parent) {}
public slots:
void startPrinting();
void stopPrinting();
};
In your main code, then do something like this, assuming that you have the two buttons named MainWindow_StartButton and MainWindow_StopButton:
QThread* printerThread = new QThread(qApp);
SqlPrinter* printer = new SqlPrinter;
printer->moveToThread(printerThread);
printerThread->start();
QObject::connect(MainWindow_StartButton, &QPushButton::clicked, printer, &SqlPrinter::StartPrinting);
QObject::connect(MainWindow_StopButton, &QPushButton::clicked, printer, &SqlPrinter::StopPrinting);
Don't forget to clean up SqlPrinter afterwards!
I'm trying to update the main window by calling updateGUI function in a thread every 500 ms. The window is displayed but not updated with the new values unless I close the window. When I do so, a new window is opened with the new value. I found this question but it didn't answer my question. I knew that (as stated in qt documentation)
QApplication::exec enters the main event loop and waits until
exit() is called.
I tried to use processEvents() but the main window is opened and closed repeatedly and very fast that I can't even see it. Here is my code:
float distanceToObject;
bool objectDetected;
Modes currentMode;
void timerStart(std::function<void(void)> func, unsigned int interval)
{
std::thread([func, interval]()
{
while (true)
{
auto x = std::chrono::steady_clock::now() + std::chrono::milliseconds(interval);
func();
std::this_thread::sleep_until(x);
}
}).detach();
}
int updateGUI(void)
{
int argc = 0;
char **argv = NULL;
QApplication a(argc, argv);
MainWindow w;
// Set text of a label
w.setDistance(QString::number(distanceToObject));
// Also update objectDetected and currentMode values
w.show();
//a.processEvents();
return a.exec();
}
void sendMsg(void)
{
// Send heartbeat signal to another device
}
void receiveMsg(void)
{
// Read messages from the other device and update the variables
// These two values change continuously
objectDetected = true;
distanceToObject = 5.4;
}
void decide(void)
{
// The core function of the program. Takes relatively long time
// Run a decision-making algorithm which makes decisions based on the values received from the other device.
// Update some variables according to the made decisions
currentMode = Auto;
// Execute functions according to the made decisions.
setMode(currentMode);
}
int main(void)
{
timerStart(updateGUI, 500);
timerStart(sendMsg, 1000);
timerStart(receiveMsg, 10);
timerStart(decide, 500);
}
How can I update the main window with the variables' values correctly?
Your thread does not update the MainWindow, but it does create an entirely new QApplication and MainWindow on every iteration. Your thread should be stuck inside QApplication::exec until you quit the application (e.g. by closing the window). Only then should your thread's loop make further progress.
In general, you must be very careful when doing updates from outside the main thread, since typically GUI operations must be performed inside the main thread.
Think about using QThread, which already comes with its own event loop, which you can use to notify/update your window using a respective slot.
Without further details about what you are actually trying to achieve, it is not possible to give you further direction. I, at least, recommend that you create your QApplication and MainWindow inside the main thread (e.g. main). Then it depends what you are trying to 'update'. If you need to progress some data, then you can do that within your second thread and send the results to your MainWindow instance using signal-slot. If you need to draw onto the window, then this either has to be done in the main thread directly, or you might find a way to render into a separate buffer (i.e. QImage) from within your thread and then send this buffer to the main thread for drawing it into the window.
I try to sketch how something like this can be done. Notice, however, that this it neither complete nor compilable, but merely an outline.
First, you have your MainWindow and add to it a signal, that notifies all observers to start doing their work (will become clear in a moment). Furthermore, you add slots that will be invoked whenever one of your values changes. Those slots run in the main thread (and are members of the MainWindow) and thus can update the window however they need to:
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
// constructors and stuff
void startWorking()
{
emit startWorkers();
}
public slots:
void onModeChanged(Modes m)
{
// update your window with new mode
}
void onDistanceChanged(float distance)
{
// update your window with new distance
}
signals:
void startWorkers();
};
Next, you build a Worker class, that encapsulates all the 'background work' you like to do (basically what your thread did in your original code):
class Worker : public QObject
{
Q_OBJECT
public:
// constructors and stuff
public slots:
void doWork()
{
while(!done)
{
// do stuff ...
Modes m = // change mode
emit modeModified(m);
// do stuff ...
float distance = // compute distance
emit distanceModified(distance);
// do stuff ...
}
}
signals:
void modeModified(Modes m);
void distanceModified(float distance);
};
Note, that Worker must inherit QObject and that your doWork method must be a public slot. Furthermore, you add a signal for each of the values you like your MainWindow to be informed about. No implementation for them is needed, since it is generated by the Qt MOC (Meta Object Compiler). Whenever one of the respective values changes, simply emit the corresponding signal and pass the new value.
Lastly, you put everything together:
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
MainWindow window;
// create a worker object
Worker* worker = new Worker;
// connect signals and slots between worker and main window
QObject::connect(worker, &Worker::modeModified,
&window, &MainWindow::onModeChanged);
QObject::connect(worker, &Worker::distanceModified,
&window, &MainWindow::onDistanceChanged);
QObject::connect(&window, &MainWindow::startWorkers,
worker, &Worker::doWork);
// create a new thread
QThread* thread = new QThread;
// send worker to work inside this new thread
worker->moveToThread(thread);
thread->start();
// show window and start doing work
window.show();
window.startWorking();
// start main loop
int result = app.exec();
// join worker thread and perform cleanup
return result;
}
Alright, let's go through it. First, you create your QApplication and MainWindow inside your main thread. Next, create an instance of your Worker object (could create multiple here). Then you connect the signals of the worker to the slots of the window and vice versa. Once these connections are established, whenever you emit a signal, the connected slot is invoked by Qt (and passed values are transmitted). Notice, that this connection works across thread boundaries. Whenever a signal is emitted from a thread different then the receiving object's thread, Qt will send a message, which is processed in the receiving object's thread.
Then you tell Qt that you want your worker to live inside another thread using QObject::moveToThread. See here for a very detailed explanation of how to correctly use QThread and objects inside it.
The rest is then simple. show your window and start processing. Here different ways are possible. I just call the startWorking method here, which then emits the startWorkers signal, which is connect to the worker's doWork method, such that doWork will start executing after this signal is received by the other thread.
You then call QApplication::exec which runs the main thread's event loop, where all these signals are processed by Qt. Once your application is closed (e.g. by calling quit or closing the main window) the exec method returns and you are back in main. Notice, that you need to correctly close the thread (e.g. by sending an addition signal that stops the while loop) and join it. You also should delete all the allocated objects (worker, thread). I omitted this here for simplicity of the code example.
Answering your Question
I have many functions, e.g., updateClips and mavReceive that should be called periodically and run independently from each other. I should create a different Worker class for each function, as each has different signals, and a QThread object for each of these functions, right? I don't need startTimer() anymore? If yes, how can I control the calling interval for each function (used to be done in startTimer()
from the comment:
The answer greatly depends on what exactly you mean by "should be called periodically". Who is supposed to call them? The user? Or should they just be executed periodically?
So in principle, you can have multiple workers in one thread. However, if they are supposed to do work all the time (spin in a while loop) it does not make sense, since one is running and all others are blocked. In that case you would have one thread for each worker.
If I understand you correctly, you are interested in updating something periodically (e.g. every 500ms). In that case I highly recommend using the QTimer. You can set an interval and then start it. The timer will then periodically emit the timeout signal, which you can connect to whatever function (more precisely slot) you want to have executed.
An updated version of the Worker could look like this:
class Worker : public QObject
{
Q_OBJECT
public:
Worker()
{
QObject::connect(&modeTimer_, &QTimer::timeout,
this, &Worker::onModeTimerTimeout);
QObject::connect(&distanceTimer_, &QTimer::timeout,
this, &Worker::onDistanceTimerTimeout);
modeTimer_.start(500); // emit timeout() every 500ms
distanceTimer_.start(100); // emit timeout() every 100ms
}
public slots:
void onModeTimerTimeout()
{
// recompute mode
Modes m = // ...
emit modeModified(m);
}
void onDistanceTimerTimeout()
{
// recompute distance
float distance = // ...
emit distanceModified(distance);
}
signals:
void modeModified(Modes m);
void distanceModified(float distance);
private:
QTimer modeTimer_;
QTimer distanceTimer_;
};
Notice, the connections established in the constructor. Whenever one of the timers times out, the connected slot is invoked. This slot then may compute whatever it needs to and afterwards send the result back to the MainWindow in the main thread using the same signal as before.
So, as you see, you can have multiple timers / re-computations / update signals within one Worker (and thus, one thread). However, the crucial point for an implementation is, how long the computations take. If they take very long (e.g. nearly as long as the intervals) then you should think about using multiple threads to speed up the computation (meaning: perform one computation in each thread). As I slowly seem to get a clearer picture of what you want to achieve, I am wondering whether it is only about these periodic updates that you 'misused' the thread for in your question. If this is indeed the case, then you do not need that thread and Worker at all. Then simply add the timers to your MainWindow and connect their timeout signal to the respective slot of the MainWindow directly.
Problem
I want to use a QTimer to update a derived QSplashScreen that draws a progress bar (using paint commands, not a widget) to estimate when the program will start running.
By necessity, this happens prior to the exec call of the QCoreApplication. I've gotten this to work (in release mode only) on both X11 and Windows, by putting a timer in a second thread, and calling a function in the splash screen which updates the progress and repaints the widget. However, this doesn't work in debug mode as it produces the following error:
"ASSERT failure in QCoreApplication::sendEvent: "Cannot send events to objects owned by a different thread."
I'm not really worried about this assertion as the code doesn't crash in release and it's just a splash screen, however I need to be able to run the program in debug so I'd like to either a) refactor the code so it doesn't trigger the assertion, or b) dissable this particular assertion.
Tried:
Using update() instead of repaint(). This doesn't cause an assertion, but it also doesn't repaint because the main thread is too busy loading in the shared libraries, etc, and the timer events don't get processed until I'm ready to call finish on the splash screen.
starting QTimer in main loop. Same result as above.
Using a QT::QueuedConnection. Same result.
Main
#include <QApplication>
#include <QtGui>
#include <QTimer>
#include <QThread>
#include "mySplashScreen.h"
#include "myMainWindow.h" // contains a configure function which takes a
// LONG time to load.
int main( int argc, char* argv[] )
{
// estimate the load time
int previousLoadTime_ms = 10000;
QApplication a(argc, argv);
MySplashScreen* splash = new MySplashScreen(QPixmap(":/splashScreen"));
// progress timer. Has to be in a different thread since the
// qApplication isn't started.
QThread* timerThread = new QThread;
QTimer* timer = new QTimer(0); // _not_ this!
timer->setInterval(previousLoadTime_ms / 100.0);
timer->moveToThread(timerThread);
QObject::connect(timer, &QTimer::timeout, [&]
{
qApp->processEvents(); splash->incrementProgress(1);
});
QObject::connect(timerThread, SIGNAL(started()), timer, SLOT(start()));
timerThread->start();
splash->show();
a.processEvents();
myMainWindow w;
QTimer::singleShot(0, [&]
{
// This will be called as soon as the exec() loop starts.
w.configure(); // this is a really slow initialization function
w.show();
splash->finish(&w);
timerThread->quit();
});
return a.exec();
}
Splash Screen
#include <QSplashScreen>
class MySplashScreen : public QSplashScreen
{
Q_OBJECT
public:
MySplashScreen(const QPixmap& pixmap = QPixmap(), Qt::WindowFlags f = 0)
: QSplashScreen(pixmap, f)
{
m_pixmap = pixmap;
}
virtual void drawContents(QPainter *painter) override
{
QSplashScreen::drawContents(painter);
// draw progress bar
}
public slots:
virtual void incrementProgress(int percentage)
{
m_progress += percentage;
repaint();
}
protected:
int m_progress = 0;
private:
QPixmap m_pixmap;
};
MyMainWindow
#include <QMainWindow>
class myMainWindow : public QMainWindow
{
public:
void configure()
{
// Create and configure a bunch of widgets.
// This takes a long time.
}
}
The problems are because the design is backwards. The GUI thread should not be doing any loading. The general approach to GUI threads is: do no work in the GUI thread. You should spawn a worker thread to load what you need loaded. It can post events (or invoke slots using a queued connection) to the GUI thread and its splash screen.
Of course, the worker thread should not create any GUI objects - it can't instantiate anything deriving from QWidget. It can, though, instantiate other things, so if you need any expensive-to-obtain data, prepare it in the worker thread, and then cheaply build a QWidget in the GUI thread once that data is available.
If your delays are due to library loading, then do load all the libraries in a worker thread, explicitly, and ensure that all of their pages are resident in memory - for example by reading the entire .DLL after you're loaded it as a library.
The MyMainWindow::configure() could be called in a worker thread, as long as it doesn't invoke any QWidget methods nor constructors. It can do GUI work, just not visible on screen. For example, you can load QImage instances from disk, or do painting on QImages.
This answer provides several approaches to executing a functor in a different thread, GCD-style.
If you are constructing widgets that are expensive to construct, or construct many of them, it's possible to make sure that the event loop can run between the instantiation of each widget. For example:
class MainWindow : public QMainWindow {
Q_OBJECT
QTimer m_configureTimer;
int m_configureState = 0;
Q_SLOT void configure() {
switch (m_configureState ++) {
case 0:
// instantiate one widget from library A
break;
case 1:
// instantiate one widget from library B
...
break;
case 2:
// instantiate more widgets from A and B
...
break;
default:
m_configureTimer.stop();
break;
}
}
public:
MainWindow(QWidget * parent = 0) : QMainWindow(parent) {
connect(&m_configureTimer, SIGNAL(timeout()), SLOT(configure()));
m_configureTimer.start(0);
//...
}
};
I'm fighing since last week with problem caused by update of QPlainTextEdit. I'm trying to create separate from QMainWindow Dialog window with QPlainTextEdit inside. The problem begins when I try to use appendHtml signal (also tried with appendText), text that is placed is not visible unless marked by by mouse. Repainting or updating cause in program crash or no visible action.
Simplified code of QDialog with QPlainTextEdit header:
namespace Ui {
class LogWindow;
}
class LogWriter: public QDialog
{
Q_OBJECT
QMutex print_lock;
public:
class Log{
Q_OBJECT
const static int MAX_SIZE = 100;
bool to_terminal;
QString color;
QMutex *print_lock;
QPlainTextEdit *text_place;
QVector< QPair<QString,time_t> > history;
LogWriter * obj;
public:
bool print;
Log(bool _print,QString _color,LogWriter *obj_ = NULL)
{print = _print; color = _color; obj = obj_;}
void setLock(QMutex *print_lock_){print_lock = print_lock_;}
void setTextField(QPlainTextEdit *_text) {text_place = _text;}
Log& operator<<(QString &a);
Log& operator<<(const char* a);
};
static LogWriter* getInstance()
{
static LogWriter instance; // Guaranteed to be destroyed.
// Instantiated on first use.
return &instance;
}
~LogWriter();
Log LOW,MEDIUM,HIGH;
Ui::LogWindow *ui;
signals:
void signalLogAppend(QString);
};
Simplified code of methods definitions:
LogWriter::LogWriter(QWidget * parent): QDialog(parent) {
ui = new Ui::LogWindow;
ui->setupUi(this);
LOW.setLock(&print_lock);
MEDIUM.setLock(&print_lock);
HIGH.setLock(&print_lock);
connect(this,SIGNAL(signalLogAppend(QString)),ui->plainTextEdit,
SLOT(appendHtml(QString)),Qt::DirectConnection);
}
LogWriter::Log& LogWriter::Log::operator<< (QString &s){
history.push_front(qMakePair(s,time(NULL)));
if(history.size() > MAX_SIZE) history.pop_back();
if(print){
//print_lock->lock();
QString text = "<font color=\"";
text += color + "\">";
text += s + "</font>";
//cout << text.toStdString() << endl;
//text_place->appendHtml(text);
//text_place->repaint();
emit (obj)->signalLogAppend(text);
//print_lock->unlock();
}
return *this;
}
I have two separate ui files (first for main window, second for log window).
I have to use log window all across my program (something about 10 threads), and I stucked on this issue. My question is - is it possible to force GUI update without using main thread and if not - what else possibilities I have. If possible I would rather avoid reconstructing all my code - it would take me some time to do it. Right now logging is super easy - I ony need:
LogWindow *log = LogWindow::getInstance();
log->MEDIUM << "something";
As additional info I add QTCreator warning:
QObject::connect: Cannot queue arguments of type 'QTextBlock'
(Make sure 'QTextBlock' is registered using qRegisterMetaType().)
QObject::connect: Cannot queue arguments of type 'QTextCursor'
(Make sure 'QTextCursor' is registered using qRegisterMetaType().)
If I understand your code correctly, you're trying to log from a background thread and are using a direct connection to pass the signal to the GUI thread? That's not going to work, you have to send the signal via the default connection so Qt can figure out that it's a cross-thread signal and pass it across threads accordingly (ie, via the message loop on the foreground thread).
In Qt, any GUI interaction has to happen in the Main/foreground thread otherwise bad things happen as you discovered. You can certainly send a signal from a background thread to trigger a GUI update - I do this all the time - but you need to ensure that you're using the correct connection for it. The direct connection results in a direct function call and is not going to work for you in this case.
In your code, the problem is the call to connect() - you explicitly specify the connection mode for the signal to slot connection when you should just use the default setting. If you set the connection explicitly to Qt::DirectConnection, the underlying code will execute a direct call to the specified slot, which means that you end up calling the slot in the thread context of the signal. You don't want that because the signal is triggered in a background thread.
You can't pass arbitrary types/classes to signals and slots. The list is limited and not all Qt classes are in the list. To add types/classes to the list of those that can be passed to a signal/slot, you must call qRegisterMetaType for that class. I recommend calling it in the constructor of the class you're trying to pass to a signal like this:
MyClass::MyClass() : MyParentClass()
{
static int reg = qRegisterMetaType<MyClass>("MyClass");
}
The static int ensures the registration is only called once and before any instance of MyClass could ever be used.
In my application I have a tray icon and so I overrode closeEvent so that the application "minimizes" when certain things happen. However, I do expect that upon pressing exit, that the application will completely exit. However, after overriding closeEvent and calling the function quit(), it seems to bypass the MainWindow destructor, where I have some code.
What am I missing in closeEvent to properly close the application so that the destructor of MainWindow is called, as is the case when closeEvent isn't overriden?
I've tried using
QMainWindow::closeEvent(event);
and a few other things, but the destructor is never called.
My close event implementation is:
void MainWindow::closeEvent(QCloseEvent * event)
{
if(m_closeCompletely == false)
{
if (trayIcon->isVisible())
{
QMessageBox::information(this, tr("Hello"),
tr("The program will keep running in the "
"system tray. To terminate the program, "
"choose <b>Quit</b> in the context menu "
"of the system tray entry."));
}
}
else
{
event->accept();
}
}
The WA_DeleteOnClose attribute needs to be set to call the destructor on a close event, otherwise a widget or window is just hidden from view. Add the following to the class constructor:
this->setAttribute(Qt::WA_DeleteOnClose);
This will cause the destructor to be called on a close event.
It all depends on where and how you allocated the objects you are using. If you create the QApplication and QMainWindow on the heap then they will lurk around until the OS cleans up the memory. Sending a close() to a QWidget (and QMainWindow is one) will only close it .. hide it visually from the user. You can show() later on, close() does not destruct it.
You could use something like this:
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
MyMainWindow mw;
mw.show();
int rc = app.exec();
// optional: do_cleanup();
return rc;
}
app and mw are constructed on the stack. They will be destructed in reverse order when the app returns from the exec() call (usually when you send quit() to the QCoreApplication::instance()). You can even provide a plain cleanup function, no need to put something into the destructor of the UI.
Remember that all the Application Resources are hold by the QApplication;
Once your MainWindow is closed, it just be close, aka hide from the screen;
I wonder why you want to destructor your MainWindow, it seems that you've made a system tray entry, so my suggestion is just to hide the MainWindow but to destructor, cause there is a system tray, you may want to click it to show the MainWindow.
Hope to Help. : )
I just ran into this same problem and it was because I was running an infinite loop that never allowed the application to close.
forever
{
_runGameLoop();
}
I had to make the loop end when MainWindow closed
while(!M->isClosed())
{
_runGameLoop();
}
And of course I had to implement isClosed()
bool MainWindow::isClosed()
{
return _isClosed;
}
void MainWindow::closeEvent(QCloseEvent*)
{
_isClosed = true;
}