Python cannot cancel timer thread - python

I am trying to detect a long press event and cancel it if the button is released before the timer times out, however the timer never gets cancelled and fires if it is a short press or a long one:
from threading import Thread
but_down = Timer(1.5,long_press)
if(but=='down'):
but_down.start()
else:
but_down.cancel()
def long_press():
print('long press')

Your code didn't run for me due to errors, but once I fixed those it worked fine:
This outputs long press after 1.5 seconds:
from threading import Timer
but = "down"
def long_press():
print('long press')
but_down = Timer(1.5,long_press)
if(but=='down'):
but_down.start()
else:
but_down.cancel()
This outputs nothing:
from threading import Timer
but = "up"
def long_press():
print('long press')
but_down = Timer(1.5,long_press)
if(but=='down'):
but_down.start()
else:
but_down.cancel()
I don't know what but is but my guess is that your but=='down' test might be the cause of the error.

Related

How to program a task with a timer in my Python code?

I want to execute a task after certain time, so I have tried a countdown timer with a condition of being finished (when countdown variable = 0, the task is performed). The thing is that I don't want to stop the execution of the main program while performing the countdown. I have tried this:
import time
def countdown(num_of_secs):
while(num_of_secs):
time.sleep(1)
num_of_secs -= 1
return num_of_secs
So, I run my code setting a number of seconds to the countdown, and when this countdown reaches the 0 value, a task must be executed. Using this code (it uses a while), when I call my function "countdown" it stops the execution of the main program, so it is the same as a big time.sleep. I want to carry out this countdown in the background, without stopping other actions until the countdown finishes and the task starts.
Thank you
Another alternative is by using threading.
I've got a simple example here with 2 Threads where the working thread is waiting for the countdown thread to finish and starting. The Main is still working fine.
import threading
import time
def do_something():
countdown_thread.join()
print("Starting Task")
time.sleep(3)
print("Finished Task")
def countdown(num_of_secs):
while(num_of_secs):
time.sleep(1)
num_of_secs -= 1
print(num_of_secs)
if __name__ == '__main__':
countdown_thread = threading.Thread(target=countdown, args=(3,))
work_thread = threading.Thread(target=do_something)
countdown_thread.start()
work_thread.start()
while True:
print("Main doing something")
time.sleep(1)
Example picture for multithreading: Sequential vs Threading
Usually python only has a single program flow, so every instruction needs to complete before the next one can get executed.
For your case you need asynchronicity, with e.g. asyncio.sleep(5) as a separate task in the same event loop.
import asyncio
async def sleeper():
print('Holding...')
await asyncio.sleep(5)
print('Doing Work!')
async def work():
print('Doing work')
print('while')
print('the other guy is sleeping')
async def main():
await asyncio.gather(sleeper(), work())
asyncio.run(main())
The most common and easiest way to implement this would be with a Timer object from the threading library. It would go as follows:
import threading
import time
i = 0
done = False
def show_results():
print("results from GPIO readings")
print("=)")
global done
done = True # signal end of while loop
def read_GPIO():
print("reading GPIO...")
t = threading.Timer(60, show_results) # task will trigger after 60 seconds
t.start()
# your while loop would go here
read_GPIO() # do work
while not done:
print("waiting", i) # doing work while waiting for timer
time.sleep(1)
i += 1
pass
Notice that the time library is used only for illustrative purposes. You could also start the timer recursively to check periodically GPIOs and print results or trigger an event. For more information on the threading library or the Timer object check the docs

Keyboard macro press X each few secons while i press other keys

