Funktion periodisch aufrufen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
DB7WN
User
Beiträge: 49
Registriert: Samstag 18. März 2017, 22:11

Hallo allerseits,
Ich möchte einen Wert aus einer MYSQL-Datenbank lesen und in einem kleinen Fenster auf dem Desktop darstellen.
Im Prinzip funktioniert das auch. Ich will allerdings diese Datenabfrage periodisch wiederholen (alle 5min), weil sich der Wert ändert. (hier mal zum testen auf 3 sec gestellt)
An dieser Stelle komme ich nicht weiter. Das Programm bleibt scheinbar in der while-Schleife stehen. Ich hab mal gelesen, dass bei solchen Verzögerungen in der mainloop diese dann nicht richtig funktioniert.
Wie soll ich diese periodische Abfrage bewerkstelligen?

Code: Alles auswählen

from tkinter import *
import mysql.connector
import time
from time import sleep


def abfrage():
        connection = mysql.connector.connect (host = 'xxx', user = 'xxx', password = 'xxx', database = 'xxx')
        cursor = connection.cursor()
        #die Tabelle 'wsensor' wird absteigend sortiert nach Datum und eine Zeile wird selectiert (LIMIT1) - d.h. dann 'die letzte Zeile'
        cursor.execute("SELECT*from wsensor ORDER BY jahr DESC, monat DESC, tag DESC, stunde DESC, minute DESC LIMIT 1")
        #das 7. Element der Zeile wird gelesen und in einen string gewandelt
        wert = str(cursor.fetchone()[6])    
        connection.close()
        labeltext="Temperatur :", wert, " °C"
        return labeltext

fenster = Tk()
fenster.geometry("120x50+1230+640")
fenster.title("")
label0=Label(fenster, text = "Leinenborn")
label0.place(x=10, y=0)
label=Label(fenster)
label.place(x=10, y=15)

while 1:
      labeltext = abfrage()
      label.configure(text = labeltext)
      sleep (3)

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

Du darfst bei GUIs nicht den mainloop blockieren. Sonst kann sie keine Ereignisse wie zB Mausklicks verarbeiten.

Stattdessen musst du selbst Ereignisse erzeugen, mit der after-Methode. Damit kannst du periodisch Code ausführen lassen. Es finden sich hier im Forum Tonnen von Diskussionen genau dazu.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@DB7WN: zusätzlich noch Bemerkungen zum Code: *-Importe vermeiden. `import tkinter as tk` und alle tk-Namen per `tk.` ansprechen. Eingerückt wird immer mit 4 Leerzeichen pro Ebene, am besten Editor so einstellen, dass das Automatisch beim Drücken von TAB passiert.
Wer füllt die Datenbank? Datenbanken kennen TIMESTAMP, das sollte als Datum benutzt werden und nicht 5 einzelne Felder.
Falls sich mal die Reihenfolge der Datenbankfelder ändert, funktioniert das Programm nicht mehr, deshalb immer "SELECT FELD1,FELD2 ..." statt * benutzen. Wenn Du nur den Sensorwert willst, holst Du Dir auch nur den Sensorwert.
Auch wenn Tk mit allem möglichen zurecht kommt, solltest Du nur Strings als Labeltext verwenden und den per .format erzeugen.
DB7WN
User
Beiträge: 49
Registriert: Samstag 18. März 2017, 22:11

Danke, Sirius3, für die Hinweise. Dass die "while 1" - Schleife in der mainloop Unsinn ist, ist mir mittlerweile selbst aufgefallen.
Eine "after-methode" hab ich noch nicht gekannt, ich werd mich mal drum kümmern.
Die Datenbank wird von mir selbst gefüttert, ich hab also alles im Griff, was da passiert.
Deine anderen grundsätzlichen Anmerkungen scheinen mir sehr nützlich zu sein.
DB7WN
User
Beiträge: 49
Registriert: Samstag 18. März 2017, 22:11

Also mit "label.after(....)" hab ich's jetzt hingekriegt. Mir ist aufgefallen, dass ich jedes beliebige Widget da benutzen kann. Es geht auch mit "fenster.after(...) oder label0.after(...). Es muss ja nur dafür gesorgt werden, dass die Funktion "abfrage()" wiederholt wird. Aber dass das mit beliebigen Widgets geht kommt mir trotzdem etwas unlogisch vor.
Ist es jetzt so, dass der Programmablauf nur innerhalb der Funktion geschieht? Könnte jetzt trotzdem innerhalb des (root)-fensters sich noch anderes abspielen, oder wird die mainloop garnicht mehr abgearbeitet?
Die Sache mit dem Import von Bibliotheken hab ich schon öfter gelesen, aber noch nicht richtig verinnerlicht - ich probier meistens aus, wie's geht.
Die Datenbank habe ich irgendwann mal selbst angelegt, hab aber versäumt einen Timestamp mitzuführen. Wenn ich mal Lust habe, versuche ich mal den nachträglich zuzufügen, das zeitliche sortieren ist jetzt wirklich etwas umständlich.
Wie Du siehst, bin ich ein Hobby-Programmierer, der nichts so wirklich richtig kann, lerne aber doch gerne mal was dazu. Zum Glück brauche ich das nicht wirklich.

Code: Alles auswählen

def abfrage():
        connection = mysql.connector.connect (host = 'xxx', user = 'xxx', password = '2yRnuvhb8', database = 'db_351346_2')
        cursor = connection.cursor()
        #die Tabelle 'wsensor' wird absteigend sortiert nach Datum und eine Zeile wird selectiert (LIMIT1) - d.h. dann 'die letzte Zeile'
        cursor.execute("SELECT*from wsensor ORDER BY jahr DESC, monat DESC, tag DESC, stunde DESC, minute DESC LIMIT 1")
        #das 7. Element der Zeile wird gelesen und in einen string gewandelt
        wert = str(cursor.fetchone()[6])
        label.config(text = time.time())
        connection.close()
        label.after(3000,abfrage)
        

fenster = Tk()
fenster.geometry("120x50+1130+640")
fenster.title("")

label0=Label(fenster, text = "Leinenborn")
label0.place(x=10, y=0)

label=Label(fenster)
label.place(x=10, y=15)

abfrage()

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

Nein, MainLoop ist immer noch relevant. Nur dein timer Code wird Regelmäßig ausgeführt. Warum das auf jedem Widget geht ist mir auch nicht klar. Es schadet aber auch nicht.

Du solltest auch davon absehen, jedes mal die Datenbankverbindung zu öffnen. Das reicht einmal am Anfang des programmlaufs. Danach reichen dann neue cursor bzw. eine Transaktion.
Antworten