Python break out of poll when key pressed - python

Apologies for the newbie Python post, but a bit of Googling and I still can't find what I need.
I have the following Pi Hat; https://github.com/modmypi/Jam-HAT, ...and I'm using their docs to guide me; https://gpiozero.readthedocs.io/en/stable/recipes.html
My idea is pretty simple;
run script when button pressed
wait for it to run, showing flashy lights whilst running
if I press another button, stop/kill script
However, I'm stuck on the part where I need to stop the script with a button
#!/usr/bin/env python3
import gpiozero
import subprocess
from signal import pause
from time import sleep
button1 = gpiozero.Button(18)
button2 = gpiozero.Button(19)
green1 = gpiozero.LED(16)
green2 = gpiozero.LED(17)
red1 = gpiozero.LED(5)
red2 = gpiozero.LED(6)
script = ""
yellow1 = gpiozero.LED(12)
yellow2 = gpiozero.LED(13)
def kill_hello():
global script
script.kill()
print("Killed PID: ", script.pid)
red1.on()
sleep(.5)
red1.off()
red2.on()
sleep(.5)
red2.off()
def say_hello():
global script
green1.on()
sleep(.5)
green1.off()
green2.on()
sleep(.5)
green2.off()
script = subprocess.Popen(['/home/kali/hello.sh'])
print("Script PID: ", script.pid)
while script.poll() is None:
if not button2.when_pressed:
green1.on()
sleep(.5)
green1.off()
sleep(1)
button2.when_pressed = kill_hello
print("Press Ctrl & C to Quit")
try:
button1.when_pressed = say_hello
pause()
except KeyboardInterrupt:
print( "\n...Quit!")
I've tried a try/except where that while loop is, but that has also not worked (not like the KeyboardInterrupt). It doesn't recognise the button has been pressed, and I'm assuming because it's not within a valid catch/else/something block.
Any suggestions for a simple break out of loop, please?

It seems the issue is around a single process is hogging Python, thus creating multiple threads helped;
#!/usr/bin/env python3
import gpiozero
import subprocess
import time
import threading
from signal import pause
button1 = gpiozero.Button(18)
button2 = gpiozero.Button(19)
green1 = gpiozero.LED(16)
green2 = gpiozero.LED(17)
red1 = gpiozero.LED(5)
red2 = gpiozero.LED(6)
script = ""
yellow1 = gpiozero.LED(12)
yellow2 = gpiozero.LED(13)
switch = True
def blink():
def run():
while (switch == True):
green1.on()
time.sleep(.2)
green1.off()
time.sleep(.2)
if switch == False:
break
thread = threading.Thread(target=run)
thread.start()
def switchon():
global switch
global script
switch = True
print('switch on')
script = subprocess.Popen(['/home/kali/hello.sh'])
blink()
def switchoff():
global switch
global script
print('switch off')
script.kill()
switch = False
try:
button1.when_pressed = switchon
button2.when_pressed = switchoff
pause()
except KeyboardInterrupt:
print( "\n...Quit!")

Related

Python exit code execution after pressing key

I want to make clicker for my game and i don't know how can I exit program after I press a key. I've tried
import sys
screenWidth, screenHeight = pyautogui.size()
currentMouseX, currentMouseY = pyautogui.position()
if keyboard.is_pressed("p"):
sys.exit()
for user in range(0, 1):
pyautogui.moveTo(849, 657) # załóż druzyne
pyautogui.click()
pyautogui.PAUSE = 0.2
pyautogui.typewrite('Bandaelo')
pyautogui.PAUSE = 0.3
pyautogui.moveTo(953, 742) # potwierdź
pyautogui.click()
pyautogui.PAUSE = 0.4
pyautogui.moveTo(948, 656) # potwierdz
pyautogui.click()
and more code like this
but it doesn't work. Can You help me?
This should do the trick, you still need to put your code:
import sys
import pyautogui
def main():
screenWidth, screenHeight = pyautogui.size()
currentMouseX, currentMouseY = pyautogui.position()
try:
while 1:
var = input("enter p to exit: ")
if var == 'p':
break
else:
print('test something')
# put your code here...
# and more code like this
except KeyboardInterrupt:
sys.exit()
raise
if __name__ == '__main__':
main()
You need to import sys object. Then call the exit() method to stop your program.
For your case you can try:
import keyboard
import sys
if keyboard.is_pressed("p"):
sys.exit()
EDIT : I have done a little research and find when writing a multithreaded app, raise SystemExit and sys.exit() both terminates only the running thread. In this case, you can use os._exit() which exits the whole process.
import os
import keyboard
if keyboard.is_pressed('p'):
os._exit(0)

Thread halts program execution

