Threading in python - user input with timer - python

I am trying to run two functions in parallel:
Timer
Input field
Program should terminate either when Timer ends or when user provides an answer.
Everything is working fine, but when the time is up I still can input an answer, and I want process to terminate.
Can somebody help me with that issue ?
Thanks !
Here is my code:
import sys
import time
import threading
def countdown2(seconds):
global stop_timer
stop_timer = False
start = time.time()
while not stop_timer:
if time.time() - start >= seconds:
stop_timer = True
print(f'End of time {time.time() - start}')
print(f'End of time in {time.time() - start}')
countdown_thread = threading.Thread(target=countdown2, args=(5,))
countdown_thread.start()
while not stop_timer:
answer = input('Provide answer: ')
if answer:
stop_timer = True
break
print('End')

Here's an example of how you could do this:
from threading import Thread
import time
import signal
import os
got_input = False
def countdown(duration):
global got_input
for _ in range(duration):
time.sleep(1)
if got_input:
return
if not got_input:
os.kill(os.getpid(), signal.SIGINT)
def main():
global got_input
(t := Thread(target=countdown, args=[5])).start()
try:
answer = input('Please enter a value: ')
got_input = True
print(f'You entered: {answer}')
except KeyboardInterrupt:
print('\nToo slow')
finally:
t.join()
if __name__ == '__main__':
main()

Related

How can I put 'keyboard module' function into async code?

