Dynamische Anzeige über Tkinter

Fragen zu Tkinter.
Antworten
Downunder32ffm
User
Beiträge: 11
Registriert: Dienstag 19. September 2017, 22:35

Hallo Zusammen,

ich habe ein kleines Problem mit dem Tkinter modul (Befehl). Das Program soll bei einer Tasterbetätigung Protokolieren mit einem Zeitstempel
wann der Taster gedrückt worden ist. Bis dahin funktioniert alles.
Ich wollte als ontop das beim Ausführen der Datei und der betätigung des Taster ein Fenster aufgeht und mir den Zeitstempel darstellt, so wie
er mir in der Protokolldatei abgelegt wird aber leider funktioniert es nur beim 1. Tastendruck und dann muss ich das fenster schliessen und wieder drücken damit es wieder aufgeht und als zweites Problem hab ich einen Zeit Delay von ein paar ms zwischen Protokolldatei und darstellung.

Programmierung:

import timestamp as T
import tkinter as tk
import RPi.GPIO as GPIO
from time import sleep

#def timestamp():
#now = time.time()
#UTC = time.gmtime(now)
#msecs = "%04d" %int((now -int(now))*10000)
#return time.strftime("%d-%m-%Y %H:%M:%S.", UTC) + msecs

counter = 0

def counter_label(label):

def count():

global counter
counter = ("PTT pressed: " +T.timestamp())
return label.config(text=str(counter))
label.after(1000, count)
count()

GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.IN, pull_up_down=GPIO.PUD_UP)

count = 0

while True :

GPIO.wait_for_edge(11, GPIO.RISING)
count += 1

file = open("Abschluss.txt", "a")
Time = ["PTT pressed:" +T.timestamp()]
for i in Time:
file.write(str(i) +"\n")
file.close()

root = tk.Tk()
root.title("Counting Seconds")
label = tk.Label(root, fg ="green")
label.pack()

counter_label(label)
button = tk.Button(root, text='stop', width = 25, command = root.destroy)
button.pack()

print("PTT pressed:",T.timestamp())

sleep (0.05)
T.timestamp()
root.mainloop()

Ich hoffe ihr könnt mir weiterhelfen

Liebe Grüsse

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

Bitte poste deinen Code nochmal, in der dafuer gedachten Code-Box. So ist er nicht wirklich verstaendlich.

Jenseits davon ist dein Problem das Problem das hier 98% der Leute mit GPIO und Tkinter haben. Such mal im Forum danach - es finden sich wirklich hunderte Beitraege, inklusive langer Erklaerungen und laufendem Beispielcode.

Die Kurzfassung: GUI und eine dauerhafte Schleife wie du sie hast vertragen sich nicht, eine GUI muss responsiv bleiben und dazu darf Code nur kurz laufen. Die weiteren Stichworte hier sind

- after-Method in tkinter.
- add_event_detect statt wait_for_edge.
- eine Queue zum kommunizieren zwischen GPIO-Thread und GUI-Thread.
Downunder32ffm
User
Beiträge: 11
Registriert: Dienstag 19. September 2017, 22:35

Code: Alles auswählen

import timestamp as T
import tkinter as tk
import RPi.GPIO as GPIO
from time import sleep

#def timestamp():                            #Definition Zeitstempel                    
  #now = time.time()
  #UTC = time.gmtime(now)                  #gmtime = UTC Zeit, localtime = Zeitzone Uhrzeit
  #msecs = "%04d" %int((now -int(now))*10000)
  #return time.strftime("%d-%m-%Y %H:%M:%S.", UTC) + msecs

counter = 0

def counter_label(label):
    def count():
        global counter
        counter = ("PTT pressed: " +T.timestamp())
        return label.config(text=str(counter))
        label.after(1000, count)
    count()


GPIO.setmode(GPIO.BOARD)
GPIO.setup(11, GPIO.IN, pull_up_down=GPIO.PUD_UP)
count = 0

while True :
    GPIO.wait_for_edge(11, GPIO.RISING)
    count += 1

    file = open("Abschluss.txt", "a")
    Time = ["PTT pressed:" +T.timestamp()]
    for i in Time:
        file.write(str(i) +"\n")#+"\n"
    file.close()

    root = tk.Tk()
    root.title("Counting Seconds")
    label = tk.Label(root, fg ="green")
    label.pack()
    counter_label(label)
    button = tk.Button(root, text='stop', width =  25, command = root.destroy)
    button.pack()
      
    print("PTT pressed:",T.timestamp())
    
    sleep (0.05)
    T.timestamp()
    root.mainloop()
    
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

@Downunder32ffm: GUIs arbeiten ereignisorientiert, Du versuchst hier einen seriellen Programmablauf.