I have a program which has to run a 'watcher' function in the background, which checks if the time specified on an 'alarm' object equals to the current time, and if so, it triggers the alarm. This is the function:
def watcher(this):
while this.run:
this.lock.acquire(True)
for i , a in enumerate(this.alarms):
if a.activate:
if a.repeat:
if a.dateTime.minute + (a.dateTime.hour*60) == datetime.datetime.now().minute + (datetime.datetime.now().hour*60):
this.trigger(i)
while True:
if keyboard.is_pressed('a'):
this.stop()
break
elif a.dateTime.date() == datetime.datetime.now().date() and a.dateTime.minute + a.dateTime.hour*60 == datetime.datetime.now().minute + datetime.datetime.now().hour*60:
this.trigger(i)
while True:
if keyboard.is_pressed('a'):
this.stop()
break
this.lock.release()
The lock object is created previously inside the class this function is in.
It works when i run it on a program where i set the alarms beforehand, but when i have to run another program alongside it that manipules the alarms, the other program hangs and only this one runs.
Here part of the main program:
import alarms
import datetime
import manager
import threading
import keyboard
lock = threading.Lock()
mngr = manager.manager()
print("Program Test")
tr = threading.Thread(target = mngr.watcher)
tr.start()
while True:
command = input()
if len(command) == 0:
print('')
elif command.split()[0] == "rem":
lock.acquire(True)
try:
mngr.remove_alarm(int(command.split()[1]) - 1)
except IndexError as excp:
print('Invalid alarm number. Use the command "list" to get alarms')
except Exception as excp:
print('Wrong usage of command: use like this: rem {alarmNumber}')
print('DEBUG: ' + str(excp))
finally:
lock.release()
Am i doing the synchronization wrong? Upon even instancing the Thread on the main program it halts execution.

raspberry pi 3 webbrowser controlled by buttons (Python)

