5

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.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
16tons
  • 675
  • 5
  • 14
  • 1
    For the 3. point see [here](http://stackoverflow.com/questions/1051333/combing-an-external-event-loop-with-qts). Though I'd advise you to create a thread for your own thing, if it constantly needs to process something. Also, why is this question tagged with both C and C++? I don't see any problems about language interoperability. Please fix the tags accordingly. – tambre Jan 21 '17 at 14:45
  • Thanks for the info. It's tagged as C and C++ since those are the languages I am developing in. As opposed to python. Probably for the same reason that the question you linked is tagged as C++ too? – 16tons Jan 21 '17 at 14:54
  • Well, of course you should tag the appropriate language, but I'm unable to see any C code here, so I think the C tag is irrelevant. – tambre Jan 21 '17 at 18:37
  • 1
    I don't quite understand what you mean by "without adding any window class". Is the code you posted not working? – Kevin Krammer Jan 22 '17 at 10:29
  • Thanks @KevinKrammer The code I posted works (displays the icon in the tray). The trouble starts when I try to use `connect` on my tray icon, it's quite confusing how to connect different actions (e.g. left click) to it. The `QObject::connect` examples I have seen all rely on a class inherited from `MainWindow` sort of classes and pass `this` pointer to use `connect`. For example, how do I make it so that the left click on system try icon calls a function? – 16tons Jan 22 '17 at 13:46
  • 1
    @16tons ah, I see. I don't think that actually has anything to do with needing a window, it is just that in those example the window is a convenient action "receiver" as it already exists. I'll write an answer with some other options – Kevin Krammer Jan 23 '17 at 12:13
  • Thanks, I was actually able to figure out and make it work in my code yesterday using the 3 parameter `connect`! :) – 16tons Jan 23 '17 at 14:34
  • 1
    Possible duplicate of [c++ qt tray icon menu action](https://stackoverflow.com/questions/3084825/c-qt-tray-icon-menu-action) – jackw11111 Aug 22 '18 at 23:35

1 Answers1

5

Given the clarification from the comments, you have a couple of options on how to get code called for context menu or activation actions.

  1. 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);
    
  2. Connect to stand-alone functions

    void onActivated(QSystemTrayIcon::ActivationReason reason);
    
    
    connect(trayIcon, &QSystemTrayIcon::activated, &onActivated);
    
  3. 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.

Kevin Krammer
  • 5,159
  • 2
  • 9
  • 22