Performing tasks after a certain amount of time in python - python

I am currently writing an audio playout system using python3 and afplay. I have run into a problem where I cannot get it to automatically go to the next song in the playlist, whilst still allowing for user control.
for i in range(metadata["length"] - start):
i += start
curr = songs[i]
play(directory + curr)
paused_bool = False
while True:
clear()
print("Currently playing track #" + str(i+1) + ", called '" + curr + "', from playlist '" + metadata["name"] + "'.")
command = input("What would you like to do? 'play', 'stop/exit', 'pause', or 'skip/next': ")
if command == "play" or command == "pl":
if not paused_bool:
print("It is already playing.")
else:
play(directory + curr)
elif command == "stop" or command == "st" or command == "quit" or command == "q" or command == "exit":
stop()
return
elif command == "pause" or command == "p" or command == "pa":
if paused_bool:
print("The audio shall remain paused.")
paused_bool = True
stop()
elif command == "move the hell on" or command == "skip" or command == "next" or command == "n" or command == "s" or command == "":
stop()
break
clear()
Above is the code that plays each song inside a folder. I want to be able to manually go to the next song using the "next" command, but also have it automatically go to the next after some time has passed, specifically the length of the song.
I have tried using the threading package, but I then run into a problem when I can't break the while loop from a function:
for i in range(metadata["length"] - start):
i += start
curr = songs[i]
play(directory + curr)
paused_bool = False
while True:
clear()
def auto():
time.sleep( length( curr_song ) )
stop() # stop() & break goes to the next song
break
thread = Thread(target = auto, args = [])
thread.start()
print("Currently playing track #" + str(i+1) + ", called '" + curr + "', from playlist '" + metadata["name"] + "'.")
command = input("What would you like to do? 'play', 'stop/exit', 'pause', or 'skip/next': ")
if command == "play" or command == "pl":
if not paused_bool:
print("It is already playing.")
else:
play(directory + curr)
elif command == "stop" or command == "st" or command == "quit" or command == "q" or command == "exit":
stop()
return
elif command == "pause" or command == "p" or command == "pa":
if paused_bool:
print("The audio shall remain paused.")
paused_bool = True
stop()
elif command == "move the hell on" or command == "skip" or command == "next" or command == "n" or command == "s" or command == "":
stop()
break
clear()
I apologise that the question is incredibly convuluted, but any help would be greatly appreciated.

You may want to use scheduler.
It would change somewhat the logic of your code, but it would likely be more functional.
You would, by default, schedule an event for playing the next song (use enter or enterabs). You could get the running time of the current song so you know when to schedule the next one.
If "next" is selected, you would cancel the previous event and schedule two others: 1) a new one for immediate execution, play the next song, 2) play the following song.

Related

Returning a String Endless Times

command = input("> ")
started = False
while True:
if command == "start":
if started:
print("car has already started")
else:
started = True
print("car started")
elif command == "stop":
if not started:
print("car has already stopped")
else:
started = False
print("car is stopped")
elif command == "quit":
break
it renders "car has already started" infinite time when i state "start" and likewise with other inputs. how do i inhibit this? would be very helpful if you explain it in beginner terms, thank you!!
You need to put the command inside the while loop, so that the loop stops until the user has enterd his desired command:
started = False
while True:
command = input("> ")
if command == "start":
...
Male sure to add the input to thie while loop. In your case, it is just taking one input and then the value of command is fixed. So, it is an infinite loop.
But when you change the location and keep it inside the loop, every time it asks for the input, the value of command will change.
Also, .lower() will convert the input to lower case to remove case insensitivity
started = False
while True:
command=input('>> ').lower()
if command == "start":
if started:
print("car has already started")
else:
started = True
print("car started")
elif command == "stop":
if not started:
print("car has already stopped")
else:
started = False
print("car is stopped")
elif command == "quit":
break
You need input a new command every while loop, otherwise it will fall into infinite loop with the same first inputed command condiction.
code:
started = False
while True:
command = input("> ")
if command == "start":
if started:
print("car has already started")
else:
started = True
print("car started")
elif command == "stop":
if not started:
print("car has already stopped")
else:
started = False
print("car is stopped")
elif command == "quit":
break
result:
> start
car started
> stop
car is stopped
> stop
car has already stopped
> start
car started
> start
car has already started
> quit
What is happening in your loop is:
while True - A while loop will always run until this statement is False, as True is never False, this will loop forever until the break command is ran.
if command == "start" - Because command is set outside of the while loop, it is always equal to start and this if statement will always run
The first run, it will print "car started" and every other loop after will print "car has already started".
To stop this, you need to change the value of command to change the behaviour each time you loop. To do this, you can just get an input at the start of the loop:
started = False
while True:
command = input("> ") # Input inside the loop
if command == "start":
if started:
print("car has already started")
else:
started = True
print("car started")
elif command == "stop":
if not started:
print("car has already stopped")
else:
started = False
print("car is stopped")
elif command == "quit":
break
You are never changing the value for command and the loop is running "while True", so it will never stop.
When the user gives the input, lets say "start", your script is assigning the string "start" to the command.
It is looping infinitely because the first if statement is checking the value for the variable command, which after the input will always be "start".
You can solve it in multiple ways.
One is to change the name of the variable after the if statement:
command = input("> ")
started = False
while True:
if command == "start":
if started:
print("car has already started")
command = ""
Another way is to add break commands after each if comparison.
You can also change while True to while not started. The loop will continue until you change the variable started to True, which you are already doing after each conditional block.

