简体   繁体   English

QT - QAction::eventFilter:不明确的快捷方式重载

[英]QT - QAction::eventFilter: Ambiguous shortcut overload

Searching here and other placed like qtcentre I've seen this problem has come up but can't seem to get it working.在这里搜索和其他像 qtcentre 一样的地方我已经看到这个问题出现了,但似乎无法让它工作。 I've got a MainWindow widget with a QSplitter which contains two Pane widgets (subclassed from QFrame ).我有一个带有QSplitterMainWindow小部件,其中包含两个Pane小部件(从QFrame子类化)。 Each Pane has a menubar with identical associated QActions/Shortcuts .每个窗格都有一个菜单栏,其中包含相同的关联QActions/Shortcuts

I've tried all combinations of ShortcutContexts with setShortcutContext() .我已经尝试了ShortcutContextssetShortcutContext() 的所有组合。

WindowShortcut and ApplicationShortcut contexts give the expected "Ambiguous shortcut overload". WindowShortcutApplicationShortcut上下文提供了预期的“模糊快捷方式过载”。

While WidgetShortcut and WidgetWithChildrenShortcut both do nothing.WidgetShortcutWidgetWithChildrenShortcut都什么都不做。

If I activate the menus manually they of course work fine.如果我手动激活菜单,它们当然可以正常工作。 I've also tried forcing focus on the parent widget with an overloaded enterEvent() .我还尝试使用重载的enterEvent()强制关注父小部件。

Any ideas?有任何想法吗?

thanks.谢谢。

main.h主文件

#include <QMainWindow>
#include <QFrame>

QT_BEGIN_NAMESPACE
class QAction;
class QMenu;
class QHBoxLayout;
class QSplitter;
class QWidget;
QT_END_NAMESPACE

class Pane: public QFrame
{
  Q_OBJECT

  public:
    Pane(QWidget* parent = 0);

  protected:
    void            enterEvent(QEvent *event);
    void            leaveEvent(QEvent *event);

  private:
    void            createMenus();

    QMenuBar *      m_menuBar;

  private Q_SLOTS:
    void            split();
};

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow();

private:
    void createActions();
    void createMenus();
    void setupUi(QMainWindow *MainWindow);

    QMenu *fileMenu;
    QAction *exitAct;

    QWidget *centralwidget;
    QHBoxLayout *horizontalLayout;
    QSplitter *splitter;
    QFrame *frame;
    QFrame *frame_2;
};

main.cpp主程序

#include <iostream>
#include <QApplication>
#include <QMainWindow>
#include <QSplitter>
#include <QFrame>
#include <QMenuBar>
#include <QBoxLayout>
#include "main.h"

Pane::Pane(QWidget* parent) :
    QFrame(parent)
{
    setFrameShape(QFrame::StyledPanel);
    setFrameShadow(QFrame::Raised);

    QVBoxLayout *layout = new QVBoxLayout;
    QFrame::setLayout(layout);

    m_menuBar = new QMenuBar;
    QWidget *m_widget = new QWidget;

    layout->addWidget(m_menuBar);
    layout->addWidget(m_widget);
    layout->setContentsMargins(2, 2, 2, 2);

    show();

    createMenus();
}

void
Pane::enterEvent(QEvent *event)
{   
    std::cout << "enter" << std::endl;
    setFocus();
    setStyleSheet("QFrame { border: 1px solid rgb(127, 127, 0); }");
    if (focusWidget())
        std::cout << "focuswidget = " << focusWidget()->objectName().toUtf8().constData() << std::endl;
}

void
Pane::leaveEvent(QEvent *event)
{   
    std::cout << "leave" << std::endl;
    clearFocus();
    setStyleSheet("QFrame { border: 1px solid rgb(64, 64, 64); }");
}

void
Pane::split()
{
    std::cout << "split pane" << std::endl;
}

void
Pane::createMenus()
{
    QMenu *paneMenu = m_menuBar->addMenu(tr("&Pane"));

    QAction *paneSplitAct = new QAction(tr("Split"), this);
    paneSplitAct->setShortcut(Qt::Key_S);
    paneSplitAct->setShortcutContext(Qt::WidgetWithChildrenShortcut);
    paneSplitAct->setStatusTip(tr("Split Pane"));
    connect(paneSplitAct, SIGNAL(triggered()), this, SLOT(split()));
    paneMenu->addAction(paneSplitAct);
}

MainWindow::MainWindow()
{
    setupUi(this);

    createActions();
    createMenus();
}

void MainWindow::createActions()
{
    exitAct = new QAction(tr("E&xit"), this);
    exitAct->setShortcuts(QKeySequence::Quit);
    exitAct->setStatusTip(tr("Exit the application"));
    connect(exitAct, SIGNAL(triggered()), this, SLOT(close()));
}

void MainWindow::createMenus()
{
    fileMenu = menuBar()->addMenu(tr("&File"));
    fileMenu->addAction(exitAct);
}


void MainWindow::setupUi(QMainWindow *MainWindow)
{
    if (MainWindow->objectName().isEmpty())
        MainWindow->setObjectName(QString::fromUtf8("MainWindow"));

    MainWindow->resize(800, 600);
    centralwidget = new QWidget(MainWindow);
    centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
    horizontalLayout = new QHBoxLayout(centralwidget);
    horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));
    splitter = new QSplitter(centralwidget);
    splitter->setObjectName(QString::fromUtf8("splitter"));
    splitter->setOrientation(Qt::Horizontal);

    frame = new Pane(splitter);
    frame->setObjectName(QString::fromUtf8("frame"));
    splitter->addWidget(frame);

    frame_2 = new Pane(splitter);
    frame_2->setObjectName(QString::fromUtf8("frame_2"));
    splitter->addWidget(frame_2);

    horizontalLayout->addWidget(splitter);

    MainWindow->setCentralWidget(centralwidget);

    QMetaObject::connectSlotsByName(MainWindow);
}

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    app.setOrganizationName("Trolltech");
    app.setApplicationName("Application Example");
    MainWindow mainWin;
    mainWin.show();
    return app.exec();
}

