Blinklicht schaltet nicht ab

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
PyNo
User
Beiträge: 7
Registriert: Montag 17. September 2018, 21:07

Hallo zusammen,

ich bin neu hier in diesem Forum weil ich mehr oder weniger ein Problem habe was mir keine Ruhe lässt.
Es klingt erstmal trivial aber ich habe schon alles mögliche versucht um dieses Problem zu lösen, leider ohne Erfolg.
Deshalb hoffe ich das mir ihr weiterhelfen könnt.

Es geht um folgendes:
Ich möchte nur zwei simple LEDs schalten bzw. daraus ein Blinklicht machen.

Folgendes zum Code
Ich hab am Raspberry Pi, zwei LEDs mit Vorwiderstand angeschlossen. Die Bedienung läuft über eine kleine Gui.
Wie man ganz unten am Code erkennen kann besteht die Gui aus mehreren Buttons "LED 1 an", "LED 2 an", "Blinklicht" und "Ausschalten".
LED 1 und 2 einschalten und ausschalten funktioniert wunderbar.
Das Blinklicht einzuschalten funktioniert auch.
(Ich musste das aber über einen Thread machen da mir sonst die Gui einfriert.
Multiprocessing hat warum auch immer nicht funktioniert, auch keine Fehlermeldung diesbezüglich.)

Ich bekomme aber das erzeugte Blinklicht nicht wieder ausgeschalten und daran häng ich jetzt schon mehrere Tage.

ich hab schon alles mögliche versucht über eine Break - Anweisung in der while - Schleife, über ein Alibi - Label das angesprochen wird mit der After.method(), den code umgeschrieben in OOP etc., aber nichts hat funktioniert.
Das einzige Mal als es funktionierte habe ich das mit der Shell simuliert.
Heißt ich hab überall im Code printbefehle (z.B. print(LED1 ein)) eingefügt und mir das über die Shell ausgeben lassen.


Mir ist durchaus bewusst das die sleep - Funktion in Verbindung mit einer Gui Gefahren bietet bzw. man das nicht macht.
Allerdings ist das die letzte Version, die wie oben beschrieben, funktioniert, jedenfalls bis zum Blinklicht ausschalten.

Schon einmal danke im Voraus für eure Hilfe. :mrgreen:

Code: Alles auswählen

# LED Leuchten

import RPi.GPIO as GPIO
import time
import tkinter as tk
import threading

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

GPIO.cleanup()
GPIO.setup(4, GPIO.OUT)
GPIO.setup(17, GPIO.OUT)


def LED1_on():
    GPIO.output(4, 1)

def LED2_on():
    GPIO.output(17, 1)

def Blinklicht():
    while True:
        GPIO.output(4, 1)
        time.sleep(0.2)
        GPIO.output(4, 0)
        GPIO.output(17, 1)
        time.sleep(0.2)
        GPIO.output(17, 0)

def Blink():
    blink = threading.Thread(target = Blinklicht)
    blink.start()

def Ausschalten():
    GPIO.output(4, 0)
    GPIO.output(17, 0)

main_Fenster = tk.Tk()          
main_Fenster.geometry("400x400")
main_Fenster.title("LED")

Button_LED1 = tk.Button(main_Fenster, text = "LED 1 an", bg = "yellow",  font = ("Arial", 22), 
			command = LED1_on)
Button_LED1.pack(ipadx = 400, ipady = 30)       

Button_LED2 = tk.Button(main_Fenster, text = "LED 2 an", bg = "orange",  font = ("Arial", 22), 
			command = LED2_on)
Button_LED2.pack(ipadx = 400, ipady = 30)

Button_Blinklicht = tk.Button(main_Fenster, text = "Blinklicht", bg = "green", 
				font = ("Arial", 22), command = Blink)
Button_Blinklicht.pack(ipadx = 400, ipady = 30)

Button_Ausschalten = tk.Button(main_Fenster, text = "Ausschalten", bg = "red", 
				font = ("Arial", 22), command = Ausschalten)
Button_Ausschalten.pack(ipadx = 400, ipady = 30)

main_Fenster.mainloop()   

__deets__
User
Beiträge: 14536
Registriert: Mittwoch 14. Oktober 2015, 14:29

