2016-12-28 52 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