I want to detect key presses while my main program loop runs. Say I’m computing loads of data and printing that in the REPL. I want to be able to detect any keys I press. For example, if the key ‘p’ is pressed, all the computation pauses. However, I don’t JUST wish to pause. Maybe I run a function or anything. Basically, I want to be able to detect key-down events.
How can I achieve that?
And of course I’m not talking about readline() since that requires you to press ENTER. Also, that prevents the rest of the program from running (Tell me if that can work with an @async loop. I couldn’t get it to work)
I don’t want to use 3rd party packages like GTK or Makie. Just simple Key event detection!
I found a similar question on stackoverflow.
Based on that you could do something like this
using REPL
# Run listener as separate task using channels, put keypresses in channel for main loop
function key_listener(c::Channel)
t = REPL.TerminalMenus.terminal
while true
REPL.Terminals.raw!(t, true) || error("unable to switch to raw mode")
keypress = Char(REPL.TerminalMenus.readkey(t.in_stream))
REPL.Terminals.raw!(t, false) || error("unable to switch back from raw mode")
put!(c, keypress)
end
end
function main()
channel = Channel(key_listener, 10) # Start task, 10 is buffer size for channel
stop = false
while !stop
println("Doing some long calculation")
sleep(1)
while !isempty(channel) # Process all keypresses
c = take!(channel)
if c == 'q'
println("quitting")
stop = true
close(channel)
break
else
println("$c is not a recognized command")
end
end
end
end
# Run listener as separate task using channels, put keypresses in channel for main loop
function key_listener(c::Channel)
t = REPL.TerminalMenus.terminal
while true
REPL.Terminals.raw!(t, true) || error("unable to switch to raw mode")
keypress = Char(REPL.TerminalMenus.readkey(t.in_stream))
REPL.Terminals.raw!(t, false) || error("unable to switch back from raw mode")
put!(c, keypress)
end
end
function main()
channel = Channel(key_listener, 10) # Start task, 10 is buffer size for channel
stop = false
flag = true
while !stop
if flag
println("working")
sleep(0.5)
end
while !isempty(channel) # Process all keypresses
c = take!(channel)
if c == 'q'
println("quitting")
stop = true
close(channel)
break
elseif c == 'p'
flag = false
elseif c == 'c'
flag = true
else
println("$c is not a recognized command")
end
end
end
end
Somehow adding a flag in the main loop causes a problem. Things work just as I wanted. But I was hoping ‘p’ to pause, ‘c’ to continue. But somehow, ‘p’ freezes the entire thing. No key press seems to work. Can you please help me out with this?
Hmm, would guess it is something with task scheduling there, that the key_listener
task does not get to run enough.
I think the easiest way is to just add a short sleep in the case the flag is off, it seems to do it for me at least.
if flag
println("working")
sleep(0.5)
else
sleep(0.1) # Makes sure we give some cpu time to the key_listener
end
It also seems like the Channel
constructor can take a spawn
kwarg which should (may?) give it its own thread. Though this didn’t seem to fix it for me at least.
https://docs.julialang.org/en/v1/base/parallel/#Base.Channel-Tuple{Function}
Thanks, man, I was finally able to create this simple clock object. Now I can finally move on with my project
I figured… yield() does the job as well