Seite 1 von 1

Schalter zum hin und Herschalten

Verfasst: Donnerstag 25. Februar 2021, 20:04
von Failix
Hallo zusammen,

ich habe folgendes Problem.

Ich möchte einen Button machen, der bei jeden klick abwechseln ein Dreieck rot und grün verfärbt.

Mein Ansatz der nicht funktioniert:

Code: Alles auswählen

ventil1_status = 0
farbe="red"
...

Code: Alles auswählen

def schaltung_ventil1():
    global ventil1_status
    global farbe
    if ventil1_status == 1:
        ventil1_status = 0
        farbe="green"
    else:
        ventil1_status = 1
        farbe="red"
....

Code: Alles auswählen

 zeichner.create_polygon(ventil1[0], ventil1[1], ventil1[0]-10, ventil1[1]-20, ventil1[0]+10, ventil1[0]-20, fill="green")
 button_ventil1 = Button(schaltplan, text="Ventil1", command=schaltung_ventil1)
 button_ventil1.place(x=ventil1[0]-10, y=ventil1[0]+10)
 
...

Ich habe schon einiges herum versucht, wenn jemand den Fehler findet oder einen bessern weg weiß wäre das super nett. (Am Ende soll irgendwann noch mal ein raspi mit schalten.)

Re: Schalter zum hin und Herschalten

Verfasst: Donnerstag 25. Februar 2021, 20:24
von Sirius3
Aus den Code-Fragmenten kann man ja nichts rauslesen.
Die Fehlerbeschreibung "funktioniert nicht" ist auch recht unspezifisch.

Du mußt Dir die ID des Polygons merken, damit Du dann die Farbe auch nachträglich setzen kannst.

Re: Schalter zum hin und Herschalten

Verfasst: Donnerstag 25. Februar 2021, 20:31
von Failix
Sirius3 hat geschrieben: Donnerstag 25. Februar 2021, 20:24 Aus den Code-Fragmenten kann man ja nichts rauslesen.
Die Fehlerbeschreibung "funktioniert nicht" ist auch recht unspezifisch.

Du mußt Dir die ID des Polygons merken, damit Du dann die Farbe auch nachträglich setzen kannst.
Hier ist der volle Kot.

Ich dachte nur das den entsprechende Teile übersichtlicher sind:

Code: Alles auswählen

from tkinter import *
import random
#Position Ventile
ventil1 = [50, 50]
ventil1_status = 0
farbe="blue"
#Status Ventile

def schaltung_ventil1():
    global ventil1_status
    global farbe
    if ventil1_status == 1:
        ventil1_status = 0
        farbe="green"
    else:
        ventil1_status = 1
        farbe="red"



def schalt():

    schaltplan = Tk()
    schaltplan.title('Nukular Schaltplan')
    schaltplan.geometry('420x230')
    zeichner = Canvas(schaltplan)
    zeichner.pack()

    # Positionnierung Ventile


    zeichner.create_polygon(ventil1[0]-20, ventil1[1]+10, ventil1[0], ventil1[1], ventil1[0]-20, ventil1[0]-10, fill=farbe)
    zeichner.create_polygon(ventil1[0] + 20, ventil1[1] - 10, ventil1[0], ventil1[1], ventil1[0] + 20, ventil1[0] + 10)
    zeichner.create_polygon(ventil1[0], ventil1[1], ventil1[0]-10, ventil1[1]-20, ventil1[0]+10, ventil1[0]-20, fill="green")

    # Schaltung
    button_ventil1 = Button(schaltplan, text="Ventil1", command=schaltung_ventil1)

    button_ventil1.place(x=ventil1[0]-10, y=ventil1[0]+10)






    mainloop()

Re: Schalter zum hin und Herschalten

Verfasst: Donnerstag 25. Februar 2021, 20:50
von __deets__
Kot ist Scheisse. Code ist Programmcode. Der *kann* scheisse sein. Muss aber nicht.

Re: Schalter zum hin und Herschalten

Verfasst: Donnerstag 25. Februar 2021, 20:53
von Sirius3
*-Importe sind schlecht, weil sie unkontrolliert Namen in den eigenen Namensraum schaufeln.
random wird importiert aber gar nicht benutzt.
Vergiss gleich wieder, dass es `global` gibt, das löst keine Probleme.

