16tons
16tons

Reputation: 725

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

Here is my setup:

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.

Upvotes: 7

Views: 9424

Answers (2)

David Hason
David Hason

Reputation: 1

Sorry for the late response. You can create manually using QMenu and QSystemTrayIcon Classes. This is a part of source code I have made a simple tray Icon for app, I wish it would be helpful for you.

QMenu *menu = new QMenu();
QAction *miniAction = menu->addAction("&Hide");
QObject::connect(miniAction, &QAction::triggered, &view, &QWebEngineView::hide);
QAction *maxiAction = menu->addAction("Sho&w");
QObject::connect(maxiAction, &QAction::triggered, &view, &QWebEngineView::show);
QAction *closeAction = menu->addAction("&Close");
QObject::connect(closeAction, &QAction::triggered, &a, &QApplication::exit);

QSystemTrayIcon trayIcon;
trayIcon.setIcon(QIcon(":/logo.ico"));
trayIcon.setContextMenu(menu);
trayIcon.show();

Upvotes: 0

Kevin Krammer
Kevin Krammer

Reputation: 5207

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.

Upvotes: 5

Related Questions