我正在尝试编写一个显示macOS警报并同时启动警报的python脚本。
警报关闭后应停止警报声,但不是。
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")
上面的代码应该在开始循环警报声后显示macOS警报(在文件beep.wav
中)。当警报关闭时,警报声应立即停止。
AppleScript文件dialog.scpt
触发警报,只有几行:
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
答案 0 :(得分:1)
我承认我不知道为什么你不能杀死在shell中运行的进程,使用子进程来模拟作为后台运行...,以及之后没有其他命令运行的事实意味着某处可能存在死锁。所以,让我们放弃这个解决方案。
让我提出一个更加pythonic的解决方案。音频播放部分改编自how to play wav file in python?,但现在循环播放并与python 3一起使用。
这个想法是启动一个只使用python模块在循环中播放声音的线程。线程知道全局变量。如果设置了stop_audio
变量,则线程知道它必须退出无限循环并停止播放。
您可以从其他程序控制该标志。单击消息后,设置标志,音频立即停止播放。
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")
请注意,我删除了repeat=3
参数,因为它没有被使用,我对它没有任何意义。
不使用pyaudio
的替代方法是在循环中调用外部播放器,将此替换为play_alarm
:
def play_alarm(file_name = "beep.wav"):
global stop_sound
while not stop_sound:
subprocess.call(["afplay",file_name])
当stop_sound
为True
时,声音一直播放到最后,但不会恢复。所以效果不是瞬间的,但很简单。
另一种以更具反应性的方式切割声音的替代方案:
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