Sensordaten in Label darstellen

Fragen zu Tkinter.
Antworten
Juleswal
User
Beiträge: 1
Registriert: Freitag 27. September 2019, 10:55

Hallo Zusammen,
ich meinem ersten richtigen Python Projekt, möchte ich über einen Raspberry ein Relais steuern. Gleichzeitig soll von einem Sensor die Daten in einem Label dargestellt werden. Dazu habe ich eine einfache GUI in Tkinter erstellt (mein ganzer Stolz für das erste mal :D).
Nur hängt es gerade bei mir daran, den Sensorwert auch wirklich in dem Label darzustellen. Oder ist ein Label hier nicht das richtige Widget? Dort wo momentan im Label " Value Value 1" steht, soll dann hinterher der eigentlich Wert stehen. Ich habe eine Vermutung, dass es wahrscheinlich eine Stringvar Verknüpfung mit dem Label ist? Derzeit gebe ich den Wert noch in der Konsole aus, daher auch der Printbefehl. Für eine Hilfe wäre ich sehr dankbar.

Code: Alles auswählen

from tkinter import *
import tkinter.font
from gpiozero import LED
import RPi.GPIO
RPi.GPIO.setmode(RPi.GPIO.BCM)
import board
import busio
i2c = busio.I2C(board.SCL, board.SDA)
import adafruit_ads1x15.ads1115 as ADS
from adafruit_ads1x15.analog_in import AnalogIn

#analoge ausgabe
ads=ADS.ADS1115(i2c)
chan=AnalogIn(ads, ADS.P0)
print(chan.value, chan.voltage)

##relais werden als led bezeichnet und den Pins zugewiesen
led_1 = LED(23)
led_2 = LED(24)

win = Tk()
win.title("Valve Toggler")
myFont = tkinter.font.Font(family='Helvetica', size = 12, weight = "bold")

### Event funktion##
def ledToggle1():
    if led_1.is_lit:
        led_1.on()
        ledButton["text"] = "Turn Valve Up"
    else:
        led_1.off()
        ledButton["text"] = "Stop"
        
def ledToggle2():
    if led_2.is_lit:
        led_2.off()
        ledButton["text"] = "Turn Valve Up"
    else:
        led_2.on()
        ledButton["text"] = "Stop"


def close():
    RPi.GPIO.cleanup()
    win.destroy()
    
win.protocol("WM_DELETE_WINDOW", close) 
##WIDGETS##
exitButton = Button(win,text='Exit', font = myFont,command = close, bg = 'bisque2', height =1, width =12)
exitButton.grid(row=1,column=5)

ledLabel = Label(win, text= 'Value Valve 1', font = myFont, bg ='bisque2', height =1, width =24)
ledLabel.grid(row=1,column=1)
ledButton = Button(win,text='Turn Valve Up', font = myFont,command = ledToggle1, bg = 'bisque2', height =1, width =24)
ledButton.grid(row=2,column=1)#valve 1 up
ledButton = Button(win,text='Turn Valve down', font = myFont,command = ledToggle2, bg = 'bisque2', height =1, width =24)
ledButton.grid(row=3,column=1)#valve 1 down

win.mainloop() 
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Eine StringVar als Argument an das Label und dann ein per after gesetzter timer sind die Lösung, ja.
Benutzeravatar
__blackjack__
User
Beiträge: 14090
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@Juleswal: Das ist ziemlich unübersichtlich das da Importe, Hauptprogramm, und Funktionsdefinition gemischt sind. Importe kommen alle vor dem eigentlichen Code. Und auf Modulebene sollte nur Code stehen der Konstanten, Funktionen, und Klassen definiert. Das Hauptprogramm steht üblicherweise in einer Funktion die `main()` heisst.

Das bedeutet dann das die Funktion nicht mehr einfach so auf magische Weise auf irgendwelche Variablen zugreifen können, denn auf Modulebene sollte es keine Variablen geben. Alles was eine Funktion oder Methode ausser Konstanten benötigt, sollte sie als Argument(e) übergeben bekommen. Das bedeutet insbesondere bei GUI-Programmierung das man objektorientierte Programmierung (OOP) braucht, mindestens aber `functools.partial()`.

Die Behandlung des ersten Buttons schaltet die LED ein wenn sie an ist und aus wenn sie aus ist — macht also effektiv gar nichts. Das ist doch ziemlich sicher nicht so gewollt‽

