Skip to content

Catch auto-repeat keys in pyside6 #93

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
SpicyRicecaker opened this issue May 22, 2025 · 8 comments
Open

Catch auto-repeat keys in pyside6 #93

SpicyRicecaker opened this issue May 22, 2025 · 8 comments

Comments

@SpicyRicecaker
Copy link
Contributor

Is it possible to block auto-repeat key events in pygfx for a qt window?


Background

On Linux, in both X11 & Wayland, whenever I press and hold a key, a key release event is fired on key repeat. Here is a minimal example of pyside6 demonstrating this behavior on linux:

from PySide6.QtGui import QKeyEvent
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCore import Qt
import sys

class MainWindow(QMainWindow):
    def keyReleaseEvent(self, event):
        print(f"Key released: {event.key()} ({Qt.Key(event.key()).name})")
    def keyPressEvent(self, event: QKeyEvent) -> None:
        print(f"Key down: {event.key()} ({Qt.Key(event.key()).name})")

app = QApplication(sys.argv)
window = MainWindow()
window.setWindowTitle("PySide6 Key Up Example")
window.resize(400, 200)
window.show()
app.exec()

output (after pressing and holding, but not physically releasing the 'A' key):

Key down: 65 (Key_A)
Key released: 65 (Key_A)
Key down: 65 (Key_A)
Key released: 65 (Key_A)
Key down: 65 (Key_A)
Key released: 65 (Key_A)
Key down: 65 (Key_A)
Key released: 65 (Key_A)
Key down: 65 (Key_A)
Key released: 65 (Key_A)
Key down: 65 (Key_A)
Key released: 65 (Key_A)
Key down: 65 (Key_A)
Key released: 65 (Key_A)
Key down: 65 (Key_A)
Key released: 65 (Key_A)
Key down: 65 (Key_A)
Key released: 65 (Key_A)
Key down: 65 (Key_A)
Key released: 65 (Key_A)
Key down: 65 (Key_A)
Key released: 65 (Key_A)
Key down: 65 (Key_A)
Key released: 65 (Key_A)
Key down: 65 (Key_A)
Key released: 65 (Key_A)

This makes it really hard to implement a fly camera manually, since it's really hard to tell when the player has let go of the forward button.

After a lot of gpt and trial and error, I narrowed it down to every single qt window on linux having this problem. But pyside6 has this nice function called PySide6.QtGui.QKeyEvent.isAutoRepeat() that automagically detects this key repeat behavior and filters it out.

Utilizing this, I've edited the above code to check for auto-repeating events before printing them, and the result is as follows:

from PySide6.QtGui import QKeyEvent
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtCore import Qt
import sys

class MainWindow(QMainWindow):
    def keyReleaseEvent(self, event):
        if event.isAutoRepeat():
            return
        print(f"Key released: {event.key()} ({Qt.Key(event.key()).name})")
    def keyPressEvent(self, event: QKeyEvent) -> None:
        if event.isAutoRepeat():
            return
        print(f"Key down: {event.key()} ({Qt.Key(event.key()).name})")

app = QApplication(sys.argv)
window = MainWindow()
window.setWindowTitle("PySide6 Key Up Example")
window.resize(400, 200)
window.show()
app.exec()

The code when run, has the following output, after clicking, holding (for a few seconds), and then releasing the 'A' key:

Key down: 65 (Key_A)
Key released: 65 (Key_A)

This is a functionality that I need in pygfx, is there currently a way to do this?

@almarklein
Copy link
Member

Interesting, we've not accounted for auto-repeat yet. (still the pygfx fly controller works well enough). I see that glfw and wx also have auto-repeat (did not try jupyter yet).

Some options:

  • Add a repeat field to the key events that indicates that the event is repeating. This is what most event systems seem to do (incl JS).
  • Put a flag on the canvas to filter out repeated key events.

I think the first option would make the most sense. @Korijn thoughts?

@Korijn
Copy link
Contributor

Korijn commented May 22, 2025

I would suggest to copy existing standards if there are any, like option 1.

@almarklein
Copy link
Member

@SpicyRicecaker would you be up for a pr?

@SpicyRicecaker
Copy link
Contributor Author

I'll look into it in the next few days!

@SpicyRicecaker
Copy link
Contributor Author

Ok web javascript, seems to include this .repeat field on the event.
wx and qt seem to have a method on the event to check if the event is a repeat event.
Finally glfw seems to have an integer, action, attached to the callback which can be used to check if the event is a repeat.

  • Add a repeat field to the key events that indicates that the event is repeating. This is what most event systems seem to do (incl JS).

I think this option of having a .repeat field on the event seems to be the most straightforward approach, I'll work on getting that pr'd!

@almarklein
Copy link
Member

Mmm, in jupyter_rfb the repeated events are actively suppressed.

Proposal: key_down and key_up events don't emit in repeat mode, but the 'char' event does. I think that makes sense, because when listening to key down and up events you are usually doing some sort of interaction. While the repeated events are useful when inserting/deleting text.

@SpicyRicecaker
Copy link
Contributor Author

Yes I was just looking into that too when trying to get consistent behavior on the jupyter_rfb backend!

https://github.com/vispy/jupyter_rfb/blob/f84110f2f53eca9f7a99484039bced49a4309ebc/js/lib/widget.js#L331-L345

if (!e.repeat) { that.send(event); } // dont do the sticky key thing 

I now also think it would make sense for those repeating events to be filtered out by default by the qt, glfw, and wx backends. I can't really think of a use case for having the key_down and key_up events firing in addition to the char events. By filtering repeat events by default, we still keep the functionality the same thanks to repeating char events. The only thing would be maybe arrow keys and function keys would no longer be able to be repeated, but that's probably not a huge concern.

Considering pygfx is pretty high level too, just having users not having to deal with this at all by default would also reduce friction in using the library.

@almarklein
Copy link
Member

I made some updates to jupyter_rfb while looking into this: vispy/jupyter_rfb#119

The only thing would be maybe arrow keys and function keys would no longer be able to be repeated

Yes, good point! I think they should be emitted as (repeatable) 'char' events. Same for backspace and del, and maybe a few others. But let's leave that for another day ... I consider the 'char' event very much experimental for now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants