python gpiozero when_motion callback modify global variable - python

i'm using the methode when_motion from the gpiozero library to listen on 3 motion sensor.
for what i understand, this methode permit to callback a fonction when motion is detected while the rest of the programme is running
i'm trying to modify the global variabled i've named state and pickedup to trigger different phase of the programme, to get more control on the overall behaviour.
sensors are triggering sound as expected but the main programme is stuck, never seem to pass the WaitUntil function at the beginning of the main loop
i suspect the state variable not really being global and/or not changing value (but it could be something else
how can i really affect global variable from my method in order for my main programme to go on?
every design suggestion is welcome
here's my code so far:
# a fonction that wait until a condition is fulfill to continue the code
def WaitUntil(condition, output): #defines function
wU = True
while True:
if condition: #checks the condition
output
wU = False
def State(val): #return a global variable without interrupting motion methode
global state
state = val
print("state = {}".format(val))
def Pickedup(val): #return a global variable without interrupting motion methode
global pickedup
pickedup = val
print("pickedup = {}".format(val))
class PIR: # motion sensor object
global state #variables to trigger different phase of the programme
state = 0
global pickedup
pickedup = False
def __init__(self, pin, element):
self.pir = MotionSensor(pin=pin)
self.pir.when_motion = self.motion
self.element = element
self.state = state
self.pickedup = pickedup
def motion(self):
global state
global pickedup
if state == 3: #this is for not playing a sound when you juste put back the object in its jar
self.pir.wait_for_no_motion()
sleep(2)
print("object is back")
State(0)
elif state == 0:
sa.stop_all() #stop alredy playing sound
wave_obj = sa.WaveObject.from_wave_file("{}.wav".format(self.element)) #play the sound associated with this sensor when you pick an object from the associated jar
wave_obj.play()
sleep(5)
print ("objet {} is picked".format(self.element))
Pickedup(True) #wait and consider that the object is picked
WaitUntil(state == 2,FadeOut(0.5)) #wait for programme to reach state 2 and fading out the current playing sound
pir1 = PIR(17,"feu")
pir2 = PIR(27,"eau")
pir3 = PIR(22,"vent")
while True: #from here is the start of the continuous programme
WaitUntil(pickedup == True, print("pick an object")) #here is the problem, cant seem to pass to the next line from here
print("put object in the box")
while cardcount == 0 and pickedup == True:
cards = PN5180().inventory()
cardcount = len(cards)
print(f"{len(cards)} card(s) detected: {' - '.join(cards)}")
#code continue

Related

Pyglet Audio Queue - Python

import pyglet, pafy
from pyglet.window import key
import urllib.request
from urllib.parse import *
from googleapiclient.discovery import build
api_key = ''
yt = build('youtube', 'v3', developerKey=api_key)
def download_file(search):
try:
os.remove("song.m4a")
except:
pass
request = yt.search().list(
part="snippet",
maxResults=1,
q=search
)
response = request.execute()
items = response['items']
item = items[0]
snippet = item['snippet']
title = snippet['title']
id_ = item["id"]
videoid = id_["videoId"]
url = "https://www.youtube.com/watch?v=" + videoid
info = pafy.new(url)
audio = info.getbestaudio(preftype="m4a")
audio.download('song.m4a', quiet=True)
file = 'song.m4a'
#if len(sys.argv)>1:
# file = sys.argv[1]
#
window = pyglet.window.Window()
#music = pyglet.resource.media(file)
player = pyglet.media.Player()
#player.queue(music)
#player.play()
paused = False
def help():
print("""\nCommands:
\tEsc or x \t Exit program
\tp \t Pause/unpause music
\th \t See this list again""")
print("""Welcome to this music player!
You can give a file as an argument or use the commands below.""")
help()
#window.event
def on_key_press(symbol, modifiers):
global paused
global player
global window
file = 'song.m4a'
if symbol == key.P:
if paused:
print("Resume")
player.play()
paused = False
else:
print("Pause")
player.pause()
paused = True
elif symbol == key.R:
pass
#Doesn't work :P
#player.seek(0)
elif symbol == key.H:
help()
elif symbol == key.ESCAPE:
try:
os.remove("song.m4a")
except:
pass
window.close()
elif symbol == key.Q:
srch = input("Queue Song: ")
download_file(srch)
music = pyglet.media.load(file)
player.queue(music)
#music.play()
player.next_source()
print(f'Added to queue.')
elif symbol == key.S:
srch = input("Play Song: ")
download_file(srch)
music = pyglet.media.load(file)
#music.play()
player.queue(music)
player.next_source()
pyglet.app.run()
pyglet.app.exit()
So I have been running into some issues with the Pyglet lib. I can't seem to get this part to work. Am I calling player.next_source() correctly? Because I just ran symbol == key.S put in a value for srch and nothing played. My goal is to make it so the input I enter when I press q will queue the song and automatically play after the current one, while pressing s and inputting a value there will overwrite the queue and prioritize that. Some help would be appreciated.
First, you are using player.next_source(). That forces the player to go to the next in queue. So your Q key function is incorrect, you aren't queuing it, you are queuing it and then forcing it to play. Which is what you should be doing with your S key. I think you have those confused or are mixed up on what the next_source actually does.
Secondly, when loading music, it's streamed from the file itself. It looks like you are overwriting the file each time you press a key (which isn't really ideal, and prone to errors doing it this way). So this will not work with streaming.
Your best bet is to load the file in it's entirety so that if the file gets overwritten, it's not going to cause weird issues with queued buffers. Try changing your
music = pyglet.media.load(file)
to
music = pyglet.media.load(file, streaming=False)
You also may have better luck downloading the file to a separate file or a temp file (see tempfile library) instead of trying to re-use the same filename.
Also you most likely have to store music into a global dictionary or global list to prevent it from being garbage collected. As music is a local variable to the function and will disappear as soon as the event is over. (This also means you will have to delete them from the list when you no longer need them to prevent a memory leak)

python - can't get audio player working

everything works except the next song doesn't play after the first is finished.
import os, random
from pygame import mixer
from pynput import keyboard
startup = 0
pause = 0
volume = 0.5
def Picker():
global startup
global volume
startup += 1
if startup > 1:
ThisSong = random.choice(os.listdir("C:\\Users\\...\\Music"))
NextSong = random.choice(os.listdir("C:\\Users\\...\\Music"))
ThisSong = NextSong
if ThisSong != NextSong:
mixer.init()
mixer.music.load("C:\\Users\\...\\Music" + ThisSong)
mixer.music.play(0)
mixer.music.set_volume(volume)
while mixer.music.get_busy():
def on_press(key):
global pause
global volume
if key == keyboard.KeyCode(char='-'):
volume -= 0.1
if volume < 0.1:
volume = 0.1
mixer.music.set_volume(volume)
if key == keyboard.KeyCode(char='='):
volume += 0.1
if volume > 1:
volume = 1
mixer.music.set_volume(volume)
if key == keyboard.KeyCode(char='['):
pause += 1
if pause == 1:
mixer.music.pause()
pause = 2
if pause == 3:
mixer.music.unpause()
pause = 0
with keyboard.Listener(on_press=on_press) as listener: listener.join()
else:
Picker()
else:
pass
Picker()
Picker()
screenshot of code
I can't get it to work, i'm very new to python so i'm probably missing something
obvious
Before starting: Thx #JGreenwell for copying the code.
Ok, so first, I’ll help you clean your code.
Things that are wrong
Having all that ThisSong and NextSong things: It won’t get saved when you restart Picker(). Either just have ThisSong, or place the ThisSong and NextSong assignment with the volume and pause variables:
.
pause = 0 # this is a problem! Next point
volume = 0.5
ThisSong = random.choice(...)
NextSong = random.choice(...)
The pause variable should be a boolean (True/False) and the pausing code should be like this:
.
pause = not pause
if pause:
# pause
else:
# unpause
Also, it would ideally be called paused
on_press and the Listener declaration should be outside the while loop, because otherwise they just keep being declared every time it loops. Then you should import time and put time.sleep(500) or something like that in the while loop, so that it doesn’t check every fraction of second.
As for the next song not playing, I don’t really know, but I’d suggest entering Picker() in the shell after the program has run (python -i script.py from CMD, IDLE leaves you in a shell by default). I would also suggest really following recommendation #3 as having them in the loop may break the loop and stop it from finishing. Most importantly, I would ask you to debug your code by adding print in every step of your code:
print(1)
if something:
print(2)
etc...
And seeing where it blocks
P.S.: The random dots are because you can’t have code in lists, so I had to exit the list.
Working solution, incase anyone else has the same problem as me in the future :)
from pygame import mixer
from pynput import keyboard
import threading
import random
import os
paused = 0
def player():
song = random.choice(os.listdir("C:\\users\\...\\desktop\\music"))
mixer.init()
mixer.music.load("C:\\users\\...\\desktop\\music\\" + song)
mixer.music.play(0)
while mixer.music.get_busy():
pass
else:
player()
def main():
t = threading.Thread(target = player, name = 'thread1', args = ())
t.start()
main()
def on_press(key):
global paused
if key == keyboard.KeyCode(char='['):
paused = not paused
if paused:
mixer.music.pause()
else:
mixer.music.unpause()
with keyboard.Listener(on_press=on_press) as listener: listener.join()

How to create parallel tasks in Python

I am creating a battle game for telegram groups as my beginner's project, one player challenges the other, and then a battle happens in the chat. Now I want to implement a "timeout" function for it.
I've managed to make it work using _thread:
import _thread
class Battle:
def __init__(self, chat_id):
self.chat = chat
print('Instance of battle started on chat %s' % self.chat)
self.pcount = 0
self.p1score = 0
self.p2score = 0
def reset(self):
self.pcount = 0
self.p1score = 0
self.p2score = 0
def timeout(chat_id):
time.sleep(90)
battle = battledic[chat_id]
if battledic[chat_id].timer == 1:
sendmessage(chat_id, 'no one accepted') # first parameter is the chat address and the second one is the message
battle.reset()
else:
return
battle = Battle(chat_id)
if message == "challenge":
if battle.pcount == 0:
_thread.start_new_thread(timeout, (chat_id,))
...
So basically what I'm doing is calling the timeout function, that is going to sleep in a different thread for 90 seconds, and then it checks if the battle still with only one player (pcount == 1), if it is the case, it sends the chat a message telling them the fight did not start, and then resets the battle instance.
The problem here is that I'm raising exceptions very often, even though it works fine most of the time, also sometimes it doesn't have the desired effect, closing ongoing battles. Researching I've found out that this is not the desired way of doing this butI could not find better solution, and maybe I want to run other battles from other chats in parallel too. What should be used in this case? cooperative multitasking? multiprocessing? the trheading module? thanks for any tips on this task!
I've got you started. I'm not sure how you are going to chat or keep record of who's playing.
My first thoughts were to run threads, but the while loop is easier.
# from threading import Thread
from time import sleep
no_one = True
battledic = {} # your dict here. It may be better to use a list of objects (classes) for your opponents
class Battle(object):
def __init__(self, chat_id):
self.chat_id = chat_id
self.pcount = 0
self.p1score = 0
self.p2score = 0
print('Instance of battle started on chat %s' % self.chat_id)
def reset(self):
self.pcount = 0
self.p1score = 0
self.p2score = 0
def wheres_waldo():
x = 0; no_one = True
while x < 90: # waits 90 seconds unless found
for player in battledic.keys():
if player.is_requesting: # found someone
no_one = False
break # quit looking
else:
print('No one yet')
sleep(1); x+= 1
if no_one:
sendmessage(chat_id, 'no one accepted') # first parameter is the chat address and the second one is the message
battle.reset()
else:
sendmessage(chat_id, 'Found someone')
def sendmessage(chat_id,message):
print('%s on chat %s' % (message,chat_id))
battle = Battle(chat_id)
while True:
message = raw_input('Command? ')
if message == "challenge":
if battle.pcount == 0:
wheres_waldo()
# first thoughts were to run threads but the while loop is easier
# wait = Thread(target=timeout, args=(chat_id))
# where = Thread(target=wheres_waldo)
# wait.start();where.start()
# wait.join();where.join()

Python 2 game coordinates class

I wanted to create a x-y coordinate system even though this is supposed to be a text RPG as to keep track of where everything is. So, I was experimenting on making a function and test for that function that would let the character move on a x-y grid, however, no matter what I try, I cannot make it work. Here is the code:
class Player:
def movement(charactor_movement):
proceed = 0
if charactor_movement == "left":
character.position_x = character.position_x - 1
proceed = 1
elif charactor_movement == "right":
character.position_x = character.position_x + 1
proceed = 1
elif charactor_movement == "forward":
character.position_y = character.position_y + 1
proceed = 1
elif charactor_movement == "backward" or charactor_movement == "back":
character.position_y = character.position_y - 1
proceed = 1
charactor = Player()
charactor.position_x = 0
charactor.position_y = 0
proceed = 0
while proceed == 0:
print "You are at",
print charactor.position_x,
print"x and",
print charactor.position_y,
print"y."
global charactor_movement
charactor_movement = raw_input("Where are you going?")
charactor.movement()
At this point, it does what it is supposed to do up to changing the coordinates, as it prints "You are at 0 x and 0 y" and "Where are you going?" no matter what I type. I have tried adding an else to the function which it defaulted to no matter what I typed and gave me "Sorry, I cannot understand you." Any comments on fixing or generally improving the code would be appreciated.(Note: For the testing I purposely did not add a way to exit. The class is what i need fixed.)
You are getting the same coordinates with each iteration because your values within your while loop are not changing. Incrementing character.position_x within movement will never change the value of character.position_x within your while loop, as it is outside your function's scope. You have to use the global keyword within your movement function for each variable you are changing should you want your current logic to remain the same. Additionally, why not just pass charactor_movement as a parameter to your movement function, as opposed to using global as you currently are doing.
A minimal example:
Consider the following:
def somefunct(x):
mycode = x
mycode = 'no codez'
while True:
print mycode
codez = raw_input('gimme teh codez: ')
somefunct(codez)
which outputs
>>>[evaluate untitled-1.py]
no codez
gimme teh codez: codez!
no codez
Declaring mycode as global in the function places it in the scope of the while loop when assigned, thus
def somefunct(x):
global mycode #make variable global here
mycode = x
mycode = 'no codez'
while True:
print mycode
codez = raw_input('gimme teh codez: ')
somefunct(codez)
results in the output
>>>[evaluate untitled-1.py]
no codez
gimme teh codez: codez!
codez!

