您可以使用 QSignalMapper 尝试更精细的模式,以避免需要定义尽可能多的快捷方式,但需要 c++11(至少这个实现)。
在窗口的构造函数中,使用以下代码来声明您的QShortcut
对象和 a 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 的 mapped() 槽的参数,一旦发射物体被识别。为此,它使用 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
实例来连接来自其他类型的 QObject(例如 QPushButtons)的事件,并在您的自定义插槽中识别它们,并为 QAction 数据成员设置适当的值,该数据成员可以存储通用的 QVariant istance。
此外,在使用 QShortcut 时,请注意它们的 contex ,即它们处于活动状态时,因为它可能在小部件或窗口范围内。
不幸的是,这种模式违反了纯 oop 原则,但可能比为相同目的管理许多操作(图标、文本、工具提示等)更好。
编辑:回答评论
首先,让我澄清一下,您当然可以完全跳过 QSignalMapper 的使用。这只是一个可能的解决方案(不是更好,也许是矫枉过正......但在性能方面并不是真的更差)。
正如 Mike 在评论中指出的那样,一种更简单的方法是对每个 QShotcut::activated 信号使用 lambda,但这会导致复制/粘贴代码,我总是尽量避免。您可以改为在 MainWindow 内定义一个自定义插槽,并使用 sender() 来捕获 QShortcut 并在触发它之前准备操作。
无论如何,QSignalMapper 恕我直言,更好地解释了您在做什么(从语义的角度),并且在您需要扩展与其他 QObjects 的连接时更加灵活,还支持其他类型的映射。
此外,但这与我的个人品味有关,我喜欢将逻辑上联系在一起的代码片段压缩成小片段的想法,而不是将其分散在几个插槽/函数中,因为这样更易于阅读和追溯当我需要更改它时,当然前提是这不会损害代码本身的质量。