Tastendruck unter Linux in CLI-Audioplayer verarbeiten

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Hallo!

Ich möchte in einen sehr minimalistischen Audioplayer, der mir die Ausgabe direkt auf die Kommandozeile gibt, Events für Tastendrucke einbauen. Zunächst einmal nur bei den Tasten Links/Rechts für Vor und Zurück.

Das curses-Modul eignet sich hierfür meiner Meinung nach nicht, weil dabei die eigentliche Kommandozeile verschwindet, was eben nicht passieren soll.

Ich habe daher den Code von dieser Seite etwas an meine Bedürfnisse angepasst:

Code: Alles auswählen

import termios
import tty
import sys


def getseq():
    fd = sys.stdin.fileno()
    old_settings = termios.tcgetattr(fd)
    try:
        tty.setraw(sys.stdin.fileno())
        seq = sys.stdin.read(3)
    finally:
        termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    return seq


if __name__ == '__main__':
    print "Press an 'escape' key to get its sequence..."
    print repr(getseq())
Nun stehe ich aber vor einer blöden Denkblockade. Ich muss ja jetzt wahrscheinlich mit Threads arbeiten, was ich bisher noch nie gemacht habe. Mein Player sieht bisher so aus. Wie verbinde ich nun das Reagieren auf mögliche Tastatureingaben mit dem laufenden Programm? Denn wenn nichts gedrückt wird, sollen die Tracks ja weitergespielt werden. In der bisherigen getseq() wird jedoch gestoppt und darauf gewartet, dass der Benutzer etwas drückt. Ich bräuchte so eine Art Logger, der im Hintergrund aufpasst, ob ein bestimmtes Ereignis angefordert wird, ansonsten aber still ist.
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Wie wärs mit einem Thread der aus die Keypresses wartet und einer Queue um die Keys zum Abspielthread zu transportieren?
Benutzeravatar
veers
User
Beiträge: 1219
Registriert: Mittwoch 28. Februar 2007, 20:01
Wohnort: Zürich (CH)
Kontaktdaten:

Falls du fürs abspielen gstreamer verwendest, das kannst du mit dem gobject mainloop laufen lassen dann brauchst du keine Threads.

- Jonas
[url=http://29a.ch/]My Website - 29a.ch[/url]
"If privacy is outlawed, only outlaws will have privacy." - Phil Zimmermann
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

rayo hat geschrieben:Wie wärs mit einem Thread der aus die Keypresses wartet und einer Queue um die Keys zum Abspielthread zu transportieren?
Habe jetzt mal probiert, sowas in der Art zu machen (wie gesagt: erstes Mal) - leider ohne Erfolg.

Der Code: http://paste.pocoo.org/show/102642/

Zur Erklärung:

Der Player ist jetzt eine Klasse, die von threading.Thread() erbt. Es wird für die Initialisierung eine Playlist übergeben, die in main() den Argumenten von der Kommandozeile entspricht. Die run()-Methode von Thread() habe ich mit der Abarbeitung bzw dem Abspielen der Dateien aus der Playlist überschrieben. run() gibt auch mittels yield den aktuellen Track aus, den main() für den User anzeigt. Das klappt auch alles soweit...

Jetzt dachte ich mir, ich kann aus der Player-Klasse einen Daemon machen, der ja dann eigentlich im Hintergrund laufen müsste. Das klappt aber in einem try-except-Block wohl nicht. Oder liegt es an dem for-Statement wegen dem Iterieren? Oder an beidem? Keine Ahnung...

Auf jeden Fall habe ich den ganzen PLAYING-Kram mal testweise rausgenommen:

Code: Alles auswählen

def main():
    playlist = sys.argv[1:]
    player = Player(playlist)
    player.setDaemon(True)
    player.start()
    await_user_input()
Nun wird auf einen Tastendruck gewartet, aber keine Musik abgespielt. Sobald ich dann eine Taste drücke, beendet sich das Programm kommentarlos.

Ich bin alles in allem gerade mit meinem Latein am Ende. Wäre nett, wenn sich jemand erbarmen könnte, mir auf die Sprünge zu helfen... :)

Achso: getkey() unterscheidet jetzt zwischen normalen Zeichen (Buchstabe, Zahl, Bindestrich usw) und besonderen Eingaben wie Richtungstasten, F-Tasten und ähnliches. Diese Sondertasten liefern als erstes Zeichen wohl immer "\x1b" zurück, gefolgt von zwei weiteren Zeichen mit dem eigentlichen Code für die Taste. Zumindest habe ich mir das so erklärt. Es wird also grundsätzlich erstmal ein Zeichen ausgelesen und falls dieses afu so ein Sonderzeichen hindeutet, werden stattdessen die zwei folgenden Zeichen als Rückgabewert genommen.
rayo
User
Beiträge: 773
Registriert: Mittwoch 5. November 2003, 18:06
Wohnort: Schweiz
Kontaktdaten:

Hi

Lass mal setDaemon weg und lass es normal laufen.

Hier mal wie ich es etwa gedacht habe, konnte es aber nicht testen! Ist nur dein Code modifiziert.

Code

Gruss
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

Im Wiki ist ein [wiki=Threading_Beispiel1]Beispiel[/wiki] zu threading, lass es mal laufen, schau dir an wie es funktioniert und spiel mal etwas mit rum. Auf die Weise sollte der Aha-Moment schnell eintreten.

Solange du die elementaren Dinge davon nicht verstanden hast wirst du auch dass hier nicht hinbekommen.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Klappt jetzt. Dankeschön! :)

http://paste.pocoo.org/show/102732/
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Doch noch ein Problem. Ich will das komplette Programm beenden, wenn kein Track mehr da ist. Also entweder weil der letzte durchgelaufen ist oder weil man eins weiter wollte und das Ende der Tracklist erreicht wurde.

Dummerweise wartet ja jetzt await_user_input() die ganze Zeit auf Eingaben und kommt nur aus seiner Endlosschleife, wenn die Leertaste gedrückt wird.

Ich habe es auch schon - IMHO eher unschön - mit exit() in der Player-Klasse probiert. Aber dann erhalte ich am Ende der Tracklist:

Code: Alles auswählen

Traceback (most recent call last):
  File "player.py", line 92, in <module>
    main()
  File "player.py", line 87, in main
    while await_user_input(events):
  File "player.py", line 67, in await_user_input
    key = getkey()
  File "player.py", line 57, in getkey
    key = sys.stdin.read(2)
ValueError: I/O operation on closed file
Welche anderen Möglichkeiten habe ich, um aus dem Programm zu kommen?

http://paste.pocoo.org/show/102840/
Antworten