Bitte mach dich mal mit den Namenskonventionen aus PEP8 bekannt. Du bist da kraut und rueben, und es macht das Verstaendnis des Codes schwieriger.

Zu deinem eigentlichen Problem: wenn du schon after kennst, warum blinkst du dann nicht einfach mit after? Und OO kann ich jetzt hier auch nicht erkennen, das wuerde aber dazu sehr helfen, weil du dann den Zustand, was passieren soll (an, aus, blinken) als Instanzvariable gut verwalten kannst.
PyNo
User
Beiträge: 7
Registriert: Montag 17. September 2018, 21:07

Oh so schnell hab ich mit einer Antwort nicht gerechnet, schönen dank :mrgreen:

Naja das hab ich bereits versucht. Das Problem ist, das ich trotzdem nicht in die mainloop() zurückkomme.
Ich hab mir ein Alibi - Label erstellt und gesagt das nach 500 ms die nächste Funktion aufgerufen wird. Allerdings springt der dann nur noch von Funktion zu Funktion hin und her um das blinken zu erzeugen. Folge: Gui friert wieder ein.
Wobei man das ja mit multiprocessing oder threading im Griff bekommen könnte, jedenfalls eigentlich.

Ich werde den Code nochmal in OOP einfügen.
Bezüglich zu PEP8: Ich bin noch Anfänger und dementsprechend sieht das noch nicht perfekt (Konvention) aus.
Ich bin aber bemüht das im nächsten Code besser hinzubekommen :mrgreen:
PyNo
User
Beiträge: 7
Registriert: Montag 17. September 2018, 21:07

Jetzt noch einmal meinen Code in OOP.
Hoffe das ist jetzt einigermaßen verständlicher.

Code: Alles auswählen

#------------------- Voreinstellungen----------------------------------------------#
import RPi.GPIO as GPIO
import time
import tkinter as tk     
import threading

GPIO.setmode(GPIO.BCM)
GPIO.setwarnings(False)

GPIO.cleanup()
GPIO.setup(4, GPIO.OUT)
GPIO.setup(17, GPIO.OUT)

#----------------------------------------------------------------------------------#

class Blinklicht_Gui:
	def __init__(self, master):
		self.master = master
		master.title("LED")
		master.geometry("400x400")
		
		self.Button_LED1 = tk.Button(master, text = "LED 1 an", bg = "yellow", 
					font = ("Arial", 22), command = self.LED1_on)
		self.Button_LED1.pack(ipadx = 400, ipady = 30)
		
		self.Button_LED2 = tk.Button(master, text = "LED 2 an", bg = "orange", 
					font = ("Arial", 22), command = self.LED2_on)
		self.Button_LED2.pack(ipadx = 400, ipady = 30)
		
		self.Button_Blinklicht = tk.Button(master, text = "Blinklicht", bg = "green", 
					font = ("Arial", 22), command = self.Blink)
		self.Button_Blinklicht.pack(ipadx = 400, ipady = 30)
		
		self.Button_Ausschalten = tk.Button(master, text = "Ausschalten", bg = "red", 
					font = ("Arial", 22), command = Ausschalten)
		self.Button_Ausschalten.pack(ipadx = 400, ipady = 30)
	
	def LED1_an(self):
		GPIO.output(4, 1)
		
	def LED2_an(self):
		GPIO.output(17, 1)
		
	def Ausschalten(self):
		GPIO.output(4, 0)
		GPIO.output(17, 0)
		
	def Blink(self):
		blink = threading.Thread(target = self.Blinklicht)
		blink.start()
		# Methode dient nur dazu um einen thread zu starten für die 
		# eigentlich Methode Blinklicht()
		
	def Blinklicht(self):
		while True:
			GPIO.output(4, 1)
       			time.sleep(0.2)
        		GPIO.output(4, 0)
        		GPIO.output(17, 1)
        		time.sleep(0.2)
        		GPIO.output(17, 0)
	
		
main_Fenster = tk.Tk()
Gui = Blinklicht_Gui(main_Fenster)
main_Fenster.mainloop()		
		

Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,

nimm' statt dem alten / veraltem RPI.GPIO das neuere / schönere / besser gpiozero-Modul, da haben LEDs direkt eine `blink()` Methode.

