Check if a Key is Down with Qt - c++

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.

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 QApplication::keyboardModifiers() and QApplication::queryKeyboardModifiers() methods.

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.

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.

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.

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

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

Related

QT How to check if key is pressed?

I would like to be able to check if the key is pressed at any time. I imagine such a solution:
void MyQQuickPaintedItem::keyPressEvent(QKeyEvent * event)
{
isKeyPressed[ event->key() ] = 1;
}
void MyQQuickPaintedItem::keyReleaseEvent(QKeyEvent *event)
{
isKeyPressed[ event->key() ] = 0;
}
To check if the right arrow key is pressed it would be enough to check isKeyPressed[ Qt::Key_Right ] value.
I implemented it and... it doesn't work. I don't mean that program crashes. isKeyPressed[ Qt::Key_Right ] is just always 0, even if I'm pressing this right arrow key or any other key.
EDIT:
One of header files:
...
bool isKeyPressed[255];
...
One of linked files:
...
extern bool isKeyPressed[255];
...
I don't know exactly how big isKeyPressed should be, but I don't get SIGSEGV, so the size is probably ok.
you normally dont address the problem like that... at least not using QT...
if you are interested to "catch" some key pressed events, then Qt offers ways to do that
what you can do is "connect" the shortcut to a lambda or a slot and inside there do what ever you need... like e.g. catching when the user press "control + i"
connect(new QShortcut(QKeySequence(Qt::CTRL + Qt::Key_I), this), &QShortcut::activated, [](){qDebug() << "Here we are!";});
Instead of an array you can use the map, if you are not interested in the order then you can use unordered_maps which is faster. There are rather few keys so I think the program will run fast anyway.

QKeyEvent isAutoRepeat not working?

So, I have an application where if a particular button is kept pressed it plays an audio device, when the button is released it stops the audio device. I use keyPressEvent and KeyReleaseEvent to implement this which is similar to the code below:
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if(event->isAutoRepeat())
{
event->ignore();
}
else
{
if(event->key() == Qt::Key_0)
{
qDebug()<<"key_0 pressed"<<endl;
}
else
{
QWidget::keyPressEvent(event);
}
}
}
void MainWindow::keyReleaseEvent(QKeyEvent *event)
{
if(event->isAutoRepeat())
{
event->ignore();
}
else
{
if(event->key() == Qt::Key_0)
{
qDebug()<<"key_0 released"<<endl;
}
else
{
QWidget::keyReleaseEvent(event);
}
}
}
But apparently isAutoRepeat function isn't working as I can see continuous print out of key_0 pressed and key_0 released despite the fact I haven't released the 0 key after I have pressed it. Is my code wrong or something else is wrong?
Thanks.
EDIT
I think this is happening because the MainWindow loses the keyboard focus. How can I actually find out which widget has the focus? I'm actually using some widgets when Qt::Key_0 pressed, but I thought I set all those possible widgets to Qt::NoFocus, I guess it's not working.
I'm trying to know which widget has the focus by doing the following:
QWidget * wigdet = QApplication::activeWindow();
qDebug()<<wigdet->accessibleName()<<endl;
but it always prints an empty string. How can I make it print the name of the widget which has the keyboard focus?
So as I also stumbled over this issue (and grabKeyboard didn't really help), I begun digging in qtbase. It is connected to X11 via xcb, and by default, in case of repeated keys, X11 sends for each repeated key a release-event immediately followed by a key-press-event. So holding down a key results in a sequence of XCB_BUTTON_RELEASE/XCB_BUTTON_PRESS-events beeing sent to the client (try it out with xev or the source at the end of this page).
Then, qt (qtbase/src/plugins/platforms/xcb/qxcbkeyboard.cpp) tries to figure out from these events whether its an autorepeat case: when a release is received, it uses a lookahead feature to figure if its followed by a press (with timestamps close enough), and if so it assumes autorepeat.
This does not always work, at least not on all platforms. For my case (old and outworn slow laptop (Intel® Celeron(R) CPU N2830 # 2.16GHz × 2) running ubuntu 16.04), it helped to just put a usleep (500) before that check, allowing the press event following the release event to arrive... it's around line 1525 of qxcbkeyboard.cpp:
// look ahead for auto-repeat
KeyChecker checker(source->xcb_window(), code, time, state);
usleep(500); // Added, 100 is to small, 200 is ok (for me)
xcb_generic_event_t *event = connection()->checkEvent(checker);
if (event) {
...
Filed this as QTBUG-57335.
Nb: The behaviour of X can be changed by using
Display *dpy=...;
Bool result;
XkbSetDetectableAutoRepeat (dpy, true, &result);
Then it wont send this release-press-sequences in case of a hold down key, but using it would require more changes to the autorepeat-detection-logic.
Anyway solved it.
The problem was that I have a widget which is a subclass of QGLWidget which I use to show some augmented reality images from Kinect. This widget takes over the keyboard focus whenever a keyboard button is pressed.
To solve this problem, I needed to call grabKeyboard function from the MainWindow class (MainWindow is a subclass of QMainWindow), so this->grabKeyboard() is the line I needed to add when key_0 button is pressed so that MainWindow doesn't lose the keyboard focus, and then when the key is released I needed to add the line this->releaseKeyboard() to resume normal behaviour, that is, other widgets can have the keyboard focus.

Cursor won't change for my QGraphicsView

I have created my own widget based on QGraphicsView. I did this in order to re-implement some mouse events as such:
void Workspace::mouseMoveEvent(QMouseEvent *event)
{
qDebug() << (QString("Mouse move (%1,%2)").arg(event->x()).arg(event->y()));
QGraphicsView::mouseMoveEvent(event);
}
as well as install an event filter
bool Workspace::eventFilter(QObject* obj, QEvent* e)
{
if(e->type() == QEvent::Enter)
qDebug() << "Entered Workspace";
}
I did not liked the default 'hand' mouse pointer though and I decided to change it using
this->setCursor(Qt::CrossCursor);
in my constructor.
What happens though is the mouse pointer changing into a cross only while being at the very first pixel of the widget. The moment I move further in it goes back to the default 'hand' cursor that is used to signify drag functionality.
Why is this happening and how can I change the cursor to whatever I like?
It seems that using
QApplication::setOverrideCursor(Qt::CrossCursor);
when entering the widget, and
QApplication::restoreOverrideCursor();
when exiting, does the trick.
I am not sure why setCursor did not work though.
EDIT
Actually using the above is not such a good idea, as it is simpler to just use
QApplication::changeOverrideCursor(*mCurrentCursor);
you will not have to worry about anything else this way, Qt will take care of stack en-queue/de-queue.

Keyboard class implementation in windows application

I want to implement a Keyboard class like below;
class Keyboard
{
public:
Keyboard();
~Keyboard();
bool IsKeyDown(Key);
bool IsKeyUp(Key);
void SetPressedKey(int);
void SetReleasedKey(int);
private:
Key pressedKey;
Key releasedKey;
};
Key is an enum like below;
enum Key
{
A,
Enter,
Up,
Down
};
Here is the window callback function;
case WM_KEYDOWN:
kb.SetPressedKey(wParam);
break;
case WM_KEYUP:
kb.SetReleasedKey(wParam);
break;
First of all, my design can be completely wrong. If it's an acceptable design, there are some questions that I couldn't answer. Can value of the pressedKey be overwritten if user presses two buttons at the same time and how can I determine if user uses combinations like CTRL+C. The other question is I couldn't find a way to make a relationship between wParam's value and enum keys' indexes.
Most of the implementation of this class is unnecessary and should be removed. You are re-implementing functionality that the operating system already provides. If you want to know the state of a key, simply call GetKeyState.
You may choose to wrap up the low-level Win32 API GetKeyState with your class. But do not store the information as state in your class. When you need to know the state of a key, i.e. when implementing IsKeyDown() and IsKeyUp(), call GetKeyState.
Another good reason for this is that you cannot guarantee that all keyboard messages will arrive in your window. Somebody may press a key whilst a different window is active, and then switch to your program. At that point, your attempt to track state using keyboard messages will break down because you never got the key down message. However, GetKeyState will work.
That said, if you need to know the instantaneous state, outside of a window procedure, then you would use GetAsyncKeyState rather than GetKeyState. Only you fully understand the purpose of this class and so are in a position to make that decision.
Windows already keeps track of all this information. If you want to know if a key is currently pressed, GetKeyState. This queries the keyboard state tied to the current message. So for example it's accurate to use GetKeyState to know if CTRL was pressed inside a WM_KEYDOWN message.
There is also GetAsyncKeyState, if you want to query the keyboard state "right now", rather than the state when the current message was generated.
In terms of your design I personally would try to make it a bit better by having it as a set of Key object and have the keys maintain their own state. That way you can model multiple key presses. The example (incomplete and not tested!) shows the idea.
class Keyboard
{
public:
Keyboard();
~Keyboard();
bool isPressed(Key);
void pressKey(int); // or Key
void releaseKey(int);
private:
std::set<Key> keys;
};
class Keyboard
{
public:
// define your comparison operators to use std::set or else write a comparator.
Key(uint32 id);
~Key();
bool
void setPressed();
void setReleased();
bool isPressed() const;
private:
uint32 keyId;
bool pressed;
};
In order to relate the windows key values to Key objects you could build a factory to return the keyIds that you need. Your KeyIds could be an enum or just re-use the windows values. As long as they are unique and your application understands then then there is no point in being restrictive.

First mouse movement unexpected using SDL (C++)

I'm working on a c++ project for school, and my implementation at this moment requires mouse input for controlling a ship (It's supposed to be a remake of Tyrian). Everything is working fine, except for the first time I move the mouse, it has an offset depending on where the mouse was when I started the game. I guess this is because I'm using SDL_GetRelativeMouse but how can I prevent it?
You may wish to initialize the mouse to a good known position when the application begins, possibly right before events callbacks are initialized.
Making sure the mouse is within the bounds of the window may also be appropriate. It isn't really relevant to the application outside of its boundaries anyway.
This is what I do to toggle mousegrab in a FPS-type application:
if(event.key.keysym.sym == SDLK_z)
{
if( mouse_grabbed )
{
SDL_WM_GrabInput(SDL_GRAB_OFF);
SDL_WarpMouse( display->w/2, display->h/2 );
SDL_ShowCursor(1);
}
else
{
SDL_ShowCursor(0);
SDL_WM_GrabInput(SDL_GRAB_ON);
int tx,ty;
SDL_GetRelativeMouseState(&tx, &ty);
}
mouse_grabbed = !mouse_grabbed;
}
Consuming a mouse update via the dummy SDL_GetRelativeMouseState() call was the important part.
For the moment I'll just suppress the first time mouse movement is detected. This seems to work, but it seems a rather unprofessional approach.
I use this:
Sdl.SDL_ShowCursor(Sdl.SDL_DISABLE);
Sdl.SDL_WM_GrabInput(Sdl.SDL_GRAB_ON);
e=new Sdl.SDL_Event();
pollOne();
.
.
.
private void pollOne(){
while(Sdl.SDL_PollEvent(out e)==1){
switch(e.type){
case Sdl.SDL_MOUSEMOTION:
float throwAway=((float)e.motion.xrel*headSens);
break;
}
}
}
Basically, when initializing the mouse (grabbing it and making it invisible) call pollOne to throw the first event away. Every event captured hereafter in the main event processing function called by the main loop is with mouse at center screen.