Sobald man eine GUI mit Zustand hat, braucht man Klassendefinitionen, vor allem, wenn es scheinbar irgendwann mehrere Ventile geben soll.
Wie schon geschrieben, mußt Du Dir die ID merken, um später die Farbe setzen zu können.

mainloop sollte man auf der Tk-Instanz aufgerufen werden. schalte wird gar nicht aufgerufen.

Code: Alles auswählen

import tkinter as tk

class Ventil:
    def __init__(self, canvas, position):
        self.canvas = canvas
        self.status = 0
        x, y = position
        self.id = self.canvas.create_polygon(x - 20, y + 10, x, y, x - 20, y - 10, fill="blue")
        self.canvas.create_polygon(x + 20, y - 10, x, y, x + 20, y + 10)
        self.canvas.create_polygon(x, y, x - 10, y - 20, x + 10, y - 20, fill="green")

    def schalten(self):
        self.status = 1 - self.status
        self.canvas.itemconfig(self.id, fill="green" if self.status == 0 else "red")

def main():
    schaltplan = tk.Tk()
    schaltplan.title('Nukular Schaltplan')
    schaltplan.geometry('420x230')
    zeichner = tk.Canvas(schaltplan)
    zeichner.pack()
    ventil = Ventil(zeichner, [50, 50])
    tk.Button(schaltplan, text="Ventil1",
        command=ventil.schalten
    ).pack()
    schaltplan.mainloop()

if __name__ == "__main__":
    main()

Re: Schalter zum hin und Herschalten

Verfasst: Donnerstag 25. Februar 2021, 22:31
von Failix
Ok danke das hilft mir weiter.

Re: Schalter zum hin und Herschalten

Verfasst: Freitag 26. Februar 2021, 05:56
von __blackjack__
Wobei ich den `status` hier etwas sehr verschleiert finde. Das kann man machen wenn man keine Wahrheitswerte hätte, aber beim Initialisieren ``self.status = False`` und beim Schalten ``self.status = not self.status`` wäre IMHO deutlich weniger kryptisch.

Re: Schalter zum hin und Herschalten

Verfasst: Montag 1. März 2021, 19:53
von Failix
Danke noch mal ich kann jetzt auch damit den GPIO von Pi schalten.

Ich würde nur gerne diese Funktion alternativ zum Button noch über die Zeit auslösen können. (Am Ende soll es einen Programmablauf geben, bei dem ich noch eingreifen kann)

Wenn ich das versuche

Code: Alles auswählen

def Pro1():
    Ventil().schalten()
    time.sleep(3)
   
Bekommen ich folgenden Fehlermeldung.
thinker/___init___ return self.func(*args)

Re: Schalter zum hin und Herschalten

Verfasst: Montag 1. März 2021, 20:13
von Sirius3
Das ist keine Fehlermeldung, jedenfalls nur ein Teil davon, mit dem man nichts anfangen kann.
Zeige den kompletten Code und die komplette Fehlermeldung.

Re: Schalter zum hin und Herschalten

Verfasst: Dienstag 2. März 2021, 15:12
von Failix
@Sirius3

Das ist im der Code von dir den ich mit import hole (ich habe auf die Namen geachtet):

Code: Alles auswählen

import random
import fake_GPIO as GPIO
#import RPi.GPIO as GPIO
import tkinter as tk
import time
from Schaltplan import *

def Pro1():
    Ventil().schalten()
    time.sleep(3)

Exception in Tkinter callback
Traceback (most recent call last):
File "/home/felix/miniconda3/envs/pythonProject2/lib/python3.8/tkinter/__init__.py", line 1883, in __call__
return self.func(*args)
File "/home/felix/PycharmProjects/Nukular/Programm1.py", line 9, in Pro1
Ventil2().schalten2()
TypeError: __init__() missing 2 required positional arguments: 'canvas' and 'position'


Alternativ habe ich versucht

Code: Alles auswählen

import random
import fake_GPIO as GPIO
#import RPi.GPIO as GPIO
import tkinter as tk
import time
from Schaltplan import *

def Pro1():
    Ventil2().__init__().schalten2()
    time.sleep(3)

Code: Alles auswählen

def Pro1():
    schalten2()
    time.sleep(3)