Die Methode `Blinklicht` kannst du so in einer GUI nicht nutzen, weil die `while` Schleife die GUI blockiert. `while` in einer GUI ist ganz schlecht.
Eingerückt wird bei Python mit 4 Leerzeichen, nicht mit 8.

Gruß, noisefloor
PyNo
User
Beiträge: 7
Registriert: Montag 17. September 2018, 21:07

Das ist ja krass, dass macht die Sache jedenfalls leichter.
Ich werd das gleich mal ausprobieren :mrgreen:

Ja das mit der while - Schleife ist mir bewusst das dass der schlechteste Weg ist.
Leider ist mein Wissen noch sehr begrenzt was alles möglich ist in Python.

Könntet ihr mir vielleicht dennoch einen Vorschlag machen oder Tip geben wie man das auch ohne gpiozero machen könnte.
Weil das Hauptproblem ist ja meine while - Schleife.
Die after.Methode() hat leider auch keinen Erfolg gebracht.
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

die komischen Zeilen mit den vielen Minus habe nur einen Effekt, sie stören das Lesen. Warnungen sind dazu da, behoben zu werden, und nicht um sie zu ignorieren. `cleanup` sollte daher auch zum Schluß aufgerufen werden. Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht Tabs. `Ausschalten` ist nicht definiert, so dass das Programm gleich mit einem NameError aussteigt. Um das Gleichheitszeichen von Keyword-Argumenten werden keine Leerzeichen gesetzt.

Ungetestet:

Code: Alles auswählen

import RPi.GPIO as gpio
import tkinter as tk
from functools import partial
from itertools import cycle

PIN_LEDS = [4, 17]

def initialize():
    gpio.setmode(gpio.BCM)
    gpio.setup(PIN_LEDS, gpio.OUT)

def finalize():
    gpio.cleanup()

class Blinklicht_Gui(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title("LED")
        self.geometry("400x400")
        tk.Button(self, text="LED 1 an", command=partial(self.anschalten, 0)).pack(ipadx=400, ipady=30)
        tk.Button(self, text="LED 2 an", command=partial(self.anschalten, 1)).pack(ipadx=400, ipady=30)
        tk.Button(self, text="Blinklicht", command=self.blink).pack(ipadx=400, ipady=30)
        tk.Button(self, text = "Ausschalten", command=self.ausschalten).pack(ipadx = 400, ipady = 30)
        self.ausschalten()
        self.loop()
    
    def loop(self):
        for state, pin in zip(self.states, PIN_LEDS):
            gpio.output(pin, next(state))
        self.after(200, self.loop)
    
    def anschalten(self, led):
        self.states[led] = cycle([1])

    def ausschalten(self):
        self.states = [
            cycle([0]),
            cycle([0]),
        ]

    def blink(self):
        self.states = [
            cycle([1, 0]),
            cycle([0, 1]),
        ]

def main():
    try:
        initialize()
        main_Fenster = Blinklicht_Gui()
        main_Fenster.mainloop()
    finally:
        finalize()

if __name__ == '__main__':
    main()
PyNo
User
Beiträge: 7
Registriert: Montag 17. September 2018, 21:07

Holy shit wer hätte gedacht das ein billiges Blinklicht so viel abverlangt :shock: :shock:

Nichtsdestotrotz danke ich dir für deine Hilfe. :mrgreen:
Ich werde mal in den nächsten Tagen den Code ausprobieren und versuchen ihn zu verstehen.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
wer hätte gedacht das ein billiges Blinklicht so viel abverlangt
Denkfehler deinerseits (aber typisch für Einsteiger, also nicht schlimm ;-) ): Das Blinken ist easy, die GUI braucht relativ viel Code.

Blinke geht so:

Code: Alles auswählen

rom gpiozero import LED
from signal import pause

led = LED(17)
led.blink()
pause()
GUI-Programmierung mit Python braucht halt Wissen rund um Objektorientierung, Klassen und Methoden, sonst kommt man nicht weit.

Gruß, noisefloor
PyNo
User
Beiträge: 7
Registriert: Montag 17. September 2018, 21:07

Hallo zusammen,
@ noisefloor, so ich hab mich mal durch deinen code geboxt und das Blinklicht zum laufen gebracht (bzw du hast es zum laufen gebracht).
ich hätte trotzdem noch die ein oder ander Frage zu deinem Code.

