我有一个QAction,为它分配了多个快捷方式

   test = new QAction();
   this->addAction(test);
   QList<QKeySequence> shortcuts;
   shortcuts << QKeySequence(Qt::Key_N) << QKeySequence(Qt::Key_T);
   test->setShortcuts(shortcuts);
   connect(test,SIGNAL(triggered()),this,SLOT(SomeFucntion()))

SomeFucntion中,我需要知道按下了哪个快捷方式。...是否知道呢?

最佳答案

您可以使用QSignalMapper尝试更复杂的模式,该模式可以避免定义所需数量的 Action ,而需要使用快捷方式,但是需要c++ 11(至少此实现)。

在窗口的构造函数中,使用以下代码声明QShortcut对象和QSignalMapper:

QSignalMapper* signalMapper = new QSignalMapper(this);
QShortcut* sc1 = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_N), this);
QShortcut* sc2 = new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_T), this);

connect(sc1, &QShortcut::activated, signalMapper, static_cast<void (QSignalMapper::*)(void)>(&QSignalMapper::map));
connect(sc2, &QShortcut::activated, signalMapper, static_cast<void (QSignalMapper::*)(void)>(&QSignalMapper::map));

signalMapper->setMapping(sc1, sc1);
signalMapper->setMapping(sc2, sc2);

QAction* action = new QAction();
connect(signalMapper, static_cast<void (QSignalMapper::*)(QObject*)>(&QSignalMapper::mapped),
    [action](QObject *object){

    QShortcut* sc = qobject_cast<QShortcut*>(object);
    if (sc)
    {
        action->setData(sc->key().toString());
        action->trigger();
    }
});

connect(action, &QAction::triggered, this, &MainWindow::doStuff);

由于QSignalMapper的工作方式,需要第三个连接:激活快捷方式后,由于第一个和第二个连接,它将被通知QSignalMapper,这将触发map()插槽。

QSignalMapper::map()插槽将扫描其与setMapping()API进行的映射,该映射的第一个参数是已映射的对象,第二个参数是将用于发出QSignalMapper的mapd()插槽的参数,一旦识别出发射物体。为此,它使用sender()方法并将返回的指针与您作为映射提供的已映射QObject指针进行比较。

一旦确定了QObject,QSignalMapper将发出QSignalMapper::mapped(QObject *)信号,其信号是给setMapping的第二个参数,在这种情况下,它与第一个相同,再次是指向QShortcut的指针。那被激活了。

我使用了一个lambda来捕获该信号,在该lambda中,我只是检查给定的参数是否为QShortcut指针,并在触发操作本身之前将其键序列存储在QAction的数据成员中。然后,QAction::trigger()插槽将发出QAction::triggered()信号,该信号将依次调用您的自定义插槽,在本例中为doStuff()。在这里,您可以检索键序列并使用它进行所需的操作。

因此,您的广告位实现应与此类似:
void MainWindow::doStuff()
{
    // use sender() to fetch data from action
    QAction* act = qobject_cast<QAction*>(sender());
    if (act)
    {
        QString sequence = act->data().toString();

        // debug output will show you the triggering key sequence
        qDebug() << sequence;

        // use sequence string to determine which shortcut was used

        // On Mike hint: better to reset data after use :)
        act.setData(QVariant());
    }
}

请注意,我使用的是基于QObject指针的映射。这样,您可以重用signalMapper实例来连接来自其他类型QObjects(例如QPushButtons)的事件,并在自定义插槽中识别它们,并为QAction数据成员设置适当的值,以存储通用的QVariant方式。

同样,在使用QShortcut时,请注意其contex,即它们处于 Activity 状态,因为它可能在小部件或窗口范围内。

不幸的是,这种模式违反了纯粹的oop原则,但是可能比出于相同目的管理许多 Action (图标,文本,工具提示等)更好。

编辑:回答评论

首先,让我澄清一下,您当然可以完全跳过QSignalMapper的使用。这只是一种可能的解决方案(不是更好,也许是一个过大的杀伤力……但就性能而言,并不是真的很差)。

正如Mike在评论中指出的,一种更简单的方法是对每个QShotcut::activated信号使用lambda,但这将导致复制/粘贴代码,而我总是尽量避免这样做。
您可以改为在MainWindow内定义一个自定义插槽,并使用sender()捕获QShortcut并在触发它之前准备操作。

无论如何,QSignalMapper IMHO可以(从语义的 Angular )更好地解释您在做什么,并且在您需要扩展到其他QObject的连接(还支持其他类型的映射)时更加灵活。

此外,但这与我的个人喜好有关,我喜欢将代码片段逻辑上绑定(bind)为小片段的想法,而不是将代码片段稀疏地放在几个插槽/函数中,因为它使读取和追溯更加容易当我需要更改它时,当然,只要这不会损害代码本身的质量即可。

09-25 21:27