Zweites Fenster Ausführung von Events nicht nacheinander möglich?

Fragen zu Tkinter.
Antworten
sonorob
User
Beiträge: 6
Registriert: Samstag 21. Januar 2017, 20:58

Hallo Python tkinter Mitleser,

ich bin gerade bei den Anfängen mit Python und tkinter. Ich habe folgendes realisiert:
Einen RFID Reader an meinen Raspberry angeschlossen.
In der Datei "ausgelagert.py" dort habe ich alle Funktionen definiert die ich für das "tagreadGUI.py" benötige und importiere es entsprechend.
Sobald ich jetzt das tagreadGUI ausführe wird das Fenster geöffnet und sobald ich auf "kommen!" (Button) drücke wartet das Programm darauf dass der RFID TAG vor den Leser gehalten wird. Halte ich den RFID Leser davor, schreibt die Funktion writeuserEIN (bzw. writeuserAUS) die Daten in die MySQL.
Und dann erscheint ein zweites Fenster mit der Meldung das der RFID Tag eingelesen wurde und nach 5 Sek. schliesse ich das Fenster automatisch.

Alles soweit gut ich habe dann nur foglendes Problem (da ich ein Touchscreen verwende) bleibt der cursor auf dem "Button" stehen und ändert somit seine Farbe nicht wieder in die Ursprungsfarbe.

Frage 1.) Gibt es hier eine Möglichkeit die Ihr kennt die Farbe zu setzen oder den Cursor nach der Ausführung an einen gewissen Punkt zu setzen (z.B. rechts an den Rand)?

Frage 2.) Oder gibt es eine Möglichkeit zwei Funktionen an einen "Button Klick" zu binden?

Frage 3.) Mein anderer Lösungsansatz (optimal Variante) wäre: Das erst nach dem öffnen des zweiten Fensters der RFID TAG ausgelesen wird und dann das Label den Text ändert sobald die ID ermittelt wurde und der Datensatz in die MySQL geschrieben wurde.

Ich hoffe das ist verständlich beschrieben und ihr könnt mir evtl. paar Tips geben?

ausgelagert.py:

Code: Alles auswählen

import serial
import MySQLdb
import sys
import time
import datetime
from operator import xor

# Auslesen der RFID-Transponder
def read_rfid():
	ser = serial.Serial("/dev/serial0")
	ser.baudrate = 9600
	daten = ser.read(14)
	ser.close()
	daten = daten[2:10]
	return daten

class DB():
    def __init__(self):
        db = MySQLdb.connect(host="localhost", user="***", 
                             passwd="****", db="*****") 
        db.autocommit(True)
        self.cur = db.cursor()       

    def close_db( self ):
        self.cur.close()

# RFID Daten plus Staus und timestamp in die DB sschreiben
def writeuserEIN():  
	id = read_rfid()
	status = 1
	timestamp = time.time()
	timestamp = datetime.datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')
	db = DB()
	addone = "INSERT INTO user (rfid, status, timestamp) VALUES (%s,%s,%s)" 
	db.cur.execute(addone, (id, status, timestamp))
	db.close_db()

def writeuserAUS():  
	id = read_rfid()
	status = 0
	timestamp = time.time()
	timestamp = datetime.datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')
	db = DB()
	addone = "INSERT INTO user (rfid, status, timestamp) VALUES (%s,%s,%s)" 
	db.cur.execute(addone, (id, status, timestamp))
	db.close_db()
	
tagreadGUI.py :

Code: Alles auswählen


from ausgelagert import *
import sys
from Tkinter import *
import tkFont
import sys
import os

def r():
	uno = writeuserEIN()
				
	def c():
		fenster1 = Tk()
		fenster1.title("RFID READ!!!")
		fenster1.attributes('-fullscreen', True)
		fenster1.config(cursor="none")
		lab = Label(fenster1, bg='lightgreen', text="RFID\n TAG\n wurde\n erfolgreich\n eingelesen!", font=("Helvetica", 35))
		lab.pack()
		fenster1.event_generate('<Motion>', warp=True, x=438, y=50)
		def beenden():
			fenster1.destroy()
		
		fenster1.after(5000, beenden)
	
	lbl1 = Label(fenster, text="RFID TAG vor das Geraet halten!", bg='lightgreen')
	lbl1.pack()
	ro = c()

def d():
	due = writeuserAUS()
	
	def c():
		fenster1 = Tk()
		fenster1.title("RFID READ!!!")
		fenster1.attributes('-fullscreen', True)
		fenster1.config(cursor="none")
		lab = Label(fenster1, bg='lightgreen', text="RFID\n TAG\n wurde\n erfolgreich\n eingelesen!", font=("Helvetica", 35))
		lab.pack()
		
		def beenden():
			fenster1.destroy()
		
		fenster1.after(5000, beenden)

fenster = Tk()
myFont = tkFont.Font(family = 'Helvetica', size = 55, weight = 'bold')
fenster.title("CIAO!!!")
fenster.attributes('-fullscreen', True)
fenster.config(cursor="none")

but1 = Button(fenster, text="KOMMEN!", font = myFont, command=r)
but1.pack()
but1.configure(background='green')
but1.config(activebackground='yellow')

but2 = Button(fenster, text="GEHEN!", font = myFont, command=d)
but2.pack()
but2.configure(background='red')
but2.config(activebackground='yellow')

but3 = Button(fenster, text = "Exit", font=("Helvetica", 30), command = fenster.quit) 
but3.pack()
but3.configure(background='blue')
but3.config(activebackground='green')

fenster.mainloop()
Zuletzt geändert von Anonymous am Samstag 21. Januar 2017, 22:34, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
BlackJack

@sonorob: Das zweite `Tk`-Exemplar ist auf jeden Fall ein Fehler weil das *das* Hauptfenster ist. Davon darf nur ein Objekt existieren. Zusätzliche Fenster muss man mit `Toplevel` erstellen, ansonsten hat man undefiniertes Verhalten.

Weitere Anmerkungen: Die `DB`-Klasse schliesst die Verbindung nicht wieder und so wie dort im Moment bloss der `Cursor` nicht mal richtig gekapselt wird, macht die Klasse nicht wirklich Sinn.

Was soll das Zuweisen an einen Namen wenn gleich in der nächsten Zeile ein anderer Wert an den gleichen Namen gebunden wird?

`writeuserEIN()` und `writeuserAUS()` unterscheiden sich durch *einen einzigen Wert*. Das ist eigentlich *eine* Funktion die den Status als Argument übergeben bekommt.

Einige Namen `ausgelagert`, `writeuserEIN()`, `writeuserAUS()`, `ser`, `cur`, `r()`, `c()`, `lab`, `lbl1`, `ro`, `d()`, und einiges davon noch mal in `d()`. Namen sollten dem Leser vermitteln was der Wert hinter dem Namen im Programm bedeutet. Ohne das er kryptische Abkürzunge raten muss. Wenn man anfängt Namen zu numerieren, dann hat man entweder nicht über gute Namen nachgedacht, oder man möchte gar nicht mehrere Namen sondern eine Datenstruktur. Oft ist das eine Liste.

`r()` und `d()` sind auch wieder eigentlich nur *eine* Funktion. Immer wenn Du so etwas kopierst und wieder einfügst, solltest Du innehalten und darüber nachdenken, denn in 99% der Fälle macht man da etwas falsch.

Es gibt überflüssige, weil nicht verwendete und doppelte Importe. Sternchenimporte sind Böse™. Damit holt man sich alle Namen des Moduls in den aktuellen Namensraum. Es ist schwer zu sehen wo welcher Name herkommt und es besteht die Gefahr von Namenskollisionen. Ausserdem macht es das Modulkonzept sinnlos. Man lagert doch nicht etwas in ein anderes Modul aus, nur um es dann doch wieder alles mit einem Sternchen zu importieren. Dann hätte man es auch in einem Modul lassen können.
sonorob
User
Beiträge: 6
Registriert: Samstag 21. Januar 2017, 20:58

Danke Black Jack für die Hinweise ich probiere mich die kommenden Tage an deinen Hinweisen und berichte dann. Thx :D
sonorob
User
Beiträge: 6
Registriert: Samstag 21. Januar 2017, 20:58