main.pro主程序

HEADERS       = main.h
SOURCES       = main.cpp
CONFIG       += no_keywords

UPDATE: Adding an addAction(paneSplitAct) call at the end of Pane::createMenus() in conjunction with using Qt::WidgetShortcut context seems to give me what I want.更新:Pane::createMenus()末尾添加一个addAction(paneSplitAct)调用并结合使用Qt::WidgetShortcut上下文似乎给了我我想要的。

From what I understand of the docs this is supposed to create a context menu in the widget.根据我对文档的理解,这应该在小部件中创建一个上下文菜单。 I don't appear to be getting one (right mouse click I assume) but that's ok since I don't want one.我似乎没有得到一个(我假设是鼠标右键单击)但是没关系,因为我不想要一个。 The eventEvent() and leaveEvent() overrides are still needed to set the focus correctly. eventEvent()leaveEvent()重写仍然需要正确设置焦点。

I've had similar problems: 2 different widgets had the same shortcut for an action (different action between the widgets).我遇到过类似的问题:2 个不同的小部件具有相同的操作快捷方式(小部件之间的不同操作)。 As long as only one of the widgets was visible in the application, everything worked.只要应用程序中只有一个小部件可见,一切正常。 As soon as both were visible, I got this "ambiguous shortcut overload" message.一旦两者都可见,我就收到了这条“不明确的快捷方式过载”消息。

The solution was: both action's contexts have to be set correctly to Qt::WidgetWithChildrenShortcut, then it worked fine - just according to current focus.解决方案是:两个动作的上下文都必须正确设置为 Qt::WidgetWithChildrenShortcut,然后它工作正常 - 只是根据当前的焦点。

If only one action has the correct context, the one with the default context got triggered and the message was displayed.如果只有一个操作具有正确的上下文,则会触发具有默认上下文的操作并显示消息。

If both actions had no context set (default), the Action created last was triggered and the message displayed.如果两个操作都没有设置上下文(默认),则触发最后创建的操作并显示消息。

So if you add an Action with a Shortcut anywhere, remember to think about the correct context and setting it, be restrictive!因此,如果您在任何地方添加带有快捷方式的操作,请记住考虑正确的上下文并设置它,要加以限制!

AFAIK for most scenarios like this setting the shortcut context to WidgetShortcut is the correct thing to do.对于像这样的大多数场景, AFAIK将快捷方式上下文设置为WidgetShortcut是正确的做法。 The problem though is that your duplicate actions are in menu bars which cannot have focus (in the traditional widget sense), which is why it doesn't do anything.但问题是您的重复操作位于无法获得焦点的菜单栏中(在传统的小部件意义上),这就是它什么都不做的原因。

It may make more sense to put the shared actions into the main window and make them application shortcuts.将共享操作放入主窗口并使其成为应用程序快捷方式可能更有意义。 Then in the main window slots that the actions trigger, work out which Pane object has focus and push the commands onto it.然后在动作触发的主窗口槽中,找出哪个Pane对象具有焦点并将命令推送到它上面。

This solution worked well for me to trigger action with a shortcut when the menu bar was disabled or not used.当菜单栏被禁用或未使用时,此解决方案对我来说效果很好,可以通过快捷方式触发操作。

Routine to add the shortcut:添加快捷方式的例程:

void StingrayEditor::add_shortcut(const QJsonObject& item_json)
{
    QString item_path = item_json["path"].toString();
    QString shortcut = item_json["shortcut"].toString();
    if (!shortcut.isEmpty()) {
        QKeySequence key_sequence = QKeySequence::fromString(shortcut);
        QAction* shortcut_action = new QAction(item_path, this);
        if (!key_sequence.isEmpty()) {
            shortcut_action->setShortcut(key_sequence);
            shortcut_action->setShortcutContext(Qt::ApplicationShortcut);
        }
        connect(shortcut_action, &QAction::triggered, this, [item_path]()
        {
            // Action to be executed
        });

        // Add the action to the main window.
        addAction(shortcut_action);
    }
}

It is important that you use shortcut_action->setShortcutContext(Qt::ApplicationShortcut);使用shortcut_action->setShortcutContext(Qt::ApplicationShortcut);很重要shortcut_action->setShortcutContext(Qt::ApplicationShortcut);

Then you'll need to filter/listen for events to catch the QEvent::Shortcut :然后你需要过滤/监听事件来捕捉QEvent::Shortcut

bool StingrayEditor::eventFilter(QObject* obj, QEvent* e)
{
    switch (e->type()) {
    case QEvent::Shortcut: {
        QShortcutEvent* sev = static_cast<QShortcutEvent*>(e);
        if (sev->isAmbiguous()) {
            foreach(const auto& action, actions()) {
                if (action->shortcut() == sev->key()) {
                    action->trigger(); // Trigger the action that matches the ambigous shortcut event.
                    return true;
                }
            }
        }
    }
    // ...
    default: break;
    }

    return false;
}

Do not forget to register for events like so:不要忘记注册这样的活动:

qApp->installEventFilter(this);

"overload" means you have used this shortcut for multiple actions. “过载”表示您已将此快捷方式用于多项操作。 Each action shortcut must be unique.每个操作快捷方式都必须是唯一的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM