Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QTrayIcon context menu and QWidgetAction
QtWS25 Last Chance

QTrayIcon context menu and QWidgetAction

Scheduled Pinned Locked Moved Unsolved General and Desktop
qsystemtrayiconqwidgetaction
8 Posts 2 Posters 2.5k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • G Offline
    G Offline
    grisuthedragon
    wrote on last edited by
    #1

    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.

    1 Reply Last reply
    1
    • B Offline
      B Offline
      Bonnie
      wrote on last edited by
      #2

      Are you sure it is realizable?
      I think system tray menu is different with normal QMenu.

      G 2 Replies Last reply
      0
      • B Bonnie

        Are you sure it is realizable?
        I think system tray menu is different with normal QMenu.

        G Offline
        G Offline
        grisuthedragon
        wrote on last edited by
        #3
        This post is deleted!
        1 Reply Last reply
        0
        • B Bonnie

          Are you sure it is realizable?
          I think system tray menu is different with normal QMenu.

          G Offline
          G Offline
          grisuthedragon
          wrote on last edited by grisuthedragon
          #4

          @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.

          1 Reply Last reply
          0
          • B Offline
            B Offline
            Bonnie
            wrote on last edited by
            #5

            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);
                }
            }
            
            G 1 Reply Last reply
            1
            • B Bonnie

              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);
                  }
              }
              
              G Offline
              G Offline
              grisuthedragon
              wrote on last edited by grisuthedragon
              #6

              @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.

              B 1 Reply Last reply
              0
              • G grisuthedragon

                @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.

                B Offline
                B Offline
                Bonnie
                wrote on last edited by
                #7

                @grisuthedragon The workaround I can think of is, show the menu by yourself, not through setContextMenu.
                You can handle the QSystemTrayIcon::activated signal.
                When the ActivationReason is QSystemTrayIcon::Context, popup your QMenu on the tray icon, whose position could be got from QSystemTrayIcon::geometry().
                But I've not tried that, just a thought.

                G 1 Reply Last reply
                0
                • B Bonnie

                  @grisuthedragon The workaround I can think of is, show the menu by yourself, not through setContextMenu.
                  You can handle the QSystemTrayIcon::activated signal.
                  When the ActivationReason is QSystemTrayIcon::Context, popup your QMenu on the tray icon, whose position could be got from QSystemTrayIcon::geometry().
                  But I've not tried that, just a thought.

                  G Offline
                  G Offline
                  grisuthedragon
                  wrote on last edited by
                  #8

                  @Bonnie That could be a way, but this breaks with the OS style and I knew that some of our users will claim that this looks ugly. Then I have to search for a new idea representing the information in tray's menu.

                  1 Reply Last reply
                  0

                  • Login

                  • Login or register to search.
                  • First post
                    Last post
                  0
                  • Categories
                  • Recent
                  • Tags
                  • Popular
                  • Users
                  • Groups
                  • Search
                  • Get Qt Extensions
                  • Unsolved