threading in pygame - python

I've written a program in python and pygame on Linux. My issue is that when I call my function that dials out on a modem and then delivers a message the program pauses until the function has finished. I need a way to subprocess or thread the function. I'm new to python and pygame so this may be simple but everything I've tried has failed.
Also, I'm sure there's probably a more elegant way to process the sounds and pauses. The first sleep gives the modem time to call. The next two are for natural sounding pauses between words and sentences, and the last is to give the entire script time to deliver the message before the modem hangs up.
I'm calling the function and passing variables like this:
A = 'Electronic_Chime.mp3'
B = 'please_check.mp3'
C = 'three.mp3'
contact_user(A,B,C)
And this is the function:
def contact_user( A, B, C ):
ser.write("ATDT441\r") # Attention - Dial via tone number
time.sleep(6)
pygame.mixer.music.load(A)
pygame.mixer.music.play()
time.sleep(2)
pygame.mixer.music.load(B)
pygame.mixer.music.play(1)
time.sleep(2)
pygame.mixer.music.load(C)
pygame.mixer.music.queue(C)
pygame.mixer.music.play()
time.sleep(10)
Any ideas or suggestions would be appreciated.
PS. I have tried:
thread1 = threading.Thread(contact_user(A,B,C))
thread1.start()
The program seems to behave exactly the same even though I have threaded contact_user(A,B,C).

My main issue was that I wasn't passing my arguments to my thread properly. Following is the code that worked properly for me.
My function:
def contact_user(A,B,C):
ser.write("ATDT411\r") # Attention - Dial Tone 411
time.sleep(5) # Wait for modem to finish dialing
pygame.mixer.music.load(A)
pygame.mixer.music.play() # Play attention chime
time.sleep(2) # Wait long enough for first message chime to play before moving on
pygame.mixer.music.load(B)
pygame.mixer.music.play(1) # Play next message
time.sleep(2) # Wait long enough for second message to play before moving on
pygame.mixer.music.load(C) # Load last message
pygame.mixer.music.queue(C) # Queue second instance of last message
pygame.mixer.music.play() # Play last message twice
time.sleep(10) # Wait long enough for last message to play
ser.write("ATH\r") # Attention - Hang up line
ser.write("ATZ\r") # Attention - Reset modem
ser.close()
Calling function from main program:
t = threading.Thread(name = 'dial', target = contact_user, args = (A,B,C))
t.start()
Thank you to everyone that helped me with this. Very appreciated.

Related

Python - Checking if there is an input from user without stopping the loop

I have a small script where I have a continuous loop. The loop runs and I expect that the user will give an input whenever he wants. I want to check if the user wrote something in the console, if yes, I will do something extra, if not, the loop continues.
The problem is that when I call the input() function, the program waits for user input.
The code that I will use here will be just a simple version of my code to show better what is my problem.
i=0
while True:
i+=1
if 'user wrote a number':
i+= 'user's input'
The objective is to not stop the loop if the user do not input anything. I believe this is a simple thing but I didn't find an answer for this problem.
Thank you for your time!
You can execute the background task (the while True) on a separate thread, and let the main thread handle the input.
import time
import threading
import sys
def background():
while True:
time.sleep(3)
print('background task')
def handling_input(inp):
print('Got {}'.format(inp))
t = threading.Thread(target=background)
t.daemon = True
t.start()
while True:
inp = input()
handling_input(inp)
if inp == 'q':
print('quitting')
sys.exit()
Sample execution (lines starting with >> are my inputs):
background task
>> a
Got a
>> b
Got b
background task
>> cc
Got cc
background task
background task
>> q
Got q
quitting
Process finished with exit code 0
The only caveat is if the user takes longer than 3 seconds to type (or whatever the value of time.sleep in background is) the input value will be truncated.
I'm don't think you can do that in one single process input(), because Python is a synchronous programming languaje,the execution will be stoped until input() receives a value.
As a final solution I'd recommend you to try implement your functions with parallel processing so that both 'functions' (input and loop) can get executed at the same time, then when the input function gets it's results, it sends the result to the other process (the loop) and finish the execution.

python urwid timeout on idle

