variable per slider ändern und einer Funktion übergeben

Python auf Einplatinencomputer wie Raspberry Pi, Banana Pi / Python für Micro-Controller
Antworten
jockelb
User
Beiträge: 3
Registriert: Mittwoch 24. August 2016, 08:42

Hallo

ich habe eine Gartenbewässerung gebaut mit einem Raspberry Pi3. Das funktioniert auch alles ganz gut.Jetzt soll noch eine GUI her.
Die tasten funktionieren auch wunderbar. Nur muss ich zum einschalten und zum ausschalten auf den button klicken. Das will ich durch einen Slider den man für das warten bis das Wasser wieder abgestellt werden soll einstellen kann.
Ich möchte also erst den schieberegler auf z.B. 300 Sekunden stellen und dann das Bewässern starten für die 300Sekunden
Leider kriege ich das nicht hin. Mit globalen und lokalen Variable habe ich schon erklärungen gelesen ,krieg ich irgenwie nicht in mein kopf
Bisher sieht es so aus.

Code: Alles auswählen

from tkinter import *
import RPi.GPIO as GPIO
GPIO.setmode(GPIO.BOARD)
GPIO.setwarnings(False)

hochbeet_pin = 33
kirschbaum_pin = 31
rasen_pin = 15
schuppen_pin = 29
teich_pin = 13


GPIO.setup(hochbeet_pin, GPIO.OUT)
GPIO.setup(kirschbaum_pin,GPIO.OUT)
GPIO.setup(rasen_pin, GPIO.OUT)
GPIO.setup(schuppen_pin, GPIO.OUT)
GPIO.setup(teich_pin, GPIO.OUT)


 
root = Tk() # Fenster erstellen
root.wm_title("Garten  GUI") # Fenster Titel
root.config(background = "#FFFFFF") # Hintergrundfarbe des Fensters
 
# Hier kommen die Elemente hin
leftFrame = Frame(root, width=200, height = 400)
leftFrame.grid(row=0, column=0, padx=10, pady=3)
 
leftLabel1 = Label(leftFrame, text="Platzhalter Text")
leftLabel1.grid(row=0, column=0, padx=10, pady=3)
leftLabel2 = Label(leftFrame, text="Dies ist ein Text\nmit mehreren Zeilen.")
leftLabel2.grid(row=1, column=0, padx=10, pady=3)
 
imageEx = PhotoImage(file = '200x200')
Label(leftFrame, image=imageEx).grid(row=2, column=0, padx=10, pady=3)
 
 
rightFrame = Frame(root, width=800, height = 600)
rightFrame.grid(row=0, column=1, padx=10, pady=3)
 
#E1 = Entry(rightFrame, width=50)
#E1.grid(row=0, column=0, padx=10, pady=3)
 
def schuppen():
    GPIO.output(schuppen_pin, True)
def hochbeet():
    GPIO.output(hochbeet_pin, True)

def rasen():
    GPIO.output(rasen_pin, True)
def kirschbaum():
    GPIO.output(kirschbaum_pin, True)

def teich():
    GPIO.output(teich_pin, True)
def aus():
    GPIO.output(schuppen_pin, False)
    GPIO.output(hochbeet_pin, False)
    GPIO.output(rasen_pin, False)
    GPIO.output(teich_pin, False)
    GPIO.output(kirschbaum_pin, False)	
def auto():
    GPIO.output(schuppen_pin, True)
	sleep (warten,get())  
    GPIO.output(schuppen_pin, False)
	sleep 1

    GPIO.output(hochbeet_pin, True)
	sleep (warten,get())
    GPIO.output(hochbeet_pin, False)
	sleep 1

    GPIO.output(rasen_pin, True)
	sleep (warten,get())
    GPIO.output(rasen_pin, False)
	sleep 1

    GPIO.output(teich_pin, True)
	sleep (warten,get())
    GPIO.output(teich_pin, False)
	sleep 1

    GPIO.output(kirschbaum_pin, True)
	sleep (warten,get())
    GPIO.output(kirschbaum_pin, False)

 
buttonFrame = Frame(rightFrame)
buttonFrame.grid(row=1, column=0, padx=10, pady=3)
    
B1 = Button(buttonFrame, text="Gewächshaus", bg="#FFFF00", width=15, command=schuppen)
B1.grid(row=0, column=0, padx=20, pady=10)
 
B2 = Button(buttonFrame, text="Hochbeet", bg="#FFFF00", width=15, command=hochbeet)
B2.grid(row=0, column=1, padx=20, pady=10)

B3 = Button(buttonFrame, text="Kirschbaum", bg="#FFFF00", width=15, command=kirschbaum)
B3.grid(row=1, column=0, padx=20, pady=10)

