Von Print zum GUI - aber wie?

Fragen zu Tkinter.
Antworten
Klaus_ww
User
Beiträge: 8
Registriert: Donnerstag 2. Februar 2023, 20:12

Hallo zusammen,

ich "spiele" erst seit Kurzem mit Python (3.10) und meine Kenntnisse sind somit rudimentär.
Per pyserial lese ich Werte aus 2 Messgeräten aus und gebe sie per Print aus - funktioniert.

Im nächsten Schritt soll per tkinter ein GUI her, auf dem eben diese Werte angezeigt werden. Wir sprechen von 2 Spalten, also jeweils Wertepaaren.
Es muss nur angezeigt werden, daraus copy/paste wäre schick aber nicht nötig.

Frage: wie würdet ihr das am besten angehen?
- alles in z.B. Text-Widget packen?
- separates Frame mit Grid-Struktur?
- ???

Das ganze soll anfängerfreundlich aber auch funktional sein.
Freue mich auf sachdienliche Hinweise.
Danke
Klaus
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Nur ein Textfeld zu benutzen, stellt ja keine echte Veränderung zum Terminal dar. Das ist auch ein Textfeld. Der simpelste Start sind Labels in einem Frame, zb vertikal oder horizontal layoutet. Grid ist auch ok. Wichtig: textvariable benutzen, damit die Werte aus der seriellen Schnittstelle in einem Hintergrundthread abgerufen und via der Variablen den Weg in den Main-Thread mit der GUI finden. Alternativ mit after arbeiten, oder createfilehandler: https://stackoverflow.com/questions/334 ... ocket-data
Klaus_ww
User
Beiträge: 8
Registriert: Donnerstag 2. Februar 2023, 20:12

Ok, dann bin ich ja nicht ganz auf dem Holzweg. Werde mich mal daran versuchen, danke für Deine Unterstützung.
Klaus_ww
User
Beiträge: 8
Registriert: Donnerstag 2. Februar 2023, 20:12

Ich muss nochmal um Hilfe bitten - und mich noch etwas deutlicher ausdrücken.
Das Anzeigen von ausgelesenen Werten funktioniert.
ABER: ich möchte die Werte im GUI alle angezeigt bekommen, die Liste soll also immer um die neusten Werte ergänzt werden.

Bekomme ich nicht hin, habe ein krudes Konstrukt mit konkatenierenden Strings versucht ... gruselig.
Hat jemand einen Codeschnipsel für mich an dem ich mich entlang hangeln kann?
Benutzeravatar
Kebap
User
Beiträge: 686
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

Was spricht denn gegen print?
Dann hast du auch alle Werte untereinander.
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Zeig bitte, was du versucht hast. Denn sich jetzt was selbst aus den Fingern zu saugen, um dann zu hoeren zu bekommen "ich benutze aber das blabla-Widget" ist frustrierend.
Klaus_ww
User
Beiträge: 8
Registriert: Donnerstag 2. Februar 2023, 20:12

Warum kein print? Weil zum Schluss eine .exe erzeugt werden soll, um beim Anwender keine Python-Installation nötig zu machen (und wenn das anders geht bin ich für Hinweise ebenfalls dankbar).

Folgendes Grundkonstrukt habe ich verwendet, was im Grunde ja anzeigt was ich will - nur eben nicht Zeile für Zeile wenn neue Daten reinkommen.

Code: Alles auswählen

from tkinter import *
import time

win = Tk()

# Set the geometry
win.geometry("700x350")

# Add the list of items
days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat","Sun"]

def insertText():
    for day in days:
        text.insert(END, day + '\n')

text = Text(win, width=80, height=15)
text.pack()

insertText()
mainloop()
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Warum soll da auch was Zeile für Zeile ausgegeben werden, wenn Du alles in einer Funktion ausgibst?


*-Importe benutzt man nicht, time wird importiert, aber nicht benutzt.
Man darf keine globalen Variablen benutzen, und es sollte eine main-Funktion geben.
Die größe eines Fensters ergibt sich aus dessen Inhalt.
Variablennamen und Funktionen werden komplett klein geschrieben:

Code: Alles auswählen

import tkinter as tk

def insert_text(text, lines):
    for line in lines:
        text.insert(tk.END, f'{line}\n')

def main():
    win = tk.Tk()

    # Add the list of items
    days = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat","Sun"]
    text = tk.Text(win, width=80, height=15)
    text.pack()
    insert_text(text, days)
    win.mainloop()

if __name__ == "__main__":
    main()
Klaus_ww
User
Beiträge: 8
Registriert: Donnerstag 2. Februar 2023, 20:12

ok, keine * Importe - Danke
time ist ein Überbleibsel

Einen Lösungsansatz sehe ich in dem verbesserten Programm nicht, es dient ja auch nur der Veranschaulichung.

