Implementing a System Tray Icon in Qt Without a Main Window

System tray integration is a common requirement for applications that need to run continuously in the background. This feature proves particularly useful for implementing utility-style programs where window hiding, quick access menus, and notification functionality need to be accessible from the system tray.

This article addresses a specific scenario: creating a Qt application that displays only a system tray icon without presenting a traditional main window, while allowing dynamic updates to tooltip notifications.

Initial Implementation Attempt

The following code establishes a basic system tray icon with a context menu:

trayIcon->setToolTip(QStringLiteral("Notification Status"));

trayIcon->setIcon(style()->standardIcon(QStyle::SP_TitleBarNormalButton));

trayIcon->show();

QMenu *contextMenu = new QMenu;

QAction *showAboutAction = contextMenu->addAction(QStringLiteral("About"));

connect(showAboutAction, &QAction::triggered, this, this { QWindow *popupWindow = new QWindow; popupWindow->show(); });

trayIcon->setContextMenu(contextMenu);


</div>With this approach, creating a window instance in the main function will automatically display the tray icon since it remains visible independently.

Encountered Issue
-----------------

However, this implementation presents a critical flaw. When triggering the context menu and selecting an action (such as "About"), subsequent dismissal of the popup menu causes the system tray icon to vanish, ultimately terminating the application.

The root cause stems from the main event loop terminating prematurely after window interactions, which consequently destroys the tray icon instance.

Investigation and Solutions
---------------------------

Several approaches were evaluated to resolve this behavior:

**Approach 1:** Displaying a window with no taskbar presence and minimized state - The tray icon still disappeared.

**Approach 2:** Displaying a window without any special window configuration - The tray icon persisted correctly.

These tests confirmed that maintaining the main event loop's execution is essential for keeping the tray icon active.

Working Solution: Transparent Main Window
-----------------------------------------

The effective solution involves creating an invisible main window by applying specific window flags and attributes:

<div>```
window->setWindowFlags(Qt::Tool | Qt::FramelessWindowHint);
window->setAttribute(Qt::WA_TranslucentBackground);

Important Note: When using QMainWindow as the base class, manually disable the toolbar and status bar to prevent any visual artifacts.

Alternative Approach

Another viable method involves restarting the event loop whenever it exits, effectively preventing the application from terminating:

Dynamic Bubble Notifications

Implementing dynamic notification updates can be achieved using a timer mechanism:

connect(notificationTimer, &QTimer::timeout, this, [this](size_t counter) { trayIcon->showMessage( QStringLiteral("Alert #%1").arg(counter), QStringLiteral("System notification content..."), QSystemTrayIcon::Information, 1000 ); });

notificationTimer->start(1000);


</div>This timer-based approach triggers a notification every second, demonstrating how to dynamically update bubble messages. Adjust the timing interval according to specific application requirements.

Tags: Qt C++ QSystemTrayIcon system-tray gui

Posted on Sat, 08 Aug 2026 16:13:16 +0000 by pyr02k1