Is there a way to have a urwid app to do a sys.exit() after a configurable timeout if no input has been received from the user in more than 30 seconds?
We are facing network outages and that leads to the SSH Session being dropped but the client program keeps running and holds a Database lock and manually killing is the only option for now and hence this requirement.
You can set an alarm in the main loop that will call whatever code you want when it times out. Here I call a function that uses the ExitMainLoop exception, but sys.exit() would also work.
This code cancels the previous alarm (if any) when keyboard input happens, then sets a new alarm. As long as keys are coming in, the alarm should never go off.
Internally, as of late 2020, for alarms urwid seems to use Python's time.time(), which is not guaranteed to only go forward one-second-per-second. The alarm might go off early, exiting the program, if the system clock gets adjusted forward (by NTP?).
import urwid
timeout_time=30
def urwid_exit(loop, user_data):
raise urwid.ExitMainLoop
def handle_input(input):
global txt
global loop
#don't reset the alarm on a mouse click,
# and don't try to display it (the .set_text would error if given a tuple)
if not type(input) is tuple:
if hasattr(handle_input, "last_alarm") and handle_input.last_alarm:
loop.remove_alarm(handle_input.last_alarm)
handle_input.last_alarm = loop.set_alarm_in(timeout_time, urwid_exit)
txt.set_text("key pressed: %s" % input)
txt = urwid.Text("Hello world")
fill = urwid.Filler(txt, 'top')
loop = urwid.MainLoop(fill, unhandled_input=handle_input)
#call handle input once without any input to get the alarm started
handle_input(None)
loop.run()
A slight variation on StephenK's answer is to use loop.event_loop.enter_idle(callback) instead of unhandled_input. The callback function will be run whenever urwid enters an idle state, including after processing a keypress event. This is somewhat more general: the timer starts after all activity has finished. (Say, the last keypress starts an action that takes many seconds to finish)
The relevant documentation is at
https://urwid.readthedocs.io/en/latest/reference/main_loop.html
import urwid
timeout = 10
txt = urwid.Text(
'This program will exit after '
f'_{timeout}_ seconds of inactivity '
'(no keypresses, etc)\n',
align='center'
)
fill = urwid.Filler(txt, 'top')
loop = urwid.MainLoop(fill)
alarm_handle = None
def alarm_callback(_loop, _user_data):
raise urwid.ExitMainLoop
def idle_callback():
global alarm_handle
loop.remove_alarm(alarm_handle) # remove old alarm
alarm_handle = loop.set_alarm_in(timeout, alarm_callback)
text = txt.get_text()[0] + f"\nAlarm has been reset _{alarm_handle[1]}_ times"
txt.set_text(text)
loop.event_loop.enter_idle(idle_callback)
loop.run()

How can I play a sound while other lines of code execute simultaneously?

I want my code to do this, but with music playing in the background:
import time
while True:
print ('ligma')
time.sleep(1.5)
I tried this:
import time
import winsound
while True:
print ('ligma')
time.sleep(1.5)
winsound.PlaySound("dank", winsound.SND_ALIAS)
but, it repeats the sound then repeats the word. I am expecting it to repeat the word and play the sound at the same time.
You need to play the sound on another thread, so your other code can be executing at the same time.
import time
import winsound
from threading import Thread
def play_sound():
winsound.PlaySound("dank", winsound.SND_ALIAS)
while True:
thread = Thread(target=play_sound)
thread.start()
print ('ligma')
time.sleep(1.5)
EDIT:
I have moved the thread declaration into the loop. My initial answer had it created outside of the loop, which caused a RuntimeError. Learn more here: https://docs.python.org/3/library/threading.html#threading.Thread.start
It's called asynchronous sound, and the winsound.SND_ASYNC flag on PlaySound will let you play a sound while your code continues to execute:
winsound.PlaySound("dank", winsound.SND_ALIAS|winsound.SND_ASYNC)
From memory, this will give you a single sound channel i.e. playing other sounds will cut off any currently playing sounds. If more concurrent playback is required, something like PyGame is required.
There is an optional second argument that is set to True automatically. To play music asynchronously set that argument to False.
playsound('file',False)

How to have a continuous beep or other simple sound until key press event in python

I've written the following simple program in python in order for the user to test/adjust their sound volume. It uses winsound.Beep() to make a repeating beeping noise until it's interrupted by a msvcrt.kbhit() event, and uses threading to allow input while the winsound.Beep() is going on:
import winsound, threading, msvcrt
class soundThread(threading.Thread):
def __init__(self, threadID, pitch, duration):
threading.Thread.__init__(self)
self.threadID = threadID
self.pitch = pitch
self.duration = duration
def run(self):
soundTone(self.pitch,self.duration)
def soundTone(pitch,duration):
winsound.Beep(pitch,int(duration*1000))
def main():
"""main code module"""
print("The computer will play a beeping tone at a frequency of 440 MHz.")
print("It will continue to do so until you press a key which will cause the")
print("sound to stop and the program to exit. While the tone is sounding")
print("adjust your volume so that you can hear the tone clearly and")
print("comfortably.")
print("")
print("Tone starting now...")
loopBool = True
soundBool = True
i = 0;
while loopBool:
i+=1
# make beeping sound
if soundBool:
beep = soundThread(i,440,2.0)
beep.start()
# stop loop if a key is pressed
if msvcrt.kbhit():
loopBool = False
print("Key press detected. Waiting for sound to stop...")
# don't make new beep thread if current one is still active
if beep.isAlive():
soundBool = False
else:
soundBool = True
print("Sound stopped. Exiting program.")
if __name__ == '__main__':
main()
So the code itself works fine. However aesthetically I don't really like having the winsound.Beep() having to start and stop over and over again, and when a key is pressed the program has to wait for the current Beep() thread to end before the entire program can end. However, I can't think of any way to do it using winsound, and I can't find any other built in modules that allow simple generation of sounds like this.
I saw some very similar questions on this site, but all the solutions required non-standard modules like audiolab or PyMedia, but I'd like to do this using standard modules if possible. Is there some way to have a continuous beep until a keyboard interrupt, without having to install any non-standard modules? If there isn't any way with the basic modules, I'm using the Anaconda python distribution, so I at least have access to numpy and scipy, etc., though I kind of doubt those packages would be helpful here.
There is minor correction to your code.
The method name should be in snake_case:
# don't make new beep thread if current one is still active
if beep.is_alive():
not CamelCase:
# don't make new beep thread if current one is still active
if beep.isAlive():