I'm playing a game that i need to press X each 1 sec, while i press other keyboard keys, but my hands are responding to pain
first i tried to do it on javascript:
const robot = require("robotjs");
function Sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms);
});
}
async function Main() {
console.log("running...");
await Sleep(2500);
PressTogether();
await Main();
}
function PressTogether() {
robot.keyTap("x");
}
Main();
also on python
import pyautogui
import time
print("hey")
while True:
time.sleep(2.5)
pyautogui.press("x")
time.sleep(2.5)
pyautogui.press("x")
print("bye")
Both are pressing the X each 2.5 seconds, but both got the same problem
it freezes my keyboard, i cant press any other key
why this is happen? how to fix?
Your keyboard doesn't react since by using sleep, you suspend the whole thread. It then is not able to do anything except blocking execution.
After sleeping for 2.5 seconds you just do sending out a keyboard action (pressing x key).
Then you are suspending your thread again.
Doing so, your process feels like being frozen because you effectively block your only one main thread most of the time.
You can overcome to that behavior by introducing another thread, which is responsible for your keyboard inputs taking them from stdin.
Here is one approach for python
import pyautogui
from time import sleep
from threading import Thread
from pynput.keyboard import Key, Listener
# a custom function that blocks for a moment and simulates key press
def akp_task():
print("hey")
while True:
time.sleep(2.5)
pyautogui.press("x")
time.sleep(2.5) #this could be omitted
pyautogui.press("x") #this could be omitted
# another function that handles manual key press inputs
def mkpi_task(key):
print('\nmanual pressed: {0}'.format(key))
if key == Key.delete:
# Stop listener
return False
# create a thread for auto key press
akp_thread = Thread(target=akp_task)
# run the thread
akp_thread.start()
# create another thread for your manual key press inputs
with Listener(on_press = mkpi_task) as listener:
listener.join()
References:
threading
manual key press handling

Python timer start and reset

I am attempting to get a Timer functionality to work in Python (Python 2.7 currently).
Here is what I have so far. I am struggling with a threading issue and resetting the timer.
from threading import Timer
def api_call():
print("Call that there api")
t = Timer(10.0,api_call)
def my_callback(channel):
if something_true:
print('reset timer and start again')
t.cancel()
t.start()
print("\n timer started")
elif something_else_true:
t.cancel()
print("timer canceled")
else:
t.cancel()
print('cancel timer for sure')
try:
if outside_input_that_can_happen_a_lot:
my_callback()
finally:
#cleanup objects
Basically, my_callback() can be called a lot of times very quickly and can hit any part of the "if", "elif", or "else" statements.
The issue I am having is that when the something_true variable is true, then it will start a timer. Which works great the first time. Every time after that that it is called, I get a threading error telling me that only one thread can be used for the timer.
Basically, I want to be able to reset my timer on the first "if" and cancel if the "elif" or "else" is hit.
Based on my testing, this is because threads can only be started once, and as the timer relies on a thread, the timer can only be started once.
This means that the only way to re-start the timer would be to do:
def newTimer():
global t
t = Timer(10.0,api_call)
newTimer()
instead of the t = Timer part, and do
t.cancel()
newTimer()
t.start()
instead of the current re-start code.
This makes your full code:
from threading import Timer
def api_call():
print("Call that there api")
def newTimer():
global t
t = Timer(10.0,api_call)
newTimer()
def my_callback(channel):
if something_true:
print('reset timer and start again')
t.cancel()
newTimer()
t.start()
print("\n timer started")
elif something_else_true:
t.cancel()
print("timer canceled")
else:
t.cancel()
print('cancel timer for sure')
try:
if outside_input_that_can_happen_a_lot:
my_callback()
finally:
#cleanup objects
Hope this helps.

asynchronous progress spinner

