关闭macOS警报后不会终止子进程

2024-05-23 17:50:27 发布

您现在位置:Python中文网/ 问答频道 /正文

我正在尝试编写一个python脚本,它显示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

Tags: tomessagedefstatusshowexittimeoutmacos
1条回答
网友
1楼 · 发布于 2024-05-23 17:50:27

我承认我不知道为什么不能终止在shell中运行的进程,使用子进程来模拟后台运行…,而且在这之后没有其他命令运行,这意味着可能在某个地方出现了死锁。所以让我们放弃这个解决方案。你知道吗

让我提出一个更具Python效应的解决方案。音频播放部分改编自how to play wav file in python?,但现在以循环方式播放,并与python3配合使用。你知道吗

其思想是启动一个线程,只使用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

相关问题 更多 >