How to catch the QSystemTrayIcon quit event? - c++

I have a QSystemTrayIcon subclass. Inside it, I have:
quitAction = new QAction(tr("&Quit"), m_parent);
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
CloseEvent doesn't work here. How can I catch this quit event? I want to save data before closing.

You should connect your cleanup/save code to the QCoreApplication::aboutToQuit() signal.
This signal is emitted when the application is about to quit the main event loop, e.g. when the event loop level drops to zero. This may happen either after a call to quit() from inside the application or when the users shuts down the entire desktop session.

Related

How to set focus after execution QAction

Main widget steals focus after execution QAction.
I need the focus to be set to popup widget.
QAction *action = new QAction(tr("show popup"), this);
connect(action, &QAction::triggered, this, &MyWidget::showPopup);
addAction(action);
void MyWidget::showPopup()
{
QMessageBox* popup = new QMessageBox(this);
popup->setModal(true);
popup->show();
popup->setFocus();
}
MyWidget inherits from QWidget.
Because you just created popup, it's not 'there' yet in the GUI. Even the show() doesn't instantly show it. After you leave the scope of MyWidget::showPopup(), the GUI event loop will continue looping and be able to process your new popup. Thus the setFocus() call comes too early.
But there is help underway:
QWidget::setFocus() is a slot, so you can invoke it.
If you use a timer (QTimer::singleShot(0, popup, SLOT(setFocus()));), it should work.
Maybe you'll need to use 10ms instead of 0ms.

What is the signal emitted when we close a Window in qt

I have my MainWindow that open a second Window.
When I click on a button in my second window, a thread is launched and I want my thread to end when I quit my second window.
What is the signal emitted when my SecondWindow is closed ?
Thank you for your futures answers
There is no signal emitted when widgets (including QMainWindows) are closed. If a widget is set to be deleted when it is closed then you could use the following QObject signal to detect when the widget is about to be destroyed...
void destroyed(QObject *obj = Q_NULLPTR)
However this will only work if your window has the Qt::WA_DeleteOnClose flag enabled (it is not enabled by default).
Alternatively and probably more preferably you can implement the standard widget close event and emit your own signal to indicate that the window was closed:
void MainWindow::closeEvent( QCloseEvent* event )
{
emit MySignalToIndicateThatTheWindowIsClosing();
event->accept();
}

Qt: How to close a dialog window opened with exec()?

I'm making a c++ application in Qt, and need to programmatically close a dialog window (opened with this->exec();) via code after a certain function finishes executing.
I'm using Qt 5.6.
Thanks in advance!
Here is an example of my code, that doesn't work (Worker is the dialog Class):
void MainWindow::on_pushButton_2_clicked()
{
//When Start button clicked:
Worker worker;
worker.exec();
//worker.run(1);
worker.accept();
}
So when pushButton_2 is clicked, I want a dialog to open that gives out the current progress, and when that is done, I want it to close.
Edit:
Now you posted more code....
worker.exec();
worker.accept(); // or worker.close();
exec() starts QDialog events processing loop and will return only when completed (after accept(), reject() or done(int) is called). So worker.accept() will not be reached (you should see that if using your debugger). It must be called by worker itself after a user action (button click by instance).
What you meant to do is:
worker.show();
QThread::sleep(2); // wait for 2 seconds
worker.accept();
Then, worker.accept() will be executed at some point. Dialog is shown, but it's modal.
Old post (before edit):
You can call accept() to do as if user clicked OK or reject() to do as if user clicked Cancel.
Note that those are slots, so you can fire them by connecting a signal to them (signal to be emitted when you function finishes executing for instance).
Example:
void MyDialog::doSomethingAndClose()
{
// do your stuff here
accept(); // will close the dialog
}
or:
void MyDialog::doSomethingAndClose()
{
// do your stuff here
emit weAreDone();
}
If you earlier connected (in MyDialog constructor for instance):
connect( this, SIGNAL(weAreDone()), this, SLOT(accept()) );
Just connect your custom signal with QDialog::done(int) and emit signal after your function finishes executing.
As I've just learned, the issue is caused by the gui not updating automatically.
Here is a link to a SO question that fixes this issue.

How to Avoid Qt Access Violation Errors When Using QThread?