hi I am trying to get 2 webcam outputs to be displayed in a webbrowser which runs in full screen mode.
So far it works aslong as I keep the fullscreen/kiosk disabled, once the full screen is on, my script pauses.
When I close the webbrowser the script continues and I can use my buttons which open a new browser and then the script pauses again (the purpose is that the script loops endlessly without pauses)
import RPi.GPIO as GPIO
from time import sleep
import os
url1 = 'http://10.0.0.31/mjpg/video.mjpg'
url2 = 'http://10.0.0.32/mjpg/video.mjpg'
GPIO.setmode(GPIO.BCM)
array = [4,17]
GPIO.setup(array, GPIO.IN, pull_up_down=GPIO.PUD_UP)
os.system('chromium-browser --app=http://10.0.0.31/mjpg/video.mjpg --kiosk')
while True:
if ( GPIO.input(17) == False ):
print('Button 1 Pressed')
os.system('date')
os.system('pkill chromium-browser')
print (GPIO.input(17))
os.system('chromium-browser --app=http://10.0.0.32/mjpg/video.mjpg --kiosk')
sleep(0.5)
if ( GPIO.input(4) == False ):
print('Button 2 Pressed')
os.system('date')
os.system('pkill chromium-browser')
print (GPIO.input(4))
os.system('chromium-browser --app=http://10.0.0.31/mjpg/video.mjpg --kiosk')
sleep(0.5)
else:
os.system('clear')
print ('Press a key')
sleep(0.1)
Is there a way to fix this without big changes (as I noticed a lot of people are fan of selenium, that's not an option) and using subprocess instead of os.system gives same result.
The reason this is happening is because os.system() waits for a process to finish, and won't continue your code until this happens. By using the "&" at the end of your command, the command does not have to finish, and your buttons should still be useable.
#Import libraries
import RPi.GPIO as GPIO
from time import sleep
import os
#Set up the URLs
url1 = 'http://10.0.0.31/mjpg/video.mjpg'
url2 = 'http://10.0.0.32/mjpg/video.mjpg'
#Set up GPIO pins
GPIO.setmode(GPIO.BCM)
array = [4,17]
GPIO.setup(array, GPIO.IN, pull_up_down=GPIO.PUD_UP)
#Open the first webpage
os.system('chromium-browser --app=http://10.0.0.31/mjpg/video.mjpg --kiosk & ')
while True:
if ( GPIO.input(17) == False ):
print('Button 1 Pressed')
os.system('date')
os.system('pkill chromium-browser')
print (GPIO.input(17))
os.system('chromium-browser --app=http://10.0.0.32/mjpg/video.mjpg --kiosk &')
sleep(0.5)
if ( GPIO.input(4) == False ):
print('Button 2 Pressed')
os.system('date')
os.system('pkill chromium-browser')
print (GPIO.input(4))
os.system('chromium-browser --app=http://10.0.0.31/mjpg/video.mjpg --kiosk &')
sleep(0.5)
else:
os.system('clear')
print ('Press a key')
sleep(0.1)

Running one function without stopping another

How can I run a timer while asking for user input from the console? I was reading about multiprocessing, and I tried to use this answer: Python: Executing multiple functions simultaneously. When I tried to get it going, it gave me a bunch of framework errors.
Right now it runs start_timer(), but then stops it when it runs cut_wire().
Here's my start_timer function:
def start_timer():
global timer
timer = 10
while timer > 0:
time.sleep(1)
timer -= 1
sys.stdout.write ("There's only %i seconds left. Good luck. \r" % (timer))
sys.stdout.flush()
cut_wire()
if timer == 0:
print("Boom!")
sys.exit()
and this is the cut_wire function:
def cut_wire():
wire_choice = raw_input("\n> ")
if wire_choice == "cut wire" or wire_choice == "Cut Wire":
stop_timer()
else:
print("Boom!")
sys.exit()
Of course it stops running when it plays the cut_wire function because "raw_input" command reads the text and wait for the user to put the text and press enter.
My suggestion is to check for they key press "Enter" and when then key was press, read the line. If the key wasn't press, just continue with your timer.
Regards.
Instead of using raw_input() use this function taken from here.
def readInput( caption, timeout = 1):
start_time = time.time()
sys.stdout.write('\n%s:'%(caption));
input = ''
while True:
if msvcrt.kbhit():
chr = msvcrt.getche()
if ord(chr) == 13: # enter_key
break
elif ord(chr) >= 32: #space_char
input += chr
if len(input) == 0 and (time.time() - start_time) > timeout:
break
print '' # needed to move to next line
if len(input) > 0:
return input
else:
return ""
Thearding option
To make sure that both functions run completely simultaneously you can use this example of threading event:
import threading
event = threading.Event()
th = theading.Thread(target=start_timer, args=(event, ))
th1 = theading.Thread(target=cut_wire, args=(event, ))
th.start()
th1.start()
th.join()
th1.join()
In your function you can set an event using event.set(), check it using event.is_set() and clear it using event.clear().
Only addressing your concerns, here is a quick fix using threading :
import time
import sys
import os
def start_timer():
global timer
timer = 10
while timer > 0:
time.sleep(1)
timer -= 1
sys.stdout.write ("There's only %i seconds left. Good luck. \r" % (timer))
sys.stdout.flush()
#cut_wire() ==> separate call is easier to handle
if timer == 0:
print("Boom!")
os._exit(0) #sys.exit() only exits thread
def cut_wire():
wire_choice = raw_input("\n> ")
if wire_choice == "cut wire" or wire_choice == "Cut Wire":
stop_timer()
else:
print("Boom!")
os._exit(0) #same reason
if __name__ == '__main__':
import threading
looper = threading.Thread(target=start_timer)
looper.start()
cut_wire()

Fabric get() progressbar

I am currently generating a python-script with the fabric framwork that is supposed to collect a backup from a remote server and store it locally on the client running fabric.
Now, since the backup file is >400MB, it takes quite some time to transfer it. And here is where my question bumps in:
Is there any kind of progressbars for the fabric get()-function? Or rather, is it possible to add a progressbar somehow?
Here's a piece of my code:
def collect_backup():
env.warn_only=True
run('uptime')
print "Copying scrips to be run..."
filename, remotepath = _getlatest()
print "Copy complete."
print "Collecting backup..."
localpath = _collect(filename, remotepath)
def _collect(filename, remotepath):
a=remotepath + filename
localpath="/home/bcns/backups/"
####Here's the get() I was talking about
get(a, localpath)
return(localpath)
The "filename" and "remotepath" variables are set in another function.
There is a lot of great info at the following site:
http://thelivingpearl.com/2012/12/31/creating-progress-bars-with-python/
Here is their solution for a console prog bar with threading:
import sys
import time
import threading
class progress_bar_loading(threading.Thread):
def run(self):
global stop
global kill
print 'Loading.... ',
sys.stdout.flush()
i = 0
while stop != True:
if (i%4) == 0:
sys.stdout.write('\b/')
elif (i%4) == 1:
sys.stdout.write('\b-')
elif (i%4) == 2:
sys.stdout.write('\b\\')
elif (i%4) == 3:
sys.stdout.write('\b|')
sys.stdout.flush()
time.sleep(0.2)
i+=1
if kill == True:
print '\b\b\b\b ABORT!',
else:
print '\b\b done!',
kill = False
stop = False
p = progress_bar_loading()
p.start()
try:
#anything you want to run.
time.sleep(1)
stop = True
except KeyboardInterrupt or EOFError:
kill = True
stop = True
Hope that helps or at least gets you started.

Categories

Resources