Code: Alles auswählen

def Pro1():
    Ventil2().__init__(position=1).schalten2()
    time.sleep(3)

Re: Schalter zum hin und Herschalten

Verfasst: Dienstag 2. März 2021, 15:28
von Sirius3
Programmieren durch Raten funktioniert einfach nicht.
Der Code macht so gar keinen Sinn, weil Du jetzt gar keine GUI mehr hast.
Und ein Ventil braucht eine GUI.

Re: Schalter zum hin und Herschalten

Verfasst: Dienstag 2. März 2021, 15:52
von __blackjack__
Und was heisst „auf die Namen geachtet“? Im Beispiel Sirius3 war keiner der Namen nummeriert, sollten sie auch nicht, und sie hielten sich an die Konventionen. Und Sternchen-Importe sind auch aus eigenen Modulen keine gute Idee.

Re: Schalter zum hin und Herschalten

Verfasst: Donnerstag 4. März 2021, 19:06
von Failix
Ich bekomme es nicht hin die Funktion def schalten1() ohne Buttom auszulösen.

Darum habe ich es jetzt auf GPIO umgestellt. Sieht so aus:

Code: Alles auswählen

class Ventil1:

    def __init__(self, canvas, position):
        self.canvas = canvas
        self.status = 0
        self.id1_ventil1 = self.canvas.create_polygon(ventil1[0]-20, ventil1[1]+10, ventil1[0], ventil1[1], ventil1[0]-20, ventil1[1]-10, fill="green")
        self.id2_ventil1 = self.canvas.create_polygon(ventil1[0]+20, ventil1[1]-10, ventil1[0], ventil1[1], ventil1[0] + 20, ventil1[1] + 10, fill="red")
        self.canvas.create_polygon(ventil1[0], ventil1[1], ventil1[0]-10, ventil1[1]-20, ventil1[0]+10, ventil1[1]-20, fill="green")

    def schalten1(self):
        self.status = 1 - self.status
        self.canvas.itemconfig(self.id1_ventil1, fill="green" if GPIO.input(2) == GPIO.HIGH else "red")
        self.canvas.itemconfig(self.id2_ventil1, fill="red" if GPIO.input(2,) == GPIO.HIGH else "green")
        GPIO.output(2, GPIO.HIGH) if self.status == 1 else GPIO.output(2, GPIO.LOW)
Und funktioniert, aber nur solange ich den GPIO über diese Funktion schalte. Wenn ich ihn mit einem Befehl außerhalb diese Funktion anspreche ändert sich die Visualisierung nicht. Die Funktion mainloop sollte doch def schalt() bzw. das Fenster darin aktualisieren oder habe ich was falsch verstanden?

Hier ist der komplette CODE.

Code: Alles auswählen

import tkinter as tk
import random
import time
#import fake_GPIO as GPIO
import RPi.GPIO as GPIO
#Position Ventile
ventil1 = [50, 50]
ventil2 = [50, 200]
ventil3 = [200, 50]

Status2=0

class Ventil1:

    def __init__(self, canvas, position):
        self.canvas = canvas
        self.status = 0
        self.id1_ventil1 = self.canvas.create_polygon(ventil1[0]-20, ventil1[1]+10, ventil1[0], ventil1[1], ventil1[0]-20, ventil1[1]-10, fill="green")
        self.id2_ventil1 = self.canvas.create_polygon(ventil1[0]+20, ventil1[1]-10, ventil1[0], ventil1[1], ventil1[0] + 20, ventil1[1] + 10, fill="red")
        self.canvas.create_polygon(ventil1[0], ventil1[1], ventil1[0]-10, ventil1[1]-20, ventil1[0]+10, ventil1[1]-20, fill="green")

    def schalten1(self):
        self.status = 1 - self.status
        self.canvas.itemconfig(self.id1_ventil1, fill="green" if GPIO.input(2) == GPIO.HIGH else "red")
        self.canvas.itemconfig(self.id2_ventil1, fill="red" if GPIO.input(2,) == GPIO.HIGH else "green")
        GPIO.output(2, GPIO.HIGH) if self.status == 1 else GPIO.output(2, GPIO.LOW)