How do I have numbers increment slowly over a course of time throughout runtime

I am trying to make a text based game in which the user is a pilot in space. I want to create a movement system but am unsure how to do it. I want the user to be able to put in the desired grid coordinates, and his vehicle will begin to change its grid coords to get closer and closer to the ones he inputted.
Now, to do this I will probably need multithreading and a time element. But I am unsure how I can use a time element. Any advice is greatly appreciate, i'm just trying to learn here. Thanks guys!
from Gundam2 import Mobilesuits
#Main Variable/Object declarations:
Leo1=Mobilesuits(100,100,"Leo","leo desc","dockpit desc",100,[100,100,100])
Leo2=Mobilesuits(100,100,"Leo","leo desc","dockpit desc",100,[300,100,100])
Leo3=Mobilesuits(100,100,"Leo","leo desc","dockpit desc",100,[100,150,100])
currentmobilesuit=Leo1
#Main Function declarations
def commands(user_input,currentmobilesuit):
if user_input == "radar":
currentmobilesuit.radar()
elif user_input == "commands":
print("Command list:\nradar")
else:
print("Invalid command\nType 'commands' for a list of valid commands")
#Main execution
while True:
commands(raw_input(),currentmobilesuit)
class Mobilesuits:
#class global variables/methods here
instances = [] #grid cords here
def __init__(self,armor,speed,name,description,cockpit_description,\
radar_range, coordinates):
Mobilesuits.instances.append(self)
self.armor=armor
self.speed=speed
self.name=name
self.description=description
self.cockpit_description=cockpit_description
self.radar_range=radar_range
self.coordinates=coordinates
def can_detect(self, other):
for own_coord, other_coord in zip(self.coordinates, other.coordinates):
if abs(own_coord - other_coord) > self.radar_range:
return False
return True
def radar(self):
for other in Mobilesuits.instances:
if other is not self and self.can_detect(other):
print "%s detected at %s" % (other.description, other.coordinates)
Games typically have a "master loop" of some kind; yours does here:
#Main execution
while True:
commands(raw_input(),currentmobilesuit)
The simplest thing to do is to count in the loop:
#Main execution
turn_count = 0
while True:
commands(raw_input(),currentmobilesuit)
turn_count += 1
If you wanted the real time taken to have some impact on the counter, or be the counter, you can get the current time from the time module calling time.time().
#Main execution
import time
time_start = time.time()
time_elapsed = 0
while True:
commands(raw_input(),currentmobilesuit)
time_elapsed = time.time() - time_start
A couple other thoughts:
Make a Game class, and put the turn counter and game loop in that.
Have the commands function return a number that is the number of time units that took place during the command; for example, entering an invalid command might take 0 turns, while repairing a robot might take 5.
#Main execution
turn_count = 0
while True:
turns_taken = commands(raw_input(),currentmobilesuit)
turn_count += turns_taken
You can use non-blocking I/O. This will help you avoid the complications of threading. Here's your sample code implemented with a non-blocking read of stdin:
#!/usr/bin/python
import sys
import select
call_count = 0
#Main Function declarations
def commands(user_input):
global call_count
if len(user_input) > 0:
print('call count: ' + str(call_count) + ' user entered: ' + user_input)
def raw_input_no_block():
global call_count
call_count = call_count + 1
input_avail = select.select([sys.stdin], [], [], 0.1)[0] #wait for 0.1 seconds
if input_avail:
return sys.stdin.readline()
else:
return ''
#Main execution
while True:
commands(raw_input_no_block())

Categories

Resources