Qt MainWindow CloseEvent Mac Cmd+Q - c++

On OS X 10.9 with Qt 5.2 and the following application code
#include "mywindow.h"
#include <QApplication>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
MyWindow w();
w.show();
return a.exec(); }
where MyWindow derives from QMainWindow and overwrites the closeEvent(QCloseEvent*) method, this method is called when I close the application window (e.g. click the windows (x) close button), but it is not invoked when I close the application via the main Menu -> "Quit " or via the "CMD + Q" key shortcut.
From looking around the web and numerous question I got the impression that closing the application should invoke the closeEvent for all top level windows. Is this not the case, or is something going wrong here?
Edit: In addition to the above ways of closing the application, are there any other instances that I in general should handle that would result in QApplication::quit rather than invoking the window's close event? What about a system shutdown for example?
When handling a close event, I'm confirming that the user really wants to quit and I make sure cleanup like writing back changed settings is happening. Should I maybe move cleanup / saving settings to the destructor instead and do the confirmation query in closeEvent?

By default on the Mac, Qt will create an Apple Menu | Quit if a menubar doesn't exist with either quit or exit entry. That default created entry will call QApplication::quit() which will not trigger your MyWindow::closeEvent().
In your UI you should add a menu item named Exit (on the Mac it will be automagically renamed to Quit) and in the MyWindow class constructor you should connect that action to the close() slot (which is inherited from QWidget).
Update- To take a shot at your additional questions, no the destructor should probably only be used for deallocation of memory (releasing file locks, etc). Anything that could potentially involve user interaction (such prompting for a file location or alerting the user via a QMessageBox that something failed) will need to go in the closeEvent method. Saving window geometry (and other simple items using QSettings) should also be done via the closeEvent (though, I have seen code that saves geometry in the destructor work, there could be some edge cases where it does unexpected things).

Related

How to write QT system tray app without a window class, and integrate it with another process?

Here is my setup:
A background process that keeps running and does it's job.
A launcher which launches the aforementioned process and monitors it, relaunches it if crashed or killed.
I wish to add a system tray access to the launcher process (and the launcher process ideally will contain code for system tray display) and enable basic options (start, stop etc) to be triggered from the system tray context menu. The system tray does not need a window of it's own. Just a windowless system tray with a Context menu that contains 2-3 options.
Since the all code written so far is in C/C++ and I need it to run on Windows and Linux, QT comes across as obvious choice. I have found it quite frustrating to get past basic QT launcher tray display. Nearly every example I have seen of QSystemTrayIcon includes a 'mainwindow' inheritance.
Below is the code I am using to create system tray.
#include <QtWidgets/QApplication>
#include <QtCore/QDebug>
#include <QtGui/QIcon>
#include <QtWidgets/QSystemTrayIcon>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenu>
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QPixmap oPixmap(32,32);
//QMenu* menu1 = new QMenu(); // want to get a context menu from system tray
oPixmap.load ("systemTrayIcon.png");
QIcon oIcon( oPixmap );
QSystemTrayIcon *trayIcon = new QSystemTrayIcon(oIcon);
qDebug() << trayIcon->isSystemTrayAvailable();
trayIcon->setContextMenu( menu1);
trayIcon->setVisible(true);
trayIcon->showMessage("Test Message", "Text", QSystemTrayIcon::Information, 1000);
return app.exec();
}
The code displays system tray alright, but I haven't been able to get around on how to add menus to it. What I want is:
1) Add the context menu to the system tray above without adding any window class (unless that is not possible)
2) Connect those context menu items to functions in my existing code
3) The app.exec() seems to be an infinite loop that processes QT events. However, since my launcher has it's own event loop, I want to make it so that the QT event loop is integrated with my launcher loop. In other words, add some non-QT tasks to the event loop.
Given the clarification from the comments, you have a couple of options on how to get code called for context menu or activation actions.
A receiver object: basically what the examples where using, just that you don't derive your receiver class from any window type.
For macro based signal/slot connections, the base type needs to be QObject or something derived from that, for function pointer based connect it can be any class
class MyReceiver : public QObject
{
Q_OBJECT
public slots:
void onActivated(QSystemTrayIcon::ActivationReason reason);
};
// in main()
MyReceiver receiver;
// macro based connect
connect(trayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)),
&receiver, SLOT(onActivated(QSystemTrayIcon::ActivationReason)));
// or function pointer based connect
connect(trayIcon, &QSystemTrayIcon::activated,
&receiver, &MyReceiver::onActivated);
Connect to stand-alone functions
void onActivated(QSystemTrayIcon::ActivationReason reason);
connect(trayIcon, &QSystemTrayIcon::activated, &onActivated);
With a C++11 capable environment, connect to a lambda
connect(trayIcon, &QSystemTrayIcon::activated,
[](QSystemTrayIcon::ActivationReason reason) {});
For the context menu the same techniques apply, the "sender" objects are the QAction items you add to the menu and their signal is triggered() or toggled(bool) depending on whether the action can be just clicked or toggled between and "on" and "off" state.

