【问题标题】:QToolButton to show menu after pressing AltQToolButton 在按下 Alt 后显示菜单
【发布时间】:2018-08-13 20:47:58
【问题描述】:

我有一个带有相关菜单的工具按钮。

m_mainMenuButton = new ToolButton("menu.png", tr("Open menu"));
m_mainMenuButton->setMenu(m_mainMenu);
m_mainMenuButton->setPopupMode(QToolButton::InstantPopup);

我希望在用户按下并释放 Alt 时显示此菜单。这样,普通的QMenuBar 就会在 Windows 上被激活(我想要这个工具按钮而不是 QMenuBar)。我试过这个:

m_mainMenuButton->setShortcut(QKeySequence(Qt::Key_Alt));

但是当按下并释放 Alt 时它不显示菜单。或者这样:

auto shortcut = new QShortcut(QKeySequence(Qt::Key_Alt), this);
connect(shortcut, &QShortcut::activated, m_mainMenuButton, &QToolButton::showMenu);

它也没有做任何事情。我试图覆盖按键和释放事件,但后来我发现它会干扰其他使用 Alt 键作为修饰符的快捷键,例如“Alt+Left”。

任何想法如何做到这一点?

更新一个最小的例子,它表明 Alt 不能用作快捷方式。

#include <QAction>
#include <QApplication>
#include <QLabel>
#include <QMainWindow>
#include <QMenu>
#include <QShortcut>
#include <QToolButton>
#include <QVBoxLayout>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    QMainWindow w;

    auto label = new QLabel();

    auto menu = new QMenu(&w);
    // intentionally added a shortcut which contains Alt as modifier to test it does not interfere with the menu
    menu->addAction("Action", [label]{ label->setText("Trigered!"); }, QKeySequence("Alt+Left"));

    auto btn = new QToolButton();
    btn->setMenu(menu);
    btn->setPopupMode(QToolButton::InstantPopup);

    // the following lines do not have any effect, the menu is not shown when Alt is pressed and released
    auto shortcut = new QShortcut(QKeySequence(Qt::Key_Alt), &w);
    QObject::connect(shortcut, &QShortcut::activated, btn, &QToolButton::showMenu);

    auto container = new QWidget();
    auto layout = new QVBoxLayout(container);
    layout->addWidget(btn);
    layout->addWidget(label);
    w.setCentralWidget(container);
    w.show();

    return a.exec();
}

【问题讨论】:

  • 什么是this
  • this 在我的例子中是主窗口,它被设置为快捷方式的父窗口。没关系,也可以是按钮。
  • @eyllanesc 我更新了问题并添加了一个示例。
  • 我怀疑 ALT 作为修饰符不是有效的快捷方式,因为到目前为止我已经尝试过有效的快捷方式是 MODIFIER + KEY 或只是 KEY。

标签: c++ qt


【解决方案1】:

这是一个受QMenuBar中Qt实现启发的版本:

class AltButton : public QToolButton {
public:
    AltButton(QWidget *parent) : QToolButton(parent)
    {
        // To handle initial ALT-press in parent
        parent->installEventFilter(this);
        // If reparenting should be possible, override changeEvent
        // You can also make an app-wide event filter if the button should catch all alt presses
    }


protected:
    bool altPressed = false;
    bool eventFilter(QObject *watched, QEvent *event)
    {
        if (altPressed) {
            // Alt-press registered before, check alt-Release
            switch (event->type())
            {
            case QEvent::KeyPress:
            case QEvent::KeyRelease:
            {
                QKeyEvent *kev = static_cast<QKeyEvent*>(event);
                if (kev->key() == Qt::Key_Alt || kev->key() == Qt::Key_Meta) {
                    if (event->type() == QEvent::KeyPress)
                        break; // Alt-Press handled below by shortcut override
                    // Alt-Release. Toggle button
                    this->showMenu();
                }
            }
            // fallthrough
            case QEvent::MouseButtonPress:
            case QEvent::MouseButtonRelease:
            case QEvent::MouseMove:
            case QEvent::FocusIn:
            case QEvent::FocusOut:
            case QEvent::ActivationChange:
            case QEvent::Shortcut:
                // These events cancel a alt-trigger
                altPressed = false;
                // Stop listening for global alt-releas
                qApp->removeEventFilter(this);
                break;
            default:
                break;
            }
        } else if (isVisible()) {
            if (event->type() == QEvent::ShortcutOverride) {
                QKeyEvent *kev = static_cast<QKeyEvent*>(event);
                if ((kev->key() == Qt::Key_Alt || kev->key() == Qt::Key_Meta) && kev->modifiers() == Qt::AltModifier) {
                    // Alt-Press. Listen globally for alt-release
                    altPressed = true;
                    qApp->installEventFilter(this);
                }
            }
        }
        return false;
    }
};

int main(int argc, char**argv) {
    QApplication a(argc,argv);

        QMainWindow w;

        auto label = new QLabel();

        auto menu = new QMenu(&w);
        // intentionally added a shortcut which contains Alt as modifier to test it does not interfere with the menu
        menu->addAction("Action", [label]{ label->setText("Trigered!"); }, QKeySequence("Alt+Left"));

        auto btn = new AltButton(&w);
        btn->setMenu(menu);
        btn->setPopupMode(QToolButton::InstantPopup);

        // the following lines do not have any effect, the menu is not shown when Alt is pressed and released
        //auto shortcut = new QShortcut(QKeySequence(Qt::Key_Alt), &w);
        //QObject::connect(shortcut, &QShortcut::activated, btn, &QToolButton::showMenu);

        auto container = new QWidget();
        auto layout = new QVBoxLayout(container);
        layout->addWidget(btn);
        layout->addWidget(label);
        w.setCentralWidget(container);
        w.show();

        return a.exec();
}

