USB event capture in While True Loop - python

I'm looking to capture single USB keyboard events within a "while True" loop which includes a timer function as well. Evdev is close to what I need but it's device.read_loop does not allow for inclusion of a clock function - it's a closed loop. Any ideas as to how to capture a single USB keyboard event which I can control when it is checked? I'm using Python 3.4 so asyncio is not an option. Thank you.

Threading will help you here. As Python Module of the Week (PyMOTW) says:
Using threads allows a program to run multiple operations concurrently
in the same process space.
In your case you can still read the keyboard input in a blocking loop in its own thread and let the sleep function check the time in another thread without getting blocked by the read_loop of evdev. Just set the radio_sleep_time to the amount of seconds you want to wait until the radio sleeps (you can use minutes and radio_sleep_time = 4 * 60 instead to get 4 minutes).
from time import time
from threading import Thread
from evdev import *
radio_sleep_time = 4 # sleep time for radio in seconds
device = InputDevice('/dev/input/event3') # pick the right keyboard you want to use
def read_kb():
for event in device.read_loop():
# only use key events AND only key down can be changed depending on your use case.
if event.type == ecodes.EV_KEY and event.value == 1:
keyevent = categorize(event) # just used here to have something nice to print
print(keyevent) # print key pressed event
def wait_loop():
pass # whatever radio is doing when its waiting to sleep if anything.
class Time1(Thread):
def run(self):
while True:
read_kb()
class Time2(Thread):
def run(self):
t0 = time() # starting time
# time() (current time) - t0 (starting time) gives us the time elapsed since starting
while not time() - t0 > radio_sleep_time: # loop until time passed is greater than sleep_time
wait_loop() # do sleep stuff
print(time() - t0, ">", radio_sleep_time)
print("SLEEP")
# sleep radio here
# but continue to listen to keyboard
Time1().start()
Time2().start()

Related

Python: can the time library do task: the loop should stop when a certain time passes?

Can the time library help with the task: the loop should stop when a certain time passes? I'm going to write a light game program in Python that should stop after for example 1 minute and output the result.
I can't find information about this function.
The straightforward solution is to run a while-loop with a time-checking boolean expression:
from datetime import datetime, timedelta
end_time = datetime.now() + timedelta(minutes=1)
while end_time >= datetime.now():
print("Your code should be here")
Another more sophisticated approach is to run the program in a separate thread. The thread checks for an event flag to be set in a while loop condition:
import threading
import time
def main_program(stop_event):
while not stop_event.is_set():
print("Your code should be here")
stop_event = threading.Event()
th_main_program = threading.Thread(target=main_program, args=(stop_event,))
th_main_program.start()
time.sleep(60)
stop_event.set()
In the approaches shown above the program execution finishes gracefully but an iteration within the while-loop has to be finished to check the boolean expression. This means the program doesn't exit immediately once the timeout is reached.
To make the main program exit right away once the timeout is reached, we can use daemon thread. Please note that daemon threads are abruptly stopped at shutdown. Their resources may not be released properly:
import threading
import time
def main_program():
while True:
print("Your code should be here")
th_main_program = threading.Thread(target=main_program, daemon=True)
th_main_program.start()
time.sleep(60)
You need to take time at the start and break your loop when the difference between current time and time at the start is more than you want.
import time
start_time = time.time()
while True:
current_time = time.time()
if current_time - start_time > 60: #time in seconds
break
#your code
You can also add time.sleep(0.01) to the loop to limit your fps.

How to "continue" a for loop from a callback function with pynput and winsound?

