2016-12-28 53 views
0

我想写一个python脚本,它显示macOS警报并同时启动警报。关闭macOS警报后,子进程没有中断

警报关闭后应该停止警报声,但事实并非如此。

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") 

上述代码应开始循环的报警声(文件beep.wav中)后显示的MacOS警报。当警报关闭时,警报声应立即停止。

的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

'如果过程不无:'=>'如果过程:'。不要与'is'比较。 –

+0

我宁愿创建一个线程来播放声音,而不是使用'subprocess'与shell中的循环在后台运行......听起来对我来说很脆弱。 –

+0

@ Jean-FrançoisFabre我刚刚实现了这个建议('if process:'),但脚本仍然以相同的方式失败。 – theonlygusti

回答

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_soundTrue,声音一直玩到最后,但没有按”恢复。所以效果不是即时的,但很简单。

而另一替代切割声音在一个更具反应性的方法:

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 
+0

我不能再分配'process'了吗?因为该函数什么都不返回? – theonlygusti

+0

我没有名为'pyaudio'的模块,我不想全局安装它 – theonlygusti

+0

另外,你的答案中没有使用'threading'模块:/ – theonlygusti