Auf Label anzeigen ob LED leuchtet oder nicht

  • Hallo zusammen,

    ich war mir nicht sicher, ob mein Thread eher hier oder ins Elektronik-Forum gehört. Bei Bedarf bitte einfach verschieben.

    Mein Problem:

    Ich habe gestern meine ersten Experimente mit dem Raspberry Pi 4 gemacht und über ein GUI-Programm eine LED zum Leuchten und Blinken gebracht. Das hat auch super funktioniert. Nun möchte ich, dass bei der "Blinkerfunktion" ein Label anzeigt,ob die LED nun brennt oder nicht. Da hänge ich gerade - wenn ich eine Meldung auf der Konsole ausgebe,klappt esaber.

    Bei meinem jetzigen Code erhalte ich folgende Fehlermeldung: RecursionError: maximum recursion depth exceeded while calling a Python object

    Als Anfänger sagt mir das leider nicht viel.

    Ich würde zur Erläuterung hier mal meinen Code posten und würde mich über Unterstützung freuen.

  • Ok, ich habe es mit dem Beispiel versucht und mir ist soweit auch klar, wie das abläuft mit der Uhr, die sich selbst updatet. Aber auf meinen Code bezogen, klappt das nach wie vor nicht. Vielleicht ist das unverschämt von mir zu verlangen, aber ich würde mich tierisch freuen, wenn du mal einen Blick auf den Code werfen könntest, den ich folgendermaßen abgeändert habe und mir einen kleinen Schubs in die richtige Richtung geben.

  • Horand In `anzeiger()` rufst Du `anzeiger()` auf was `anzeiger()` aufruft, was wiederum `anzeiger()` aufruft, wo dann wieder `anzeiger()` aufgerufen wird, …

    Das geht so nicht, und Du benutzt `after()` an der Stelle auch falsch. Das erwartet etwas aufrufbares, nicht das *Du* das schon aufrufst und dann den *Rückgabewert* stattdessen übergibst. ``self.anzeiger()`` ruft die Methode auf und wird zu deren Rückgabewert ausgewertet. ``self.anzeiger`` ist die Methode selbst als Wert.

    Beim `after()` wird ein weiteres Argument angegeben welches `anzeiger()` aber gar nicht erwartet. Es wäre auch nicht sinnvoll, denn an das Label kommt die Methode ja bereits über `self` heran, das braucht gar nicht übergeben werden.

    Da `anzeiger()` per `after()` immer wieder aufgerufen wird, darf man das nur einmal aufrufen, denn damit setzt man das Aufrufen alle Zehntelsekunde in Gang. Wenn man das mehrfach startet, dann läuft das auch mehrfach parallel. Der Sinn dieses Vorgehens erschliesst sich mir aber sowieso nicht so ganz, denn man könnte in `blinken()` statt den Ausgang direkt zu setzen und dann nebenläufig in `anzeiger` den Zustand im Label anzuzeigen in `blinken()` auch einfach die Methoden `an()` und `aus()` aufrufen, die ja bereits den Labeltext passend setzen. Damit ist `anzeiger()` überflüssig. Falls man bei der Ausgabe zwischen "LED" und aktivem "Blinker" unterscheiden möchte, könnte man diesen Präfix zum Zustand des Objekts hinzufügen.

    Kommen wir zu `blinken()`: Der Fehler hier ist das da eine Schleife mit `time.sleep()`-Aufrufen läuft. Solange die läuft passiert in der GUI nichts, das heisst die Anzeige friert solange ein, zwischenzeitliche Änderungen werden nicht übernommen, wenn die GUI wieder aktiv wird, zeigt sie den endgültigen Zustand nach Ablauf der Schleife an. *Hier* muss man mit `after()` arbeiten und die einzelnen Schritte der Schleife und das warten mit `time.sleep()` mittels `after()` umschreiben.

    Weitere Anmerkungen: Vergiss bitte die Existenz von ``global``. Wenn man Klassen verwendet gibt es keine Ausrede mehr das einzusetzen. Zumal das hier auch überhaupt gar nicht wirklich verwendet wird, denn der Name den Du damit als global deklarierst wird nur lokal in der Funktion verwendet.

    Das GPIO-Modul würde ich durch `gpiozero` ersetzen. Das hat die bessere, modernere API, und das mit dem Blinken wird einfacher, denn dafür hat die `LED`-Klasse dort eigens eine Methode.

    Das aufsetzen vom GPIO-Modul, also Modus und Pins, sowie das momentan nicht ganz vollständige Aufräumen würde ich aus der Klasse herausziehen. Dann ist das leichter nachvollziehbar, weil nicht auf mehrere Methoden verteilt, und auch sicherer, weil die `ende()`-Methode beispielsweise nicht aufgerufen wird falls der Benutzer das Programm im Terminal mit Strg+C oder im Fenster über das Kreuzchen im Fenstertitel abbricht. `ende()` wird dann überflüssig weil man den Aufruf zum beenden der GUI-Hauptschleife auch direkt als `command` für den Button angeben kann.

    Die LED-Pinnummer sollte man als Konstante definieren statt sie mehrfach über den ganzen Code verteilt zu wiederholen.

    Bei den ganzen `button*`-Attributen fehlt ein Unterstrich zwischen den Worten (`button_an`, `button_aus`, …). Und wenn mann nicht gerade Yoda heisst, dann sollten die Worte auch andersherum sein (`an_button`, `aus_button`, …). Allerdings wird an alle diese Attribute der Wert `None` gebunden, weil das der Rückgabewert von `pack()` ist. Das macht wenig Sinn.

    Zwischenstand (ungetestet):

    “Dawn, n.: The time when men of reason go to bed.” — Ambrose Bierce, “The Devil's Dictionary”

  • Hallo blackjack,

    erstmal ein dickes Lob und vielen Dank für deine Zeit und Mühe, die du investierst. Es ist toll, dass es Leute gibt, die ihr Wissen hier teilen. Ich habe jetzt dank dir erstmal jede Menge Input zum Weiterarbeiten. Heute Abend werde ich mir das alles noch mal in Ruhe zu Gemüte führen:

    Wenn ich dich recht verstanden habe, waren folgende Fehler die Hauptfehlerquellen:

    - in meiner Anzeigefunktion habe ich durch das after die Funktion immer wieder aufgerufen, so dass der von mir im Eingangsposting beschriebene Fehler entstand

    - in der Blinken-Methode ist time.sleep ungeeignet, da diese die GUI einfriert. Stattdessen hier mir after arbeiten.

  • Hallo,

    oh man ziemlich deprimierend das __blackjack__ so schnell ist ^^

    Naja dann will ich nur kurz zeigen wie einfach die Blinkerei mit gpiozero dafür gibt es extra die 'blink'-Funktion:

    Code
    blink(on_time=1, off_time=1, n=None, background=True)

    Dann fällt auch das 'cleanup' am Schluss weg.


    Edit:

    Zitat von gpiozero

    background (bool) – If True (the default), start a background thread to
    continue blinking and return immediately. If False, only
    return when the blink is finished (warning: the default value of
    n will result in this method never returning).

    Das wäre eventuell für dein Problem auch interessant.

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • Hallo,

    habe gerade aufgeräumt und eine LED entdeckt,da ist mir dieses Thema eingefallen.

    Auf der Grundlage von __blackjack__ 's Code mit gpiozero. Getestet:

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

  • Hallo,

    ich grabe meinen Thread von vergangener Woche nochmal aus. Ich heute endlich mal wieder ein bisschen zum Programmieren gekommen und habe mit 3 Lichtern ein Programm geschrieben, das unter anderem eine Art Autoampel darstellen kann. Man kann auch einstellen, wie oft die Ampel durchlaufen soll (habe das nochnicht variabel gemacht, sondern als erstes Mal "3" als Anzahl Durchläufe angegeben.

    Das Ganze funktioniert auch soweit, aber ich wollte meinen Code mal zeigen, ob der einigermaßen annehmbar ist. Da mein Wissen hauptsächlichnoch aus guten alten DOS-Tagen herkommt, bin ich sicher nicht ganz "up to date", insbesondere was GUI-Programmierung betrifft. Und ich möchte, wenn möglich, was dazulernen.

    Besonders interessieren würde mich, ob man das Problem auch mit for oder while Schleifen lösen könnte (ich habs jedenfalls nicht hingekriegt). Vielmehr habe ich den Eindruck, dass in der GUI-Programmierung anstatt for und while diese after-Funktionen genutzt werden oder sehe ich das falsch?

    Für eine Beantwortung meiner Fragen, schon der Neugierde wegen, würde ich mich sehr freuen.

    Horand

    Hier poste ich mal den Code:

  • Hallo,

    anstatt GPIO solltest du auf das aktuelle, einfachere und schönere 'gpiozero' umsteigen. Mit 'as' werden Modul umgenannt. Du nennst in deinem Fall 'GPIO' ind 'GPIO' um. Stattdessen wäre hier ''from RPi import GPIO' richtig. 'time' wird importiert aber nicht genutzt, das kann also weg.

    Ich würde dann nicht immer speziell allen LED's auf 0 und eine auf 1 setzen bei deinen Funktionen 'rot_anschalten' und den folgenden, so finde ich es schöner:

    In der Funktion 'autoampel_vorbereiten' erstellst du 'self.anzahl_eingeben', aber die Eingabe die gemacht wird, wird nicht weiter im Code verwendet.

    In check_durchlauf_beendet' würde ich nicht reine 'if'-Blöcke verwenden, sondern diese mit 'elif' weiterführen. Es kann ja nur eine if-Bedingung wahr sein, dann müssen die anderen nicht abgefragt werden. Das gilt auch für die Funktion 'autoampel'.

    In 'main' unterdrückst du Warnungen, welche Warnungen treten denn auf? Wenn ich das richtig interpretiere müsste der Code ohne Warnungen durchlaufen, dann kann diese Zeile auch weg. An sich ist es sinnvoller die Warnungen zu betrachten und sie zu beseitigen anstatt zu unterdrücken.

    Eine Idee für einen Schleifendurchgang habe ich leider gerade auch nicht. Bin auch kein Programmierer und kann dir nur das mitteilen was mir aufgefallen ist.

    Ungetestet (und hoffentlich alles berücksichtigt:

    Grüße

    Dennis

    🎧 With the music execution and the talk of revolution, it bleeds in me and it goes 🎧

    Einmal editiert, zuletzt von Dennis89 (21. Februar 2021 um 22:49)

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!