I have been doing some searching in this subject and there doesn't seem to be a very clear answer to my question. I currently am working a project where the user clicks on a button and once he does I call a python function, reply()
, the function starts two different threads, a
(stands for audio) and r
(stands for routines) those two threads are supposed to work together and basically point to a direction in the map based as the narrator speaks:
def reply(index, path2, path3):
a = threading.Thread(target=playaudio(path3))
r = threading.Thread(target=routine(path2))
r.start()
a.start()
I am wondering if there is a way to access the function and stop those threads for both the playaudio
and routine
functions on user click of a stop button, so that if the user no longer wants to see it, all he needs to do is stop the presentation. The two functions are set up as follows:
# play audio function starts here:
def playaudio(path):
try:
subprocess.Popen(["mpg123", path])
except Exception as ex:
tkMessageBox.showinfo('Error', 'An error occurred. ' + str(ex))
# routine function starts here
# routine controls the servo module
def routine(path):
with open(path, 'rb') as f:
reader = csv.reader(f)
settings = list(reader)
print(settings)
i = 1
while i < (len(settings) - 1):
try:
setall(int(settings[i][1]), int(settings[i][2]), int(settings[i][3]), int(settings[i][4]))
delay = float(settings[i+1][0]) - float(settings[i][0]) - 0.015 #includes processing time
time.sleep(delay)
i += 1
except Exception as ex:
pass
setall(int(settings[i][1]), int(settings[i][2]), int(settings[i][3]), int(settings[i][4]))
These will be initiated with a Tkinter.Button
element on the main screen, and will play both the audio and control the servo module.
button = Tkinter.Button(window, text='Audio 4', command=lambda: reply(1, 'path/to/excel/file.csv', '/path/to/audio/file.mp3'))
button.config(width="30", height="5")
button.place(x=490, y=40)
For the stopping function I thought it would be a solution to add another Tkinter
button element but with a different function to quit the subprocess
whenever the user clicks on it.
stop_button = Tkinter.Button(window, text='Stop Audio', command=lambda: stop())
stop_button.config(width="30", height="5")
stop_button.place(x=490, y=360)
For the actual stop()
function I attempted a few methods such as stop()
and destroy()
but the either the audio or the servo continue to run, or the actual program shuts off.
So my question is, what should I be doing differently? I would really appreciate any feedback in this problem.
Have you looked into the Queue module? You can send messages to threads with it.
So, presuming you have q
(an instance of Queue
) which is accessible to your thread functions, your routine()
thread function could look something like this:
def routine(path):
# skip some stuff...
while i < (len(settings) - 1):
if not q.empty():
message = q.get()
if message == 'stop':
break
# rest of your function
In your playaudio()
function you'll want to hold onto the Popen
object you created and use the `terminate() method to end the process. Take a look at the subprocess documentation for more information on how to do that.
Then, when the user clicks the "stop" button, you can post a message to the queue:
def stop():
q.put('stop')
As an aside, take look at the multiprocess module too. Python's GIL prevents threading from happening properly, and multiprocess is able to get around that and make use of multiple cores.