Определите, какой ярлык нажат в Qt - PullRequest
0 голосов
/ 06 июня 2018

У меня есть 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 Мне нужно знать, какой ярлык был нажат .... Есть ли способ узнать это?

Ответы [ 2 ]

0 голосов
/ 06 июня 2018

Вы можете попробовать более сложный шаблон с 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, поддерживая также другие типы отображений.

Кроме того, но это связано с моим личным вкусом, мне нравится идея иметь фрагменты кода, которые логически связаны между собой в небольшие фрагменты, вместо того, чтобы разбирать их между несколькими слотами / функциями, потому что это облегчает чтениеи отслеживать, когда мне нужно изменить его, конечно, только если это не повредит качеству самого кода.

0 голосов
/ 06 июня 2018

Вы должны создать отдельный QAction для каждого ярлыка и сгруппировать их, используя QSignalMapper .

...