#GPIO # TKINTER

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
marouensassi
User
Beiträge: 2
Registriert: Dienstag 26. November 2019, 09:09

Hallo Profis und Experte ; )


ich habe einen Problem beim GPIO-taster zu steuren ; beim Taster betätigen ( gpio.input(16)==1 ) soll ein TEXT auf dem GUI statusbar anzeigen, tut sich aber nichts

alle andern Funktionen ( ) sind Ok , ( ist das überhaup möglich ? alternative velleicht ?ich brauche eure Hilfe .... ) ( Vorschläge für das Code? andere Schreibweise ?)

Danke im Voraus


Code: Alles auswählen

 

    import time as t
    	from tkinter import*
    	import threading
    	import RPi.GPIO as gpio
    	diff = 0
    	diff2 = 0
    	notaus1 = 0
    	notaus2= 0
    	gpio.setmode(gpio.BCM)
    	gpio.setwarnings(False)
    	''' INPUT  definieren  '''
    	gpio.setup(16, gpio.IN, pull_up_down = gpio.PUD_DOWN) #start
    	#_________________________destroy_Funktion()__________________
    	def on_closing():
    	   
    	   window.destroy() 
    	#_____________________________PROBLEM IST HIER----(Taster_Funktion())________________
    	def GPIO_TASTER () :
    	  while gpio.input(16)== 1:
    	        statusbar1["text"] ="Button gedrückt"
    	         
    	threading.Thread(target=Funktion_knopf).start()   
    	  
    	   #_____________________________stop_BUTTON______________________________
    	def stop ():
    	   global notaus1
    	   global notaus2
    	   notaus1 = 1
    	   notaus2 = 1
    	   statusbar1["text"] ="STOP"
    	   
    	    
    	#" _________________________Start_Wischen_______________________________"
    	                #---------------Knopf-----------------
    	def Start_wischen ():
    	   global notaus1
    	   global notaus2
    	   notaus2 = 0.2
    	   statusbar1["text"] ="Start_Wischen"
    	   Wischen()
    	   
    	                # -------------Function()-------------
    	def Wischen () :
    	   if notaus1 != 0.1 :
    	        
    	        def Run2 ():
    	            
    	            global diff2
    	            start_t2 = t.time() 
    	            while notaus2==0.2 and  diff2 < 23 :
    	                end_t2 =t.time()
    	                diff_2 =int(end_t2 - start_t2)+1000
    	                statusbar2["text"] = ("time_Wischen is :", diff_2)
    	                
    	        threadd = threading.Thread(target=Run2).start()
    	        
    	#" _________________________ SENSOR-TEST_______________________________"
    	                #---------------Knopf-----------------
    	def Start_SensorTest () :
    	   global notaus1 
    	   global notaus2
    	   notaus1 =0.1
    	   statusbar1["text"] ="Start_Test"
    	   Test()
    	               # -------------Function()-------------
    	def Test (): 
    	   if notaus2 != 0.2 :
    	       
    	    def Run():
    	        
    	        global diff
    	        start_t = t.time()
    	        while notaus1 == 0.1   and diff < 15 :  
    	            end_t = t.time()
    	            diff = int(end_t - start_t)
    	            statusbar2["text"] = ("Test_time is :", diff)
    	            
    	    thread = threading.Thread(target=Run).start()
    	   
    	#" _________________________ GUI_______________________________"
    	window = Tk()
    	window.title("GUI")
    	#outputs
    	statusbar1 = Label(window, text="auf start druecken", width=25,height =5, borderwidth=15 ,relief = SUNKEN)
    	statusbar1.grid(row= 6, column = 2)
    	statusbar2 = Label(window, text="Time", width=25,height =5, borderwidth=15 ,relief = SUNKEN)
    	statusbar2.grid(row= 7, column = 2)
    	# variable to store input
    	b1=Button(window, text="SensorTest",command =Start_SensorTest)
    	b1.grid(row=2, column = 2, padx= (100,5), pady=5)
    	b2=Button(window, text="Startwischen ",command = Start_wischen)
    	b2.grid(row=3, column = 2, padx= (100,5), pady=5)
    	b3=Button(window, text="Abbrechen ",command =stop)
    	b3.grid(row=4, column = 2, padx= (100,5), pady=5)
    	window.protocol("WM_DELETE_WINDOW", on_closing)
    	window.mainloop()
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Sirius3
User
Beiträge: 18227
Registriert: Sonntag 21. Oktober 2012, 17:20

Als erstes solltest Du mal den Code aufräumen. time nicht als t abkürzen, keine *-Importe, hier `import tkinter as tk`.
Die Einrückungen sind allesamt kaputt. Eingerückt wird immer mit 4 Leerzeichen pro Ebene.
Kein `global` keine verschachtelten Funktionen.
Statt seltsamer Kommentarzeilen die keinen Mehrwert bieten benenne Deine Funktionen und Variablen aussagekräftig.

