Linux Qt QSystemTrayIcon in console application - c++

I have a working qt application. It is not a MainWindow application and has no GUI elements at all so far. It runs on startup of the linux box (as a background service). However, it has to wait for ~40-60 seconds until it can start while the IP/DHCP sorts its self out.
So I wanted to give the user some feedback as to what is going on. I thought it might be nice to add a system tray icon to look a little bit more professional then a terminal (though I could just do that).
However, after reading the documentation on QSystemTrayIcon I am still not sure if I have to use it as part of a MainWindow style application (i.e. do I need to add main window and make the sys tray a part of that), or can I just run it within my "console" style app?

i created a console application with system tray myself for win7 so i have no info about linux clients. it does not display any console, it only uses logging for output and the systemtray to display 'user readable content'.
your application need to use QApplication instead of QCoreApplication in order to draw widgets and create the QSystemTrayIcon to display the system tray icon. now just fill the menu as you please.
the code in main.cpp might look like this:
int main(int ac, char** av){
QApplication a(ac, av);
QSystemTrayIcon i;
QMenu* m = i.contextMenu();
m->addAction(tr("hello world!"));
i.show();
return a.exec();
}
cheers

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.

Qt - minimizing and maximizing app without standard titlebar buttons

I have a Qt application where there is only one widget which is not a MainWindow, just a QLabel. It has not title bar (obtained through setWindowFlags(Qt::FramelessWindowHint)), thus the standard minimize and maximize buttons are not available. But there is a sidebar at the widget's side where we provide a minimize button that should behave as the standard minimize button.
How do I do it?
I tried the solutions at Minimizing a Qt application and Qt C++ minimize and maximize window. Neither works. I can hide the app of course, on clicking the minimize button. Problem is, how to restore it through standard mechanisms?
Scenarios:
On Mac, the dock icon is clicked. Thankfully, OS X native APIs can give me a notification when dock icon is clicked, so based on that, I can unhide the widget and make it active.
On Mac, the app icon is double clicked in /Applications, or clicked in Launchpad. How do I get a notification for this? The code doesn't even come to the main.cpp that I can unhide it from there if it is hidden.
On Windows, the application icon in the taskbar is clicked (the app does not have a tray icon). How do I get notification for that and unhide it?
On Windows, the app icon is double clicked either in Program Files, or may be a desktop shortcut icon. How do I get notification for this to unhide it?
If this aproach is not correct, how do I show and hide the app as per standard behavior on both Mac and Windows?
Platform - Qt 5.3.1, 32 bit.
This is not an answer, but it might help sorting out which events are triggered (points 1,2 and 3).
#include <QApplication>
class newapp : public QApplication {
Q_OBJECT
public:
newapp( int &argc, char **argv ) : QApplication(argc, argv) {}
protected:
bool event(QEvent *ev) {
std::cerr << "APPLICATION EVENT " << ev->type() << std::endl;
return QApplication::event(ev);
}
};
and in your main.cc you will have
newapp myapp(argc,argv);
instead of
QApplication myapp(argc,argv)
Once you identify the events triggered, you're almost done: you need to tell to you newapp to restore the size of the Qlabel (there are tons of ways to do this, so provide your implementation and you might get more focused help)
For point 4, it's something else due to the different behavior between OSes as in Windows double clicking for the second time an app, will launch another instance of the application (on OSx it will raise the first opened app). You might find something that suit you in this project HERE

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.

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);