Label aktualiesieren

Fragen zu Tkinter.
Antworten
kiaralle
User
Beiträge: 68
Registriert: Donnerstag 19. August 2021, 19:11

Hallo, ich bin der Ralf, 54 und plage mich gerade ein wenig für mein Projekt mit Python. Es macht Spaß, aber ich komme nicht so richtig weiter.
Ich möchte einen Wert welche ich von einem Arduino messe verarbeiten. Aktuell hängt der Arduino am Linux-Rechner, später am Raspi.
Den Wert erhalte ich, der Wert wird im Label angezeigt.

Meine Frage, wie bekomme ich es hin, das der Wert ständig gelesen und aktualisiert wird?
Über einen Tipp und ein paar erklärenden Worten, würde ich mich freuen.

Code: Alles auswählen

from tkinter import *

import serial
ser = serial.Serial('/dev/ttyACM0')
ser.baudrate = 9600
#ser.open()
#ser.flush()
ser.flushInput()
ser_bytes = ser.readline()
ampere=(ser_bytes.decode("utf-8"))
print(ampere)
    
# Ein Fenster erstellen
fenster = Tk()
# Den Fenstertitle erstellen
fenster.title("Ladeanlage Kia")
fenster.geometry("800x480")
fenster.configure(bg='#123456')

kopf_1_label = Label(fenster, text="Wallbox")
kopf_1_label.place(x = 10, y = 10)
kopf_1_label.configure(bg='#123456')
kopf_1_label.configure(fg='#00ffff')
kopf_1_label.configure(font='Helvetica 20 bold italic')

ampere_label = Label(fenster, text=ampere)
ampere_label.place(x = 10, y = 100)
ampere_label.configure(bg='#123456')
ampere_label.configure(fg='#ffffff')
ampere_label.configure(font='Helvetica 40 bold italic')

exit_button = Button(fenster, text="Beenden", command=quit)
exit_button.place(x=680,y=360)

  

# In der Ereignisschleife auf Eingabe des Benutzers warten.
fenster.mainloop()


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

Wir besprechen das hier recht regelmäßig, der Begriff den du suchst ist “after”, der Timer in tkinter. Damit kannst du regelmäßig etwas abfragen. Wir haben auch diverse Beispiele geliefert im tkinter Forum. Mal ein bisschen stöbern.
Benutzeravatar
Dennis89
User
Beiträge: 1153
Registriert: Freitag 11. Dezember 2020, 15:13

Hallo,

du musst nach dem erstellen der Labels nicht drei mal ‚configure‘ aufrufen, die Eigenschaften kannst du alle in ‚Label()‘ packen.
Wenn du die genannten Beispiele von @__deets__ gefunden hast, wirst du merken dass du zu mindest Funktionen in deinem Programm brauchst.
Um das Arbeiten damit zu lernen hat Python ein offizielles Tutorial. Einfach nach „Python 3.9 Tutorial“ googlen und den Link mit „python.org“ nehmen.
Bin gerade nur am Handy, sonst hätte ich es dir hier direkt verlinkt, aber das ist mir zu fummlig 🙊

Grüße
Dennis
"When I got the music, I got a place to go" [Rancid, 1993]
kiaralle
User
Beiträge: 68
Registriert: Donnerstag 19. August 2021, 19:11

Guten Morgen,
danke für die Infos. Da werd ich heute Abend mal weiter in die Tasten hauen.
Die vielen .configure andere ich auch noch ab.
Für den blutigen Anfänger ist es so noch etwas verständlicher.


Gruß Ralf
kiaralle
User
Beiträge: 68
Registriert: Donnerstag 19. August 2021, 19:11

So, nochmals vielen Dank für den Richtungshinweis.

Hab mal meine Code etwas gekürzt. So lässt es sich eventuell leichter lernen.
Der Wert wird gelesen und auch angezeigt.
Gibt es noch etwas zu bemerken? Bitte gern meckern, das hilft :-)
Thonny scheint auch nichts mehr zu meckern :D