Dann noch das, was auch schon im anderen Forum geschrieben wurde: Threads dürfen die GUI nicht verändern. Benutze add_event_detect, eine Queue für die Kommunikation und eine Ereignisschleife mit after.
Gibt es hier im Forum auch schon etliche Beispiele dafür.
Benutzeravatar
__blackjack__
User
Beiträge: 13938
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@marouensassi: Warum ist das denn so komisch eingerückt? Da ist zu viel Einrückung vor jeder Zeile und eingerückt wird mit vier Leerzeichen pro Ebene.

Namen sollten nicht kryptisch abgekürzt werden und einbuchstabige Namen sind in aller Regel gar nicht gut. Das geht wenn der Gültigkeitsbereich auf einen Ausdruck beschränkt ist, oder für `i`, `j`, `k` wenn das ganzzahlige Indexwerte sind, oder `x`, `y`, `z` für Koordinaten. Aber sicher nicht für das `time`-Modul das modulweit sichtbar ist.

Für den Zweck würde man auch nicht die `time.time()`-Funktion verwenden, sondern `time.monotonic()`.

Sternchen-Importe sind Böse™. Bei `tkinter` holt man sich so fast 200 Namen ins Modul von denen nur ein kleiner Bruchteil verwendet wird. Nicht nur Namen die in `tkinter` definiert werden, sondern auch welche die `tkinter` selbst von woanders importiert.

Auf Modulebene gehört nur Code der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Literale Zeichenketten sind keine Kommentare, dafür ist ``#`` da. Diese komischen Linienkommentare sollte man sich auch sparen. Insgesamt sind die meisten Kommentare in dem Quelltext überflüssig. Einige sogar irreführend oder gar falsch. Faustregel: Kommentare erklären nicht was der Code macht, denn das steht da ja bereits als Code, sondern warum er das so macht, sofern das nicht offensichtlich ist.

Kommentare die einen schlechten oder unpassenden Namen erklären sollte man durch eine bessere Namensgebung beseitigen.

Bei Funktionsdefinitionen kommt zwischen Funktionsname und öffnender Klammer der Argumentliste kein Leerzeichen. Insgesamt ist das mit Leerzeichen und auch -zeilen ziemlich uneinheitlich.

Man sollte keine Warnungen unterdrücken sondern deren Ursache beseitigen. Das heisst vor dem Ende des Programms `gpio.cleanup()` aufrufen. Was man am besten mit einem ``try``/``finally`` sicherstellt.

Für magische Zahlen wie Pinnummern definiert man am besten eine Konstante, damit man die a) leicht ändern kann, und b) überall statt einer nichtssagenden Zahl ein verständlicher Name steht.

Namen werden in Python klein_mit_unterstrichen geschrieben. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

`Funktion_knopf` ist nirgends definiert. Das sollte `GPIO_TASTER` sein‽

Threads sollte man in der Regel die `daemon`-Option mitgeben, sonst lässt sich das Programm nicht so leicht beenden.

Wenn man Namen durchnummeriert will man sich entweder bessere Namen ausdenken, oder gar keine Einzelwerte sondern eine Datenstruktur. Oft eine Liste.

Ein Grid-Layout macht hier nicht wirklich Sinn wenn alle Elemente in Spalte 2 untereinander landen. Man sollte auch keine Spalten oder Zeilen frei lassen.

Alles was eine Funktion oder Methode ausser Konstanten benötigt, wird als Argument(e) übergeben. ``global`` hat in einem normalen Programm nichts zu suchen. Wenn man sich Zustand über Funktionsaufrufe hinweg merken möchte und das nicht über Rückgabewerte geregelt werden kann, braucht man objektorientierte Programmierung (OOP). Bei `tkinter` kann man sich bis zu einem gewissen Grad noch mit `tkinter.Variable`-Objekten und `functools.partial()` durchmogeln, aber letztlich braucht man für jede nicht-triviele GUI OOP.

Mich machen ja diese ganzen `notaus1`, `notaus2`, `diff`, `diff2` wuschig. Und dann gibt es auch noch `diff2` und `diff_2` in *einer* Funktion. Da besteht hochgradig Verwechslungsgefahr beziehungsweise vermute ich mal ganz stark das `diff_2` eigentlich `diff2` heissen sollte‽ Und die beiden `diff`\s müssen die wirklich global sein oder als Argumente übergeben werden? Im Moment gehen die beiden Aktionen die man mit den Buttons anstossen kann ja nur *einmal*, beziehungsweise bis `diff` und `diff2` ihren jeweiligen höchstwert erreicht haben, denn die werden ja dann nie wieder unter diesen Wert gesetzt.

Das aus Python-Tupeln in Tcl Listen werden und die wiederrum als Zeichenkette interpretiert zu den einzelnen Bestandteilen durch ein Leerzeichen getrennt werden ist für Python-Programmierer eher undurchsichtig. Da sollte man auf Python-Seite die Zeichenkette mit Python-Mitteln formatieren.