1. Frage: Wieso ist eigentlich hinter tk.Tk.__init__(self) kein Doppelpunkt? Natürlich darf da keiner hin weil sonst sagt er "invalid Syntax".
Aber wieso? Ich meine sonst muss ja auch immer ein Doppelpunkt dahinter.

2. Frage: Zum code Schnipsel
Wir definieren eine Klasse "Blinklicht_Gui" und vererben das an die Unterklasse tk.Tk, worin unsere gesamte Gui sich befindet oder besser gesagt der Bauplan zur Gui.
Kann man das so sagen oder ist das falsch ausgedrückt?

Code: Alles auswählen

class Blinklicht_Gui(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.title("LED")
        self.geometry("400x400")
3. Frage: Wieso muss man eigentlich in der tk.Tk Klasse die Methoden erneut aufrufen (initialisieren).
Ich mein wenn man den Button drückt dann wird doch die Methode eh aufgerufen.

4. Frage: Ich hab die gesamte Sache mal um einen Button erweitert und nicht mehr einen "Ausschalten Button" sondern für jede LED einen Ausschalter.
Das bedeutet wir haben bei der Methode ausschalten nicht nur unseren self Parameter sondern noch einen weiteren.

Code: Alles auswählen

def ausschalten(self, leds):
    self.states[leds] = cycle([0])
Leider musste ich feststellen das man so keine Methode aufrufen kann weil "states" nicht definiert ist.
(Komischerweise kann man aber Methoden aufrufen die nur einen self - Parameter haben und dann wird das Objekt mit dem Namen states definiert)
Bzw. ich geh mal davon aus das er die Methode checkt, dann die Argumente, dann die rechte seite (cycle) und erst dann die linke seite (self.states) und durch diese Reihenfolge kann er das Argument LED nicht weiter übergeben da es zu diesem Zeitpunkt noch nicht definiert wurde.
Wie könnte man denn das Problem lösen?
Oder wo kann ich denn das definieren damit er nicht weiter rummeckert.
Bei stack overflow meinte jemand das man sowas gleich in die init - Methode stecken soll dann muss man sich später nicht rumärgern
wenn die Fehlermeldung kommt (zb. in meinem Fall: 'tkapp' object has no attribute 'states')
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

@PyNo
Dein erster OOP-Versuch weiter oben ist leider nicht so toll. OOP heißt nicht, dass man nur alles in __init__() an self bindet, ohne später auf self zuzugreifen (bis auf eine Ausnahme). Dann kann man das auch gleich als Funktionen schreiben. Da würde ich nochmal die Strukturierung überdenken an deiner Stelle. ;-)
Sirius3
User
Beiträge: 17747
Registriert: Sonntag 21. Oktober 2012, 17:20

Auch wenn ich nicht noisefloor bin, zu Deinen Fragen: Doppelpunkte stehen nur wenn ein neuer Block beginnt, ein Funktionsaufruf leitet aber keinen Block ein.
Blinklicht_Gui ist die Unterklasse, besser Kindklasse, tk.Tk dagegen die Elternklasse.
Ein Methode ruft nicht automatisch die Methode der Elternklasse auf, das muß man schon selbst tun, hier ruft also Blinklicht_Gui.__init__ tk.Tk.__init__ auf, weil sonst ja das Fenster nicht richtig initialisiert wird. Die Frage nach dem Button verstehe ich nicht.

Du mußt für das ›ausschalten‹ nur exakt das selbe machen wie beim ›anschalten‹. Die Fehlermeldung kann ich nicht nachvollziehen, dazu bräuchte man den genauen Code, den Du dafür geschrieben hast.
Benutzeravatar
noisefloor
User
Beiträge: 3856
Registriert: Mittwoch 17. Oktober 2007, 21:40
Wohnort: WW
Kontaktdaten:

Hallo,
Auch wenn ich nicht noisefloor bin, zu Deinen Fragen:
Ich hatte darauf nicht geantwortet, weil es ja Sirius3 Code ;-)

Gruß, noisefloor
PyNo
User
Beiträge: 7
Registriert: Montag 17. September 2018, 21:07

Oh shit, sorry Leute da hab ich mich verguckt.
Mein Beitrag galt für Sirius3, zumindest die Fragen zu seinem Code.
Antworten