I'm creating an application that will play audio on a different thread. The thread runs fine until it reaches the statement where I create an AudioSegement from the pydub library. I want it to continue executing after this statement so that the audio will play in the background.
def add_stream_queue(self):
search_result = self.search()
stream = Stream(search_result.get_title(), search_result.get_link())
self.stream_queue.append_queue(stream)
if self.first_stream:
self.stream_thread = threading.Thread(target=self.stream_queue.play_stream_queue, args=[self.play_audio])
self.stream_thread.start()
Here is where I create the thread and it calls the function play_stream_queue() which is in the class StreamQueue.
def play_stream_queue(self, play_audio):
while self.current_stream < self.get_size():
self.first_stream = False
self.manage_stream.download_stream(self.get_stream(self.current_stream).get_link())
play_audio.start(self.get_stream(self.current_stream).get_title())
self.current_stream += 1
print("reached")
self.first_stream = True
In this function I start to play the audio in the line play_audio.start(...). The thread executed perfectly at this point.
def start(self, filename):
self.filename = self.STREAM_FOlDER + filename + ".m4a"
self.stream_file = AudioSegment.from_file(self.filename, format="m4a")
print("TEST")
# Create an interface to PortAudio
self.p = pyaudio.PyAudio()
.
.
.
In the PlayAudio class is where the thread is giving me issues. The thread will run fine until it reaches the statement,
self.stream_file = AudioSegment.from_file(self.filename, format="m4a")
The thread will not continue after this statement.
The odd part is that it used to work perfectly fine and the thread would continue executing after this statement but then it randomly stopped working. I also notice if the program has an error and crashes the stream_thread will start playing.
Related
In Python I have my main program which is basically a console 'gui' class. Its main method is a running thread that keeps the mains screen with options printed, and keeps waitin for user to input a choice.
The gui class has another object initialized that has its own running Threads. One of the Threads in this other object basically requires the main GUI thread to switch to a different mode / stop, but because it always 'pauses' on any input(), it will never switch.
So is there way, since I have access to the gui thread from the other object, to send an empty stdin to the gui thread so it gets over input()?
Example: (not actual code)
from threading import Thread
class Gui:
def __init__(self):
self.mainthread = Thread(target=self.console_loop, daemon=True)
self.server = Server(self.mainthread)
self.mainthread.start()
self.mainthread.join()
def console_loop(self):
while True:
if some_terminating_condition:
break
while self.server.guiloop:
"""
.
. various code
.
"""
a = input()
if a == "Some input":
self.server.guiloop = False
self.server.do_stuff()
while not self.server.guiloop:
"""
More code
"""
class Server:
def __init__(self, guithread):
self.guiloop = True
self.gui_thread = guithread
def do_stuff(self):
self.guiloop = False
"""
Code to stop gui waiting for input <-- What I need
"""
# Arbitrary code
Sorry for any mistakes, wrote it on the fly, since my code is too big to copy over
I'm writing a simple time tracking application in Python3 and PyQt5. Time is tracked in separate thread. Function that this thread is running doesn't access GUI code. On Windows10 application freezes after trying to close it. It's caused by calling thread.join(). I need to end the process in task manager to close it. On Linux Mint it works fine. I'm using threads from threading library. It doesn't work also with QThread's. If I comment out the thread.join() line it closes without a problem, but the code that's running by this thread doesn't finish.
Thread is initialized in __init__() function of Window class.
self.trackingThread = Thread(target = self.track)
Function that is responsible for tracking time:
def track(self):
startTime = time()
lastWindowChangeTime = startTime
while self.running:
# check if active window has changed
if self.active_window_name != get_active_window_name():
if self.active_window_name in self.applications_time:
self.applications_time[self.active_window_name] += int(time() - lastWindowChangeTime) // 60 # time in minutes)
else:
self.applications_time[self.active_window_name] = int(time() - lastWindowChangeTime) // 60 # time in minutes
lastWindowChangeTime = time()
self.active_window_name = get_active_window_name()
totalTime = int(time() - startTime) // 60 # time in minutes
if date.today() in self.daily_time:
self.daily_time[date.today()] += totalTime
else:
self.daily_time[date.today()] = totalTime
Joining the thread:
def saveAndQuit(self):
self.running = False
self.trackingThread.join() # the line that's causing application freeze
self.save()
QApplication.instance().quit()
EDIT:
Example:
https://pastebin.com/vt3BfKJL
relevant code:
def get_active_window_name():
active_window_name = ''
if system() == 'Linux':
active_window_name = check_output(['xdotool', 'getactivewindow', 'getwindowname']).decode('utf-8')
elif system() == 'Windows':
window = GetForegroundWindow()
active_window_name = GetWindowText(window)
return active_window_name
EDIT2:
After removing those 2 lines app closes without any problem. Is there any other way of getting active window name on Windows except win32gui?:
window = GetForegroundWindow()
active_window_name = GetWindowText(window)
The issue occurs because GetWindowText() is blocking, and so your thread can never join. To understand why, we have to delve into the win32 documentation
If the target window is owned by the current process, GetWindowText causes a WM_GETTEXT message to be sent to the specified window or control. If the target window is owned by another process and has a caption, GetWindowText retrieves the window caption text. If the window does not have a caption, the return value is a null string. This behavior is by design. It allows applications to call GetWindowText without becoming unresponsive if the process that owns the target window is not responding. However, if the target window is not responding and it belongs to the calling application, GetWindowText will cause the calling application to become unresponsive.
You are attempting to join the thread from within a function (saveAndQuit) that has been called by the Qt event loop. As such, until this function returns, the Qt event loop will not process any messages. This means the call to GetWindowText in the other thread has sent a message to the Qt event loop which won't be processed until saveAndQuit finishes. However, saveAndQuit is waiting for the thread to finish, and so you have a deadlock!
There are several ways to solve the deadlock, probably the easiest to implement is to recursively call join, with a timeout, from the Qt event loop. It's somewhat "hacky", but other alternatives mean things like changing the way your thread behaves or using QThreads.
As such, I would modify your saveAndQuit as follows:
def saveAndQuit(self):
self.running = False
self.trackingThread.join(timeout=0.05)
# if thread is still alive, return control to the Qt event loop
# and rerun this function in 50 milliseconds
if self.trackingThread.is_alive():
QTimer.singleShot(50, self.saveAndQuit)
return
# if the thread has ended, then save and quit!
else:
self.save()
QApplication.instance().quit()
I had a similar problem and someone here on SO advised me to use something like this:
class MyThread(QThread):
def __init__(self):
super().__init__()
# initialize your thread, use arguments in the constructor if needed
def __del__(self):
self.wait()
def run(self):
pass # Do whatever you need here
def run_qt_app():
my_thread = MyThread()
my_thread.start()
qt_app = QApplication(sys.argv)
qt_app.aboutToQuit.connect(my_thread.terminate)
# Setup your window here
return qt_app.exec_()
Works fine for me, my_thread runs as long as qt_app is up, and finishes it's work on quit.
edit: typos
I started learning python recently, and I am facing a situation that I do not even know if it is expected, or if something is wrong.
I am learning parallel threading to have two independent processes on the same program (UI control on one thread, image processing on another)
So, to test this I created this simple code:
(Camera is a custom class that connects to a usb webcam)
import thread
from vii.camera import Camera
class Process(object):
def __init__(self, width=800, height=600):
self._cam = Camera(width, height)
self._is_running = False
self._current_image = None
def start(self):
thread.start_new(self._run(), (self))
def _run(self):
self._cam.start()
self._is_running = True
while self._is_running:
self._current_image = self._cam.update()
self._current_image.show()
def get_image(self):
return self._current_image
def stop(self):
self._is_running = False
self._cam.close()
thread.exit()
process = Process()
process.start()
print("You will never see this output")
while (True):
key = raw_input()
if key == 'q':
process.stop()
break
The thread is created with success, and I am able to see the image. Now, I need to be able to affect it (stop it, get data from it) from the main thread. But the problem is that the code never enters in the while loop.
Is this behaviour expected? If it is, is there a way for me to achieve the functionality I need?
I am trying to write a python script which displays a macOS alert and starts an alarm at the same time.
The alarm sound should be stopped after the alert is closed, however it isn't.
def show_alert(message="Flashlight alarm"):
"""Display a macOS dialog."""
message = json.dumps(str(message))
exit_status = os.system("osascript dialog.scpt {0}".format(message))
return exist_status
def play_alarm(file_name = "beep.wav", repeat=3):
"""Repeat the sound specified to mimic an alarm."""
process = subprocess.Popen(['sh', '-c', 'while :; do afplay "$1"; done', '_', file_name], shell=False)
return process
def alert_after_timeout(timeout, message, sound = True):
"""After timeout seconds, show an alert and play the alarm sound."""
time.sleep(timeout)
process = None
if sound:
process = play_alarm()
exit_status = show_alert(message)
if process is not None:
os.killpg(os.getpgid(process.pid), signal.SIGINT)
process.kill()
# also, this below line doesn't seem to open an alert.
show_alert(exit_status)
alert_after_timeout(1, "1s alarm")
The above code should display a macOS alert after starting to loop an alarm sound (in the file beep.wav). When the alert is closed, the alarm sound should instantly stop.
The AppleScript file dialog.scpt triggers the alert, it is only a few lines long:
on run argv
tell application "System Events" to display dialog (item 1 of argv) with icon file (path of container of (path to me) & "Icon.png")
end run
I admit I don't know why you cannot kill your process running in a shell, using subprocess to mimic running as background..., and the fact that no other command runs after that means that there's probably a deadlock somewhere. So let's drop that solution.
Let me propose a more pythonic solution. The audio play part was adapted from how to play wav file in python? but now plays in a loop and works with python 3 as well.
The idea is to start a thread that plays a sound in a loop using only python modules. The thread is aware of a global variable. If the stop_audio variable is set, then the thread knows it has to quit the infinite loop and stop playing.
you control the flag from the other procedure. Once the message has been clicked on, set the flag, audio stops playing immediately.
import pyaudio
import wave
import threading
# global variable used to gently tell the thread to stop playing
stop_audio = False
def show_alert(message="Flashlight alarm"):
"""Display a macOS dialog."""
message = json.dumps(str(message))
exit_status = os.system("osascript dialog.scpt {0}".format(message))
return exit_status
# initialize audio
def play_alarm(file_name = "beep.wav"):
#define stream chunk
chunk = 1024
#open a wav format music
f = wave.open(file_name,"rb")
#instantiate PyAudio
p = pyaudio.PyAudio()
#open stream
stream = p.open(format = p.get_format_from_width(f.getsampwidth()),
channels = f.getnchannels(),
rate = f.getframerate(),
output = True)
while not stop_audio:
f.rewind()
#read data
data = f.readframes(chunk)
#play stream
while data and not stop_audio:
stream.write(data)
data = f.readframes(chunk)
#stop stream
stream.stop_stream()
stream.close()
#close PyAudio
p.terminate()
def alert_after_timeout(timeout, message, sound = True):
"""After timeout seconds, show an alert and play the alarm sound."""
time.sleep(timeout)
process = None
if sound:
t = threading.Thread(target=play_alarm,args=("beep.wav",))
t.start()
exit_status = show_alert(message)
global stop_sound
if sound:
stop_sound = True # tell the thread to exit
t.join()
show_alert(exit_status)
alert_after_timeout(1, "1s alarm")
Note that I've dropped the repeat=3 parameter as it wasn't used and I made no sense of it.
An alternative without using pyaudio would be to call the external player in a loop, replace play_alarm above by this:
def play_alarm(file_name = "beep.wav"):
global stop_sound
while not stop_sound:
subprocess.call(["afplay",file_name])
when stop_sound is True, the sound keeps on playing till the end, but doesn't resume. So the effect is not instantaneous, but it's simple.
And another alternative to cut the sound in a more reactive way:
def play_alarm(file_name = "beep.wav"):
global stop_sound
while not stop_sound:
process = subprocess.Popen(["afplay",file_name])
while not stop_sound:
if process.poll() is not None:
break # process has ended
time.sleep(0.1) # wait 0.1s before testing process & stop_sound flag
if stop_sound:
process.kill() # kill if exit by stop_sound
Im writing simple app, which reads (about a million) lines from file, copy those lines into list, and if next line will be different then previous it runs a thread, to do some job with that list. Thread job is based on tcp sockets, sending and receiving commands via telnet lib.
Sometimes my application hangs and does nothing. All telnet operations I wrapped into try-except statements, also read and write into sockets has timeouts.
I thought about writing watchdog, which will do sys.exit() or something similiar on that hang condtition. But, for now I'm thinking how to create it, and still got no idea how to do it. So if You can trace me, it would be great.
For that file I'm creating 40 threads. Pseudo code looks:
lock = threading.Lock()
no_of_jobs = 0
class DoJob(threading.Thread):
def start(self, cond, work):
self.work = work
threading.Thread.start(self)
def run(self)
global lock
global no_of_jobs
lock.acquire()
no_of_jobs += 1
lock.release()
# do some job, if error or if finished, decrement no_of_jobs under lock
(...)
main:
#starting conditions:
with open(sys.argv[1]) as targetsfile:
head = [targetsfile.next() for x in xrange(1)]
s = head[0]
prev_cond = s[0]
work = []
for line in open(sys.argv[1], "r"):
cond = line([0])
if prev_cond != cond:
while(no_of_jobs>= MAX_THREADS):
time.sleep(1)
DoJob(cond, work)
prev_cond = cond
work = None
work = []
work.append(line)
#last job:
DoJob(cond, work)
while threading.activeCount() > 1:
time.sleep(1)
best regards
J
I have successfully used code like below in the past (from a python 3 program I wrote):
import threading
def die():
print('ran for too long. quitting.')
for thread in threading.enumerate():
if thread.isAlive():
try:
thread._stop()
except:
pass
sys.exit(1)
if __name__ == '__main__':
#bunch of app-specific code...
# setup max runtime
die = threading.Timer(2.0, die) #quit after 2 seconds
die.daemon = True
die.start()
#after work is done
die.cancel()