Code: Alles auswählen

from tkinter import *
import time
import serial
ser = serial.Serial('/dev/ttyACM0')
ser.baudrate = 9600

# Ein Fenster erstellen
fenster = Tk()
# Den Fenstertitle erstellen
fenster.title("Ladeanlage Kia")
fenster.geometry("800x480")
fenster.configure(bg='#123456')

kopf_1_label = Label(fenster, text="Wallbox")
kopf_1_label.place(x = 10, y = 10)
kopf_1_label.configure(bg='#123456')
kopf_1_label.configure(fg='#00ffff')
kopf_1_label.configure(font='Helvetica 20 bold italic')

def  read_ampere():
    ser_bytes = ser.readline()
    ampere=(ser_bytes.decode("utf-8"))
    ampere_label = Label(fenster, text=ampere)
    ampere_label.place(x = 10, y = 100)
    ampere_label.configure(bg='#123456', fg='#ffffff', font='Helvetica 40 bold italic')
    print(ampere)
    ampere_label.after(1000, read_ampere)       


exit_button = Button(fenster, text="Beenden", command=quit)
exit_button.place(x=680,y=360)
read_ampere()

# In der Ereignisschleife auf Eingabe des Benutzers warten.
fenster.mainloop()
Der Ralf
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

*-Importe sind böse, weil man damit nicht nachvollziehen kann, woher welcher Name kommt.
Alles auf oberster Ebene gehört auch in eine Funktion, damit der Code importierbar und damit testbar wird. Dadurch vermeidet man auch globale Variablen, denn alles was eine Funktion braucht, muß sie über ihre Argumente bekommen.
Du erzeugst jede Sekunde ein neues Label, das Du über die ganzen anderen Labels, die Du schon erzeugt hast, drüberlegst, so dass Du bald hunderte an Labels anzeigen mußt. Du mußt ein Label anlegen, und nur dessen Inhalt ändern.
Entweder liefert die serielle Schnittstelle schneller Werte, so dass sich der Puffer langsam füllt und Du veraltete Werte anzeigst, oder es ist langsamer, so dass `readline` blockiert und die ganze GUI einfriert. Man mußt die Eingabe von der GUI entkoppeln, z.B. durch einen Thread, der eine Queue füllt, die regelmäßig durch diese after-Routine gelesen wird.

place benutzt man nicht, weil es zu Problemen bei anderen Auflösungen oder anderen Systemen führen kann. Benutze grid oder pack.

Das steht alles schon in etlichen anderen Beiträgen hier im Forum. Einfach mal ein bißchen Suchen.

Was soll die 1 in `kopf_1_label`?
kiaralle
User
Beiträge: 68
Registriert: Donnerstag 19. August 2021, 19:11

Die 1 in kopf_1_label ist noch ein Rest von meinem ersten Versuch. Sollten mal zwei Köpfe werden. Wallbox...... Solar.
Glaube das ändere ich jetzt mit Frames? Muss ich noch mal schauen. Eventuell mit eine Button zwischen zwei Ansichten umschalten.
Mein Display ist eh nur 4,5Zoll . Also sollte weniger Anzeigen mehr sein.

Nur mit import tkinter.. also ohne * läuft mein Code nicht. Oder wie meinst du das?
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@kiaralle: Üblich ist ein ``import tkinter as tk``, und ja, dann musst Du natürlich auch den Code entsprechend anpassen, denn dann gibt es ja nicht mehr auf magische Weise alle Namen die in dem Modul definiert (und auch von wo anders importiert werden) nicht mehr. Genau das ist ja der Sinn das *nicht* zu machen, dass man am Ende besser sieht wo die Namen her kommen, weil man das dann dazu schreiben muss, oder explizit beim Import schon hinschreiben muss was man aus dem Modul haben möchte.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
Benutzeravatar
__blackjack__
User
Beiträge: 13079
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@kiaralle: Die `quit()`-Funktion die von der „Beenden“-Schaltfläche aufgerufen wird, gibt es im Grunde gar nicht. Die ist von der/für die interaktive Python-Shell gedacht und beendet das Programm an der Stelle. Man will hier aber eigentlich nur die GUI-Hauptschleife beenden, und dafür ist die `quit()`-*Methode* auf dem Fenster-Objekt vorgesehen. Dann kann man den Code auch so schreiben, das am Programmende die serielle Schnittstelle wieder sauber geschlossen wird.

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk

from serial import Serial


def read_ampere(ampere_label, connection):
    ampere_label["text"] = connection.readline().decode("utf-8")
    ampere_label.after(100, read_ampere, ampere_label, connection)


def main():
    with Serial("/dev/ttyACM0", 9600) as connection:
        background_color = "#123456"
        font_template = "Helvetica {} bold italic"
        
        fenster = tk.Tk()
        fenster.title("Ladeanlage Kia")
        fenster.configure(bg=background_color)

        tk.Label(
            fenster,
            text="Wallbox",
            bg=background_color,
            fg="#00ffff",
            font=font_template.format(20),
        ).pack(side=tk.TOP)

        ampere_label = tk.Label(
            fenster,
            bg=background_color,
            fg="#ffffff",
            font=font_template.format(40),
        )
        ampere_label.pack(side=tk.TOP)

        exit_button = tk.Button(fenster, text="Beenden", command=fenster.quit)
        exit_button.pack(side=tk.TOP)

        read_ampere(ampere_label, connection)

        fenster.mainloop()


if __name__ == "__main__":
    main()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
kiaralle
User
Beiträge: 68
Registriert: Donnerstag 19. August 2021, 19:11

@_blackjack_
Danke für den Code. Mit soviel Hilfestellung hätte ich jetzt nicht gerechnet.
Dein Code verstehe ich jetzt viel besser, als das was man sonnst im Netz findet.
Ich programmiere etwas in PHP. Da hat es auch etwas gedauert um zuerkennen was im Netz veraltet publiziert wird. Das geht schon an wenn oft Tkinter statt Tkinter als Import steht. Da stehst du oft kurz vor der Verzweiflung.

Nochmals vielen Dank an euch allen.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

Das Programm von __blackjack__ hat immer noch das Problem, dass readline blockiert.
Das kann man durch timeout=0 verhindern.

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk
from serial import Serial


class AmpereUpdater():
    def __init__(self, ampere_label, connection):
        self.ampere_label = ampere_label
        self.connection = connection
        self.partial_line = b""

    def update(self):
        while True:
            data = self.connection.read_until(b"\n")
            if not data:
                break
            self.partial_line += data
            if self.partial_line.endswith(b'\n'):
                self.ampere_label["text"] = self.partial_line.decode("utf-8")
                self.partial_line = b""
        self.ampere_label.after(100, self.update)


def main():
    with Serial("/dev/ttyACM0", 9600, timeout=0) as connection:
        background_color = "#123456"
        font_template = "Helvetica {} bold italic"
        
        fenster = tk.Tk()
        fenster.title("Ladeanlage Kia")
        fenster.configure(bg=background_color)

        tk.Label(
            fenster,
            text="Wallbox",
            bg=background_color,
            fg="#00ffff",
            font=font_template.format(20),
        ).pack(side=tk.TOP)

        ampere_label = tk.Label(
            fenster,
            bg=background_color,
            fg="#ffffff",
            font=font_template.format(40),
        )
        ampere_label.pack(side=tk.TOP)

        exit_button = tk.Button(fenster, text="Beenden", command=fenster.quit)
        exit_button.pack(side=tk.TOP)

        updater = AmpereUpdater(ampere_label, connection)
        updater.update()
        fenster.mainloop()


if __name__ == "__main__":
    main()
Antworten