Alt-Left 快捷键按预期工作,按钮菜单由 Alt-Pres-Release 切换。

【讨论】:

  • 效果很好。我只需要了解如何为顶层窗口安装事件过滤器。 changeEvent() 并不总是有效,因为这个按钮可以是小部件 X 的子元素,它可以重新设置为 Y,然后可以重新设置为顶级窗口 Z。如果我想为 Z 安装事件过滤器,那么按钮的 @ 987654324@ 不会在 Y 被重新设置为 Z 时被触发。也许我做错了什么......无论如何,我可以从顶层窗口手动调用 installFilter() 并且它可以工作。
  • 在链接的 QMenuBar 实现中,我看到了一些关于祖父更改的内容(我认为是在事件过滤器中)。也许您也可以将这种方法用于您的案例。
  • 最后,我在构造函数中传递了范围小部件(此菜单按钮处理 Tab 键的小部件)。到目前为止工作得很好。谢谢。
【解决方案2】:

尝试使用 QObject::installEventFilter
(QObject::eventFilter(obj, event))。

例如

QtStackOverflow.h

#pragma once

#include <QtWidgets/QMainWindow>
#include <QToolButton>
#include "ui_QtStackOverflow.h"

class QtStackOverflow : public QMainWindow
{
    Q_OBJECT

public:
    QtStackOverflow(QWidget *parent = Q_NULLPTR);

private:
    Ui::QtStackOverflowClass ui;
};

class KeyPressEater : public QObject
{
   Q_OBJECT

public:
   KeyPressEater(QToolButton*btn) : keyOtherPush(false), keyAltPush(false) { _btn = btn; }

protected:
   bool eventFilter(QObject *obj, QEvent *event);

private:
   QToolButton * _btn;
   bool keyOtherPush;
   bool keyAltPush;
};

main.cpp

#include "QtStackOverflow.h"
#include <QtWidgets/QApplication>

#include <QObject>
#include <QEvent>
#include <QKeyEvent>
#include <QLabel>
#include <QMainWindow>
#include <QMenu>
#include <QShortcut>
#include <QToolButton>
#include <QVBoxLayout>

int main(int argc, char *argv[])
{
   QApplication a(argc, argv);

   QMainWindow w;

   auto label = new QLabel();

   auto menu = new QMenu(&w);
   // intentionally added a shortcut which contains Alt as modifier to test it does not interfere with the menu
   int number = 0;
   menu->addAction("Action", [label, &number] {
      label->setText(QString("%1 %2 ").arg("Trigered!").arg(number));
      number++;
   }, QKeySequence("Alt+Left"));

   auto btn = new QToolButton();
   btn->setMenu(menu);
   btn->setPopupMode(QToolButton::InstantPopup);

   // the following lines do not have any effect, the menu is not shown when Alt is pressed and released
   auto shortcut = new QShortcut(QKeySequence(Qt::Key_Alt), &w);
   QObject::connect(shortcut, &QShortcut::activated, btn, &QToolButton::showMenu);

   KeyPressEater *m_keyPressEater;
   m_keyPressEater = new KeyPressEater(btn);
   qApp->installEventFilter(m_keyPressEater);

   auto container = new QWidget();
   auto layout = new QVBoxLayout(container);
   layout->addWidget(btn);
   layout->addWidget(label);
   w.setCentralWidget(container);
   w.show();

   return a.exec();
}

bool KeyPressEater::eventFilter(QObject *obj, QEvent *event)
{
   if (event->type() == QEvent::KeyPress)
   {
      int key = static_cast<QKeyEvent *>(event)->key();

      if (key == Qt::Key_Alt)
      {
         keyAltPush = true;
      }
      else {
         keyOtherPush = true;
      }

      return QObject::eventFilter(obj, event);
   }
   else if (event->type() == QEvent::KeyRelease)
   {
      int key = static_cast<QKeyEvent *>(event)->key();

      if (key == Qt::Key_Alt) {
         if (keyAltPush == true && keyOtherPush == false) {
            _btn->showMenu();
         }
      }
      else {
         keyAltPush = false;
         keyOtherPush = false;
      }
      return true;
   }
   else {
      return QObject::eventFilter(obj, event);
   }
}

在这种情况下,您将随时获得所有按键。

然后你需要检查 QObject *senderObj = sender()

【讨论】:

  • 但是即使我按下例如“Alt+Left”也会触发,这是另一个操作的有效快捷方式。快捷方式的问题是您首先按“Alt”(这是打开菜单的时间 - 我不想要这个),然后按“左”。
  • 我已经更正了我的代码。您需要检查 QObject *senderObj = sender() - 谁发送密钥“Alt+Left”
  • 我认为您误解了这个问题。我的问题不是触发“Alt+Left”,我可以用普通快捷键做到这一点。我的问题是只触发一个快捷方式“Alt”。问题是这不能作为普通快捷方式处理,因为它无法对按键事件做出反应(此时您不知道是否会按下更多键以形成像 Alt+Left 这样的快捷方式)。所以它需要对按键释放事件做出反应。但是当 Alt+Left 等快捷键被释放时,它就会被触发。
  • 您的代码(更新:最小示例)运行良好!如果我按“Alt+Left”,我会看到“Trigered!”
  • 是的...但问题在于应该打开菜单的“Alt”。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多