使用 Qt 检查按键是否按下

问题描述 投票:0回答:7

我正在摆弄一些图形,并且我已经使用箭头键实现了简单的相机移动。我的第一个方法是重写

keyPressEvent
来做这样的事情:

switch(key)
{
   case up: MoveCameraForward(step); break;
   case left: MoveCameraLeft(step); break;
   ...
}

这并不像我希望的那样起作用。例如,当我按住前进键时,相机会向前移动“步”单位,然后暂停一会儿,然后继续移动。我猜测这就是事件的生成方式,以避免在按键时间较长的情况下发生多个事件。

所以,我需要在我的

Paint()
例程中轮询键盘。我还没有找到如何用 Qt 做到这一点。我想到有一个
map<Key, bool>
,它将在
keyPressEvent
keyReleaseEvent
中更新,并在
Paint()
中轮询该地图。还有更好的想法吗?感谢您的任何见解。

c++ qt event-handling keyboard-events
7个回答
17
投票

这并不能解决检测按下哪些键的一般问题,但如果您只想查找键盘修饰符(shift、ctrl、alt 等),则可以通过静态

QGuiApplication::keyboardModifiers()
检索它
QGuiApplication::queryKeyboardModifiers()
方法。


7
投票

因此,我需要在 Paint() 例程中轮询键盘。我还没找到 如何用 Qt 来做呢?我想到有一张地图 在 keyPressEvent 和 keyReleaseEvent 中更新并轮询该映射 绘画()。

你的第二种方法是我会做的,除了我会使用连续的、周期性的 QTimer 事件来轮询键盘按下的地图,并在必要时调用 QWidget::Update() 函数来使显示小部件失效。由于多种原因,强烈建议不要在 Paint() 中执行非绘画操作,但我不知道如何解释这一点。


3
投票

没有 Qt API 用于检查按键是否被按下。 您可能需要为不同的平台编写单独的代码并添加一些

#ifdef
逻辑。

在 Windows 上,您可以使用

GetKeyState()
GetKeyboardState()
,它们都在
windows.h
中声明。


1
投票

使用 Qt 时这并不简单,但 Gluon 团队一直在致力于解决这个问题(以及其他许多问题)。 GluonInput 解决了这个问题,并且可以作为 Gluon 的一部分使用:http://gluon.gamingfreedom.org/ 它也是一个很好的、类似 Qt 的 API,所以虽然它是一个额外的依赖项,但您应该可以使用它。


1
投票

这是由自动重复按键引起的:

当我按住前进键时,相机会移动 前进“步”单位,然后暂停一段时间然后继续 移动。

在 QT5 中,您可以通过

isAutoRepeat()
对象的功能
QKeyEvent
检测按下的键。如果按住该键,则
isAutoRepeat()
将返回
true
。 例如:

void MainWindow::keyPressEvent(QKeyEvent *event)
{
    if (event->isAutoRepeat())
    {
        return;
    }

    if (!event->isAutoRepeat())
    {
        qDebug() << "[MainWindow::keyPressEvent()] " << event->key() << "; " << event->isAutoRepeat();
    }
}

void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
    if (event->isAutoRepeat())
    {
        return;
    }
    qDebug() << "[MainWindow::keyReleaseEvent()] " << event->key() << "; " << event->isAutoRepeat();
}

0
投票

在 Qt5 中使用 QGuiApplication::keyboardModifiers() 和 QGuiApplication::queryKeyboardModifiers() 进行键盘修饰符


0
投票

@ksming 的答案很好,但不完整,并且可能有未处理的边缘情况。在 Windows 和 OSX 上,当窗口被拖动、调整大小或失去焦点时,可能不会接收到按键事件。这可能意味着您的地图可能会进入不一致的状态。

处理此问题的最佳方法是每当检测到这些事件之一时使用

QEvent::NonClientAreaMouseButtonPress

重置地图

也就是说,

  1. 按下某个键时,请在地图中记下此内容
  2. 松开按键后,将其从地图中删除
  3. 当窗口被拖动时,您的地图将被清除 - 这允许您保持一致的状态,因为您不可能知道那段时间的状态。

缺点是您无法在拖动或调整窗口大小时处理事件,但我认为在大多数用例中这应该没问题。只需让用户在完成窗口处理后处理这些即可。另一种方法是以平台相关的方式处理密钥。

警告 - 在 OSX 上,您似乎必须从应用程序级别本身收听

NonClientAreaMouseButtonPress
。也就是说,小部件不会像 Windows 上那样接收此事件。

class KeyPressFilter : public QObject {
public:
    bool eventFilter(QObject* aObject, QEvent* aEvent) final {
        if (aEvent->type() == QEvent::NonClientAreaMouseButtonPress) {
            // Handle your event here ...
            // Optionally return
        }
        return QObject::eventFilter(aObject, aEvent);
    }
};

{
    QApplication myApplication{...};
    
    KeyPressFilter myKeyFilter;
    myApplication.installEventFilter(&myKeyFilter);

    myApplication.exec();
}

这样的东西应该有效

出于完全相同的原因,您可能还想处理

QEvent::WindowDeactivate
(如果在处理按键时焦点发生变化)。

可以在此处找到有关此问题的讨论的链接(从 QT 6.5.1 开始准确)

© www.soinside.com 2019 - 2024. All rights reserved.