In my case, I want to hook the keyboard keys that is pressed.
And when I press "0", I want to kill the terminal.
Both functions should work at the same time.
But my code doesn't work. What is wrong with this code?
import keyboard
import asyncio
def sleep(a):
rand1 = random.uniform(0, 0.009)
rand2 = random.uniform(0.01, 0.02)
result = random.uniform(rand1, rand2)
asyncio.sleep(a + result)
async def record_start():
while True:
k = keyboard.read_key()
k = keyboard.read_key()
print(k)
async def record_stop():
while True:
if keyboard.is_pressed('0'):
print('stop')
sleep(1)
exit()
async def main():
await asyncio.gather(
record_stop(),
record_start(),
)
asyncio.run(main())
I tried out using another modules.
And I assume that problem is modules or way to use "while"
your record_start function never gives a chance to any other async code to run.
Introduce an awaiting call in it, like await asyncio.sleep(.01) in it (it may be sleep(0) but I'd advise a larger interval), and things should work.
I just solved this issue.
It doesn't need to use 'asyncio' module.
instead, I run this code with 'threading' module.
from threading import Thread
import os
def thread_1():
while True:
start_time = time.time()
k = keyboard.read_key()
k = keyboard.read_key()
print("sleep(%s)" % round(time.time() - start_time, 3))
print("press('%s')" % k)
def thread_2():
while True:
if keyboard.is_pressed('0'):
print('stop')
pid = os.getpid()
os.kill(pid, 2)
if __name__ == "__main__":
t1 = Thread(target=thread_1)
t2 = Thread(target=thread_2)
print('start')
t1.start()
t2.start()

How to stop 'enter spamming' in a python reaction timer

I have been trying to make a reaction timer for a project to test reaction times. It uses 'perf_counter' to record the times before and after an input to test how long it takes to press the enter key. The issue is that the enter key can be spammed which makes it seem if they have a reaction time of 0.000001 seconds. I have made a class which disables the keyboard and enables it when I want. Even in that case, people are able to sneak in extra enter presses between the disables and enables. I have attached the code below. Any ideas how to prevent enter spamming?
import time, random, msvcrt
from math import log10, floor
def round_sig(x, sig=5):
return round(x, sig-int(floor(log10(abs(x))))-1)
class keyboardDisable():
def start(self):
self.on = True
def stop(self):
self.on = False
def __call__(self):
while self.on:
msvcrt.getwch()
def __init__(self):
self.on = False
import msvcrt
disable = keyboardDisable()
disable.start()
print('When I say __GO__ you hit ENTER! This will happen 3 times. Got it?')
time.sleep(2)
print('Ready')
time.sleep(1)
print('Steady')
time.sleep(random.randint(2,5))
print('#####__GO__######')
disable.stop()
tic = time.perf_counter()
a = input()
toc = time.perf_counter()
if msvcrt.kbhit():
disable.start()
timeSpent = toc-tic
print('Your first time was '+str(timeSpent) + ' seconds')
time.sleep(1)
print('The next one is coming up.')
time.sleep(1)
print('Ready')
time.sleep(1)
print('Steady')
time.sleep(random.randint(2,5))
print('#####__GO__######')
disable.stop()
tic2 = time.perf_counter()
b = input()
toc2 = time.perf_counter()
if msvcrt.kbhit():
disable.start()
timeSpent2 = toc2-tic2
print('Your second time was '+str(timeSpent2) + ' seconds')
time.sleep(1)
print('The last one is coming up.')
time.sleep(1)
print('Ready')
time.sleep(1)
print('Steady')
time.sleep(random.randint(2,5))
print('#####__GO__######')
disable.stop()
tic3 = time.perf_counter()
c = input()
toc3 = time.perf_counter()
timeSpent3 = toc3-tic3
print('Your last time was '+str(timeSpent3) + ' seconds')
average = (timeSpent + timeSpent2 + timeSpent3)/3
numAverage = round_sig(average)
print('Your average time is '+str(numAverage) + ' seconds')
The keyboard-disabling code never really runs.
Here's a simplification of your program that uses a function to capture one reaction time and calls it thrice.
The clear_keyboard_buffer() function (that should consume all outstanding keystrokes) was borrowed from https://stackoverflow.com/a/2521054/51685 .
import time, random, msvcrt, math
def round_sig(x, sig=5):
return round(x, sig - int(math.floor(math.log10(abs(x)))) - 1)
def clear_keyboard_buffer():
while msvcrt.kbhit():
msvcrt.getwch()
def get_reaction_time():
print("Ready")
time.sleep(1)
print("Steady")
time.sleep(random.randint(2, 5))
print("#####__GO__######")
clear_keyboard_buffer()
tic = time.perf_counter()
a = input()
toc = time.perf_counter()
return toc - tic
print("When I say __GO__ you hit ENTER! This will happen 3 times. Got it?")
time1 = get_reaction_time()
print(f"Your first time was {time1} seconds")
time.sleep(1)
print("The next one is coming up.")
time2 = get_reaction_time()
print(f"Your first time was {time2} seconds")
time.sleep(1)
print("The last one is coming up.")
time3 = get_reaction_time()
print(f"Your first time was {time3} seconds")
average = (time1 + time2 + time3) / 3
print(f"Your average time is {round_sig(average)} seconds")
This solution uses a Thread to start the timer, while the main thread waits for input all the time. That way, it is possible to catch early key presses:
from threading import Thread
import random
import time
def start():
global started
started = None
time.sleep(random.randint(2,5))
print("#### GO ####")
started = time.time()
t = Thread(target=start)
print("ready...")
# start the thread and directly wait for input:
t.start()
input()
end = time.time()
if not started:
print("Fail")
else:
print(end-started)
t.join()

How to implement a timed background function?

A straightforward application of:
Prompt for user input.
Start countdown (or count up) timer.
Wait on user input (as timer counts down/up).
If user inputs a correct response, conditional statement 1
Else, conditional statement 2
If user exceeds a preset time, timer expires and user is directed accordingly.
I've tried some of the solutions offered on this web site. However, in all cases, the count up/down timer seems to stop once the user input prompt is generated. In other words, the timer does not seem to run as a separate (background) thread.
import threading
import time
class TimerClass(threading.Thread):
def __init__(self):
threading.Thread.__init__(self)
self.event = threading.Event()
self.count = 10
def run(self):
while self.count > 0 and not self.event.is_set():
print (self.count)
int_answer = (int(input('Enter your age: '), base = 10)
str_answer = str(int_answer)
while str_answer == '':
self.count -= 1
self.event.wait(10)
if (int_answer > 50) :
<do something>
else:
<do somethingelse>
def stop(self):
self.event.set()
tmr = TimerClass()
tmr.start()
time.sleep(1)
tmr.stop()
The program should go to condition 1 if a response of > 50 is provided; else, go to condition 2 if a response of <= 50 is entered. The timer should expire after a period of 10 secs. if the user has not provided a response (with user notification).
I've adapted the code from this answer to your needs:
import threading
import queue
import time
def read_kbd_input(inputQueue):
print('Ready for keyboard input:')
while (True):
input_str = input()
inputQueue.put(input_str)
def main():
inputQueue = queue.Queue()
inputThread = threading.Thread(target=read_kbd_input, args=(inputQueue,), daemon=True)
inputThread.start()
start_time = time.time()
while True:
if (inputQueue.qsize() > 0):
input_str = inputQueue.get()
# Insert your code here to do whatever you want with the input_str.
print("input_str = {}".format(input_str))
break
time.sleep(0.1) # poll each 100ms
if time.time() - start_time > 2: # timeout after 2 sec
break
print("End.")
if (__name__ == '__main__'):
main()

Daemon thread not exiting despite main program finishing

I've already referred to this thread, but it seems to be outdated
and there doesn't seem to be a clean explanation
Python daemon thread does not exit when parent thread exits
I'm running python 3.6 and trying to run the script from either IDLE or Spyder IDE.
Here is my code:
import threading
import time
total = 4
def creates_items():
global total
for i in range(10):
time.sleep(2)
print('added item')
total += 1
print('creation is done')
def creates_items_2():
global total
for i in range(7):
time.sleep(1)
print('added item')
total += 1
print('creation is done')
def limits_items():
#print('finished sleeping')
global total
while True:
if total > 5:
print ('overload')
total -= 3
print('subtracted 3')
else:
time.sleep(1)
print('waiting')
limitor = threading.Thread(target = limits_items, daemon = True)
creator1 = threading.Thread(target = creates_items)
creator2 = threading.Thread(target = creates_items_2)
print(limitor.isDaemon())
creator1.start()
creator2.start()
limitor.start()
creator1.join()
creator2.join()
print('our ending value of total is' , total)
limitor thread doesn't seem to be ending despite being a daemon thread.
Is this a way to get this working from IDLE or Spyder?
Thanks.
I had the same Problem and solved it by using multiprocessing instead of threading:
from multiprocessing import Process
import multiprocessing
from time import sleep
def daemon_thread():
for _ in range(10):
sleep(1)
print("Daemon")
if __name__ == '__main__':
multiprocessing.freeze_support()
sub_process = Process(target = daemon_thread, daemon = True)
sub_process.start()
print("Exiting Main")
I haven't yet really understood why I need the call to freeze_support() but it makes the code work.

Q : Python time

I would like to use ·time()· to launch an event. An example would be to print("test") for 3 seconds. For that I did this:
from time import time, sleep
from random import random
t = time()
n = 3
print(n, time() - t)
for i in range(100):
sleep(0.04)
print(time() - t)
if time() - t > n:
print("test")
break
and it works! But in my game, in a while loop, it does not work... Why not?
If I've understood correctly, it seems you don't know how to run a simple gameloop and run some test code after 3 seconds, here's some naive approach:
from time import time, sleep
from random import random
start_time = time()
n = 3
while True:
elapsed_time = time() - start_time
sleep(0.04)
print(elapsed_time)
if elapsed_time > n:
print("test")
break
if you want to achieve something else during the 3 second delay period, rather than just going round a while loop, try using a time-delayed thread. For example, the following
import threading
import time
def afterThreeSec():
print("test")
return
t1 = threading.Timer(3, afterThreeSec)
t1.setName('t1')
t1.start()
print ("main")
time.sleep(1)
print ("main")
time.sleep(1)
print ("main")
time.sleep(1)
print ("main")
time.sleep(1)
print ("main")
gives the output:
main
main
main
test
main
main

Categories

Resources