Преобразование глобального сигнала выхода в исключение - PullRequest
0 голосов
/ 23 января 2020

Я работаю над аудиоплеером с терминальным интерфейсом на основе VL C и Curses.

Недавно мне посоветовали написать на SE Code Review пост , чтобы использовать исключения для выхода из программа вместо использования логического 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')

Я ожидал бы, что оба фрагмента будут вести себя одинаково и выйдут при нажатии клавиши 'q', но вместо этого правильно будет работать только логический метод.

Этот код также на github . Спасибо за ваше время.

1 Ответ

0 голосов
/ 24 января 2020

Это не совсем ответ на исходный вопрос, который я разместил, поскольку в нем не используются исключения, поэтому я не буду отмечать его как решение. В любом случае, как указано в комментариях, возвращение false при обратном вызове Pynput завершает поток слушателя. Мониторинг состояния этого потока слушателя может сказать вам, когда закрывать второй поток «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
...