Ziel: ich lese zyklisch Daten aus der seriellen Schnittstelle und will diese in einem Fenster ausgeben - so wie das vergleichsweise bei print erfolgt.
Im Beispiel sollen das die Wochentage sein die nacheinander im Text-Widget gelistet werden sollen.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich sehe da keinen seriellen Code. Also ist das wohl nicht der Code, um den es wirklich geht 🤷🏼‍♂️
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Wir kennen Deinen Anwendungsfall nicht. Eine Veranschaulichung sollte das Wesentliche enthalten, nämlich, dass Du etwas von einer seriellen Schnittstelle lesen willst.
Klaus_ww
User
Beiträge: 8
Registriert: Donnerstag 2. Februar 2023, 20:12

Ok, ihr wollt den seriellen Part sehen. Kein Problem, aber das ist ja nicht mein Problem.

Code: Alles auswählen

import serial
import time

ser = serial.Serial('COM5', 9600)
time.sleep(5)

def read_string(channel):
    command = f'ch{channel}\n'
    print('Command sent: ' + command)
    ser.write(command.encode('utf-8'))

    time.sleep(1)
    notFinish = True
    while notFinish == True:
        time.sleep(0.1)
        nbChars = ser.inWaiting()
        if nbChars > 0:
            data_read = ser.read(nbChars)
            notFinish = False
            print('data read: ' + data_read.decode('ASCII'))

count = 0
while (count < 5):
    read_string(count+1)
    count +=1
    time.sleep(.2)

time.sleep(.5)
ser.write(b'off\n')

ser.close()
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Doch. Das ist dein Problem. Der Code funktioniert so nicht mit einer Ereignis-basierten GUI. Keine der von mir genannten Methoden zur nebenläufigen Anfrage der seriellen Schnittstelle kommt zum Einsatz. Also gehts auch nicht.
Klaus_ww
User
Beiträge: 8
Registriert: Donnerstag 2. Februar 2023, 20:12

Gut - wie komm ich hier weiter?
Alleine nicht, sonst würde ich nicht fragen.
Werte aus der seriellen Schnittstelle in einem Hintergrundthread abgerufen und via der Variablen den Weg in den Main-Thread mit der GUI finden. Alternativ mit after arbeiten, oder createfilehandler:
Reicht mir als Hilfestellung offenbar nicht, sorry.

Meine Hoffnung war ein grundsätzlich funktionierendes Minimalbeispiel.
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

GUI ist halt komplex.

Was hast du denn für Beispiele gefunden?
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

Das hast du bis dato halt nicht ausgedrückt. Du hast diese Hinweise bekommen, und dich dazu nicht weiter geäußert. Stattdessen war dann jetzt nur davon die Rede, dass die Darstellung funktioniert, nur irgendwas mit Konkatenierung wäre komisch. Woher soll man also wissen, wo es hakt? Erst recht ohne Code?

Jetzt ist es ein bisschen spät, um da noch was abzuliefern. Muss auf morgen warten. Der bestehende serielle Code ist übrigens großer Mist. Statt einfach blockierend mit read zu warten, wird da unnötig eine Schleife weiter getrieben. Und dann wieder künstlich mit sleep eingebremst.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

Ein einfache Methode wäre die Schnittstelle mit timeout=0 zu öffnen und dann regelmäßig per `read` alles, was bis dahin aufgelaufen ist, einzulesen und darzustellen.

Code: Alles auswählen

import tkinter as tk
import serial

def insert_text(text, ser):
    text.insert(tk.END, ser.read().decode())
    text.after(100, insert_text, text, ser)

def main():
    win = tk.Tk()
    ser = serial.Serial('COM5', 9600, timeout=0)
    time.sleep(5)
    ser.write(b"ch0\n")

    text = tk.Text(win, width=80, height=15)
    text.pack()
    insert_text(text, ser)
    win.mainloop()

if __name__ == "__main__":
    main()
Benutzeravatar
Kebap
User
Beiträge: 686
Registriert: Dienstag 15. November 2011, 14:20
Wohnort: Dortmund

Klaus_ww hat geschrieben: Dienstag 14. Februar 2023, 20:53 Warum kein print? Weil zum Schluss eine .exe erzeugt werden soll, um beim Anwender keine Python-Installation nötig zu machen (und wenn das anders geht bin ich für Hinweise ebenfalls dankbar).
Verstehe das immer noch nicht. Das ist doch kein Grund für oder gegen print oder Tk. Beides wird ein Python-Programm sein, und beides kann zu einer .exe gepackt werden, damit der Anwender nicht nochmal einzeln Python installieren muss. (Statt dessen installiert er mehrfach Python, nämlich als Teil jeder solchen .exe aber hey :mrgreen: )
MorgenGrauen: 1 Welt, 8 Rassen, 13 Gilden, >250 Abenteuer, >5000 Waffen & Rüstungen,
>7000 NPC, >16000 Räume, >200 freiwillige Programmierer, nur Text, viel Spaß, seit 1992.
Klaus_ww
User
Beiträge: 8
Registriert: Donnerstag 2. Februar 2023, 20:12

Tja, offenbar hab ich einfach (noch) zu wenig Plan von dem ganzen Zeug. Aber mit lesen, probieren und eurer Hilfe wird das hoffentlich besser.

Der Code von Sirius3 funktioniert - vielen Dank dafür, werde ich so verwenden und schauen wie weit ich komme.
Antworten