Python: After making a work-around for an EOFError, code doesn't ask for input in second run of a while loop

Little context: I'm working with a Zumi (physical toy car that can be programmed with Python)
However, this issue pertains to threading, looping, and inputs. All my threading works fine. However, a specific while loop acts weirdly after the first iteration.
from zumi.zumi import Zumi
import threading, time, sys
zumi = Zumi()
def commands(star):
global comm
print("thread 1 started")
time.sleep(1)
while star == "t":
try:
inp = str(input("Command: ")) <---
if inp.lower() == "d":
comm = "d"
elif inp.lower() == "r":
comm = "r"
elif inp.lower() == "p":
comm = "p"
elif inp.lower() == "b":
print("Killing Zumi")
zumi.stop()
sys.exit()
else:
print("I don't understand.")
except EOFError as e:
print(end = "")
inp = ""
def constacheck(star):
global comm
print("thread 2 started")
while star == "t":
if comm == "d":
forwards()
elif comm == "r":
backwards()
elif comm == "p":
parallel()
star = "t"
inp = ""
comm = ""
print("create and start thread 1")
x = threading.Thread(target=commands, args=(star))
x.start()
time.sleep(1)
print("create and start thread 2")
z = threading.Thread(target=constacheck, args=(star))
z.start()
I put an arrow which points to the area of interest. The first time I run the code, I am asked for the input, and it does everything correctly. However, immediately afterwards, it should ask for the same input, and change the variables according (which will them affect my second loop- constacheck). Instead it returns an EOFError: Error when reading a line, which I solved by making the code repeat the loop, ignoring the error. However, every loop afterwards raises the same error, and thus creates a constant loop of printing "Command: "
It looks something like this:
Command: d
Driving forwards.
Command: Command: Command: Command: Command: Command: Driving forward.Command: Command: Command: Command: Command: Command: Command: Driving forward.
etc.
How do I stop the EOFError, and every time the while loop in commands() loops, ask for an input?
Thanks.

How to play next song in the playlist automatically if no key is pressed by the user?

I want to give the option to the user to play, pause, play next or previous song. If the user does not press anything, the next song in the list should be played automatically. Currently the program halts for the input from the user.
I have tried playing the song in another thread and wait for the user to enter the choice in main thread. But the problem is that main thread waits for the input from the user before playing the next song.
def music(file):
pygame.mixer.music.load(file)
pygame.mixer.music.play()
try:
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(0)
except:
pass
def pause():
print(file + " paused")
pygame.mixer.music.pause()
global set
set = 1
def playsong():
global set
global t1
print ("Playing song named : " + file)
if file != "" and set==0:
t1 = threading.Thread(target = music ,args=(file,))
t1.setName("audio bajao")
t1.start()
ch = 1
song_list = ["song1.mp3","song2.mp3"]
while((i < len(song_list)) and (ch != 5) ):
ch = input("Enter choice ")
file = song_list[i]
if(ch == 1):
playsong()
elif(ch == 2):
ch = 1
pause()
elif(ch == 3):
#play previous song
ch = 1
i -= 2
elif (ch == 4):
#play next song
ch = 1
elif (ch == 5):
break
i += 1
I want the program should play the next song from the list when the song ends if no key is pressed by the user. It should not ask the user again to press the play key to play the next song.
You can try pygame.mixer.get_busy()
To implement this, add some kind of timer variable, I personally prefer tick counters, So...
inactive_ticks = 0
After that, in your main event loop, use pygame.mixer.get_busy() to check if there is music being played or not, then...
if not pygame.mixer.get_busy():
inactive_ticks += 1
if inactive_ticks == 100:
# Play the next song
inactive_ticks = 0
That should work.

Python not reading string a second time

