Вы можете попробовать более сложный шаблон с QSignalMapper, который избавляет от необходимости определять столько действий, сколько вам нужно ярлыков, но требует 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);
3-е соединение требуется из-за способа работы QSignalMapper: когда активируется ярлык, он будет уведомленв QSignalMapper благодаря 1-му и 2-му соединениям, которые активируют слот map ().
Слот QSignalMapper :: map () будет сканировать свои сопоставления, выполненные с помощью API setMapping (), первый аргумент которого - сопоставленный объект, а второй - параметр, который будет использоваться для генерации сопоставленного() слот QSignalMapper, после идентификации объекта-эмитента.Для этого он использует метод sender () и просто сравнивает указатель, возвращенный к отображенным указателям QObject, которые вы указали в качестве отображений.
После идентификации QObject QSignalMapper будет излучать QSignalMapper :: mapped (QObject *)) сигнал, аргумент которого является вторым аргументом, заданным для setMapping, и в этом случае он такой же, как и первый, который снова является указателем на активированный QShortcut.
Я использовал лямбду для перехвата этого сигнала, и внутри этой лямбды я просто проверяю, что данный параметр является указателем 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, который может хранить общий istance QVariant.
Также при использовании QShortcut помните об их contex , то есть когда они активны, как это может быть в виджете или области видимости окна.
К сожалению, этот шаблон нарушает принципы чистого опа, но может быть лучше, чем управление многими действиями (значок, текст, всплывающая подсказка и т. Д.) Для той же цели.
РЕДАКТИРОВАТЬ: ответить на комментарии
Прежде всего, позвольте мне уточнить, что вы, конечно, можете вообще отказаться от использования QSignalMapper.Это просто возможное решение (не лучшее, может быть, излишнее ... но не совсем худшее с точки зрения производительности).
Более простой способ, как указал Майк в комментариях, состоит в использовании лямбда-выражений для каждогоСигнал QShotcut :: active, но это приведет к копированию / вставке кода, которого я всегда стараюсь избегать.Вместо этого вы можете определить собственный слот внутри MainWindow и использовать sender (), чтобы перехватить QShortcut и подготовить действие перед его запуском.
В любом случае, QSignalMapper IMHO лучше объясняет, что вы делаете (с семантической точки зрения) и более гибок в случае, если вам нужно расширить соединение с другими объектами QObjects, поддерживая также другие типы отображений.
Кроме того, но это связано с моим личным вкусом, мне нравится идея иметь фрагменты кода, которые логически связаны между собой в небольшие фрагменты, вместо того, чтобы разбирать их между несколькими слотами / функциями, потому что это облегчает чтениеи отслеживать, когда мне нужно изменить его, конечно, только если это не повредит качеству самого кода.