Ist es richtig das immer nur der Text vom zweiten Button geändert werden soll, auch wenn der erste gedrückt wurde? Das ist jedenfalls das was Dein Code im Moment macht weil Du beide Buttons an den gleichen Namen gebunden hast, also am Ende nur der zweite an diesen Namen gebunden ist.

Namen schreibt man in Python klein_mit_unterstrichen. Ausnahmen sind Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Sternchen-Importe sind Böse™. Da holt man sich gerade bei `tkinter` über 150 Namen ins Modul von denen nur ein kleiner Bruchteil tatsächlich verwendet wird. Es besteht die Gefahr von Namenskollisionen und man holt sich auch Namen ins Modul die gar nicht im importieren Modul definiert werden, sondern auch was diese Modul selbst importiert hat.

`my` ist in aller Regel kein sinnvoller Bestandteil von Namen, denn wenn es nicht noch `their` oder `our` als Präfix gibt, sagt `my` nichts aus. Weg damit.

Keine Abkürzungen in Namen. Wenn man `channel` meint, sollte man nicht `chan` schreiben.

Wirklich fies wird es bei den Namen wenn sie etwas ganz anderes bezeichnen als tatsächlich dahinter steckt. Statt überall `led` in Namen zu verwenden die eigentlich für Relais stehen, sollte da auch tatsächlich `relais` in den Namen stehen. Alles andere ist verwirrend und genau das sollten Namen ja nicht sein. Wenn man dann noch statt `LED` zu missbrauchen `DigitalOutputDevice` verwendet, kann man sich den Kommentar „relais werden als led bezeichnet“ auch komplett sparen. Statt `is_lit`, was bei einem Relais keinen Sinn macht, verwendet man bei `DigitalOutputDevice` das Property `is_active`.

`RPi.GPIO` mit `gpizero` zu mischen sieht mir gefährlich aus. `gpizero` muss nicht `RPi.GPIO` als Backend verwenden.

Wenn `RPi.GPIO` dann ist das `cleanup()` nicht sicher genug. Das sollte nicht davon abhängen das der Programmierer aufpasst, dass das Programmende immer durch die `close()`-Funktion läuft, sondern mit einem ``try``/``finally`` im Hauptprogramm sichergestellt werden.

`DigitalOutputDevice` hat eine `toggle()`-Methode.

Ungetesteter Zwischenstand:

Code: Alles auswählen

#!/usr/bin/env python3
from functools import partial
import tkinter as tk
import tkinter.font

from adafruit_ads1x15.ads1115 import ADS1115, P0
from adafruit_ads1x15.analog_in import AnalogIn
import board
import busio
from gpiozero import DigitalOutputDevice
from RPi import GPIO

FONT = tkinter.font.Font(family="Helvetica", size=12, weight="bold")


def toggle_releais_a(relais, widget):
    #
    # FIXME This doesn't do much and looks wrong.
    #
    widget["text"] = "Turn Valve Up" if relais.is_active else "Stop"


def toggle_relais_b(relais, widget):
    widget["text"] = "Turn Valve Up" if relais.is_active else "Stop"
    relais.toggle()


def main():
    try:
        GPIO.setmode(GPIO.BCM)
        ads = ADS1115(busio.I2C(board.SCL, board.SDA))
        channel = AnalogIn(ads, P0)
        print(channel.value, channel.voltage)

        relais_a = DigitalOutputDevice(23)
        relais_b = DigitalOutputDevice(24)

        window = tk.Tk()
        window.title("Valve Toggler")

        options = dict(font=FONT, bg="bisque2")
        tk.Button(
            window, text="Exit", command=window.quit, width=12, **options
        ).grid(row=1, column=2)

        options["width"] = 24
        tk.Label(window, text="Value Valve 1", **options).grid(row=1, column=1)

        relais_a_button = tk.Button(window, text="Turn Valve Up", **options)
        relais_a_button.grid(row=2, column=1)

        relais_b_button = tk.Button(window, text="Turn Valve down", **options)
        relais_b_button.grid(row=3, column=1)

        #
        # FIXME Should it really be `relais_b_button` in both cases?
        #
        relais_a_button["command"] = partial(
            toggle_releais_a, relais_a, relais_b_button
        )
        relais_b_button["command"] = partial(
            toggle_relais_b, relais_b, relais_b_button
        )

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


if __name__ == "__main__":
    main()
“Vir, intelligence has nothing to do with politics!” — Londo Mollari
Antworten