Seite 1 von 1

tkinter after

Verfasst: Samstag 11. November 2023, 01:48
von Nobima
Guten Tag,
ich aktualisiere eine Anzeige mit after. Die sich selbst immer wieder aufrufende after-Funktion holt den Werte aus der Datenbankabfrage, die in einem separatem Modul ausgeführt wird. Der Modul muß dazu bei jeder Abholung neu ausgeführt werden um den Wert zu aktualisieren. Leider tut er das nicht. Wie kann ich das hinbekommen ?

Re: tkinter after

Verfasst: Samstag 11. November 2023, 02:43
von __blackjack__
@Nobima: Vorweg: Die Funktion ruft sich nicht selbst auf. Das wäre dann ja Rekursion. Die Funktion sagt mit `after()`, dass sie gerne nach einer gewissen Zeit wieder aufgerufen werden möchte. Beendet sich dann. Gibt also die Kontrolle wieder an die GUI-Hauptschleife zurück. Und wird dann nach der gewünschten Zeit (±) von der GUI-Hauptschleife erneut aufgerufen.

Module werden genau einmal ausgeführt: Wenn sie das erste mal importiert werden. Und dabei darf nichts passieren ausser das Konstanten, Funktionen, und Klassen definiert werden. Beim Import darf keine Datenbankverbindung aufgebaut werden und schon gar keine Abfrage von Daten gemacht werden. Die darin definierten Funktionen kannst Du aufrufen und Objekte aus den Klassen erstellen.

Re: tkinter after

Verfasst: Samstag 11. November 2023, 19:48
von Nobima
Vielen Dank für die Erläuterung. Gibt es für die Formulierung der Klasse im Modul und den Aufruf der Klassenvariablen im Hauptscript ein einfaches Beispiel (ohne Datenbank).

Re: tkinter after

Verfasst: Samstag 11. November 2023, 20:03
von Dennis89
Hallo,

ich bin mir nicht ganz sicher was du genau für ein Bsp. möchtest. Ich habe eins zu 'after' gefunden, das ich mal geschrieben habe:

Code: Alles auswählen

#!/usr/bin/env python3

import tkinter as tk
from random import randint


class TemperaturDisplay(tk.Frame):
    def __init__(self, master):
        tk.Frame.__init__(self, master)
        tk.Label(self, text='Außentempertur:').grid(row=0, column=0)
        self.außentemperatur = tk.Label(self, text='0 °C')
        self.außentemperatur.grid(row=1, column=0)
        self.sensoren_auslesen()
        
    def außentemperatur_aktualisieren(self, außentemperatur):
        self.außentemperatur.config(text = f'{außentemperatur} °C')
        
    def sensoren_auslesen(self):
        außentemperatur = randint(0, 30)
        self.außentemperatur_aktualisieren(außentemperatur)
        self.after(1000, self.sensoren_auslesen)
                
def main():
    root = tk.Tk()
    root.title('Temperaturübersicht')
    app = TemperaturDisplay(root)
    app.pack()
    app.mainloop()
    

if __name__ == "__main__":
    main()
Grüße
Dennis

Re: tkinter after

Verfasst: Sonntag 12. November 2023, 01:06
von Nobima
Ich versuche, mich der Aufgabe mit ganz einfachen Scripten zu nähern.
Im Haupscript steht:

Code: Alles auswählen

import tkinter as tk
import Modultest

window = tk.Tk()
window.geometry('500x180')
window.title('TEST')

foo = Modultest.Foo()
foo.periodically()

window.mainloop()
Im Modul "Modultest" steht:

Code: Alles auswählen

import tkinter as tk
import random

class Foo(tk.Tk):
    def periodically(self):
        Z = random.randint(0, 99)
        print(Z)
        self.after(2000, self.periodically)
Soweit funktioniert es periodisch. Leider wird außer dem großen Fenster noch ein kleines unerwünschtes Fenster mit dem Titel "tk #2" dargestellt. Außerdem möchte ich die Zufallszahl Z im Hauptscript verfügbar haben. Für eine Lösung bin ich dankbar.

Re: tkinter after

Verfasst: Sonntag 12. November 2023, 01:22
von __blackjack__
@Nobima: `Tk` ist ein Fenster. Wenn Du zwei davon erstellst, dann hast Du zwei Fenster. Wobei es Zufall ist, dass das reibungslos funktioniert, denn `Tk` ist *das* Hauptfenster, davon darf es immer nur eines geben. Für zusätzliche Fenster ist `Toplevel` da.

Was heisst ”zur Verfügung haben”? Wenn Du kein zweites Fenster haben möchtest, dann wird aus dem Beispiel nicht so wirklich klar was Du eigentlich machen willst. Warum ist das denn überhaupt auf zwei Module aufgeteilt?

Re: tkinter after

Verfasst: Sonntag 12. November 2023, 08:23
von Nobima
Ich möchte im Hauptscript die Grafik für die Anzeige von Messwerten gestalten. Zur besseren Übersicht möchte ich das periodische Abholen aktueller Messwerte aus einer Datenbank in ein anderes Modul auslagern.

Re: tkinter after

Verfasst: Sonntag 12. November 2023, 08:54
von Sirius3
Bis zu einer gewissen Länge des Programms, hast du die beste Übersicht, wenn alles in einer Datei steht.
Viel wichtiger ist eine fachliche Trennung. Funktionen, die die Datenbank abfragen, sollten nichts mit der GUI zu tun haben. Das Hauptfenster steuert die Nutzer-Interaktion, und damit auch wann die Datenbank abgefragt wird.