Python Multithreaded Messenger Simulation. Stuck on timerThread update. What do?

I have a piece of code that simulates a system of messengers (think post office or courier service) delivering letters in a multithreaded way. I want to add a way to manage my messengers "in the field" to increase the efficiency of my system.
tl;dr: How do I update my tens-to-hundreds of timerthreads so they wait longer before calling their function?
Here's what the code I've written so far is supposed to do in steps.
Someone asks for a letter
We check to see if there are any available messengers. If none, we say "oops, sorry. can't help you with that"
If at least one is available, we send the messenger to deliver the letter (new timer thread with its wait param as the time it takes to get there and back)
When the messenger gets back, we put him in the back of the line of available messengers to wait for the next delivery
I do this by removing Messenger objects from a double ended queue, and then adding them back in after a timerthread is done waiting. This is because my Messengers are all unique and eventually I want to track how many deliveries each has had, how far they have traveled, and other stuff.
Here's a pseudoish-codesnippet of the larger program I wrote for this
numMessengers=5
messengerDeque=deque()
pOrder=0.0001
class Messenger:
def __init__(self):
for i in range(numMessengers):
messenger=Messenger()
messengerDeque.append(messenger)
def popDeque():
messenger=idleDeque.popleft()
print 'messenger #?, sent'
return messenger
def appendDeque(messenger):
print 'messenger #?, returned'
messengerDeque.append(messenger)
def randomDelivery():
if numpy.random.randint(0,10000)<=(pOrder*10000):
if len(messengerDeque)!=0:
messenger=popDeque()
tripTime=distance/speed*120
t=threading.Timer(tripTime,appendDeque,args=[messenger])
t.start()
else:
print "oops, sorry. can't help you with that"
The above works in my program.
What I would like to add is some way to 'reroute' my messengers with new orders.
Lets say you have to deliver a letter within an hour of when you get it. You have five messengers and five orders, so they're all busy. You then get a sixth order.
Messenger 2 will be back in 20 minutes, and order six will take 30 minutes to get to the delivery destination. So instead of saying "oops, we can't help you". We would say, ok, Messenger 2, when you get back, immediately go deliver letter six.
With the code I've written, I think this could be done by checking the active threads to see how long until they call their functions, pick the first one you see where that time + how long your new delivery takes is < 1 hr, cancel it, and start a new thread with the time left plus the new time to wait.
I just don't know how to do that.
How do you check how long is left in a timerthread and update it without making a huge mess of your threads?
I'm also open to other, smarter ways of doing what I described.
YAY PYTHON MULTITHREADING!!!!!
Thanks for the help
Using the class threading.Timer wont fulfill your needs. Although there is a "interval" member in Timer instances, once the Timer(thread) started running any changes in interval (time-out) are not considered.
Furthermore you need to know how much time is still left for the timer to be triggered, for which there isn't a method as far as I know.
Furthermore you probably also need a way to identify which Timer instance you need to update with the new timeout value, but this is up-to you.
You should implement your own Timer class, perhaps something along the lines of:
import threading
import time
class MyTimer(threading.Thread):
def __init__(self, timeout, event):
super(MyTimer, self).__init__()
self.to = timeout
self.evt = event
def setTimeout(self, v):
self.end = time.time() + v
def run(self):
self.start = time.time()
self.end = time.time() + self.to
while self.end > time.time():
time.sleep(0) # instead of thread.yield
self.evt()
def getRemaining(self):
return self.end - time.time()
def hi(): print "hi"
T=MyTimer(20,hi)
T.start()
for i in range(10):
time.sleep(1)
# isAlive gives you True if the thread is running
print T.getRemaining(), T.isAlive()
T.setTimeout(1)
for i in range(3):
time.sleep(1)
print T.getRemaining(), T.isAlive()

Categories

Resources