B4 = Button(buttonFrame, text="Rasen", bg="#FFFF00", width=15, command=rasen)
B4.grid(row=1, column=1, padx=20, pady=10)

B5 = Button(buttonFrame, text="Teich", bg="#FFFF00", width=15, command=teich)
B5.grid(row=2, column=0, padx=20, pady=10)

B6 = Button(buttonFrame, text="AUS", bg="#FF0000", width=15, command=aus)
B6.grid(row=2, column=1, padx=20, pady=10)

B7 = Button(buttonFrame, text="AUTOMATIK", bg="#FF0000", width=15, command=auto)
B7.grid(row=3, column=2, padx=20, pady=10)




 
warten = Scale(rightFrame, from_=30, to=1200, resolution=10, orient=HORIZONTAL, length=400)
warten.grid(row=2, column=0, padx=10, pady=3)
 
 
root.mainloop() # GUI wird upgedatet. Danach keine Elemente setzen
 


kann mir da jemand helfen?

Danke
Zuletzt geändert von Anonymous am Donnerstag 6. Juli 2017, 10:08, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das ist niemals Code den du laufen laesst. Da sind krasse Syntaxfehler drin (sleep 1 zb geht so nicht), sleep als Funktion ist nicht importiert und nimmt nur ein Argument, nicht zwei. get() gibt es nicht. Und so weiter.

Offensichtlich hast du einfach Code von https://tutorials-raspberrypi.de/progra ... erstellen/ kopiert, den mit deinem GPIO-Kram vermanscht, und hoffst jetzt darauf, dass hier einer bloed genug ist, dir ein fertiges Programm zu schreiben.

Diese Abgreifer-Mentalitaet die gerade im Raspberry PI Lager herrscht finde ich persoenlich zum kotzen. Aus der Idee eines Forums wie diesem sich gegenseitig zu unterstuetzen beim Versuch, die Python zu bezwingen, wird versucht einen kostenlosen Selbstbediengungsladen zu machen, bei dem man auch noch pissig wird wenn die Leute nicht sofort springen wenn man ankommt. :evil: :evil: :evil:
BlackJack

@jockelb: Da sind Syntaxfehler drin, das ist also nicht der Code den Du tatsächlich laufen lässt. Wenn man die Syntaxfehler behebt, läuft das aber immer noch nicht weil noch weitere Fehler enthalten sind. Und das ist *nicht* nur im GUI-Teil, kann also auch nicht vor dem Einbau der GUI so funktioniert haben.

Du vermischst auch GUI und Programmlogik und das alles direkt auf Modulebene. Dort sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Konstantennamen werden per Konvention `KOMPLETT_GROSS_GESCHRIEBEN`. Andere Namen ausser Konstanten und Klassen `klein_mit_unterstrichen`. Siehe auch den Style Guide for Python Code.

Sternchen-Importe sind Böse™. Da verliert man ziemlich schnell den Überblick was wo definiert ist und wo etwas her kommt, und es besteht die Gefahr von Namenskollisionen.

Die Warnungen von `GPIO` sollte man nicht ausschalten sondern den Grund für die Warnungen beseitigen. Zum Beispiel in dem man dafür sorgt, das am Programmende auf jeden Fall `GPIO.cleanup()` aufgerufen wird.

Die meisten `GPIO`-Funktionen die Pin-Nummern entgegen nehmen, können statt mit einer Nummer auch mit einer Liste von Nummern aufgerufen werden. Also `setup()` und `output()` beispielsweise.

Kommentare sollten dem Leser einen Mehrwert über den Code bieten, also nicht einfach nur das noch mal sagen, was da schon als Code sehr deutlich steht. Faustregel: Nicht kommentieren *was* der Code macht, das steht bereits da, sondern *warum* er das so macht. Und das auch nur sofern das unklar ist. Und zwar jemandem der die Sprache kann und die Dokumentation lesen kann.

Links und Rechts in den Namen für GUI-Elemente sind keine gute Idee. Wenn man die GUI dann mal anders organisieren möchte, hat man entweder falsche und irreführende Namen, oder muss die alle ändern. Besser ist es Namen zu wählen die Beschreiben was in den GUI-Elementen angezeigt wird.

Die ganzen Funktionen die Pins aktiv setzen haben keine üblichen Funktionsnamen, also ein Name der die Tätigkeit der Funktion beschreibt. Zudem unterscheiden sich die Funktionen nur durch die Argumente der `GPIO.output()`-Funktion. Dafür müsste man keine Funktionen schreiben, sondern könnte die mit `functools.partial()` erzeugen. Auch `aus()`, weil man wie schon gesagt auch eine Liste mit Pin-Nummern an `GPIO.output()` übergeben kann.