eine kurze Frage hätte ich noch:
Wie kann ich eine Funktion automatisch starten lassen, bzw. zwei?
Habe das bereits mit invoke() probiert jedoch scheitert das.
Es gibt ja mit destroy() die Funktionalität ein Fenster zu schliessen ich suche das gegenteil so eine art "autostart".

Und ich bin noch nicht hinter die Reihenfolge gekommen wie ich es schaffe erst das "Toplevel" aufgeht, dann der TAG eingelesen wird und dann sich das Fesnter schliesst.
Zuletzt geändert von sonorob am Sonntag 22. Januar 2017, 12:55, insgesamt 1-mal geändert.
BlackJack

@sonorob: Ich verstehe die Frage nicht. An der Stelle im Programmcode wo Du die Funktion ausführen möchtest, rufst Du sie einfach auf. Und wenn es zwei Funktionen sind, dann halt beide hintereinander.
sonorob
User
Beiträge: 6
Registriert: Samstag 21. Januar 2017, 20:58

Ja ich rufe die function auf mit z.B. hier mit command = a

Code: Alles auswählen

def a():
	
		
	top = Toplevel()
	top.title("RFID TAG!")
	top.attributes('-fullscreen', True)
	msg = Message(top, bg='lightgreen', text="RFID\n TAG\n vor das\n Ding halten!", font=("Helvetica", 10))
	msg.pack()
	button = Button(top, text="Exit", command=top.destroy)
	button.pack()
	
	h = rfid_read()
	
def rfid_read():

	ser = serial.Serial("/dev/serial0")
	ser.baudrate = 9600
	daten = ser.read(14)
	ser.close()
	daten = daten[2:10]
	id = daten
	status = 1
	timestamp = time.time()
	timestamp = datetime.datetime.fromtimestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')
	db = DB()
	addone = "INSERT INTO user (rfid, status, timestamp) VALUES (%s,%s,%s)" 
	db.cur.execute(addone, (id, status, timestamp))
	db.cur.close()
	db.close_db()
	

fenster = Tk()
myFont = tkFont.Font(family = 'Helvetica', size = 55, weight = 'bold')
fenster.title("CIAO!!!")
fenster.geometry('400x400')

but1 = Button(fenster, text="KOMMEN!", font = myFont, command = a)
but1.pack()
was passiert ist jedoch dass sobald ich den Button Klicke er direkt die serial aufruft und somit wartet das der Tag davor gehalten wird, hat er die Daten in die DB geschrieben dann erst öffnet sich das "Toplevel" Fenster.
Verständlicher?
Zuletzt geändert von Anonymous am Sonntag 22. Januar 2017, 15:12, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Codebox-Tags gesetzt.
Sirius3
User
Beiträge: 17746
Registriert: Sonntag 21. Oktober 2012, 17:20

@sonorob: Du willst also wirklich dass zwei Dinge "gleichzeitig" passieren, nämlich, dass Deine GUI läuft und dass etwas von einer seriellen Schnittstelle gelesen wird. Dazu brauchst Du einen Hintergrundprozess (Thread) der das Lesen der Schnittstelle erledigt, während im Vordergrund das Fenster geöffnet wird.

Warum sind jetzt rfid_read und der Datenbankeintrag in eine Funktion gerutscht? MySQL kann auch mit Datetime-Objekten umgehen, so dass es unnötig ist, die Zeit in einen String umzuwandeln. datetime.now liefert auch die aktuelle Zeit, da ist time.time unnötig. Besser noch wäre es, sich die Zeit inklusive Zeitzone (alternativ UTC per datetime.utcnow) geben zu lassen, dass Deine Zeiterfassung nicht zwei mal im Jahr durcheinander gerät. Am allerbesten läßt man die Datenbank selbst die aktuelle Zeit schreiben (per NOW()-SQL-Funktion).
sonorob
User
Beiträge: 6
Registriert: Samstag 21. Januar 2017, 20:58

@Sirius3:
Vielen Dank für den Hinweis ' Thread' war das richtige wonach ich gesucht habe.
Mein Code funktioniert nun so wie ich es mir vorgestellt habe...möchte deine Tips da noch einbauen und dann poste ich ihn hier nochmal. Evtl. Braucht den ja jmd anders nochmal ;) :mrgreen:
Antworten