stuck on adding a timer to my keyboard press program - python

Im making a keyboard program that repeatedly presses a key every 0.1 seconds and uses esc as the hotkey to stop/start the program.
import keyboard
import time
keyboard.wait('esc')
name = ("python")
while name == "python":
keyboard.press_and_release('5')
time.sleep(0.1)
#this is where i want to add the timer
name = ("notpython")
I want to add the timer there so that after a few seconds the name variable changes from python to notpython and making the while loop false.
I've tried the time sleep function but it keeps printing 5 and doesnt stop.

maybe you can try something like this:
import keyboard
import time
keyboard.wait('esc')
name = ("python")
timer = time.time()
while name == "python":
keyboard.press_and_release('5')
time.sleep(0.1)
timer2 = time.time()
if timer2 - timer >= 5: name = "notpython"

Related

How to stop a loop with keyboard.add_hotkey, while this loop is started by keyboard.add_hotkey?

The following code works perfectly, the loop can be stop by pressing esc:
import time
import keyboard
run = 1
def end():
global run
run = 0
print(run)
def do_stuff():
while run:
print('running')
time.sleep(0.5)
keyboard.add_hotkey('esc', end)
do_stuff()
But if I start this loop with another add_hotkey, I cannot stop it with esc anymore.
import time
import keyboard
run = 1
def end():
global run
run = 0
print(run)
def do_stuff():
while run:
print('running')
time.sleep(0.5)
keyboard.add_hotkey('esc', end)
# do_stuff()
keyboard.add_hotkey('enter', do_stuff)
keyboard.wait()
What should I do to stop this loop? I tried to replace the while run: with while not keyboard.is_pressed('esc'):. It can stop the loop if I hold the esc for a while. But it doesn't seem like a good solution.
=======================
updates:
the following works:
import keyboard
import threading
run = 1
def end():
global run
run = 0
print(run)
def do_stuff():
while run:
print('running')
time.sleep(0.5)
def new_do_stuff():
t = threading.Thread(target=do_stuff, name='LoopThread')
t.start()
keyboard.add_hotkey('esc', end)
keyboard.add_hotkey('enter', new_do_stuff)
keyboard.wait('esc')
Since in the second example you enter the do_stuff() loop through the hotkey and never leave the do_stuff() loop, the system is still captured in the hotkey command and is not listening for hotkeys anymore. You would have to find a way to leave the loop after the keyboard.add_hotkey('enter', do_stuff) command and enter it externally through another way, so the system listens for hotkey-entries again.
I'm not aware of the context you're using this in, but using some sort of a main-loop, that does nothing but wait for a flag to be set (it should be set when you get the hotkey interrupt) and then enters the do_stuff() loop seems like a way to solve it.

problems with failsafe and threading

I am trying to make a python autoclicker as seen below:
import queue
from pynput import mouse
from pynput.mouse import Controller, Button
from sys import exit
from threading import Thread
from time import sleep
from keyboard import is_pressed
mouse = Controller()
failSafeKey = 'q'
delay = 1
def clicking():
while TRUE:
mouse.click(Button.left)
sleep(delay)
def failSafe():
while TRUE:
if is_pressed(failSafeKey):
exit()
print("exiting")
sleep(0.1)
delay = float(input("Enter delay: "))
failSafeKey = input("Enter failsafe key (ex. ctrl+s or q): ")
Thread(clicking())
Thread(failSafe())
However, whenever I press the failsafe key, nothing happens. I have tried removing the threads, but nothing changed. Can anyone give pointers on what might be happening?

Stopping a python program when an arbitrary key was pressed while the program is running

I recently got to know about the python module signal. With that, we can capture a SIGINT and do what we want after capturing it. I used it as below. In that case I am just using SIGINT to print that program is going to be stopped and stop the program.
import signal
import os
import time
def signalHandler(signalnumb, frame):
print("Signal Number:", signalnumb, " Frame: ", frame)
print('Exiting the program...')
os._exit(0)
signal.signal(signal.SIGINT, signalHandler)
c=0
# Loop infinite times using the while(1) which is always true
while 1:
print(c)
#sleep for 1 seconds using the sleep() function in time
time.sleep(1)
c=c+1
Now I want to give any signal from keyboard(for example pressing 'q') and as soon as signal was recieved, the python program should be stopped. Has anyone got some experience on how to do that? Any other method rather than using signal module (for example using multithreading) is accepted.
Edit1-
Later I tried to use pynput module as suggested in one of a similar kind of question. For sure I have done a mistake. It doesn't work as I expected. It means with a key press, I couldn't stop the for loop from running.
from pynput import keyboard
import time
def on_press(key):
for i in range(100):
print(i)
time.sleep(1)
if key == keyboard.Key.esc:
return False # stop listener
try:
k = key.char # single-char keys
except:
k = key.name # other keys
if k in ['1', '2', 'left', 'right']: # keys of interest
# self.keys.append(k) # store it in global-like variable
print('Key pressed: ' + k)
return False # stop listener; remove this if want more keys
listener = keyboard.Listener(on_press=on_press)
listener.start() # start to listen on a separate thread
listener.join() # remove if main thread is polling self.keyspython
Can someone point out how to do it using pynput in correct way?
This was my original implementation:
a = input('Press a key to exit')
if a:
exit(0)
However, it seems that you need a piece of code that will allow for any key to be clicked and immediately exit out of the program, without hitting enter afterwards. This may be a better way to do that:
import readchar
print("Press Any Key To Exit")
k = readchar.readchar()
Hope this helps!
After carefully understanding about the threads and pynput module, I managed to stop a for loop (or any program which runs as a separate thread) using a key press callback.
from pynput import keyboard
import os
import threading
import time
loop_running = False
def on_press(key):
print(dir(key))
global loop_running
#if key == keyboard.Key.delete and not loop_running:
if ('char' in dir(key)) and (key.char == 's') and (not loop_running):
t=threading.Thread(target=start_loop)
t.start()
#elif key==keyboard.Key.tab: #if this is used, the attributes related to 'key' will be changed. you can see them since I have used a print(dir(key))
elif key.char == 'q':
loop_running=False
def start_loop():
global loop_running
loop_running = True
for i in range(100):
if not loop_running:
os._exit(0)
print(i)
time.sleep(1)
with keyboard.Listener(on_press=on_press) as listner:
listner.join()

