QTrayIcon context menu and QWidgetAction
-
I am currently developing a SystemTray-only application in Qt 5. Therefore, want to add Widgets, like Buttons and Labels, to the context menu of the tray icon. Due to Qt's documentation this should be realizable using the QWidgetAction. As an example I created a QLabel inside the context menu using:
TrayApp::TrayApp(QObject *parent) : QSystemTrayIcon(parent) { QIcon trayIcon(":/icons/hi32-app.png"); setIcon(trayIcon); QMenu *trayMenu = new QMenu(); QLabel* pLabelWidget = new QLabel("Test"); QWidgetAction *labelWidgetAct = new QWidgetAction(0); labelWidgetAct->setDefaultWidget(pLabelWidget); trayMenu->addAction(labelWidgetAct); setContextMenu(trayMenu); }
The tray icon appears and has a context menu with one item, but the item is empty. If a use a QButton or more advanced QWidgets with layouts to add serveral widgets, nothing happens as well.
I am using Qt 5.12. from Ubuntu 19.10.
-
This post is deleted!
-
@Bonnie It seems that people already done this with Qt, like https://github.com/bochi/kueue/blob/master/ui/systray.cpp ( in Qt4), but is could have a connection to https://bugreports.qt.io/browse/QTBUG-26840.
-
I've checked the source code.
Seems only when the Qt platform plugin doesn't provide a "native menu" function, then QMenu will be used to show the context menu.void QSystemTrayIcon::setContextMenu(QMenu *menu) { Q_D(QSystemTrayIcon); QMenu *oldMenu = d->menu.data(); d->menu = menu; d->updateMenu_sys(); if (oldMenu != menu && d->qpa_sys) { // Show the QMenu-based menu for QPA plugins that do not provide native menus if (oldMenu && !oldMenu->platformMenu()) QObject::disconnect(d->qpa_sys, &QPlatformSystemTrayIcon::contextMenuRequested, menu, nullptr); if (menu && !menu->platformMenu()) { QObject::connect(d->qpa_sys, &QPlatformSystemTrayIcon::contextMenuRequested, menu, [menu](QPoint globalNativePos, const QPlatformScreen *platformScreen) { QScreen *screen = platformScreen ? platformScreen->screen() : nullptr; menu->popup(QHighDpi::fromNativePixels(globalNativePos, screen), nullptr); }); } } }
Otherwise a native menu will be created, and the actions will be copied to it.
But that does not handle a QWidgetAction.void QMenuPrivate::copyActionToPlatformItem(const QAction *action, QPlatformMenuItem *item) { item->setText(action->text()); item->setIsSeparator(action->isSeparator()); if (action->isIconVisibleInMenu()) { item->setIcon(action->icon()); if (QWidget *w = action->parentWidget()) { QStyleOption opt; opt.init(w); item->setIconSize(w->style()->pixelMetric(QStyle::PM_SmallIconSize, &opt, w)); } else { QStyleOption opt; item->setIconSize(qApp->style()->pixelMetric(QStyle::PM_SmallIconSize, &opt, 0)); } } else { item->setIcon(QIcon()); } item->setVisible(action->isVisible()); #if QT_CONFIG(shortcut) item->setShortcut(action->shortcut()); #endif item->setCheckable(action->isCheckable()); item->setChecked(action->isChecked()); item->setHasExclusiveGroup(action->actionGroup() && action->actionGroup()->isExclusive()); item->setFont(action->font()); item->setRole((QPlatformMenuItem::MenuRole) action->menuRole()); item->setEnabled(action->isEnabled()); if (action->menu()) { if (!action->menu()->platformMenu()) action->menu()->setPlatformMenu(platformMenu->createSubMenu()); item->setMenu(action->menu()->platformMenu()); } else { item->setMenu(0); } }
-
@Bonnie So that means, my plan does not work out of the box with QT? Any idea for an workaround?
But I do not see why QWidgetAction is not supported by the code, since it it derived from QAction and thus a routine handling QAction does also handle the QWidgetAction.
PS: I found https://stackoverflow.com/questions/59130151/how-to-make-qlabel-wrapped-in-qwidgetaction-match-the-position-and-the-font-size and it seems that it works on MacOS X and Python.
-
@grisuthedragon The workaround I can think of is, show the menu by yourself, not through
setContextMenu
.
You can handle theQSystemTrayIcon::activated
signal.
When theActivationReason
isQSystemTrayIcon::Context
, popup your QMenu on the tray icon, whose position could be got fromQSystemTrayIcon::geometry()
.
But I've not tried that, just a thought.