Die beiden `Run()`-Funktionen sollten nicht verschachtelt definiert sein, und dann auch bessere Namen als `Run()` und `Run2()` haben. Zudem wurde ja schon angemerkt, dass man die GUI nicht von anderen Threads aus manipulieren darf, also sollten hier keine Threads sondern auch `after()` verwendet werden.

Die Frage ist auch ob das überhaupt zwei Funktionen sein sollten, denn die sehen nahezu gleich aus. Man könnte also auch *eine* Funktion schreiben die entsprechend parametriert wird.

Im Grunde sind die beiden Startfunktionen auch sehr symmetrisch und unterscheiden sich nur durch Werte, aber es gibt so schon genug/zu viele Argumente. Das will man wirklich sinnvoll auf Klassen aufteilen.

Was auch ziemlich schräg ist sind die Werte die `notaus1` und `notaus2` annehmen können. `notaus1` kann die Werte 0, 0.1, und 1 annehmen, und `notaus2` die Werte 0, 0.2, und 1. Mit den Zahlen wird nirgends gerechnet und die werden nirgends angezeigt und es wird nur auf Ungleichheit gegen 0.1 beziehungsweise 0.2 getestet. Was soll das denn bedeuten? Kann es sein, dass das einfach nur Wahrheitswerte sein sollten statt so komischer Zahlen‽

Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk
from functools import partial
from time import monotonic

from RPi import GPIO as gpio

BUTTON_PIN = 16


def update_status_bar(statusbar):
    #
    # FIXME This does not work in a GUI as a thread.  Use callbacks from `gpio`,
    # a `queue.Queue` and `after()` to communicate with the main thread.
    #
    while gpio.input(BUTTON_PIN):
        statusbar["text"] = "Button gedrückt"


def _run(
    time_label,
    notaus,
    diff,
    start_time,
    notaus_value,
    max_diff,
    diff_offset,
    label_prefix,
):
    if notaus.get() == notaus_value and diff.get() < max_diff:
        end_time = monotonic()
        diff.set(int(end_time - start_time) + diff_offset)
        time_label["text"] = f"{label_prefix}: {diff.get()}"
        time_label.after(
            100,
            _run,
            time_label,
            notaus,
            diff,
            start_time,
            notaus_value,
            max_diff,
            diff_offset,
            label_prefix,
        )


def start_sensor_test(notaus1, notaus2, diff, statusbar, time_label):
    notaus1.set(0.1)
    statusbar["text"] = "Start Test"
    if notaus2.get() != 0.2:
        _run(
            time_label, notaus1, diff, monotonic(), 0.1, 15, 0, "Test time is"
        )


def start_wischen(notaus1, notaus2, diff, statusbar, time_label):
    notaus2.set(0.2)
    statusbar["text"] = "Start Wischen"
    if notaus1.get() != 0.1:
        _run(
            time_label,
            notaus2,
            diff,
            monotonic(),
            0.2,
            23,
            1000,
            "Time wischen is",
        )


def on_stop(notaus1, notaus2, statusbar):
    notaus1.set(1)
    notaus2.set(1)
    statusbar["text"] = "STOP"


def main():
    try:
        gpio.setmode(gpio.BCM)
        gpio.setup(BUTTON_PIN, gpio.IN, pull_up_down=gpio.PUD_DOWN)

        window = tk.Tk()
        window.title("GUI")

        diff = tk.IntVar(value=0)
        diff2 = tk.IntVar(value=0)
        notaus1 = tk.DoubleVar(value=0)
        notaus2 = tk.DoubleVar(value=0)

        pack_options = {"padx": (100, 5), "pady": 5}
        sensor_test_button = tk.Button(window, text="Sensor Test")
        sensor_test_button.pack(**pack_options)
        start_wischen_button = tk.Button(window, text="Start wischen")
        start_wischen_button.pack(**pack_options)
        cancel_button = tk.Button(window, text="Abbrechen")
        cancel_button.pack(**pack_options)

        label_options = {
            "width": 25,
            "height": 5,
            "borderwidth": 15,
            "relief": tk.SUNKEN,
        }
        statusbar = tk.Label(window, text="auf start drücken", **label_options)
        statusbar.pack()
        time_label = tk.Label(window, text="Time", **label_options)
        time_label.pack()

        sensor_test_button["command"] = partial(
            start_sensor_test, notaus1, notaus2, diff, statusbar, time_label
        )
        start_wischen_button["command"] = partial(
            start_wischen, notaus1, notaus2, diff2, statusbar, time_label
        )
        cancel_button["command"] = partial(
            on_stop, notaus1, notaus2, statusbar
        )

        window.mainloop()
    finally:
        gpio.cleanup()


if __name__ == "__main__":
    main()
“Java is a DSL to transform big Xml documents into long exception stack traces.”
— Scott Bellware
Antworten