Re: tkinter after

Verfasst: Sonntag 12. November 2023, 09:53
von Nobima
Genau das ist meine Absicht. In meinem einfachen Testbeispiel ist die GUI im Hauptfenster (Anzeige der Zufallszahl) und die periodische Abfrage im importierten Modul (Erzeugung der Zufallszahl).

Re: tkinter after

Verfasst: Sonntag 12. November 2023, 10:39
von Sirius3
Angestossen wird das periodische Abfragen von der GUI, gehört also nicht in das Modul mit den Datenbankabfragen.

Re: tkinter after

Verfasst: Sonntag 12. November 2023, 10:55
von Nobima
Ok, daran arbeite ich gerade . Habe aber mit meinem einfachen Beispiel noch keinen Erfolg, weil ich die im Modul erzeugte
Zufallszahl nicht in die GUI zum Anzeigen bekomme.

Re: tkinter after

Verfasst: Sonntag 12. November 2023, 12:25
von __blackjack__
@Nobima: Ergebnisse von Funktionen werden über den Rückgabewert kommuniziert. Genau so kommst Du doch auch an den Zufallswert den `random.randint()` intern erzeugt.

Re: tkinter after

Verfasst: Sonntag 12. November 2023, 14:57
von Nobima
Die allgemeinen Hinweise sind sicher sehr hilfreich, bringen mich aber nicht weiter. Mit einem entsprechenden Code-Schnipsel, der zeigt, wie die Rückgabe des Wertes aus der Modul-Klasse ins Hauptscript erfolgt, würde mir helfen.

Re: tkinter after

Verfasst: Sonntag 12. November 2023, 15:18
von Dennis89
Hallo,

kannst du das an Hand meines Beispiels nicht? Kann es sein, dass eventuell das Allgemeine Verständnis mit dem Umgang von Klassen und Funktionen noch fehlt?
Meinst du das so?

Code: Alles auswählen

#!/usr/bin/env python3

import tkinter as tk
from random import randint


class Sensor:
    def __init__(self):
        pass

    @property
    def temperature(self):
        return randint(0, 30)


class TemperatureDisplay(tk.Frame):
    def __init__(self, master, sensor):
        tk.Frame.__init__(self, master)
        tk.Label(self, text="Außentempertur:").grid(row=0, column=0)
        self.outside_temperature = tk.Label(self, text="0 °C")
        self.outside_temperature.grid(row=1, column=0)
        self.start = tk.Button(self, text="Start", command=self.read_sensor)
        self.start.grid(row=2, column=0)
        self.sensor = sensor

    def update_temperature(self):
        self.outside_temperature.config(text=f"{self.sensor.temperature} °C")

    def read_sensor(self):
        self.update_temperature()
        self.after(1000, self.read_sensor)


def main():
    sensor = Sensor()
    root = tk.Tk()
    root.title("Temperaturübersicht")
    app = TemperatureDisplay(root, sensor)
    app.pack()
    app.mainloop()


if __name__ == "__main__":
    main()
Grüße
Dennis

Re: tkinter after

Verfasst: Sonntag 12. November 2023, 18:13
von __blackjack__
@Nobima: Modul-Klasse und Hauptskript spielen dabei überhaupt keine Rolle. Funktionen und Rückgabewerte funktionieren überall gleich, egal in welchem Modul die Funktion definiert ist, und egal von wo aus die Funktion aufgerufen wird.

Die `randint()`-Funktion hast Du doch auch in aus einer Methode heraus aufgerufen und das Ergebnis dann in der Funktion dort verwendet, also ausgegeben. Genau so macht man das halt. Du hast da also schon ein selbst geschriebenes Beispiel was genau das macht was Du willst. Statt einer Funktion die eine Zufallszahl liefert aus einem anderen Modul zu verwenden, musst Du halt eine schreiben, die Deine Messwerte liefert, und die in dem ersten Modul verwenden, so wie Du das im zweiten Modul mit `randint()` machst.

Re: tkinter after

Verfasst: Sonntag 12. November 2023, 21:24
von Nobima
@Dennis89: Vielen Dank für das Beispiel. Ich habe es nun damit so hinbekommen, wie es es mir vorstelle. Ich nehme es auch zum Anlaß, mein Verständnis für Klassen zu verbessern.

Re: tkinter after

Verfasst: Sonntag 12. November 2023, 23:34
von __blackjack__
Wobei die `Sensor`-Klasse in dem Beispiel so natürlich nicht sinnvoll ist, weil es keine Klasse ist. Also jetzt bitte nicht eine Funktion sinnlos in eine Klasse stecken. Ist ja nicht Java. 😈

Re: tkinter after

Verfasst: Montag 13. November 2023, 07:54
von Dennis89
Beim Umschreiben des Codes habe ich mir schon gedacht, dass so eine Bemerkung kommen wird 😀

Wollte nur nicht mit irgendwelchen erfundenen Angaben in der __init__ verwirren.

Grüße
Dennis

Re: tkinter after

Verfasst: Montag 13. November 2023, 10:40
von __blackjack__
Ja, das war mir schon klar, dass Du da ein Beispiel vereinfacht hast. Wollte nur sichergehen, dass das auch klar wird für jemanden für den Klassen noch neu sind. 🙂