In `auto()` wiederholt sich immer wieder der gleiche Quelltextblock mit nur minimalen Änderungen. Das würde man besser als Schleife über die Pin-Nummern schreiben, denn die sind ja das einzige was sich verändert.

`auto()` wird mit einer GUI so aber nicht funktionieren, denn die Rückruffunktionen dürfen nur kurz laufen. Während eine Funktion läuft die aus der GUI-Hauptschleife heraus aufgerufen wurde, blockiert die gesamte GUI. Das heisst man muss die einzelnen Schritte entweder in einzelne kurze Aufrufe aufteilen, oder mit Threads oder Prozessen arbeiten, zum Beispiel mit `concurrent.futures`.

Funktionen und Methoden sollten nur mit Werten arbeiten die als Argumente übergeben wurden. Ausnahme: Konstanten. `auto()` dürfte also nicht einfach so auf `warten` zugreifen.

Wenn man durchnummerierte Namen hat, will man sich entweder mehr Gedanken um sinnvolle Namen machen, oder eine Datenstruktur anstelle von einzelnen Namen verwenden. Meistens eine Liste. In diesem Fall ist das bei den `Button`\s aber total unnötig unterschiedliche Namen zu verwenden, denn die werden ja sowieso nicht weiter benötigt. Da kann man auch einfach jeden an den Namen `button` binden, solange man das Objekt braucht. Oder man fasst die beiden Schritte zu einem Ausdruck zusammen, dann braucht man gar keinen Namen vergeben.

Für GUI-Programmierung muss man letztendlich objektorientierte Programmierung (OOP) einsetzen. Dann braucht man auch keine globalen Variablen, die man sowieso vermeiden sollte, da sie in der Regel mehr Probleme schaffen als Lösen. Bei dem bisher gezeigten kommt man vielleicht noch mit `functools.partial()` und Funktionen aus, aber auf lange Sicht solltest Du OOP lernen.
jockelb
User
Beiträge: 3
Registriert: Mittwoch 24. August 2016, 08:42

Da ich mit meinen uralten C64 Programmierkennissen in Basic laut einen Video tutorial gut geeignet bin für Python habe ich mich daran gewagt. Ich sehe ein das es nicht reicht und werde hier teilnehmen
https://www.vhsit.berlin.de/VHSKURSE/Bu ... ?id=458451
Ich hoffe Beruhigt deets ein wenig und ich verstehe danach mehr von BlackJack
Ist ganz klar keine Werbung für die Schule. Ist für mich nur der logische Schritt

MFG

Jörg
BlackJack

@jockelb: Also CBM BASIC V2 hat mit Python eher nicht so viel zu tun. Ausser das Ausgaben mit ``print`` gemacht werden, Zuweisung von Werten an Namen mit ``=``, und das es Zahlen, Zeichenketten, und die Grundrechenarten gibt, haben die kaum etwas miteinander zu tun. Das ``for``-Schlüsselwort haben sie beide, aber ``for``-Schleifen funktionieren schon komplett anders in beiden Programmiersprachen.

Mal die meisten Punkte aus meinem letzten Beitrag umgesetzt, allerdings noch ohne OOP, und komplett ungetestet:

Code: Alles auswählen

from functools import partial
import tkinter as tk
from RPi import GPIO

HOCHBEET_PIN = 33
KIRSCHBAUM_PIN = 31
RASEN_PIN = 15
SCHUPPEN_PIN = 29
TEICH_PIN = 13
ALL_PINS = [SCHUPPEN_PIN, HOCHBEET_PIN, RASEN_PIN, TEICH_PIN, KIRSCHBAUM_PIN]


def do_step_a(button, pins, delay):
    try:
        pin = next(pins)
    except StopIteration:
        button['state'] = tk.NORMAL
    else:
        GPIO.output(pin, True)
        button.after(delay, do_step_b, button, pin, pins, delay)


def do_step_b(button, current_pin, pins, delay):
    GPIO.output(current_pin, False)
    button.after(1000, do_step_a, button, pins, delay)


def start_automatic(button, delay_scale):
    button['state'] = tk.DISABLED
    do_step_a(button, iter(ALL_PINS), delay_scale.get() * 1000)
 