I need to have my program play wav files from a directory and skip to the next one if the user presses F9. I have found I need to set the winsound to Async mode to get it to skip to the next file while the previous file is still playing, but at the same time i need to run a background process like time.sleep(5) or else the program will exit immediately.
I have a for loop to run through the wav files, but I can't directly call continue from the on_press() function so I tried using a conditional global variable to sleep or not sleep. Thus, I can stop the current playing wav file with PlaySound(None, winsound.Purge) but I can't continue to the next iteration of the for loop or get my time.sleep(5) to conditionally run - it will always run and I will always have to wait those 5 seconds to start the next iteration. Is there a problem with the way I'm using the callback or should I use a different input lib or something?
from pynput import keyboard
import winsound
import os
mySkip = 0
currentIdx = 0
def clamp(n, minn, maxn):
return max(min(maxn, n), minn)
def on_press(key):
global mySkip
global currentIdx
if key == keyboard.Key.f9:
winsound.Beep(5500, 200)
mySkip = 1
currentIdx = clamp(currentIdx + 1, 0, 3)
winsound.PlaySound(None, winsound.SND_PURGE)
listener = keyboard.Listener(on_press=on_press)
listener.start()
WAVDir = "C:/Users/me/PycharmProjects/projectName/WAV"
wavList = os.listdir(WAVDir) #have 4 files in here, "test0.wav" to "test4.wav"
for idx, i in enumerate(wavList):
soundPath = WAVDir + "/test" + str(currentIdx) + ".wav"
print(soundPath)
winsound.PlaySound(soundPath, winsound.SND_ASYNC)
if mySkip == 0:
time.sleep(5)
currentIdx += 1
mySkip = 0
Here's a solution I came up with using polling - which is never my favorite thing to do, but winsound is notoriously uncooperative when it comes to threading, and I believe that threading is the way to go here:
def main():
from pathlib import Path
from threading import Event, Thread
from pynput import keyboard
from collections import namedtuple
import wave
skip_sound = Event()
def play_sound_thread(path, max_duration_seconds):
from winsound import PlaySound, SND_ASYNC
PlaySound(path, SND_ASYNC)
elapsed_seconds = 0
poll_interval = 1 / 8
while not skip_sound.is_set() and elapsed_seconds < max_duration_seconds:
skip_sound.wait(poll_interval)
elapsed_seconds += poll_interval
def on_press(key):
if key is keyboard.Key.f9:
nonlocal skip_sound
skip_sound.set()
Wave = namedtuple("Wave", ["path", "file_name", "duration_seconds"])
waves = []
wav_dir = Path("./wavs")
for wav_path in wav_dir.glob("*.wav"):
wav_path_as_str = str(wav_path)
file_name = wav_path.name
with wave.open(wav_path_as_str, "rb") as wave_read:
sample_rate = wave_read.getframerate()
number_of_samples = wave_read.getnframes()
duration_seconds = number_of_samples / sample_rate
wav = Wave(
path=wav_path_as_str,
file_name=file_name,
duration_seconds=duration_seconds
)
waves.append(wav)
listener = keyboard.Listener(on_press=on_press)
listener.start()
for wav in waves:
print(f"Playing {wav.file_name}")
thread = Thread(
target=play_sound_thread,
args=(wav.path, wav.duration_seconds),
daemon=True
)
thread.start()
thread.join()
if skip_sound.is_set():
print(f"Skipping {wav.file_name}")
skip_sound.clear()
return 0
if __name__ == "__main__":
import sys
sys.exit(main())
How it works:
Every time a sound needs to be played, a new thread is spawned and the winsound.PlaySound stuff is done in the thread. PlaySound receives the SND_ASYNC argument, so that the sound is played asynchronously, and this operation by itself does not block execution in the thread. The thread's execution is then blocked, however, by a loop, which terminates when either a certain amount of time has elapsed (the sound has had enough time to finish playing) or when the skip_sound event is set.
The on_press callback sets the skip_sound event when the f9 key is pressed.
I've created a namedtuple called Wave, which is just meant to act like a simple data bucket. All the relevant information we need for any single wave file is contained in one of these.
The main for-loop (the second one, the first one just compiles a list of Wave objects), we iterate through our Wave objects. We then spawn a new thread for the current Wave object. We then start the thread to start playing the sound, and thread.join it so that the main thread waits for this sub-thread to finish - basically blocking until the sub-thread is finished. When the on_press callback is triggered, it terminates the sub-thread prematurely, and as a result the join operation will no longer be blocking, allowing us to reset our skip_sound event if necessary and move on to the next iteration.
Things I like about this solution:
It works
Things I don't like about this solution:
Like I said, the play_sound_thread function plays the sound
asynchronously, and then polls until either the skip_sound event is
triggered, or a certain amount of time has elapsed. Polling is gross. I just sort of arbitrarily decided to poll in 1/8th of a second intervals.
In order to determine the maximum duration for which to poll, you
need to open the wave file, parse it, and divide the number of
samples by the sample rate. It feels dirty to basically open the same
file twice - once in wave.open, and again in winsound.PlaySound.

About modal operators in Blender Python