How to add On and Off buttons with pyautogui?

I made a simple auto-clicker in python. Every three seconds the script clicks wherever your mouse is. How would I add "on and off" keys? I imagine it is a simple if/else statement but I don't know how to write it.
As of Wed Sep 15 12:10, I do not have an answer that works well.
import pyautogui
import time
def Auto_Click():
width, height = pyautogui.position()
pyautogui.click(width, height)
time.sleep(3)
while True:
Auto_Click()
I'd suggest listening to specific key presses indefinitely to switch clicking on and off. And as there is an indefinite loop for the clicking as well, you will need multithreading (to perform clicking and listening for key presses simultaneously).
Notes
The auto clicker is switched off by default right on start (To avoid clicks at unwanted positions on screen right after running it). Press SHIFT to toggle it after pointing the mouse at wanted position.
Press ESC to exit the program.
I have used SHIFT and ESC keys for toggles so that the key presses won't show up in the next prompt unlike the character keys.
Use the below code if you really need to use pyautogui. Here is the solution using pynput for handling both mouse and keyboard. (My code is basically a modified version which uses keyboard module and pyautogui instead)
import time
import keyboard
import pyautogui
import threading
INTERVAL = 0.5 # Time interval between consecutive clicks
DELAY = 0.5 # Time delay between consecutive program cycles [after the clicks are turned off]
TOGGLE_KEY = 'shift' # Key to toggle the clicking
EXIT_KEY = 'esc' # Key to stop and exit from the program
class AutoClicker(threading.Thread):
def __init__(self, interval, delay):
super(AutoClicker, self).__init__()
self.interval = interval
self.delay = delay
self.running = False
self.program_running = True
def start_clicking(self):
self.running = True
def stop_clicking(self):
self.running = False
def exit(self):
self.stop_clicking()
self.program_running = False
def toggle_clicking(self):
if self.running:
self.stop_clicking()
else:
self.start_clicking()
def click(self):
width, height = pyautogui.position()
pyautogui.click(width, height)
# This function is invoked when the thread starts.
def run(self):
while self.program_running:
while self.running:
self.click()
time.sleep(self.interval)
time.sleep(self.delay)
if __name__ == '__main__':
# Run indefinite loop of clicking on seperate thread
auto_clicker_thread = AutoClicker(INTERVAL, DELAY)
auto_clicker_thread.start() # Invokes run() function of the thread
# So that we can listen for key presses on the main thread
keyboard.add_hotkey(TOGGLE_KEY, lambda: auto_clicker_thread.toggle_clicking())
keyboard.add_hotkey(EXIT_KEY, lambda: auto_clicker_thread.exit())

I am having trouble adding timer to my code

I am working on this project with a motion sensor in which I would like to have the monitor turned off when there is no motion after a certain amount of time has passed. But every time there is a motion I would like the timer to reset.
I have the code working for turning the monitor on and off with motion, but how do I add the timer?
Any help will be appreciated. My code:
from gpiozero import MotionSensor
import time
from subprocess import call
pir = MotionSensor(4)
while True:
pir.wait_for_motion()
print("Screen On")
call(["/usr/bin/vcgencmd", "display_power", "1"])
time.sleep(30)
pir.wait_for_no_motion()
print("Screen Off")
call(["/usr/bin/vcgencmd", "display_power", "0"])
time.sleep(1)
As well as wait_for_motion(), gpiozero also provides a variable, motion_detected. The code below sets a variable, startpoint to the current time in seconds since 1/1/1970. It then starts a loop which:
Checks if motion is detected - if so, sets the startpoint variable back to the current time (which, of course, will be different to what it was previously) and turns on the display.
Checks if the startpoint variable is more than 30 seconds before the current time. Because every motion detection resets this variable, we know that there must have been at least 30 seconds since the last motion detection. If so, turns off the display.
startpoint = time.time()
while True:
if pir.motion_detected:
startpoint = time.time()
call(["/usr/bin/vcgencmd", "display_power", "1"])
print("Display on")
elif time.time() > (startpoint+30):
call(["/usr/bin/vcgencmd", "display_power", "0"])
print("Display off")
You could also use threading for this.
from enum import Enum
from gpiozero import MotionSensor
from subprocess import call
from threading import Timer
from time import sleep
from typing import Optional
pir = MotionSensor(4)
timer: Optional[Timer] = None
class MonitorState(Enum):
ON = "1"
OFF = "0"
def set_monitor_state(state: str):
call(["/usr/bin/vcgencmd", "display_power", state.value])
def start_timer():
global timer
if timer:
timer.cancel()
timer = Timer(30, set_monitor_state, (MonitorState.OFF,))
timer.start()
start_timer()
while True:
pir.wait_for_motion()
start_timer()
set_monitor_state(MonitorState.ON)
I'm not sure if the Timer actually counts as being done when the callback returns or before that. In the first case you could run into troubles when the set_monitor_state(MonitorState.ON) get called while the timer runs it's callback on another thread. You might want to use locking in this case.

Categories

Resources