Daneben gibt es noch ein paar grundsätzliche Fehler: Du mischst Tabs mit Spaces, so dass ich mich frage, ob das Programm so überhaupt lauffähig ist. Achte darauf, dass Dein Editor so eingestellt ist, dass beim Drücken auf die Tab-Taste 4 Leerzeichen eingefügt werden, und dass jede Einrückung per 4 Leerzeichen pro Ebene gemacht wird.

Variablennamen sollten generell klein geschrieben werden. Du hast ein Modul timestamp, das Du als T importierst. T ist ein zu kurzer Name, um daraus irgendetwas abzulesen. Warum importierst Du nicht gleich die Funktion timestamp, die als einziges aus dem Modul gleichen Namens benutzt wird?
`GPIO` wird aus historischen Gründen falsch mit Großbuchstaben geschrieben, es hindert Dich aber niemand es als `as gpio` zu importieren.
`global` hat in einem ordentlichen Programm nichts zu suchen (auch wenn es schwierig ist, ordentliche RasPi-Programme zu finden). Bei Dir ist es völlig unnötig, weil `counter` nur lokal benutzt wird. Eine Funktion, die per after aufgerufen wird, hat keinen Rückgabewert, und der Rückgabewert von config ist auch immer None.

Zeile 35: eine ein-elementige Liste zu erzeugen, um dann über das EINE Element mit einer for-Schleife zu gehen, ist reichlich umständlich.

In einem Programm sollte nur eine Instanz von Tk erzeugt werden. Deine ersten Schritte wären also, allen GUI-Code vom Code zum Lesen der Taster zu trennen, zweiteres in einen Thread auszulagern und zwischen GUI und Thread mit Hilfe von Queues zu kommunizieren.
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du fragst den pin ab, und trittst danach erst in den gui mainloop ein. Der blockiert bis zum schließen des Fensters. Und dann geht die Chose von vorne los.

Du musst aber mainloop bleiben, sonst keine GUI.

Zu Tipps wie man es anders programmiert, siehe meinen ersten Post.
Downunder32ffm
User
Beiträge: 11
Registriert: Dienstag 19. September 2017, 22:35

__deets__ hat geschrieben:Bitte poste deinen Code nochmal, in der dafuer gedachten Code-Box. So ist er nicht wirklich verstaendlich.

Jenseits davon ist dein Problem das Problem das hier 98% der Leute mit GPIO und Tkinter haben. Such mal im Forum danach - es finden sich wirklich hunderte Beitraege, inklusive langer Erklaerungen und laufendem Beispielcode.

Die Kurzfassung: GUI und eine dauerhafte Schleife wie du sie hast vertragen sich nicht, eine GUI muss responsiv bleiben und dazu darf Code nur kurz laufen. Die weiteren Stichworte hier sind

- after-Method in tkinter.
- add_event_detect statt wait_for_edge.
- eine Queue zum kommunizieren zwischen GPIO-Thread und GUI-Thread.

@deets:
Danke für deine Ideen ich habe es versucht deinen Rat mit add_event_detect versucht leider bekomme ich es einfach nicht hin das es funktioniert. Hast du einen Vorschlag wie man dieses lösen kann?
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Zeig bitte was du versucht hast (Code-tags nicht vergessen), dann koennen wir uns das anschauen.

Und bitte kein full-quote von einem Posting, vor allem, wenn es direkt darueber steht.
Downunder32ffm
User
Beiträge: 11
Registriert: Dienstag 19. September 2017, 22:35

Hallo deets,
bin komplett Anfänger in der Programmierung. Das ist momentan was ich gerade probiere aber es will nicht funktioniert

Code: Alles auswählen

import RPi.GPIO as gpio
from datetime import datetime as DateTime


def generate_timestr():
    return "{:%Y-%m-%d %H:%M:%S.%f}".format(DateTime.utcnow())[:-3]

def do():
    current_time = generate_timestr()
    with open("Abschluss.txt", "a") as output:
        output.write("PTT pressed: {}\n".format(current_time))
    print (current_time)
    
gpio.setmode(gpio.BOARD)
gpio.setup(11, gpio.IN, pull_up_down=gpio.PUD_UP)
count = 0

gpio.add_event_detect(11, gpio.FALLING, callback = do)

while True :
    count += 1
    if gpio.event_detected(11):
        do()
Danke
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Da ist doch gar kein Tkinter Code drin.

Und du kombinierst hier zwei Dinge - einen Event-Callback sowie das explizite Abfragen, ob es ein Event gegeben hat. Das ist nicht zielfuehrend.

Der erste Schritt ist ein Programm zu schreiben, das in der while-Schleife einfach alle Zeit verschlaeft, und zu sehen ob und was der event-callback macht.
Downunder32ffm
User
Beiträge: 11
Registriert: Dienstag 19. September 2017, 22:35

