【问题标题】:Converting a global exit signal to an exception将全局退出信号转换为异常
【发布时间】:2020-05-10 00:05:00
【问题描述】:

我正在开发一个基于 VLC 和 Curses 的终端界面音频播放器。

最近有人建议我在 SE Code Review post 上使用异常来退出程序,而不是使用 global 布尔值作为信号。

但是,当我将代码转换为使用 SystemExit 异常时,它会停止完全退出,而是挂起直到终端窗口关闭。我认为这个问题与 Pynput 的侦听器以及我如何处理它创建的线程有关,但我不确定。

使用布尔值:

import sys, os
from pynput import keyboard
from functools import partial

import cfg
from view import View
from player import Player

exit_signal: bool = False
def on_press(key: keyboard.KeyCode, view: View, player: Player):
    """Handle input"""
    global exit_signal
    key = str(key).strip('\'')
    if str(key) == 'p':
        view.notify('Playing...')
        player.play()
    elif key == 'a':
        view.notify('Paused')
        player.pause()
    elif key == 'n':
        view.notify('Skipping Forward...')
        player.skip_forward()
    elif key == 'l':
        view.notify('Skipping Back...')
        player.skip_back()
    elif key == 'q':
        view.notify('Exiting...')
        exit_signal = True
        del player
        del view
        return
    view.update_ui(player.get_metadata())

def tick(view: View, player:Player):
    """For functions run periodically"""
    metadata = player.get_metadata()
    view.update_ui(metadata)

view = View()
player = Player()

view.notify("Ready!")
with keyboard.Listener(on_press=partial(on_press, view=view, player=player)) as listener:
    while exit_signal == False:
        tick(view, player)
    listener.join() # merge to one thread
    os.system('reset') # clean up the console

使用 SystemExit:

from os import system
from pynput import keyboard
from functools import partial

import cfg
from view import View
from player import Player


def on_press(key: keyboard.KeyCode, view: View, player: Player):
    """Handle user input"""
    key = str(key).strip('\'')
    if str(key) == 'p':
        view.notify('Playing...')
        player.play()
    elif key == 'a':
        view.notify('Paused')
        player.pause()
    elif key == 'n':
        view.notify('Skipping Forward...')
        player.skip_forward()
    elif key == 'l':
        view.notify('Skipping Back...')
        player.skip_back()
    elif key == 'q':
        view.notify('Exiting...')
        del player
        del view
        raise SystemExit
    view.update_ui(player.get_metadata())


def tick(view: View, player: Player):
    """For functions run periodically"""
    metadata = player.get_metadata()
    view.update_ui(metadata)


view = View()
player = Player()

view.notify("Ready!")
listener = keyboard.Listener(
    on_press=partial(on_press, view=view, player=player))
listener.start()
try:
    while True:
        tick(view, player)
except SystemExit:
    listener.join()     # merge back to one thread
    os.system('cls' if os.name == 'nt' else 'clear')

我希望两个 sn-ps 的行为相同并在按下“q”时退出,但只有布尔方法正常工作。

此代码也在github 上。感谢您的宝贵时间。

【问题讨论】:

  • 根据文档here,它声明:“从任何地方调用pynput.mouse.Listener.stopraise StopException 或从回调中返回False 以停止侦听器。”
  • 这可能是您的程序在raise StopException 上的listener.start() 处退出,但随后它卡在while True: tick(view, player) 行内——并且函数tick 永远不会引发SystemExit,所以你的程序永远不会到达except 语句,或者它的结尾。
  • @FelipeFaria 谢谢,您的观察使我找到了我在下面发布的另一个解决方案。

标签: python multithreading exception pynput


【解决方案1】:

这并不是我发布的原始问题的真正答案,因为它不使用异常,所以我不会将其标记为解决方案。无论如何,正如 cmets 中所指出的,在 Pynput 回调中返回 false 会结束侦听器线程。监视该侦听器线程的状态可以告诉您何时关闭第二个“UI”线程:

def on_press(key: Key, view: View, player: Player):
    """Handle input"""
    # boring code
    elif key == 'q':
        view.notify('Exiting...')
        return False # kills listener thread
    return True

def main():
    view = View()
    player = Player()
    listener = Listener(on_press=partial(on_press, view=view, player=player))
    listener.start()
    while listener.running: # once the listener thread exits, end the program
        tick(view, player)
    del player
    del view

【讨论】:

    猜你喜欢
    • 2014-11-21
    • 2013-06-02
    • 1970-01-01
    • 1970-01-01
    • 2014-08-31
    • 1970-01-01
    • 2017-07-18
    • 1970-01-01
    • 2014-04-11
    相关资源
    最近更新 更多