使用 Qt 检查某个键是否按下

发布于 2024-12-04 08:55:42 字数 523 浏览 7 评论 0原文

我正在玩一些图形,并且我已经使用箭头键实现了简单的相机移动。我的第一个方法是重写 keyPressEvent 来执行如下操作:

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

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

因此,我需要在 Paint() 例程中轮询键盘。我还没有找到如何用 Qt 做到这一点。我想到有一个 map ,它将在 keyPressEventkeyReleaseEvent 中更新,并在 Paint( )。还有更好的想法吗?感谢您的任何见解。

I am playing around with some graphics, and I have implemented simple camera movement with the arrow keys. My first approach was to override keyPressEvent to do something like this:

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

This doesn't work as I wish it would. When I press and hold, for example, the forward key, the camera moves forward "step" units, then halts for a while and then continues moving. I am guessing that this is how the event is generated, in order to avoid multiple events in case of a little bit long keypress.

So, I need to poll the keyboard in my Paint() routine. I haven't found how to do it with Qt. I thought of having a map<Key, bool> which would be updated in keyPressEvent and keyReleaseEvent and poll that map in Paint(). Any better ideas? Thanks for any insights.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

一曲爱恨情仇 2024-12-11 08:55:43

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

在 Windows 上,您可以使用 GetKeyState()GetKeyboardState(),它们都在 windows.h 中声明。

There is no Qt API for checking whether a key is pressed or not.
You may have to write separate code for different platforms and add a bit of #ifdef logic.

On Windows you can use GetKeyState() and GetKeyboardState(), both declared in windows.h.

拧巴小姐 2024-12-11 08:55:43

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

This is not straight forward when using Qt, but the Gluon team has been working on exactly that problem (along with a bunch of others). GluonInput solves the issue, and is available as part of Gluon: http://gluon.gamingfreedom.org/ It is also a nice, Qt-like API, so while it's an extra dependency, it should be possible for you to use it.

羁拥 2024-12-11 08:55:43

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

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

在 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();
}

This is caused by auto repeation of keys:

When I press and hold, for example, the forward key, the camera moves
forward "step" units, then halts for a while and then continues
moving.

In QT5 you can detect key held down by function isAutoRepeat() of QKeyEvent object. If the key is held down then isAutoRepeat() will return true.
for example:

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();
}
复古式 2024-12-11 08:55:43

@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 开始准确)

@ksming's answer is good, but not complete and has a possible edge case that is not handled. It is possible on Windows and OSX that key events will not be received as a window is dragged, resized or loses focus. This can mean your map can get into an inconsistent state.

The best way to handle this is to reset the map whenever one of these events is detected, using QEvent::NonClientAreaMouseButtonPress

That is,

  1. When a key is pressed, note this in your map
  2. When a key is released, remove it from your map
  3. When the window is dragged clear your map - which allows you to remain in a consistent state, since you can't possibly know the state during that time.

The downside is you cannot handle events as the window is dragged or resized, but I think in the majority of use cases this should be fine. Just let the user handle these when they are done with their handling of the window. The alternative is handling keys in a platform dependant manner.

A word of warning - on OSX it appears you must listen to NonClientAreaMouseButtonPress from the application level itself. That is, a widget will not receive this event like it will on 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();
}

Something like this should work

You might also want to handle QEvent::WindowDeactivate for exactly the same reason (if the focus is changed as keys are being handled).

A link to a discussion on this issue can be found here (accurate as of QT 6.5.1)

多情出卖 2024-12-11 08:55:43

在 Qt5 中使用 QGuiApplication::keyboardModifiers() 和 QGuiApplication::queryKeyboardModifiers() 作为键盘修改器

Use QGuiApplication::keyboardModifiers() and QGuiApplication::queryKeyboardModifiers() for keyboard modifiers in Qt5

此刻的回忆 2024-12-11 08:55:42

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

This doesn't solve the general problem of detecting which keys are pressed, but if you are only looking for keyboard modifiers (shift, ctrl, alt, etc.), you can retrieve that through the static QGuiApplication::keyboardModifiers() and QGuiApplication::queryKeyboardModifiers() methods.

原来是傀儡 2024-12-11 08:55:42

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

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

So, I need to poll the keyboard in my Paint() routine. I haven't found
how to do it with Qt. I thought of having a map which would
be updated in keyPressEvent and keyReleaseEvent and poll that map in
Paint().

Your second method is what I would have done, except that I would use a continuous, periodic QTimer event to poll the keyboard-pressed map and call QWidget::Update() function when necessary to invalidate the display widget instead. Performing non-painting operations inside Paint() is strongly discouraged for many reasons but I do not know how to explain that well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文