I am working on an application where I use QThread to capture frames of camera (OpenCV). I followed the approach described here and moved a worker to the QThread:
m_CameraCaptureThread= new QThread();
m_ProcessingThread= new QThread();
m_CameraCapture= new CCameraCapture();
//Move camera capture object to thread
m_CameraCapture->moveToThread(m_CameraCaptureThread);
//Connect error signal
QObject::connect(m_CameraCapture, SIGNAL(error(QString,QString)), this, SLOT(reportError(QString,QString)));
//Connect the finished signal of the worker class to the thread for quitting the loop
QObject::connect(m_CameraCapture, SIGNAL(finished()), m_CameraCaptureThread, SLOT(quit()));
//This connections guarantees that the *m_CVideoCapture is automatically deleted if the event loop of the thread is terminated. Therefore, m_CVideoCapture does not need to be released manually if the capturing process is stopped.
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCaptureThread, SLOT(deleteLater()));
QObject::connect(m_CameraCapture, SIGNAL(finished()), m_CameraCapture, SLOT(deleteLater()));
QObject::connect(this, SIGNAL(exitThreads()), m_CameraCapture, SLOT(exitThread()));
This code is part of the constructor of my camera handler class. If the main application is closed I want to exit all threads. Therefore, the destructor of my CCameraHandler is:
CCameraHandler::~CCameraHandler(void)
{
emit exitThreads();
qDebug() << "CCameraHandler deleted";
}
The exit Slot in my camera capture which is called by the signal exitThreads() is:
void CCameraCapture::exitThread(){
//Stop grabbing
stopGrabbing();
//Emit finished signal which should be connected to quit() of QThread and deleteLate of this class;
emit finished();
}
As one can see from the connection setup the emitted finished() signal will quit the event loop of the thread and call deleteLater() of the Worker and the Thread. The destructor of the worker which is called looks like:
CCameraCapture::~CCameraCapture(void)
{
qDebug() << "CCameraCapture deleted";
}
The result is that the Destructor of CCameraCapture is called correctly - it appears only one time in the QDebug stream but at the end of CCameraCapture::~CCameraCapture(void) scope. I get an access violation error from OpenCVs opencv_highgui249d.dll. As I am only using:
cv::VideoCapture m_Cap;
in the class definition of CCameraCapture, the destruction of m_Cap must cause this error. At the moment I really do not know how to solve this issue. Any ideas?
Edit:
The application should close when the main window is closed using
this->setAttribute(Qt::WA_DeleteOnClose);
and
CMainWindow::~CMainWindow(){
m_CameraHandler->deleteLater();
m_ImageWidget->deleteLater();
m_ProcessedImageWidget->deleteLater();
emit windowClosed();
qDebug() << "CMainWindow deleted";
}
If the main application is closed I want to exit all threads.
Without debugging this myself, it looks like a problem here is the emit in the destructor of CCameraHandler.
One reason this is problematic is that if the user closes the application and it quits the main event loop, (started with QApplication's call to exec), any objects that have had deleteLater called may not actually be deleted. In this case, I'm specifically looking at m_CameraCaptureThread.
If we walk through the event handling of signals / slots: -
QApplication::processEvents...
CCameraCapture::exitThread()
emit finished
QThread::quit
QThread::deleteLater
By calling deleteLater, an event is placed in the current thread's event queue to process the delete after the slot function has exited. This occurs when the event loop next processes events.
However, the application is going to quit, so the event loop does not run again and the call to deleteLater is not serviced.
If all objects are running in the same thread, then signal / slot connections are direct, which would be less of an issue. However, with multiple threads, the connections are queued.
I suggest changing the destructor so that you clean up without using an emit signal here and see if the problem still persists.
Finally solved the problem: The application terminated before the threads left their eventloop. The point that a camera capture thread usually never terminates makes it necessary to exit the capturing loop at some point. If this exit is triggered by closing the application one needs to exit the threads before the application closes. I follow this example (see above). However, as the loop never terminates one needs emit a signal from the main thread to terminate. If this is done on close of the application, the signal will not arrive in time. Therefore, I connected the finished() signal of QThread to the deleteLater() of the worker
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCapture, SLOT(deleteLater()));
QObject::connect(m_CameraCaptureThread, SIGNAL(finished()), m_CameraCaptureThread, SLOT(deleteLater()));
The finished signal will be emitted on exit of the event loop and will delete the QThread and the worker. In the destructor of the class which sets up the QThread and the worker I now use
CCameraHandler::~CCameraHandler(void)
{
emit stopGrabbing();
m_CameraCaptureThread->exit();
m_CameraCaptureThread->wait();
qDebug() << "CCameraHandler deleted";
}
At first I left out the wait and the application still crashed. For me this solved the problem. Thanks for the help to figure out this solution.

How to call a slot on quit

I want to update my database just before my Qt application closes.
I want something like connect(this, SIGNAL(quit()), this, SLOT(updateDatabase()))
One way could be to introduce a quit button, but is it possible to achieve this functionality if user presses Alt+F4?
Use signal aboutToQuit() instead.
This signal is emitted when the application is about to quit the main
event loop, e.g. when the event loop level drops to zero. This may
happen either after a call to quit() from inside the application or
when the users shuts down the entire desktop session.
The signal is particularly useful if your application has to do some
last-second cleanup. Note that no user interaction is possible in this
state.
For example :
connect(this, SIGNAL(aboutToQuit()), this, SLOT(updateDatabase()));
There is another way to do it, not aboutToQuit() signal, but to re-implement the closeEvent(QCloseEvent *event). You can call you slot before the statement event->accept();
like this:
void MainWindow::closeEvent(QCloseEvent *event)
{
call_your_slot_here();
// accept close event
event->accept();
}