class Ventil2:

    def __init__(self, canvas, position):
        global Status2
        self.canvas = canvas
        self.status = Status2
        self.id1_ventil2 = self.canvas.create_polygon(ventil2[0]-20, ventil2[1]+10, ventil2[0], ventil2[1], ventil2[0]-20, ventil2[1]-10, fill="green" if self.status == 0 else "red")
        self.id2_ventil2 = self.canvas.create_polygon(ventil2[0]+20, ventil2[1]-10, ventil2[0], ventil2[1], ventil2[0] + 20, ventil2[1] + 10, fill="red" if self.status == 0 else "green")
        self.canvas.create_polygon(ventil2[0], ventil2[1], ventil2[0]-10, ventil2[1]-20, ventil2[0]+10, ventil2[1]-20, fill="green")

    def schalten2(self):
        self.status = 1 - self.status
        self.canvas.itemconfig(self.id1_ventil2, fill="green" if self.status == 0 else "red")
        self.canvas.itemconfig(self.id2_ventil2, fill="red" if self.status == 0 else "green")


class Ventil3:

    def __init__(self, canvas, position):
        self.canvas = canvas
        self.status = 0
        x, y = position
        self.id1_ventil3 = self.canvas.create_polygon(x-20, y+10, x, y, x-20, y-10, fill="green")
        self.id2_ventil3 = self.canvas.create_polygon(x+20, y-10, x, y, x + 20, y + 10, fill="red")
        self.canvas.create_polygon(x, y, x-10, y-20, x+10, y-20, fill="green")

    def schalten3(self):
        self.status = 1 - self.status
        self.canvas.itemconfig(self.id1_ventil3, fill="green" if self.status == 0 else "red")
        self.canvas.itemconfig(self.id2_ventil3, fill="red" if self.status == 0 else "green")


def Programm():
   GPIO.output(2, GPIO.HIGH)
   time.sleep(3)
   GPIO.output(2, GPIO.LOW)




def schalt():
    schaltplan = tk.Tk()
    schaltplan.title('Nukular Schaltplan')
    schaltplan.geometry('600x600')
    zeichner = tk.Canvas(schaltplan)
    zeichner.pack(fill='both')
    ventil_1 = Ventil1(zeichner, ventil1)
    tk.Button(schaltplan, text="Ventil1", command=ventil_1.schalten1).place(x=ventil1[0]-30, y=ventil1[1]+10)
    ventil_2 = Ventil2(zeichner, ventil2)
    tk.Button(schaltplan, text="Ventil2", command=ventil_2.schalten2).place(x=ventil2[0]-30, y=ventil2[1] + 10)
    ventil_3 = Ventil3(zeichner, ventil3)
    tk.Button(schaltplan, text="Ventil3", command=ventil_3.schalten3).place(x=ventil3[0]-30, y=ventil3[1] + 10)

    tk.Button(schaltplan, text="Programm", command=Programm).place(x=500, y=500)

    schaltplan.mainloop()


Re: Schalter zum hin und Herschalten

Verfasst: Donnerstag 4. März 2021, 20:21
von Sirius3
Du hast Klassen noch nicht ganz verstanden. Du brauchst nicht für jedes Ventil eine eigene Klasse, weil die ja alle exakt identisch sind.

Eingerückt wird immer mit 4 Leerzeichen pro Ebene, nicht mal 3 und mal 4.
Bei GUI-Proramm darf es kein sleep geben, das muß man mit after lösen.
Warum sollte sich ein Ventil ändern, nur weil Du irgendwo einen GP-Ausgang setzt?
Wenn Du so etwas möchtest, dann mußt Du das schon explizit programmieren.
Was ist jetzt PIN 2? Ein Eingang oder ein Ausgang?

Vergiss gleich wieder, dass es global gibt. Hat hier auch gar keinen Nutzen.
Konstanten werden nach Konvention komplett GROSS geschrieben.
Ein if-else-Ausdruck ist kein Ersatz für ein richtiges if, wenn Du mit dem Ergebnis des Ausdrucks gar nichts machst.

Wenn zu jedem Ventil ein Knopf gehört, kann man den auch in die Klasse packen.

Code: Alles auswählen

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

#Position Ventile
VENTIL_POSITIONS = [
    [50, 50],
    [50, 200],
    [200, 50],
]

