KeyPress с несколькими модификаторами не работает в QWidget

Я добавил переопределение для keyPressEvent моего основного виджета:

void MainWindow::keyPressEvent(QKeyEvent* e)
{
    if (e->key() == Qt::Key_F11)
    {
        if (e->modifiers() & Qt::AltModifier && e->modifiers() & Qt::ControlModifier)
        {
            // do stuff
        }
    }
}

Проблема в том, что не работает. Если я попробую использовать только AltModifier или ControlModifier, он будет работать (конечно, при изменении второго условия), но не для них обоих. Key () не будет равняться Qt :: Key_F11, пока я нажимаю F11. Я использую windows.

Изменить: проверено с ведением журнала, и в результате Ctrl + Alt + F11 и Ctrl + Alt + F12 не отправляют событие нажатия клавиши (в то время как другие клавиши Ctrl + Alt + Fxx отправляют).


person Tamás Szelei    schedule 28.03.2011    source источник
comment
Вы имеете в виду, что с нажатыми клавишами control и alt нажатие F11 не устанавливает для клавиши события значение Key_F11?   -  person Stephen Chu    schedule 28.03.2011
comment
Так что же в таком случае e- ›key ()?   -  person Matt Phillips    schedule 29.03.2011
comment
Кроме того, как он может хорошо работать, если нажат только один модификатор? В этом случае ваш вложенный тест if (...) должен вернуть false. Или вы просто имеете в виду, что в этом случае вы получаете e- ›key () == Qt :: Key_F11.   -  person Matt Phillips    schedule 29.03.2011
comment
В этом случае я изменил второе условие, чтобы посмотреть, работает ли оно. Он работает с F11 + Alt и F11 + Control, но не с F11 + Alt + Control. Для F11 + Alt + Control возвращаемое значение e->key() - 16777249 (точнее, это значение поля k в объекте QKeyEvent). Это значение неизменно одно и то же. Значение Qt :: Key_F11 - 16777274.   -  person Tamás Szelei    schedule 29.03.2011
comment
@Tamas Szelei, так каково значение Ctrl + Alt + k? Кроме того, наихудший сценарий: #ifdef   -  person Matt Phillips    schedule 29.03.2011
comment
Ctrl + Alt + k дает код 16777249. Что мне проверять с помощью #ifdef?   -  person Tamás Szelei    schedule 29.03.2011
comment
@Tamas Szelei Я имел в виду просто использовать #ifdef для создания версии вашего кода для Windows. Но теперь я действительно запутался - 16777249 (0x1000021) - это значение для Qt :: Key_Control, а не k.   -  person Matt Phillips    schedule 29.03.2011
comment
Вы правы, это действительно Qt :: Key_Control.   -  person Tamás Szelei    schedule 29.03.2011
comment
@Tamas Szelei Просто чтобы еще раз проверить - действительно ли вы уверены, что при нажатии Alt и Ctrl вы не получаете событие Qt :: Key_F11? На выходе qDebug () ‹* e- ›key () будет 0x1000021, когда в этой ситуации нажимается F11? Если это так, извините, все, что я могу сказать, это попробовать QtCentre.   -  person Matt Phillips    schedule 29.03.2011
comment
Я проверил с ведением журнала, и в результате Ctrl + Alt + F11 и Ctrl + Alt + F12 не отправляют ключевое событие (в то время как другие клавиши Ctrl + Alt + Fxx делают). Получение контрольного кода клавиши произошло потому, что я нажал его первым и использовал точку останова, чтобы увидеть, что происходит, и программа уже была приостановлена, пока я нажимал другие клавиши. Я попробую QtCentre, спасибо за ваше время.   -  person Tamás Szelei    schedule 29.03.2011
comment
Мне удалось найти решение, см. Мой ответ ниже, если вам интересно.   -  person Tamás Szelei    schedule 29.03.2011


Ответы (1)


Уу, так что мне удалось это решить, хотя я не совсем доволен решением. По крайней мере, тайны нет и работает :).

Причина, по которой я не получил горячие клавиши Ctrl + Alt + F11 и Ctrl + Alt + F12

Они были зарегистрированы как глобальные горячие клавиши. Мне удалось выяснить это с помощью программы ActiveHotkeys другого stackoverflow. настроение участника (большое за это спасибо!). По-видимому, не существует документированного способа узнать, какая программа зарегистрировала конкретную горячую клавишу (и она ничего не сделала в моей системе). См. Ветку moodforaday по этой проблеме.

Решение

Один из ответов в вышеупомянутой теме привел меня к другому вопросу < / а>. Ответ Эфотиниса мне идеально подошел. У меня не было опыта настройки низкоуровневых клавишных хуков, но это было не так сложно, как кажется. Ради будущего, вот как я сделал это в своем приложении Qt:

В моем mainwindow.h:

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    explicit MainWindow(QWidget *parent = 0);
    ~MainWindow();

    // ... code

private:    
    void tryLogin();
    friend LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wparam, LPARAM lparam);

};

LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wparam, LPARAM lparam);

В моем mainwindow.cpp:

// setting up the hook in the constructor
SetWindowsHookEx(WH_KEYBOARD_LL,
                     LowLevelKeyboardProc,
                     NULL,
                     0);

Код ловушки (в основном из ответа efotinis):

LRESULT CALLBACK LowLevelKeyboardProc(int code, WPARAM wparam, LPARAM lparam)
{
    KBDLLHOOKSTRUCT* kllhs = reinterpret_cast<KBDLLHOOKSTRUCT*>(lparam);
    if (code == HC_ACTION)
    {
        if (wparam == WM_KEYDOWN && kllhs->vkCode == VK_F12 &&
            (GetAsyncKeyState(VK_MENU) < 0 && GetAsyncKeyState(VK_CONTROL) < 0))
        {
            MainWindow* w = dynamic_cast<MainWindow*>(qApp->activeWindow());
            if (NULL != w)
            {
                w->tryLogin();  // this should not be blocking!
                return 1;
            }
        }
    }

    return CallNextHookEx(0, code, wparam, lparam);
}

Как видите, мы получаем указатель на окно приложения из глобального объекта QApplication. Мы используем dynamic_cast, чтобы в активном окне не было экземпляра MainWindow, мы получили бы указатель NULL.

Если вам интересно, почему вызовы GetAsyncKeyState проверяются на <0, это потому, что эта функция возвращается с установленным MSB, если ключ нажат. А когда установлен MSB, число SHORT отрицательное (на x86 / x64 и совместимых платформах). Если Windows когда-либо будет перенесена на платформу, где целые числа со знаком представлены по-другому, этот код может сломаться. Абсолютно правильный способ - создать 16-битную маску и использовать ее для проверки MSB, но мне лень это делать. :)

Следует обратить внимание на то, что когда вы вызываете функцию из своего обработчика, цикл событий Qt только что начал обработку. Это означает, что до тех пор, пока вы не вернетесь из своей функции, он заблокирует пользовательский интерфейс (заморозит его на несколько секунд). Если вы хотите показать диалоговое окно, как это сделал я, вместо exec() вызовите raise, activateWindow и show, установив модальность окна диалогового окна как модальное (возможно, в его конструкторе).

Вы можете отменить регистрацию ловушки с помощью UnHookWindowsHookEx, если хотите (что происходит когда модуль, содержащий перехватчик, выгружен). Для этого сохраните возвращаемое значение вызова SetWindowsHookEx.

person Tamás Szelei    schedule 29.03.2011