使用Qt检查按键是否被按下

16

我正在尝试制作一些图形,使用箭头键实现了简单的相机移动。我的第一个做法是重写 keyPressEvent 而实现以下内容:

switch(key)
{
   case up: MoveCameraForward(step); break;
   case left: MoveCameraLeft(step); break;
   ...
}
这并不符合我的期望。例如,当我按住前进键时,相机会向前移动“步”单位,然后停止一段时间,然后继续移动。 我猜这就是事件生成的方式,以避免在稍微长时间按下按键的情况下出现多个事件。
因此,我需要在我的Paint()例程中轮询键盘。 我还没有找到使用Qt如何实现它的方法。我想过使用一个将在keyPressEventkeyReleaseEvent中更新的map,并在Paint()中轮询该映射。 有更好的建议吗? 感谢任何见解。
7个回答

17

Qt::KeyboardModifiers QApplication::queryKeyboardModifiers() 在我使用 QT 4.8.7 时有效。 - nivpeled

7
所以,我需要在我的Paint()函数中轮询键盘。我还没有找到使用Qt如何做到这一点的方法。我考虑过使用一个map,在keyPressEvent和keyReleaseEvent中更新该map,并在Paint()中轮询该map。
你提出的第二种方法是我本来会做的,只不过我会使用连续的周期性QTimer事件来轮询键盘按下的map,并在必要时调用QWidget::Update()函数来使显示小部件无效化。在Paint()内执行非绘制操作因为很多原因而被强烈反对,但我不知道如何很好地解释这个问题。

特别鼓励这样做,因为键盘重复率可以由用户设置。这是确保它独立于您的内部滴答率的唯一方法。 - RedX
有一个线程,并且在qt论坛中有一个关于此的示例:http://qt-project.org/forums/viewreply/135346/ - bobbaluba
我知道我回答晚了12年,但不幸的是QT仍然没有办法轻松地检查任意键的状态。警告:这种方法在Windows操作系统甚至OSX上可能无法正常工作,当窗口被移动或调整大小时,释放一个键会导致该事件丢失。这可能会导致一个键被无限期地按下,从而引发各种不需要的错误。我已经在论坛上提出了这个问题:https://forum.qt.io/topic/145591/detecting-if-a-key-is-being-pressed - NightShade
我知道我回答晚了12年,但不幸的是QT仍然没有办法轻松地检查任意键的状态。警告:这种方法在Windows操作系统甚至在OSX上可能无法正常工作,当移动或调整窗口时释放一个键,该事件将会丢失。这可能导致一个键被无限期地按下,从而产生各种不希望的错误。我已经在论坛上提出了这个问题:https://forum.qt.io/topic/145591/detecting-if-a-key-is-being-pressed - undefined
从我上面提供的链接中的讨论中,我能够得到一个没有发现错误的可行解决方案。我将留下另一个答案来扩展这个解决方案。 - NightShade

3

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

在Windows上,您可以使用windows.h中声明的GetKeyState()GetKeyboardState()


1

使用Qt时,这并不是一件直截了当的事情,但Gluon团队一直在解决这个问题(以及其他一些问题)。GluonInput解决了这个问题,并作为Gluon的一部分提供:http://gluon.gamingfreedom.org/ 它还具有类似于Qt的API,因此虽然它是一个额外的依赖项,但您应该可以使用它。


1
@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版本为准)。


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

例如,当我按住前进键时,摄像机会向前移动“步长”单位,然后停顿一段时间,然后继续移动。

在QT5中,您可以通过QKeyEvent对象的isAutoRepeat()函数检测按键是否被按住。如果按键被按住,则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()来获取键盘修饰符。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接