def main():
    GPIO.setmode(GPIO.BOARD)
    try:
        GPIO.setup(ALL_PINS, GPIO.OUT)
         
        root = tk.Tk()
        root.title('Garten GUI')
        root.config(background='white')
         
        frame = tk.Frame(root)
         
        tk.Label(
            frame, text='Platzhalter Text'
        ).grid(row=0, column=0, padx=10, pady=3)
        tk.Label(
            frame, text='Dies ist ein Text\nmit mehreren Zeilen.'
        ).grid(row=1, column=0, padx=10, pady=3)
         
        image = tk.PhotoImage(file='200x200')
        tk.Label(frame, image=image).grid(row=2, column=0, padx=10, pady=3)
        
        frame.grid(row=0, column=0, padx=10, pady=3)

        frame = tk.Frame(root)
         
        button_frame = tk.Frame(frame)
        
        width = 15
        buttons_per_row = 2
        for i, (text, pin) in enumerate(
            [
                ('Gewächshaus', SCHUPPEN_PIN),
                ('Hochbeet', HOCHBEET_PIN),
                ('Kirschbaum', KIRSCHBAUM_PIN),
                ('Rasen', RASEN_PIN),
                ('Teich', TEICH_PIN),
            ]
        ):
            row, column = divmod(i, buttons_per_row)
            tk.Button(
                button_frame,
                text=text,
                bg='yellow',
                width=width,
                command=partial(GPIO.output, pin, True),
            ).grid(row=row, column=column, padx=20, pady=10)

        row = (i + buttons_per_row) // buttons_per_row
        background = 'red'
        
        tk.Button(
            button_frame,
            text='AUS',
            bg=background,
            width=width,
            command=partial(GPIO.output, ALL_PINS, False),
        ).grid(row=row, column=0, padx=20, pady=10)

        automatic_button = tk.Button(
            button_frame,
            text='AUTOMATIK',
            bg=background,
            width=width,
        )
        automatic_button.grid(row=row, column=1, padx=20, pady=10)
         
        button_frame.grid(row=1, column=0, padx=10, pady=3)

        delay_scale = tk.Scale(
            frame,
            from_=30,
            to=1200,
            resolution=10,
            orient=tk.HORIZONTAL,
            length=400,
        )
        delay_scale.grid(row=2, column=0, padx=10, pady=3)
         
        frame.grid(row=0, column=1, padx=10, pady=3)

        automatic_button['command'] = partial(
            start_automatic, automatic_button, delay_scale
        )
        root.mainloop()
    finally:
        GPIO.cleanup()


if __name__ == '__main__':
    main()
Und spasseshalber, ebenfalls komplett ungetestet, dazu als Vergleich wie das auf dem C64 in BASIC aussehen könnte wenn man die CIA #2 Port B Leitungen am Userport an Hochbeet & Co anschliessen würde:
[codebox=locobasic file=Unbenannt.txt] 10 DP=56579:DJ=5184E3:D=0:N=5:SA=30
20 DIM T$(N):T$(1)="HOCHBEET":T$(2)="KIRSCHBAUM":T$(3)="RASEN"
30 T$(4)="SCHUPPEN":T$(5)="TEICH":POKE 56577,2↑N-1
50 PRINT "{CLEAR}GARTENSTEUERUNG":PRINT "---------------":PRINT
60 FOR I=1 TO N:PRINT I;") ";T$(I);" EIN":NEXT
70 PRINT " X) ALLE AUS":PRINT " A) AUTOMATIK"
80 PRINT " +/-) AUTOMATIK ZEIT +/- 10 S"
100 GOSUB 400:POKE DP,D
110 GET A$:IF A$="" THEN 110
120 IF A$="X" THEN D=0:GOTO 100
130 IF A$="A" THEN GOSUB 500:GOTO 50
140 IF A$="+" THEN X=1:GOTO 180
150 IF A$="-" THEN X=-1:GOTO 180
160 P=ASC(A$)-ASC("1"):IF P>=0 AND P<N THEN D=D OR 2↑P
170 GOTO 100
180 SA=SA+X*10:IF SA<30 THEN SA=30
190 IF SA>1200 THEN SA=1200
200 GOTO 100
400 PRINT "{HOME}";:FOR I=1 TO N+8:PRINT:NEXT
410 PRINT "AUTOMATIK ZEIT:";SA;"SEKUNDEN ":RETURN
500 P=3:GOSUB 600:P=0:GOSUB 600:P=2:GOSUB 600:P=4:GOSUB 600:P=1
600 PRINT T$(P);" AN":T=2↑P:D=D OR T:POKE DP,D:X=SA:GOSUB 700
610 PRINT T$(P);" AUS":D=D AND NOT T:POKE DP,D:X=1
700 T0=TI:TE=T0+X*60:IF TE>DJ THEN TE=TE-DJ
710 IF TI+DJ*(TE<T0)<TE THEN 710
720 RETURN[/code]
Wenn man das schreibt ist man nicht automatisch gut geeignet für eine moderne, objektorientierte Programmiersprache auf Rechnern mit grafischer Oberfläche. :-D
Antworten