I want to make a program that uses modal operators to continue printing ("test") every 0.01 seconds in Blender's Python.
I also want to exit when I press the Esc key.
What can I do?
I wrote and executed the following code, but Blender freezes. Using a modal operator seems to be able to avoid freezing, but I wasn't sure how to use it.
import time
import threading
#A function that keeps executing print ("test")
def worker():
while True:
print("test")
def schedule(interval, f, wait=True):
base_time = time.time()
next_time = 0
while True:
t = threading.Thread(target=f)
t.start()
if wait:
t.join()
next_time = ((base_time - time.time()) % interval) or interval
time.sleep(next_time)
#Execute worker function every 0.01 second
schedule(0.01, worker, False)
In blenders text editor you will find a python template called operator_modal_timer.py that works the way you want.
The first call to the operator is the execute() method, where you create the timer with wm.event_timer_add. Then in the modal() method you respond to the timer events or cancel the operator if you get an esc key event.
If you put the operator in an addon, you can have it enabled every time you start blender.

How Do I Loop An Input In Python?

I have been searching and searching on how to figure out how to make an input or something go into a while loop. As in, the input() command won't stop my stopwatch. I have tried tkinter, pygame, and a couple other methods, but they just didn't work. If anyone can help me out, I would prefer something small and simple, if that's even possible. And to be specific on what I want to learn to do, is basically allowing, when any key is pressed, for it to instantly stop (preferably without hitting enter). Thanks, saddlepiggy!
Here is what I have so far, with nothing to activate the stopping:
#Setup (Variables and stuff)
hours = 0
minutes = 0
seconds = 0
import time
#Main Part of Code
print("Welcome to PyWatch, a stopwatch coded in Python!")
print("Press any key to start the stopwatch.")
print("Then, press any key to stop it!")
start = input("")
while hours < 48:
seconds = seconds + 1
time.sleep(1)
print(hours, "hours,", minutes, "minutes,", seconds, "seconds")
#If Statements for getting seconds/minutes/hours
if (seconds == 60):
minutes = minutes + 1
seconds = seconds - 60
if (minutes == 60):
hours =hours + 1
minutes = minutes - 60
Threading is what you want.
Create a second thread that waits for the input while your first thread handles your stopwatch code. Behold:
import threading, sys
def halt():
raw_input()
threading.Thread(target=halt).start()
while hours < 48 and threading.active_count() > 1:
seconds = seconds + 1
time.sleep(1)
# copy and past what you had before
Allow me to elaborate on what's happening: Thus far, all of the code you've written has been single threaded. This means only one line of code is executed at a time, there's only one thread of execution. Consequentially, your script can't multitask, it can't wait for input and print the time at the same time.
So when this line is evaluated
threading.Thread(target=halt).start()
The main thread creates a second thread of execution. Meanwhile, the main thread goes on and enters the while loop. The target parameter is the entry point of the thread, it's the starting point. It's analogous to if __name__ == "__main__:" for the main thread. Just as the main thread terminates when it reaches the end of if __name__ == "__main__:", our second thread will terminate once it reaches the end of halt().
The threading.active_count function tells you how many threads in are current in execution.
You can't do that in Python. You are asking for a keyboard event. Keyboard events are shipped with a GUI. This thread explains pretty much everything: QKeyPress event in PyQt
Or use an external application for your OS that can append output to a file that is read by your Python program in a loop. When a certain event is detected you can perform some actions. For Linux this tread explains: https://superuser.com/questions/248517/show-keys-pressed-in-linux

How do I make a time delay? [duplicate]