class Ventil:
    def __init__(self, canvas, position, text, pin=None):
        self.canvas = canvas
        self.status = False
        self.pin = pin
        x, y = position
        self.id1 = canvas.create_polygon(x - 20, y + 10, x, y, x - 20, y - 10, fill="green")
        self.id2 = canvas.create_polygon(x + 20, y - 10, x, y, x + 20, y + 10, fill="red")
        canvas.create_polygon(x, y, x - 10, y - 20, x + 10, y - 20, fill="green")
        tk.Button(canvas, text=text, command=self.schalten).place(x=x - 30, y=y + 10)

    def schalten(self):
        self.status = not self.status
        self.canvas.itemconfig(self.id1, fill="green" if self.status else "red")
        self.canvas.itemconfig(self.id2, fill="red" if self.status else "green")
        if self.pin:
            GPIO.output(self.pin, GPIO.HIGH if self.status else GPIO.LOW)


def blink(widget):
   GPIO.output(2, GPIO.HIGH)
   widget.after(3000, GPIO.output, 2, GPIO.LOW)


def main():
    schaltplan = tk.Tk()
    schaltplan.title('Nukular Schaltplan')
    zeichner = tk.Canvas(schaltplan, width=250, height=250)
    zeichner.pack(fill='both')
    for i, position in enumerate(VENTIL_POSITIONS, 1):
        ventil = Ventil(zeichner, position, f"Ventil{i}", 2 if i == 1 else None)
    tk.Button(schaltplan, text="Programm", command=partial(blink, schaltplan)).pack()
    schaltplan.mainloop()

if __name__ == "__main__":
    main()

Re: Schalter zum hin und Herschalten

Verfasst: Samstag 6. März 2021, 18:22
von Failix
Erstmal vielen Dank für die Antwort. Ich habe absolut keine Ahnung vom programmieren und noch weniger von Python. Ich werde mir aber noch das mit den Klasse noch ganz genau anschauen.

Vorab mal 2 Frage an deinen Code.

Bei mein PC wird er Fehlerfrei ausgeführt bei dem PI bekomme ich die Meldung das bei Line 50 ein colon fehlt. Das ist bei mir diese Line:

Line 50 bei mir ist das

Code: Alles auswählen

ventil = Ventil(zeichner, position, f"Ventil{i}", 2 if i == 1 else None)
Das nächste was ich überhaupt nicht verstanden habe, wie sage ich ihm welches Ventil er schalten soll?

Kann ich das so machen?

Code: Alles auswählen

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

#Position Ventile
VENTIL_PIN = [
    [2],
    [3],
    [4],
]

VENTIL_POSITIONS = [
    [50, 50],
    [50, 200],
    [200, 50],
]


class Ventil:
    def __init__(self, canvas, position, text, pin=None):
        self.canvas = canvas
        self.status = False
        self.pin = VENTIL_PIN
        x, y = position
        self.id1 = canvas.create_polygon(x - 20, y + 10, x, y, x - 20, y - 10, fill="green")
        self.id2 = canvas.create_polygon(x + 20, y - 10, x, y, x + 20, y + 10, fill="red")
        canvas.create_polygon(x, y, x - 10, y - 20, x + 10, y - 20, fill="green")
        tk.Button(canvas, text=text, command=self.schalten).place(x=x - 30, y=y + 10)

    def schalten(self):
        self.status = not self.status
        self.canvas.itemconfig(self.id1, fill="green" if self.status else "red")
        self.canvas.itemconfig(self.id2, fill="red" if self.status else "green")
        if self.pin:
            GPIO.output(self.pin, GPIO.HIGH if self.status else GPIO.LOW)
            
            .....

Re: Schalter zum hin und Herschalten

Verfasst: Samstag 6. März 2021, 22:53
von Sirius3
Dann ist auf dem Pi deine Python-Version zu alt und kennt noch keine Formatstrings.
Und pin ist ein Parameter von __init__, geht also so ähnlich wie mit der Position.

Re: Schalter zum hin und Herschalten

Verfasst: Sonntag 7. März 2021, 00:21
von __blackjack__
@Failix: In der gezeigten Zeile ist kein Syntaxfehler. Du müsstest schon die gesamte Fehlermeldung und den tatsächlichen Code der die auslöst zeigen.