This is the code for the progress spinner:
import sys
import time
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
spinner = spinning_cursor()
for _ in range(50):
sys.stdout.write(spinner.next())
sys.stdout.flush()
time.sleep(10)
sys.stdout.write('\b')
Output
python2.7 test.py
|
It is spinning very slowly since the loop sleeps for 10 seconds...
How do I keep rotating the spinner while the process is sleeping?
You'll have to create a separate thread. The example below roughly shows how this can be done. However, this is just a simple example.
import sys
import time
import threading
class SpinnerThread(threading.Thread):
def __init__(self):
super().__init__(target=self._spin)
self._stopevent = threading.Event()
def stop(self):
self._stopevent.set()
def _spin(self):
while not self._stopevent.isSet():
for t in '|/-\\':
sys.stdout.write(t)
sys.stdout.flush()
time.sleep(0.1)
sys.stdout.write('\b')
def long_task():
for i in range(10):
time.sleep(1)
print('Tick {:d}'.format(i))
def main():
task = threading.Thread(target=long_task)
task.start()
spinner_thread = SpinnerThread()
spinner_thread.start()
task.join()
spinner_thread.stop()
if __name__ == '__main__':
main()
You could sleep in smaller steps until you reach 10 seconds:
import sys, time
def spinning_cursor():
while True:
for cursor in '|/-\\':
yield cursor
spinner = spinning_cursor()
end_time = time.time() + 10
while time.time() < end_time:
sys.stdout.write(spinner.next())
sys.stdout.flush()
time.sleep(0.2) # adjust this to change the speed
sys.stdout.write('\b')
But this will block your main thread, so it will only be useful if you want to wait for 10 seconds without doing anything else in your Python program (e.g., waiting for some external process to complete).
If you want to run other Python code while the spinner is spinning, you will need two threads -- one for the spinner, one for the main work. You could set that up like this:
import sys, time, threading
def spin_cursor():
while True:
for cursor in '|/-\\':
sys.stdout.write(cursor)
sys.stdout.flush()
time.sleep(0.1) # adjust this to change the speed
sys.stdout.write('\b')
if done:
return
# start the spinner in a separate thread
done = False
spin_thread = threading.Thread(target=spin_cursor)
spin_thread.start()
# do some more work in the main thread, or just sleep:
time.sleep(10)
# tell the spinner to stop, and wait for it to do so;
# this will clear the last cursor before the program moves on
done = True
spin_thread.join()
# continue with other tasks
sys.stdout.write("all done\n")
Spawn two threads, A and B. Thread A runs cmd to completion. Thread B displays the spinning cursor and waits for thread A to exit, which will happen when cmd completes. At that point, thread B clears the spinning cursor and then exit.
Or use an existing library instead of re-inventing the wheel. Consider the progressbar library. You'll want the RotatingMarker progress indicator.

Is there a way to getkey()/ getchar() asynchronously in python?

I really need an asyncio compatible getkey() so I can
async def stuff():
await getkey()
So when the coroutine stuff hits the await our loop stops the task, and continues on another one.
I am new to coding but there sure is such a thing somewhere?
If not, it is possible to build such a coroutine or not?
The getkey() could return the pressed key value in any form.
But it should have cbreak and noecho on (Don't wait for enter and Don't print the pressed key).
(clarification, no real need to continue read.)
please help me^^ I know, that this way of doing it seems unusual. Curses running in it's own thread would be the right way to go. But I can use curses only for displaying.. also I am really new to coding.. and I have no time to look into this threading thing:/ I just need my 100 lines to work fluently really fast and also only once :!
If you don't want to actually wait for the keypress, one way is to use a thread to detect the keypress:
from threading import Thread
import curses
key_pressed = False
def detect_key_press():
global key_pressed
stdscr = curses.initscr()
key = stdscr.getch()
key_pressed = True
def main():
thread = Thread(target = detect_key_press)
thread.start()
while not key_pressed:
print('Hello world\r')
curses.endwin()
main()
It's not exactly nice to use global variables, but it's a quick way to do it.
Here's a solution using keyboard instead of curses:
import keyboard
import time
from threading import Thread
key_pressed = False
def detect_key_press():
global key_pressed
while not keyboard.is_pressed('q'):
pass
key_pressed = True
def main():
thread = Thread(target = detect_key_press)
thread.start()
while not key_pressed:
print("Waiting for keypress...")
time.sleep(1)
main()
Starts a thread inside main that waits for keypress

Categories