This question already has answers here:
How do I get my program to sleep for 50 milliseconds?
(6 answers)
Closed 3 years ago.
How do I put a time delay in a Python script?
This delays for 2.5 seconds:
import time
time.sleep(2.5)
Here is another example where something is run approximately once a minute:
import time
while True:
print("This prints once a minute.")
time.sleep(60) # Delay for 1 minute (60 seconds).
Use sleep() from the time module. It can take a float argument for sub-second resolution.
from time import sleep
sleep(0.1) # Time in seconds
How can I make a time delay in Python?
In a single thread I suggest the sleep function:
>>> from time import sleep
>>> sleep(4)
This function actually suspends the processing of the thread in which it is called by the operating system, allowing other threads and processes to execute while it sleeps.
Use it for that purpose, or simply to delay a function from executing. For example:
>>> def party_time():
... print('hooray!')
...
>>> sleep(3); party_time()
hooray!
"hooray!" is printed 3 seconds after I hit Enter.
Example using sleep with multiple threads and processes
Again, sleep suspends your thread - it uses next to zero processing power.
To demonstrate, create a script like this (I first attempted this in an interactive Python 3.5 shell, but sub-processes can't find the party_later function for some reason):
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor, as_completed
from time import sleep, time
def party_later(kind='', n=''):
sleep(3)
return kind + n + ' party time!: ' + __name__
def main():
with ProcessPoolExecutor() as proc_executor:
with ThreadPoolExecutor() as thread_executor:
start_time = time()
proc_future1 = proc_executor.submit(party_later, kind='proc', n='1')
proc_future2 = proc_executor.submit(party_later, kind='proc', n='2')
thread_future1 = thread_executor.submit(party_later, kind='thread', n='1')
thread_future2 = thread_executor.submit(party_later, kind='thread', n='2')
for f in as_completed([
proc_future1, proc_future2, thread_future1, thread_future2,]):
print(f.result())
end_time = time()
print('total time to execute four 3-sec functions:', end_time - start_time)
if __name__ == '__main__':
main()
Example output from this script:
thread1 party time!: __main__
thread2 party time!: __main__
proc1 party time!: __mp_main__
proc2 party time!: __mp_main__
total time to execute four 3-sec functions: 3.4519670009613037
Multithreading
You can trigger a function to be called at a later time in a separate thread with the Timer threading object:
>>> from threading import Timer
>>> t = Timer(3, party_time, args=None, kwargs=None)
>>> t.start()
>>>
>>> hooray!
>>>
The blank line illustrates that the function printed to my standard output, and I had to hit Enter to ensure I was on a prompt.
The upside of this method is that while the Timer thread was waiting, I was able to do other things, in this case, hitting Enter one time - before the function executed (see the first empty prompt).
There isn't a respective object in the multiprocessing library. You can create one, but it probably doesn't exist for a reason. A sub-thread makes a lot more sense for a simple timer than a whole new subprocess.
Delays can be also implemented by using the following methods.
The first method:
import time
time.sleep(5) # Delay for 5 seconds.
The second method to delay would be using the implicit wait method:
driver.implicitly_wait(5)
The third method is more useful when you have to wait until a particular action is completed or until an element is found:
self.wait.until(EC.presence_of_element_located((By.ID, 'UserName'))
There are five methods which I know: time.sleep(), pygame.time.wait(), matplotlib's pyplot.pause(), .after(), and asyncio.sleep().
time.sleep() example (do not use if using tkinter):
import time
print('Hello')
time.sleep(5) # Number of seconds
print('Bye')
pygame.time.wait() example (not recommended if you are not using the pygame window, but you could exit the window instantly):
import pygame
# If you are going to use the time module
# don't do "from pygame import *"
pygame.init()
print('Hello')
pygame.time.wait(5000) # Milliseconds
print('Bye')
matplotlib's function pyplot.pause() example (not recommended if you are not using the graph, but you could exit the graph instantly):
import matplotlib
print('Hello')
matplotlib.pyplot.pause(5) # Seconds
print('Bye')
The .after() method (best with Tkinter):
import tkinter as tk # Tkinter for Python 2
root = tk.Tk()
print('Hello')
def ohhi():
print('Oh, hi!')
root.after(5000, ohhi) # Milliseconds and then a function
print('Bye')
Finally, the asyncio.sleep() method (has to be in an async loop):
await asyncio.sleep(5)
A bit of fun with a sleepy generator.
The question is about time delay. It can be fixed time, but in some cases we might need a delay measured since last time. Here is one possible solution:
Delay measured since last time (waking up regularly)
The situation can be, we want to do something as regularly as possible and we do not want to bother with all the last_time, next_time stuff all around our code.
Buzzer generator
The following code (sleepy.py) defines a buzzergen generator:
import time
from itertools import count
def buzzergen(period):
nexttime = time.time() + period
for i in count():
now = time.time()
tosleep = nexttime - now
if tosleep > 0:
time.sleep(tosleep)
nexttime += period
else:
nexttime = now + period
yield i, nexttime
Invoking regular buzzergen
from sleepy import buzzergen
import time
buzzer = buzzergen(3) # Planning to wake up each 3 seconds
print time.time()
buzzer.next()
print time.time()
time.sleep(2)
buzzer.next()
print time.time()
time.sleep(5) # Sleeping a bit longer than usually
buzzer.next()
print time.time()
buzzer.next()
print time.time()
And running it we see:
1400102636.46
1400102639.46
1400102642.46
1400102647.47
1400102650.47
We can also use it directly in a loop:
import random
for ring in buzzergen(3):
print "now", time.time()
print "ring", ring
time.sleep(random.choice([0, 2, 4, 6]))
And running it we might see:
now 1400102751.46
ring (0, 1400102754.461676)
now 1400102754.46
ring (1, 1400102757.461676)
now 1400102757.46
ring (2, 1400102760.461676)
now 1400102760.46
ring (3, 1400102763.461676)
now 1400102766.47
ring (4, 1400102769.47115)
now 1400102769.47
ring (5, 1400102772.47115)
now 1400102772.47
ring (6, 1400102775.47115)
now 1400102775.47
ring (7, 1400102778.47115)
As we see, this buzzer is not too rigid and allow us to catch up with regular sleepy intervals even if we oversleep and get out of regular schedule.
The Tkinter library in the Python standard library is an interactive tool which you can import. Basically, you can create buttons and boxes and popups and stuff that appear as windows which you manipulate with code.
If you use Tkinter, do not use time.sleep(), because it will muck up your program. This happened to me. Instead, use root.after() and replace the values for however many seconds, with a milliseconds. For example, time.sleep(1) is equivalent to root.after(1000) in Tkinter.
Otherwise, time.sleep(), which many answers have pointed out, which is the way to go.
Delays are done with the time library, specifically the time.sleep() function.
To just make it wait for a second:
from time import sleep
sleep(1)
This works because by doing:
from time import sleep
You extract the sleep function only from the time library, which means you can just call it with:
sleep(seconds)
Rather than having to type out
time.sleep()
Which is awkwardly long to type.
With this method, you wouldn't get access to the other features of the time library and you can't have a variable called sleep. But you could create a variable called time.
Doing from [library] import [function] (, [function2]) is great if you just want certain parts of a module.
You could equally do it as:
import time
time.sleep(1)
and you would have access to the other features of the time library like time.clock() as long as you type time.[function](), but you couldn't create the variable time because it would overwrite the import. A solution to this to do
import time as t
which would allow you to reference the time library as t, allowing you to do:
t.sleep()
This works on any library.
If you would like to put a time delay in a Python script:
Use time.sleep or Event().wait like this:
from threading import Event
from time import sleep
delay_in_sec = 2
# Use time.sleep like this
sleep(delay_in_sec) # Returns None
print(f'slept for {delay_in_sec} seconds')
# Or use Event().wait like this
Event().wait(delay_in_sec) # Returns False
print(f'waited for {delay_in_sec} seconds')
However, if you want to delay the execution of a function do this:
Use threading.Timer like this:
from threading import Timer
delay_in_sec = 2
def hello(delay_in_sec):
print(f'function called after {delay_in_sec} seconds')
t = Timer(delay_in_sec, hello, [delay_in_sec]) # Hello function will be called 2 seconds later with [delay_in_sec] as the *args parameter
t.start() # Returns None
print("Started")
Outputs:
Started
function called after 2 seconds
Why use the later approach?
It does not stop execution of the whole script (except for the function you pass it).
After starting the timer you can also stop it by doing timer_obj.cancel().
asyncio.sleep
Notice in recent Python versions (Python 3.4 or higher) you can use asyncio.sleep. It's related to asynchronous programming and asyncio. Check out next example:
import asyncio
from datetime import datetime
#asyncio.coroutine
def countdown(iteration_name, countdown_sec):
"""
Just count for some countdown_sec seconds and do nothing else
"""
while countdown_sec > 0:
print(f'{iteration_name} iterates: {countdown_sec} seconds')
yield from asyncio.sleep(1)
countdown_sec -= 1
loop = asyncio.get_event_loop()
tasks = [asyncio.ensure_future(countdown('First Count', 2)),
asyncio.ensure_future(countdown('Second Count', 3))]
start_time = datetime.utcnow()
# Run both methods. How much time will both run...?
loop.run_until_complete(asyncio.wait(tasks))
loop.close()
print(f'total running time: {datetime.utcnow() - start_time}')
We may think it will "sleep" for 2 seconds for first method and then 3 seconds in the second method, a total of 5 seconds running time of this code. But it will print:
total_running_time: 0:00:03.01286
It is recommended to read asyncio official documentation for more details.
While everyone else has suggested the de facto time module, I thought I'd share a different method using matplotlib's pyplot function, pause.
An example
from matplotlib import pyplot as plt
plt.pause(5) # Pauses the program for 5 seconds
Typically this is used to prevent the plot from disappearing as soon as it is plotted or to make crude animations.
This would save you an import if you already have matplotlib imported.
This is an easy example of a time delay:
import time
def delay(period='5'):
# If the user enters nothing, it'll wait 5 seconds
try:
# If the user not enters a int, I'll just return ''
time.sleep(period)
except:
return ''
Another, in Tkinter:
import tkinter
def tick():
pass
root = Tk()
delay = 100 # Time in milliseconds
root.after(delay, tick)
root.mainloop()
You also can try this:
import time
# The time now
start = time.time()
while time.time() - start < 10: # Run 1- seconds
pass
# Do the job
Now the shell will not crash or not react.

Categories