I'm writing a text adventure (does anyone remember Zork?), and I'm having troubles with this code:
from random import randint
def prompt():
action = input(">>> ").lower()
if action == "exit":
quit()
elif action == "save":
save()
else:
return action
def action_error(custom=False):
if custom != False:
print(custom)
else:
phrases = ["A bunch", "of funny", "error phrases"]
print(phrases[randint(1, len(phrases)-1)])
return prompt()
action = prompt()
while True:
print(action) #Debugging purposes
if action.find("switch") != -1:
if action.find("light") != -1:
second_room() #Story continues
else:
action = action_error("What do you want to switch?")
action = action_error()
The matter is that if I enter a string that contains "switch", the next input is not picked up.
Also, anyone has better ways to parse verb-noun strings like "switch the light", "open the door" or "look around"/"look at OBJECT"?
First of all I noticed that if you enter switch twice the second time it's caught as an error by your program.
I think the problem lies at the end of the action_error function where you assign the return value to prompt(), so the input get consumed too early.
A possible fix would be:
def action_error(custom=False):
if custom != False:
print(custom)
else:
phrases = ["A bunch", "of funny", "error phrases"]
print(phrases[randint(1, len(phrases)-1)])
while True:
action = prompt()
print(action) #Debugging purposes
if action.find("switch") != -1:
if action.find("light") != -1:
second_room() #Story continues
else:
action_error("What do you want to switch?")
else:
action_error()
So no return value for action_error() and direct assignment at the beginning of the while loop.
How about, in the case of a partially entered compound action, you concatenate the new input to the old one? Then "switch" becomes "switch light", and both of your conditionals will pass.
action = prompt()
while True:
print(action) #Debugging purposes
if action.find("switch") != -1:
if action.find("light") != -1:
second_room() #Story continues
else:
action = action + " " + action_error("What do you want to switch?")
continue
action = action_error()
Bonus style suggestions:
replace a.find("b") != -1 with "b" in a
use random.choice(phrases) instead of phrases[randint(1, len(phrases)-1)]

Parallel While loops

I am writing a Python program, and I want to run two while loops at the same time. I am pretty new to Python, so this could be a elementary mistake/misconception. The project is having a Raspberry Pi monitor a sump pump to make sure that it is working, and if not, send an email to the specified recipients. One loop is going to interact with the user, and respond to commands send to it in real time through SSH.
while running is True:
user_input = raw_input("What would you like to do? \n").lower()
if user_input == "tell me a story":
story()
elif user_input == "what is your name":
print "Lancelot"
elif user_input == "what is your quest":
print "To seek the Holy Grail"
elif user_input == "what is your favorite color":
print "Blue"
elif user_input == "status":
if floatSwitch == True:
print "The switch is up"
else:
print "The switch is down"
elif user_input == "history":
print log.readline(-2)
print log.readline(-1) + "\n"
elif user_input == "exit" or "stop":
break
else:
print "I do not recognize that command. Please try agian."
print "Have a nice day!"
The other loop will be monitoring all of the hardware and to send the email if something goes wrong.
if floatSwitch is True:
#Write the time and what happened to the file
log.write(str(now) + "Float switch turned on")
timeLastOn = now
#Wait until switch is turned off
while floatSwitch:
startTime = time.time()
if floatSwitch is False:
log.write(str(now) + "Float switch turned off")
timeLastOff = now
break
#if elapsedTime > 3 min (in the form of 180 seconds)
elif elapsedTime() > 180:
log.write(str(now) + " Sump Pump has been deemed broaken")
sendEmail("The sump pump is now broken.")
break
Both of these functions are critical, and I want them to run in parallel, so how do I get them to run like that? Thanks for everyone's help!
Stuff running in parallel? Try using threading - see for example this module in the standard library, or the multiprocessing module.
You will need to create a thread for each of the while loops.
This post has some good examples of how to use threads.
On some other notes, I can't help noticing you use if variable is True: instead of if variable: and if variable is False: instead of if not variable:, the alternatives given being more normal and Pythonic.
When you do elif user_input == "exit" or "stop": this will always be true because it is actually testing if (use_input == "exit") or ("stop"). "stop" is a non-empty string, so it will always evaluate to True in this context. What you actually want is elif user_input == "exit" or user_input == "stop": or even elif user_input in ("exit", "stop"):.
Finally when you do log.write(str(now) + "Float switch turned off") it would probably better to do string formatting. You could do log.write("{}Float switch turned off".format(now)), or use % (I don't know how to do that since I only used Python 2.x for a few weeks before moving to 3.x, where % has been deprecated).

Categories

Resources