Seite 1 von 1

Problem bei einem Timer im Hintergrund.

Verfasst: Montag 4. Februar 2019, 19:08
von steffenrohwer
Hallo liebe Forenkollegen.

Ich bekomme folgende Fehlermeldung:

Code: Alles auswählen

Exception in thread Thread-3:
Traceback (most recent call last):
  File "/usr/lib/python2.7/threading.py", line 801, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.7/threading.py", line 754, in run
    self.__target(*self.__args, **self.__kwargs)
  File "InteraktivTest.py", line 28, in countdown
    time.sleep(1)
NameError: global name 'time' is not defined
Hieran verzweifle ich nun schon eine halbe Ewigkeit bei einem Teil des Steuerungsprogramms für unseren Teamstand beim F1inschools Wettbewerb. Nun muss ich mir einfach Hilfe holen. Im Folgenden ist der Betreffende code zu sehen:

Code: Alles auswählen

from time import sleep
import RPi.GPIO as GPIO
import os
import threading
from omxplayer.player import OMXPlayer
GPIO.setmode(GPIO.BOARD)
button1=35
button2=36
button3=37
imagefilmpfad="/home/pi/Standanimation.m4v"
v1vidpfad="/home/pi/V1Auto.m4v"
v2vidpfad="/home/pi/V2Auto.m4v"
finalRoadstarpfad="/home/pi/AutoV1.mov"
backround=0
Videoduration=0
timer=1
GPIO.setup(button1,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.setup(button2,GPIO.IN,pull_up_down=GPIO.PUD_UP)
GPIO.setup(button3,GPIO.IN,pull_up_down=GPIO.PUD_UP)

def countdown():
        global Videoduration
        timer = Videoduration
        print "timer start"
        while 0==0:
                if timer>0:
                        timer = timer - 1
                        time.sleep(1)
                elif timer<=0:
                        print "timer ende"
                        global backround
                        backround=0

while 1==1:
        if GPIO.input(button1)==0:
                print "Button 1 wurde gedrueckt"
                player.quit()
                sleep(0.025)
                player = OMXPlayer(v2vidpfad)
                Videoduration=player.duration()
                print "fertig"
                t = threading.Thread(target=countdown)
                t.start()
        if GPIO.input(button2)==0:
                print "Button 2 wurde gedrueckt"
                player.quit()
                sleep(0.025)
                player = OMXPlayer(v1vidpfad)
                Videoduration=player.duration()
                t = threading.Thread(target=countdown)
                t.start()
Ich bedanke mich schon im Voraus für Hilfe!

LG
Steffen Rohwer

Re: Problem bei einem Timer im Hintergrund.

Verfasst: Montag 4. Februar 2019, 19:14
von __deets__
Schau mal genau auf dein import-Statement zum time-Modul, und was du dann machst in deinem Thread. Dazu auch das offizielle Tutorial: https://docs.python.org/3/tutorial/modules.html

Im uebrigen gibt es bei deinem Code neben jeder Menge anderer Verbesserungen gar keinen Grund, einen Thread zu benutzen. Du musst dir nur

stop = time.time() + VideoDuration

merken and der Stelle, wo du jetzt den Thread aufrufst. Und dann einfach pruefen, ob time.time() > stop ist. Threads sind komplizierter als sie aussehen, und man sollte auf sie verzichten, wenn man sie nicht braucht.

Re: Problem bei einem Timer im Hintergrund.

Verfasst: Montag 4. Februar 2019, 19:29
von Sirius3
@steffenrohwer: was ist der Unterschied zwischen 0==0 und 1==1? Beides sollte gleich als True geschrieben werden.
Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht 8. `global` sollte man nicht benutzen.

Statt in einem busy-Loop auf einen Button zu warten, würde man mit add_event_detect einen Callback registrieren, der in eine Queue das Event schreibt, das dann in der Hauptschleife abgearbeitet wird. Mit einem Timeout würde man ebenso das Stoppen über die Queue implementieren.

Das ganze könnte ungefähr so aussehen:

Code: Alles auswählen

from functools import partial
from threading import Timer
from queue import Queue
from omxplayer.player import OMXPlayer
import RPi.GPIO as gpio

VIDEOS = {
    35: "/home/pi/V1Auto.m4v",
    36: "/home/pi/V2Auto.m4v",
}

def main():
    player = None
    timer = None
    try:
        queue = Queue()
        gpio.setmode(gpio.BOARD)
        for pin, filename in VIDEOS.items():
            gpio.setup(pin, gpio.IN, pull_up_down=gpio.PUD_UP)
            gpio.add_event_detect(pin, gpio.FALLING, partial(queue.put, filename))
        while True:
            filename = queue.get()
            if player is not None:
                player.quit()
                timer.cancel()
            player = timer = None
            if filename is not None:
                sleep(0.025)
                player = OMXPlayer(filename)
                duration = player.duration()
                timer = Timer(duration, queue.put, args=(None,))
    finally:
        gpio.cleanup()

if __name__ == '__main__':
    main()

Re: Problem bei einem Timer im Hintergrund.

Verfasst: Montag 4. Februar 2019, 19:40
von steffenrohwer
Vielen Dank! Das hat jetzt super schnell geklappt und in der Tat ist dieser Weg deutlich eleganter!

LG
Steffen Rohwer

Re: Problem bei einem Timer im Hintergrund.

Verfasst: Montag 4. Februar 2019, 19:42
von steffenrohwer
Sirius3 hat geschrieben: Montag 4. Februar 2019, 19:29 @steffenrohwer: was ist der Unterschied zwischen 0==0 und 1==1? Beides sollte gleich als True geschrieben werden.
Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht 8.

Statt in einem busy-Loop auf einen Button zu warten, würde man mit add_event_detect einen Callback registrieren, der in eine Queue das Event schreibt, das dann in der Hauptschleife abgearbeitet wird. Mit einem Timeout würde man ebenso das Stoppen über die Queue implementieren.
Vielen Dank für den Tipp mit dem "True", dass habe ich mal eben umgesetzt. Ist bestimmt gut für die Performance des scripts. Das mit dem "event-detect" kriege ich mit meinem Laienwissen bis morgen wohl nicht mehr umgesetzt. Trotzdem danke für den Hinweis.

LG SteffeN!

Re: Problem bei einem Timer im Hintergrund.

Verfasst: Montag 4. Februar 2019, 20:29
von __blackjack__
@steffenrohwer: Weitere Anmerkungen: Wenn man Python 2 verwendet, sollte man wenigstens versuchen so zu programmieren, das das leicht nach Python 3 portiert werden kann, bzw. im Idealfall unter beiden Versionen läuft. Wobei: Warum fängst Du noch mit Python 2 an? Da hat sein Lebensende fast erreicht.

Importe werden üblichweweise nach Standardmodulen, externen Modulen, und eigenen Modulen gruppiert.

Das `os`-Modul wird importiert, aber im Code dann gar nicht verwendet.

``as`` verwendet man bei Importen wenn man etwas umbenennen will. Das passiert bei `GPIO` aber gar nicht, also macht das da gar keinen Sinn.

Um das Gleichheitszeichen bei Zuweisungen ausserhalb von Argumentlisten, um binäre Operatoren, und nach Kommas setzt man Leerzeichen um die Lesbarkeit zu erhöhen.

Auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblichweweise in einer Funktion die `main()` heisst.

Konstanten werden per Konvention KOMPLETT_GROSS geschrieben, um sie als solche erkennen zu können. Klassen „MixedCase“ und alles andere klein_mit_unterstrichen.

`BUTTON_3`, wie man es dann schreiben würde, wird nicht verwendet.

`backround` (sic!) wird nirgends (sinnvoll) verwendet. `timer` wird gar nicht verwendet.

In `countdown()` wird nichts sinnvolles gemacht, das kann komplett weg, und damit auch die Threads.

Man muss bevor das Programm beendet wird `GPIO.cleanup()` aufrufen.

`player.quit()` wird zu einem Zeitpunkt aufgerufen an dem der Name `player` noch gar nicht definiert ist.

In den beiden ``if``\s passiert nahezu das gleiche. Das würde man statt den Code da zweimal stehen zu haben, in eine Funktion auslagern.

Wenn Du beim „busy waiting“ bleiben möchtest, dann wäre das hier ein (ungetesteter) Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python
from __future__ import absolute_import, division, print_function
from functools import partial
from time import sleep

from omxplayer.player import OMXPlayer
from RPi import GPIO

BUTTON_1 = 35
BUTTON_2 = 36
V1_VIDEO_PFAD = '/home/pi/V1Auto.m4v'
V2_VIDEO_PFAD = '/home/pi/V2Auto.m4v'


def check_button(player, pin_number, button_number, filename):
    if GPIO.input(pin_number) == GPIO.LOW:
        print('Button {} wurde gedrueckt'.format(button_number))
        if player:
            player.quit()
        sleep(0.025)  # Debouncing the button.
        player = OMXPlayer(filename)
    return player


def main():
    GPIO.setmode(GPIO.BOARD)
    try:
        GPIO.setup([BUTTON_1, BUTTON_2], GPIO.IN, pull_up_down=GPIO.PUD_UP)
        player = None
        while True:
            player = check_button(player, BUTTON_1, 1, V2_VIDEO_PFAD)
            player = check_button(player, BUTTON_2, 2, V1_VIDEO_PFAD)
    finally:
        GPIO.cleanup()


if __name__ == '__main__':
    main()
Aber das möchte man in der Regel nicht, sondern das schon von Sirius3 erwähnte `add_event_detect()`. Und der `OMXPlayer` hat auch einige Ereignisse an die man Rückruffunktionen binden kann.

Um das alles sauber hin zu bekommen braucht man aber objektorientierte Programmierung (OOP), denn Du hast Zustand, den Du Dir über Rückruffunktionen hinweg merken musst. Und jetzt nicht mal ansatzweise an ``global`` denken. Vergiss dieses Schlüsselwort am besten sofort.