Because nodelay is literally no delay. And unless your keyboard repeat rate is really high, there will be some iterations of the loop when the getch times out and yields no input, which would be normal.
For me I get:
999742 iterations with no input
258 iterations with input
Which seems reasonable for 11 seconds. There is no way my keyboard would repeat a key 1 million times in 11 seconds, and if it did it would be impossible to use the keyboard for anything but hitting all getches in this program, since it would have to count ~99 000 keys per second, which would make typing really painful. So, in short, your numbers are normal.
Non-Blacking input ncurses
python - non-blocking getch() in curses - Stack Overflow
linux - Non-blocking getch(), ncurses - Stack Overflow
python - Non-blocking console input? - Stack Overflow
I've seen many people ask a question like this, but they all seem to mean something different than what I do so I'll define what I mean by blocking:
When I hold a key on my keyboard, I want to be able to hold another, and have its input override the previous one, but when you let go of this new said key, I want the input of the other key that I was holding before to come back.
With that said I'll now explain my problem: I'm currently Programming a rogue-like in C using the ncurses library. My issue is that I cannot figure out how to get non-blocking (see above definition) input. My current method of getting input is a while loop with a switch statement that detects input using the getch() function provided by curses. Anyone got any tips, should I be taking input completely differently? I don't know how to use threads as I'm new but I'm willing to learn if it will work and run better. I'll attach a gitlab link with the code for my controls.
Controls The actual loop in question is around line 38
This isn't too urgent, I appreciate any advice and if there's anything else that I should include let me know.
The curses library is a package deal. You can't just pull out one routine and hope for the best without properly initializing the library. Here's a code that correctly blocks on getch():
#include <curses.h>
int main(void) {
initscr();
timeout(-1);
int c = getch();
endwin();
printf ("%d %c\n", c, c);
return 0;
}
From a man page (emphasis added):
The
timeoutandwtimeoutroutines set blocking or non-blocking read for a given window. Ifdelayis negative, blocking read is used (i.e., waits indefinitely for input).
For Windows, console only, use the msvcrt module:
import msvcrt
num = 0
done = False
while not done:
print(num)
num += 1
if msvcrt.kbhit():
print "you pressed",msvcrt.getch(),"so now i will quit"
done = True
For Linux, this article describes the following solution, it requires the termios module:
import sys
import select
import tty
import termios
def isData():
return select.select([sys.stdin], [], [], 0) == ([sys.stdin], [], [])
old_settings = termios.tcgetattr(sys.stdin)
try:
tty.setcbreak(sys.stdin.fileno())
i = 0
while 1:
print(i)
i += 1
if isData():
c = sys.stdin.read(1)
if c == '\x1b': # x1b is ESC
break
finally:
termios.tcsetattr(sys.stdin, termios.TCSADRAIN, old_settings)
For cross platform, or in case you want a GUI as well, you can use Pygame:
import pygame
from pygame.locals import *
def display(str):
text = font.render(str, True, (255, 255, 255), (159, 182, 205))
textRect = text.get_rect()
textRect.centerx = screen.get_rect().centerx
textRect.centery = screen.get_rect().centery
screen.blit(text, textRect)
pygame.display.update()
pygame.init()
screen = pygame.display.set_mode( (640,480) )
pygame.display.set_caption('Python numbers')
screen.fill((159, 182, 205))
font = pygame.font.Font(None, 17)
num = 0
done = False
while not done:
display( str(num) )
num += 1
pygame.event.pump()
keys = pygame.key.get_pressed()
if keys[K_ESCAPE]:
done = True
This is the most awesome solution1 I've ever seen. Pasted here in case link goes down:
#!/usr/bin/env python
'''
A Python class implementing KBHIT, the standard keyboard-interrupt poller.
Works transparently on Windows and Posix (Linux, Mac OS X). Doesn't work
with IDLE.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
'''
import os
# Windows
if os.name == 'nt':
import msvcrt
# Posix (Linux, OS X)
else:
import sys
import termios
import atexit
from select import select
class KBHit:
def __init__(self):
'''Creates a KBHit object that you can call to do various keyboard things.
'''
if os.name == 'nt':
pass
else:
# Save the terminal settings
self.fd = sys.stdin.fileno()
self.new_term = termios.tcgetattr(self.fd)
self.old_term = termios.tcgetattr(self.fd)
# New terminal setting unbuffered
self.new_term[3] = (self.new_term[3] & ~termios.ICANON & ~termios.ECHO)
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.new_term)
# Support normal-terminal reset at exit
atexit.register(self.set_normal_term)
def set_normal_term(self):
''' Resets to normal terminal. On Windows this is a no-op.
'''
if os.name == 'nt':
pass
else:
termios.tcsetattr(self.fd, termios.TCSAFLUSH, self.old_term)
def getch(self):
''' Returns a keyboard character after kbhit() has been called.
Should not be called in the same program as getarrow().
'''
s = ''
if os.name == 'nt':
return msvcrt.getch().decode('utf-8')
else:
return sys.stdin.read(1)
def getarrow(self):
''' Returns an arrow-key code after kbhit() has been called. Codes are
0 : up
1 : right
2 : down
3 : left
Should not be called in the same program as getch().
'''
if os.name == 'nt':
msvcrt.getch() # skip 0xE0
c = msvcrt.getch()
vals = [72, 77, 80, 75]
else:
c = sys.stdin.read(3)[2]
vals = [65, 67, 66, 68]
return vals.index(ord(c.decode('utf-8')))
def kbhit(self):
''' Returns True if keyboard character was hit, False otherwise.
'''
if os.name == 'nt':
return msvcrt.kbhit()
else:
dr,dw,de = select([sys.stdin], [], [], 0)
return dr != []
# Test
if __name__ == "__main__":
kb = KBHit()
print('Hit any key, or ESC to exit')
while True:
if kb.kbhit():
c = kb.getch()
if ord(c) == 27: # ESC
break
print(c)
kb.set_normal_term()
1 Made by Simon D. Levy, part of a compilation of software he has written and released under the Gnu Lesser General Public License.