Confusing waitKey buffering behavior

When using waitKey(0) in a loop with some delays, more key presses are ‘picked up’ before waitKey is ever called for a second time. In case this is OS specific I am using Windows

To illustrate what I mean here’s a small example:

import cv2
import time
import numpy as np

empty = np.zeros((400,400),dtype=np.uint8)

while True:
    cv2.imshow('empty', empty)
    k = cv2.waitKey(0)
    print(k)
    time.sleep(3)
    print("sleep over")

If I run this and press a key, as expected it will print the keycode and then “sleep over” after 3 seconds. However if I rapidly press many keys after the first one, before “sleep over” is ever printed, the program will continue to (over a long period of time) print every key press that occurred in that short space of time before “sleep over” was printed and subsequently waitKey had a chance to be called for the second time.

What I would expect to happen is that no more key presses are picked up because before the sleep is over waitKey has only been called once, so any additional key presses that occur before then shouldn’t be stored anywhere.

Does anyone know why this happens? Where is this buffer/queue of key presses being stored and can it be manually cleared?

The thing occurs with any other wait times as well, i.e

import cv2
import time
import numpy as np

empty = np.zeros((400,400),dtype=np.uint8)

while True:
    cv2.imshow('empty', empty)
    k = cv2.waitKey(1)
    if k!=-1:
        print(k)
        time.sleep(3)
        print("sleep over")

edit: it’s also worth mentioning this isn’t a scheduling thing either, it is not exclusive to sleep and occurs when sleep is replaced with intensive computation - which is how I came across this issue in the first place.

1 Like

an explicit sleep, or any kind of computation, of that duration, in a GUI event loop, is BAD. as long as your program is twiddling its thumbs instead of processing GUI events (or waiting to), the GUI will be unresponsive. that’s solved with threads.

that is how all GUIs work. don’t blame OpenCV. OpenCV simply uses GUI toolkits. the fundamental truth of GUIs is that there’s an event queue and an event processing loop and the event processing loop runs callbacks/handlers in response to events, and your if... is considered such a thing, and those handlers MUST NOT take up noticeable time.

no, you don’t have a way to clear that event queue, nor should you, because those events matter. if you want to accept them and then discard them, it’s your business, but you have to accept them.

you should respect the requirements of GUI toolkits and work within them. that means: the main loop processes events, and you do not get to sleep in there, and you get all the events, at most one per waitKey call. and any computation has to be finished quickly, or be chopped up into small pieces (e.g. per-frame computation) so that GUI events can be handled (waitKey), or be exiled into a thread.

to receive events (key events anyway) quickly, just waitKey(1) or pollKey() (i.e. quickly) until you get a -1. then you’ll know that the queue has no more, for now.

1 Like

I’m not blaming OpenCV by any means and I know I can get around this by using a separate thread and waiting for execution to be finished before accepting new inputs - that’s what I did as soon as I realised my computation inside the loop (simulated here with an exaggerated sleep) was taking a while. I was just wanted to explore it further and learn more about what was happening under the hood