How to show dialog when main window became visible for the very first time in Qt widgets application?

I have Qt widgets application that has to authorize user with web service request before he can use the program. If user can not sign in for any reason, I have to exit the application.
.NET Framework implements Load event for that: when user sees window for the very firt time (the keyword here is "user sees"), it is possible to display dialog; if dialog result is not OK, we call close on main application window.
With Qt, we can override showEvent. However, it fires before user really can see main window. When I create dialog in showEvent, it appears without main window, and when I close main window in showEvent, it doesn't close (because it is not opened at the moment) and appears later event if user failed to sign in. It appears even after QApplication::quit() call.
My question is: is there a way to receive exactly the same functionality as in .NET Framework/Windows API and get event fired when user really sees window and not when he "may be sees something or will see soon"? It is possible to start timer from showEvent to get similar effect, but I think it is not professional, because you never know what may happend in user computer (may be its CPU works on 100% now), and you can not have real guarantee that timer will show dialog at correct moment.
I would try to create MainWindow and hide() it be default. The only widget to be shown at startup should be then the Login-Dialog. I would connect the successful login with the show() slot of the QMainWindow and the login failure - with the quit slot of the application.
I usually do something like this in my main.cpp:
int main(int argc, char* argv[])
{
QApplication a(argc, argv);
LoginDialog dialog;
if(!dialog.exec()){
return 1;
}
MainWindow w;
w.show();
return a.exec();
}
that (of course) after having LoginDialog defined to inherit from QDialog and MainWindow defined to inherit from QMainWindow.
Create the main dialog at application startup, and only create the main window when the service responds positively.
You don't really need to dig deep in the event handlers.

QApplication does not exit when main window closes

I have a QMainWindow that is used to browse and view images with. These images are special medical images that are read using a wrapper that I wrote.
The GUI has a QListview on the left, which shows a list of thumbnails. When the user selects one of them, the QVTKWidget displays the corresponding image on the right. I use a vtkRenderWindowInteractor to manipulate the displayed image.
My main looks like the following:
#include <QApplication>
#include "GUIClassName.h" //inherits from QMainWindow
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
GUIClassName w;
w.show();
return a.exec();
}
My closeEvent looks like the following:
void GUIClassName::closeEvent(QCloseEvent* event)
{
// mainInteractor is a vtkSmartPointer<vtkRenderWindowInteractor>
if (this->_mainInteractor != NULL)
this->_mainInteractor->TerminateApp();
event->accept();
}
The issue is that when I close the QMainWindow, the application does not terminate. That is the command prompt stays open and Press any key to continue does not appear. If I Ctrl+break, then the application exits (obviously) but I get the following message:
QObject::~QObject: Timers cannot be stopped from another thread
I have noticed that if I only select one thumbnail and display it in the QVTKWidget, the program terminates just fine. But when I select one thumbnail, display it and then select another thumbnail, then the program does not terminate when I close the window.
I was wondering if anyone could tell me what I am doing wrong here. I would gladly copy/paste more code, but I am not sure which parts are relevant at the moment.
I have read the following in hopes of an answer, but so far I have been unable to solve this issue:
Qt app stays in memory even after MainWindow is closed
Qt process stays in memory after application closes
C++ application does not kill all processes on exit
Thanks
Did you stop all your threads?
I'm not familiar with VTK, but looks like someone run some code in thread and did not stop them properly.
RazrFalcon gave me a good hint. I am not an expert in Qt (yet) so I was looking for the Qthread that was being executed at all the wrong places.
The answer lies in the vtkRenderWindowInteractor class. For most vtkObjects (if I am not mistaken), calling new on the vtkSmartPointer deletes/stops the object if it already exists. However, this does not happen for vtkRenderWindowInteractor.
The solution was the following. When switching between thumbnails, I had to check for the existence of the interactor and if it was running, I would just call
this->_mainInteractor->TerminateApp();
to stop the hidden Qthread.

How to close correctly a Qt program?

When I try to close my Qt program, it just keeps running in the background though there's no window anymore.
Basically, I would like to know what I should do so it closes properly when I click the red cross on my main window (which has no parent).
Following this link, I tried a few things like :
QApplication app(argc, argv);
//...
app.connect(&app, SIGNAL(lastWindowClosed()), &app, SLOT(quit()));
return app.exec();
or
QApplication app(argc, argv);
//...
app.setQuitOnLastWindowClosed(true);
return app.exec();
but neither work, the process still stays in memory, after the cross is clicked.
Then, how can I close correctly my program ?
You can close your application manually using QApplication::quit().
By default the execution is terminated when the last top level window with the Qt::WA_QuitOnClose attribute has been closed. You don't need to connect lastWindowClosed to quit because it repeats the default setQuitOnLastWindowClosed behavior. You don't need to do setQuitOnLastWindowClosed(true) either because it's true by default. The code you've posted does nothing (if nothing is changed somewhere else).
Possible solutions:
Check your main window attributes. Maybe you have removed Qt::WA_QuitOnClose attribute.
If you have reimplemented closeEvent in your top level window, check that close event is being accepted.
Check if there are some other top level widgets that may be hidden but not closed. Use QApplication::topLevelWidgets() to list them.

Qt Application hangs on exit (after QDialog exec)

I'm using Qt to build a UI and I need to have a dialog window show up before the main app window, so that the user can select some files to load ans things like that.
What I've got is a fairly simple main:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
IView *view = new QtView();
view->showView();
int rc = a.exec();
std::cout << "exit" << std::endl;
return rc;
}
the QtView class is the concrete implementation of the IView interface.
It has the mainwindow instance and a QDialog instance too. int the view->showView() method this is what I've got:
void QtView::showView()
{
this->_configDialog->exec();
this->_mainAppWindow->show();
}
It works fine, the dialog opens and when the user clicks OK, exec returns and the main window appears. The problem is that when I quit the main window I get a zombie process and the app just seems to hang even though all the windows have been closes and I never get the "exit" I print out in the main just before the main returns.
I'm not sure what I'm doing wrong, but I get the same resutl even if I click on the cross to close the dialog, the main window opens up, and once closed the whole things just hangs there.
If anyone has any advice, that would be cool.
Thanks.
I Fixed my problem.
I used the accepted/rejected/finished(int) signal from the QDialog to trigger the show slot on the main app window, and display it if the dialog was accepted.
Thanks for the help anyway.
I think the possible reason for this behavior is that your configuration dialog may not have a parent widget set on it (it's a blind guess though as you haven't cited the relevant portion of the code). That's because QApplication by default would only quit when all the windows with no parent are closed. And the thing is, a dialog is not a window, technically - it has the Qt::Dialog window type rather than Qt::Window. That means any "orphaned" dialog, that is closed, will still prevent the application from exiting automatically.
The solution?
Either giving a parent to the dialog or enabling the Qt::WA_QuitOnClose attribute:
this->_configDialog->setAttribute(Qt::WA_QuitOnClose);