@deets: entschuldige für die etwas Verzögerte Antwort war etwas eingespannt.

Code: Alles auswählen

import RPi.GPIO as gpio
from datetime import datetime as DateTime
from tkinter import *

#def quit(): 

def generate_timestr():
    return "{:%Y-%m-%d %H:%M:%S.%f}".format(DateTime.utcnow())[:-3]

def do():
    while True:
            if gpio.wait_for_edge(11, gpio.FALLING):
               #count += 1
               current_time = generate_timestr()
               with open("DelayMessung.txt", "a") as output:
                    output.write("PTT pressed: {}\n".format(current_time))
               print (current_time)
    
gpio.setmode(gpio.BOARD)
gpio.setup(11, gpio.IN, pull_up_down=gpio.PUD_UP)

root = Tk()
root.title("Time Delay Messung")
root.geometry("500x50")

LabelAnzeige = Label(root,text =do())
LabelAnzeige.place(x=10, y=20, width=300, height=20)
LabelAnzeige.pack()

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

Das ergibt ja so keinen Sinn: do() gibt nix zurueck, und du benutzt es, um einem Label den Text zuzuweisen. Stattdessen sollte es wohl eher das command eines Buttons werden.
Downunder32ffm
User
Beiträge: 11
Registriert: Dienstag 19. September 2017, 22:35

Wie könnte den eine möglich Lösung aussehen. Auf der Kommandozeile funktioniert es soweit.
Mit der GUI Programmierung tue ich mir momentan echt schwer!

Wäre dankbar für jeden Hilfe um auf eine Lösung zu kommen!
Sirius3
User
Beiträge: 17703
Registriert: Sonntag 21. Oktober 2012, 17:20

@Downunder32ffm: Bei GUI-Programmierung muß man ja auch völlig umdenken. wait, sleep, Endlosschleifen, das alles darf man nicht benutzen. Statt dessen muß man asynchron auf Ereignisse warten. Grundvoraussetzung sind Kenntnisse in Objektorientierung, Nebenläufigkeit, Threads, Queues, etc. Das ist so viel, das kann man nicht alles auf einmal lernen. Fang einfach mit dem ersten Punkt an, lerne was Klassen sind, wenn Du das verstanden hast, lerne, wie man mit Klassen eine einfache GUI schreibt und erst dann versuche zu lernen, wie man nun noch externe Ereignisse in eine GUI einbindet. Es macht keinen Sinn mit dem letzten Punkt anfangen zu wollen.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Downunder32ffm

Kannst du das folgende Skript einmal ausprobieren?:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import RPi.GPIO as gpio
from datetime import datetime as DateTime

try:
    # Tkinter for Python 2.xx
    import Tkinter as tk
except ImportError:
    # Tkinter for Python 3.xx
    import tkinter as tk

APP_TITLE = "Time Delay Messung"
APP_XPOS = 100
APP_YPOS = 100
APP_WIDTH = 500
APP_HEIGHT = 100

GPIO_INPUT_PIN = 11 #18

class Application(tk.Frame):

    def __init__(self, master):
        self.master = master
        tk.Frame.__init__(self, master)

        self.var = tk.StringVar()
        label_anzeige = tk.Label(self, textvariable=self.var, bg='steelblue',
            fg='yellow', width=20, font=('Helvetica', 20, 'bold'), padx=10,
            relief='raised', bd=2)
        label_anzeige.pack(expand=True)
        
        gpio.setup(GPIO_INPUT_PIN, gpio.IN, pull_up_down=gpio.PUD_DOWN)
        gpio.add_event_detect(GPIO_INPUT_PIN, gpio.FALLING)
        gpio.add_event_callback(GPIO_INPUT_PIN, self.read_time)
        
    def read_time(self, pin):
        current_time = self.generate_timestr()
        self.var.set(current_time)
        
        with open("DelayMessung.txt", "a") as output:
            output.write("PTT pressed: {}\n".format(current_time))

        print("Current Time: {}".format(current_time))
        
    def generate_timestr(self):
        return "{:%Y-%m-%d %H:%M:%S.%f}".format(DateTime.utcnow())[:-3]
           
def main():
    app_win = tk.Tk()
    app_win.title(APP_TITLE)
    app_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
    app_win.geometry("{}x{}".format(APP_WIDTH, APP_HEIGHT))
    
    gpio.setmode(gpio.BOARD)

    Application(app_win).pack(fill='both', expand=True)
    
    app_win.mainloop()
 
 
if __name__ == '__main__':
    main()
Gruss wuf :wink:
Take it easy Mates!
Antworten