Search code examples
pythoninputkeyboardcurses

Single key detection to work with the possibility of adding long inputs


I have been facing this problem for the last week,I thought it would be trivial but after trying many different approaches I don't know what else to try.

I have an application where I need to have key detection (to move a robot arm with the keyboard) but when I press enter I need to add some inputs, which should be as long as I want, just some normal input("insert here").

I know about the python libraries to get key detection, I got pynput to work successfully but it crashes my raspberry pi when I start and stop the threads a few times,I tried the Keyboard library but the whole root requirement is a let down, I also got curses to work and this seems to be solid and is (almost) not causing any issues, so detecting 1 key is not a problem.

I of course know how to name my files and get all the information that I need by doing input(), so if I had to use one of those options the job would be rather simple, the challenge comes when I try to apply both approaches together, basically detect the keys to do everything I need, and use python Input to get all the inputs from the user as soon as enter is pressed, all the libraries to detect key seems to take full control and they don't want to release it without a fight. They seem to expect the user to always require single key detection but in my case I would need to constantly turn it on and off, I couldn't figure out any efficient (or not) way to get it to work properly.

My question is:

What is the best approach to have key detection + full user input when needed with curses (or any alternative) in a non blocky way (as my code need to do some other things while listening for keys), is creating and destroying the whole thing the only alternative?

This is my current test code that I created for simplicity (which works but blocks everything while listening for keys):

import curses
import time
import os

stdscr = None
addInput = False

def SetupCurses():
    global stdscr
    stdscr = curses.initscr()
    curses.cbreak()
    stdscr.keypad(1)

def StartCurse():
    global addInput

    key = ''
    while key != ord('q'):
        key = stdscr.getch()
        stdscr.addstr(str(key)) 
        if key == ord('a'):
            print("\nyou pressed a\n")
        if key == 10:
            print("\nyou pressed enter!\n")
            addInput = True
            break

def EndCurse():
    curses.endwin()

while(True):
    SetupCurses()
    StartCurse()
    EndCurse()

    if addInput:
        theinput = input("add your input\n")
        print(theinput)
        time.sleep(4)
        addInput = False

    #if there isn't any input to add I want the code to continue because there is non-related keys stuff to do, but of course it stopped at "StartCurse"
    #if there is something to add the code can stop at addInput

The reason for the loop is because the user can save as many positions as he want, so after adding some inputs the possibility of adding more is there.

I saw people making this non-blocking by closing the curses loop after a few seconds (which stops everything anyway...) kind of getting the input by luck...something like:

def ExecuteCurses():
    global AddInput
    #open it and close it very quickly to grab a key if it is pressed
    c = stdscr.getch()
    if c == ord('a'):
        print("you pressed a")
        AddInput = True
        time.sleep(1)
    curses.endwin()

Solution

  • If you want a full and long user input you will need to use the curses.echo() and then use the stdscr.getstr(). That will wait for the user to press enter(). And to not block the program while getting input you need threading which you will have to import at the top of your program

    And for the threading here is a link so you can find out more about threading.


    I hope it answers your question