Daten zyklisch neu generieren mittels Threads

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
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Hallo,

ich möchte gerne in einem tkinter-Label eine Temperatur anzeigen, die z.B. jede Sekunde erneuert wird. Zunächst habe ich noch nicht die Funktion verwendet, welche die Daten vom Temperatursensor einliest, sondern die Zufallszahlenfunktion genommen.

In anderen Beiträgen habt ihr mich vor Threads gewarnt. In diesem Beispiel http://www.python-forum.de/viewtopic.php?f=1&t=34497 habe ich mit Eurer Hilfe die after-Methode kennengelernt. Die after-Methode scheidet aber in diesem Fall für mich aus, da in dem Beispiel in dem Link eine Funktion immer rekursiv neu aufgerufen wird, aber angenommen, ich lese die Daten über mehrere Tage aus und schreibe die Werte z.B. auch später noch in eine Datenbank, dann würde der Speicher als mit mehr Funktionen voll werden und es irgendwann vom Interpreter wegen Speicherüberlaufs zum Abbruch kommen. Darum habe ich mich jetzt erstmal wieder für einen Thread entschieden.

Ich würde gerne mein Beispielprogramm zur Diskussion in dieses Forum reinstellen und würde mich sehr freuen, wenn ihr mir vielleicht weitere Tipps oder andere Möglichkeiten aufzeigen könntet, wie ich das noch lösen könnte.

Code: Alles auswählen

try:
    import tkinter as tkinter
except:
    import Tkinter as tkinter

import random

import time
from threading import Thread
from functools import partial

class Hole_zyklisch_Daten():
    def __init__(self, data_obj=tkinter.Label, zeitintervall=1):
        self.__zeitintervall = zeitintervall
        self.__thread_state = False
        self.__data_obj = data_obj
        self.__thread_obj = Thread()
        self.__data = 0.

    def start_thread(self):
        if self.__thread_state == False:
            self.__thread_state=True
            self.__thread_obj = Thread(target=self.__thread_function)
            self.__thread_obj.start()

    def stop_thread(self):
        self.__thread_state=False
        self.__thread_obj.join()

    def __thread_function(self):
        while self.__thread_state == True:
            # Hier Funktion, um die Daten aus dem Temperaturmodul zu holen:

            # self.__data_obj.set(str(random.random() ))
            self.__data_obj.config(text=str(random.random ()))
            print ("in thread_schleife")
            time.sleep(self.__zeitintervall)

def main():
    root = tkinter.Tk()
    label=tkinter.Label(root, text="nix")

    hole_daten = Hole_zyklisch_Daten(label)
    hole_daten.start_thread()

    label.pack()

    root.mainloop()
    hole_daten.stop_thread()

if __name__ == "__main__":
    main()
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ich denke du brauchst zwei Programme:
Das eine schreibt in Intervallen die aktuelle Temperatur in einer Datenbank.
Das andere ist deine tkinter GUI welches nur die Daten aus der Datenbank hernimmt.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

jens hat geschrieben:Ich denke du brauchst zwei Programme:
Das eine schreibt in Intervallen die aktuelle Temperatur in einer Datenbank.
Das andere ist deine tkinter GUI welches nur die Daten aus der Datenbank hernimmt.
Vielen Dank für Deine Antwort, über die ich mich gefreut habe.
Mein Problem ist aber, wie man die Daten zyklisch aktualisiert. Wenn ich jede Sekunde den Wert aus der Datenbank lesen will, dann ersetze ich eben den Befehl, der jetzt die Zufallszahlen erzeugt, durch den Datenbanklesebefehl. Es geht mir hierbei nicht darum, dass ich das Datenschreiben in die Datenbank nicht dierekt in die tkinter-GUI verknüpfen soll, sondern das ein separates Programm ist - es geht mir also generell um die Frage, wie man Daten zyklisch auslesen kann - also wie in dem Beispiel von mir mittels eines Threads - oder noch anders. Und das zyklische Holen von Daten kann ja dann auch in dem Datenbankmodul verwendet werden, aber dort auch die Frage - arbeite ich da dann mit einem separeten Thread oder anders?
Sirius3
User
Beiträge: 17748
Registriert: Sonntag 21. Oktober 2012, 17:20

@Papp Nase: In dem vorherigen Beispiel wird nichts rekursiv aufgerufen, so etwas würde BlackJack auch nie machen :wink:.

Prinzipiell solltest Du Verarbeitung und Anzeige voneinander trennen. In diesem Fall würde sich ein Publish–subscribe-Pattern anbieten. Der Temperatur-Thread veröffentlicht jeweils neue Temperaturwerte und mehrere Consumer verarbeiten sie. Also ein Datenbank-Schreiber, ein Label-Anzeiger, usw. Wie schon zuvor geschrieben, sollte man die GUI nicht aus einem Thread heraus verändern, sondern die `after`-Methode verwenden.

PS: woher kommen plötzliche die vielen schrecklichen '__' her?
BlackJack

@Papp Nase: Die Funktion wird in dem Beispiel mit `after()` nicht rekursiv aufgerufen! Rekursiv wäre es wenn sich eine Funktion direkt oder indirekt selbst aufruft. Das ist bei `after()` aber nicht der Fall denn der `after()`-Aufruf selbst kehrt ja sofort zum Aufrufer zurrück, der dann die Kontrolle an die Tk-Hauptschleife zurück gibt in dem er selber ganz normal endet. Jeder Aufruf der `_move()`-Methode kommt kommt von der Tk-Hauptschleife und der Aufruf kehrt auch dorthin zurück ohne selber noch mal direkt oder indirekt sich selbst aufgerufen zu haben.

Zum Code: Die ganzen doppelten führenden Unterstriche sollten einfache führende Unterstriche sein.

Mehrsprachige Namen in Quelltexten sind verwirrend, normalerweise belässt man es bei Englisch. Dann braucht man nicht jedes mal überlegen ob man einen Wert nun Englisch oder Deutsch benannt hat, oder noch schlimmer man hat den gleichen Begriff in beiden Sprachen für Werte mit unterschiedlichen Bedeutungen.

Der Klassenname `Hole_zyklisch_Daten` entsricht in der Schreibweise nicht den Konventionen und der Name selbst ist kein guter Name für eine Klasse. Klassen repräsentieren ”Dinge” im weitesten Sinne und werden deshalb auch entsprechend benannt. Tätigkeiten sind normalerweise beschreibungen für Funktionen oder Methoden, denn dafür stehen die ja auch, die tun etwas.

Der Defaultwert von `data_obj` macht keinen Sinn. Die Klasse erwartet an der Stelle ein Exemplar von einem Label und nicht den Datentyp. Wenn man Defaultwerte angibt, dann muss man die Funktion oder Methode auch sinnvoll aufrufen können ohne einen Wert anzugeben, sonst bräuchte man keinen Defaultwert angeben.

Das ``self.__thread_obj`` mit einem ``Thread()`` initialisiert wird, den man nirgendwo sinnvoll nutzen kann, ist auch nicht schön.

Der `_obj`-Suffix macht in der Regel keinen Sinn. In Python ist *jeder* Wert ein Objekt, das heisst das im Namen noch einmal zu erwähnen bringt dem Leser keinen Mehrwert.

Die Klasse ist insgesamt ein wenig komisch. Das sieht so aus als hätte man hier eigentlich von `Thread` erben wollen.

Vielleicht das wichtigste zu dem Programm: Es ist fehlerhaft weil man nicht aus anderen Threads als aus dem in dem die Tk-Hauptschleife läuft, GUI-Elemente verändern darf. GUI-Toolkits sind in der Regel nicht threadsicher. Das kann funktionieren, auch eine ganze Weile, aber letztendlich ist das Verhalten so eines Programms undefiniert und es kann sich jederzeit ”komisch” Verhalten bis hin zu harten Programmabstürzen.

Da wären wir dann wieder bei `after()`, denn auch wenn man E/A-Operationtn üblicherweise in eigene Threads auslagert, damit diese die GUI nicht blockieren können, kommunizieren die ihre Daten zum Hauptthread in dem die GUI-Hauptschleife läuft und die regelmässig mit `after()` prüft ob es neue Daten zum anzeigen gibt. Das wird in der Regel über eine `Queue.Queue` gelöst. Der ”Mess-Thread” schreibt seine Daten in die Queue und der GUI-Thread schaut mit Hilfe von `after()` regelmässig ob es neue Daten in dieser Queue gibt, die angezeigt werden müssen.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Vielen Dank für die Vielzahl Eurer Tipps - ich habe mich sehr darüber gefreut.

Ich habe jetzt mal versucht, das Programm etwas umzugestalten. Ich habe eine Erzeugerklasse erstellt (GetData), die in einem separaten Thread Werte aus Zufallszahlen erzeugt und in einer Queue speichert. Ich habe versucht, die tkinter-GUI und die GetData-Klasse über die Controller-Klasse kommunizieren zu lassen.

Könntet ihr mir noch ein paar Tipps geben, wie ich diesen Entwurf verbessern könnte oder ob gravierende Fehler vorhanden sind?

Code: Alles auswählen

try:
    import Tkinter as tk
except:
    import tkinter as tk

import time
import threading
from functools import partial
import random
import Queue

class GetData():
    def __init__(self, queue=Queue.Queue, update_time_s=0.8):
        self._queue=queue
        self._exitstate = False
        self._update_time_s=update_time_s
        self.start()

    def start(self):
        self._getdata_thread = threading.Thread(target=self._read_value)
        self._getdata_thread.start()

    def stop(self):
        print ("in GetData-stop")
        self._exitstate = True
        self._getdata_thread.join()

    def _read_value(self):
        while self._exitstate == False:
            self._queue.put(random.random())
            # print ("in update, number=%s" %str(self._value))
            time.sleep(self._update_time_s)

class GUI():
    def __init__(self, queue=Queue.Queue):
        self._exitstate=False
        # Variable fuer das Textdisplay
        self._queue = queue
        self._display_refreshtime_ms = 1125
        self._root = tk.Tk()
        self._display_elements()

    def _display_elements(self):
        self._textdisplay = tk.Label(self._root)
        self._textdisplay_refresh()
        # self._exit_button = tk.Button(self._root, text="Beenden", width=10, command=self._exit_gui)
        self._exit_button = tk.Button(self._root, text="Beenden", width=10)
        self._textdisplay.pack()
        self._exit_button.pack()

    def _textdisplay_refresh(self):
        print ("in textdisplayrefresh")
        while self._queue.empty() == False:
            text=self._queue.get()
            self._textdisplay.configure(text=text)
        if self._exitstate == False:
            self._root.after(self._display_refreshtime_ms, self._textdisplay_refresh)

    def start(self):
        self._root.mainloop()

    def stop(self):
        print ("in GUI-stop")
        self._exitstate=True
        self._root.destroy()

    def set_text(self, text):
        self._textdisplay_text=text

class Controller():
    def __init__(self):
        self._queue=Queue.Queue()
        self.getdata=GetData(queue=self._queue)
        self.gui=GUI(queue=self._queue)
        self.gui._exit_button.bind("<Button-1>", self._exit)
        self.gui_thread=threading.Thread(target=self.gui.start)
        self.gui_thread.start()

    def _exit(self, event):
        print ("in controller_exit")
        self.gui.stop()
        self.getdata.stop()
        # self.gui_thread.join()

def main():
    Controller()

if __name__ == '__main__':
    main()
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Bzw. Warum eigentlich der alternativ import? Also einmal tkinter mit grossem oder kleinem 't' ?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

@jens: Damit es unter Python 2 *und* 3 funktioniert!?
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

@BlackJack: Das wird bei Python 3 in diesem Fall dann aber spätestens beim Queue-import in Zeile 10 scheitern.

Ansonsten gibt es da noch immer einige Kritikpunkt, welche teilweise auch schon vorher genannt wurden.

GetData sieht noch immer so aus, also sollte es von Thread ergeben. Der Name ist auch noch immer sehr nichtssagend, da könnte man sich auch etwas besseres überlegen. Wenn der Code mit Python 2 kompatibel sein soll, dann sollten deine Klassen von object erben.

Der Wert des default-Parameters für queue ist sinnlos. Wird die Klasse ohne einen Angabe von queue-Erzeugt, dann funktioniert sie nicht. Außerdem ist das Suffix "_s" bei update_time_s irgendwie überflüssig. Soll das für "Sekunden" stehen?

``x == False`` solltest du als ``not x`` schreiben. Ein ``== True`` ist immer überflüssig. Wenn ``x == True`` wahr wird, dann hatte x bereits den Wert True. Wird der Ausdruck unwahr, dann hatte x den Wert False. Dann kannst du auch gleich ``x`` schreiben.

"_display_elements" ist ein irreführender Name. Da werden keine Widgets angezeigt, sondern erzeugt.

Die set_text-Methode hängt da irgendwie in der Luft und wird nirgends verwendet.
Das Leben ist wie ein Tennisball.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Herzlichen Dank für Eure Antworten.
EyDu hat geschrieben:GetData sieht noch immer so aus, also sollte es von Thread ergeben.
Ja - soll es, ich habe es aber irgendwie noch nicht hinbekommen das mit dem Vererben, dass muss ich im nächsten Schritt nochmal probieren. Ich habe zwar mal probiert mit class XXX(Threading...) die Threadklasse zu erben, aber hab das erstmal zurückgestellt, da es noch nicht auf anhieb funktionierte.
EyDu hat geschrieben:Der Name ist auch noch immer sehr nichtssagend, da könnte man sich auch etwas besseres überlegen.
Da stimme ich Dir zu - es ist aber erstmal ein Lernbeispiel für mich. Jetzt habe ich dort GetData als Bezeichnung gewählt, wenn ich damit später mein Temperaturmodul auslesen will, dann kann ich die Klasse natürlich auch GetTemperature oder so nennen. Ich habe mich halt versucht darauf zu konzentrieren, dass das mit den drei Klassen funktioniert - eine Controller-Klasse, eine GUI-Klasse und eben die GetData-Klasse - und noch nicht zu viel Zeit darein investiert, mir einen besseren Namen dafür zu überlegen. Vielleicht fällt mir das, wenn ich noch etwas mehr Erfahrung habe, leichter.
EyDu hat geschrieben:Außerdem ist das Suffix "_s" bei update_time_s irgendwie überflüssig. Soll das für "Sekunden" stehen?
Ja genau, dass ist für mich - weil die Funktion .after() arbeitet mit Milisekunden, die Funktion time.sleep mit Sekunden. Darum heisst auch die andere Variable "self._display_refreshtime_ms" für die .after-Methode.
EyDu hat geschrieben: Die set_text-Methode hängt da irgendwie in der Luft und wird nirgends verwendet.
Stimmt, die Funktion ist da überflüssig. Ich hatte erst als hin- und herüberlegt, wie ich den Text in der Klasse GUI anpasse, weil ich erst versucht habe, aus der Controller-Klasse den Wert zu setzen, was sich aber später erübrigt hat, weil ich es hinbekommen habe, dass die Funktion "_textdisplay_refresh" das schon macht. Ich kann die set_text-Methode also entfernen.

EyDu hat geschrieben:Der Wert des default-Parameters für queue ist sinnlos. Wird die Klasse ohne einen Angabe von queue-Erzeugt, dann funktioniert sie nicht.
Ich verwende einen Editor mit automatischer Ergänzung. Wenn ich das z.B. nicht mache hier:

Code: Alles auswählen

    def __init__(self, queue=Queue.Queue, update_time_s=0.8):
        self._queue=queue
und möchte dann in einer anderen weiteren Funktion mit der Variable self._queue arbeiten, dann bietet mir der Editor nicht in dem kleinen Fenster alle Methoden/Funktionen an, die ich mit dieser Bezeichnung machen könnte. Wenn ich es aber so dareinschreibe, dass es ein solches Element sein soll, dann ist es irgendwie vom Programmieren her etwas einfacher. Mache ich das halt nicht, dann bietet die Benutzeroberfläche mir nicht die ganzen Methoden an.
BlackJack

@Papp Nase: Aber das ist kaputter Code. Du schreibst also absichtlich fehlerhaften Quelltext. Keine besonders gute Idee. Wenn Du bessere Autovervollständigung haben möchtest, dann verwende eine statisch typisierte Programmiersprache.

`bind()` ist der falsche Weg um einer Schaltfläche eine Aktion zuzuordnen. Dafür hat `Tkinter.Button` die `command`-Option. Dann verhält sich die Schaltfläche auch so wie Benutzer das gewohnt sind.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

BlackJack hat geschrieben: `bind()` ist der falsche Weg um einer Schaltfläche eine Aktion zuzuordnen. Dafür hat `Tkinter.Button` die `command`-Option. Dann verhält sich die Schaltfläche auch so wie Benutzer das gewohnt sind.
Das mit dem .bind() habe ich in anderen Beispielen so gefunden, wo ich bei google nach der Stichwortkombination "pythn mvc tkinter" gesucht habe. Dort hat man so drauf zugegriffen. Das Problem ist da, dass ich doch eine Funktion aus der Klasse Controller aufrufen möchte.

Ich hab jetzt dass mal probiert:

Code: Alles auswählen

  self.gui._exit_button.config(command=self._exit)
und zwar hier in der Klasse - und das bind auskommentiert.

Code: Alles auswählen

class Controller():
    def __init__(self):
        self._queue=Queue.Queue()
        self.getdata=GetData(queue=self._queue)
        self.gui=GUI(queue=self._queue)
        # self.gui._exit_button.bind("<Button-1>", self._exit)

        self.gui._exit_button.config(command=self._exit)

        self.gui_thread=threading.Thread(target=self.gui.start)
        self.gui_thread.start()
Jetzt musste ich noch bei der Methode self._exit (self, event) das event wieder entfernen - so geht es auch. Ist das besser?
BlackJack

@Papp Nase: Wie gesagt: `command` ist der vorgesehene Weg, nur dann verhält sich eine Schaltfläche auch so wie das der Benutzer von allen anderen Schaltflächen in anderen Programmen gewohnt ist. Zum Beispiel das man die ”runterdrücken” kann, und dass die Aktion erst beim loslassen der Maustaste ausgeführt wird, und das man die Schaltfläche solange die Taste gedrückt ist, auch wieder verlassen kann ohne das etwas ausgelöst wird wenn man dann ausserhalb die Taste los lässt.

Mit ein paar weniger Klassen:

Code: Alles auswählen

import Queue
import random
import Tkinter as tk
from threading import Event, Thread


def queue_values(exit_event, queue, delay):
    while not exit_event.wait(delay):
        queue.put(random.random())


def start_queueing_values(exit_event, queue, delay=0.8):
    thread = Thread(target=queue_values, args=(exit_event, queue, delay))
    thread.start()
    return thread


class MainFrame(tk.Frame):
    def __init__(self, master, exit_event, queue, refresh_delay=1.125):
        tk.Frame.__init__(self, master)
        self.exit_event = exit_event
        self.queue = queue
        self.refresh_delay = refresh_delay
        self.display = tk.Label(self)
        self.display.pack()
        tk.Button(self, text='Beenden', command=self.do_exit).pack()
        self._refresh()
 
    def _refresh(self):
        try:
            while True:
                self.display['text'] = self.queue.get_nowait()
        except Queue.Empty:
            pass  # Intentionally ignored.
        self.after(int(self.refresh_delay * 1000), self._refresh)
 
    def do_exit(self):
        self.exit_event.set()
        self.quit()
 

def main():
    root = tk.Tk()
    exit_event = Event()
    queue = Queue.Queue()
    thread = start_queueing_values(exit_event, queue)
    gui = MainFrame(root, exit_event, queue)
    gui.pack()
    root.mainloop()
    thread.join()

 
if __name__ == '__main__':
    main()
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

BlackJack hat geschrieben:Mit ein paar weniger Klassen:
Cool :-) Vielen Dank für Dein Beispiel. Das mit dem Event ist was Neues, dass ich noch nicht kannte.

Das hier kapiere ich leider noch nicht so gut :-(

Code: Alles auswählen

class MainFrame(tk.Frame):
    def __init__(self, master, exit_event, queue, refresh_delay=1.125):
        tk.Frame.__init__(self, master)
Die Klasse MainFrame ist mein Hauptfenster der GUI. tk.Frame ist da dann eine Klasse, die MainFrame erbt. Ich verstehe jetzt leider nicht, was ich davon habe, dass ich die Frame-Klasse erbe.

Ein bißchen weiter unten wird der Anzeigelabel definiert:

Code: Alles auswählen

self.display = tk.Label(self)
Und hier ein Button:

Code: Alles auswählen

tk.Button(self, text='Beenden', command=self.do_exit).pack()
Warum schreibe ich dann oben in der Klassenklammer tk.Frame rein - also um die Methoden der Klasse Frame zu erben und nicht
clas MainFrame(tk.Button, tk.Label)? Weil in dem Beispiel werden doch keine Methoden der Klasse Frame verwendet. Die .pack-Methode z.B. zum Setzen des Labels

Code: Alles auswählen

self.display.pack()
ist doch auch in der Klasse Label vorhanden. Falls Du mir das noch erklären könntest, wäre ich sehr dankbar.
BlackJack

@Papp Nase: `MainFrame` ist nicht das Hauptfenster sondern ein `tk.Frame` der im Hauptfenster angezeigt wird. Das Hauptfenster ist das `Tk`-Exemplar das in der `main()`-Funktion erstellt wird.

Vererbung ist eine „ist ein(e)”-Beziehung. Und `MainFrame` *ist* kein `Label` und auch kein `Button`. Von *beidem* zu erben wäre auch komisch denn dann müsste man etwas erstellen was sowohl Label als auch Button *ist*. Was ein eigenartiges Gemisch wäre. Mein `MainFrame` *enthält* ein Label und einen Button, das hat aber nichts mit Vererbung zu tun. Das Stichwort hier ist Komposition.

Von der Klasse `Frame` wird von mir direkt nicht viel benutzt, aber dadurch das `MainFrame` ein `Frame` ist, kann man es wie einen `Frame` verwenden und auch so in die GUI integrieren, das heisst Tkinter verwendet diese Eigenschaften. Ich benutzte das ”nur” als Elternwidget für das Label und den Button, die ich in dem Frame darstelle und rufe in der `main()`-Funktion die `pack()`-Methode darauf auf um den Frame im Hauptfenster zu platzieren.
Papp Nase
User
Beiträge: 139
Registriert: Dienstag 11. März 2014, 15:12

Vielen Dank für Deine Erklärung und das Stichwort "Kompositum". Ich habe mir das mal auch im Internet angeschaut, was das ist.
Papp Nase hat geschrieben:Von der Klasse `Frame` wird von mir direkt nicht viel benutzt, aber dadurch das `MainFrame` ein `Frame` ist, kann man es wie einen `Frame` verwenden und auch so in die GUI integrieren, das heisst Tkinter verwendet diese Eigenschaften. Ich benutzte das ”nur” als Elternwidget für das Label und den Button, die ich in dem Frame darstelle und rufe in der `main()`-Funktion die `pack()`-Methode darauf auf um den Frame im Hauptfenster zu platzieren.
Mich interessiert nochmal folgendes - wenn man in einem komplexeren Programm verschiedene Funktionsbereiche hat, die auch in der GUI in verschiedenen Bereichen sind, macht es dann auch Sinn, die in verschiedenen "Frames" zusammenzufassen?

Nehmen wir mal das Beispiel von einer Temperaturerfassung, die in der GUI mehrere Funktionsframes hat

Frame1: Graphenanzeige mit Steuerfunktionen (z.B. Zommen, Ansichten verändern...)
Frame2: Messgerät steuern
Frame3: Button zum Programm beenden, Frame1 und Frame2 darin einfügen

Würde das Sinn machen, die einzelnen Klassen immer dann auch so beginnen:

Code: Alles auswählen

class GraphenFrame(tk.Frame):
    def __init__(self, master, irgendwas):
        tk.Frame.__init__(self, master)
        ... 

class BedienframeMessgerät(tk.Frame):
    def __init__(self, master, irgendwas):
        tk.Frame.__init__(self, master)
        ... 

class MainFrame(tk.Frame):
    def __init__(self, master, irgendwas):
        tk.Frame.__init__(self, master)
        ... 
BlackJack

@Papp Nase: Was man in Frames steckt und welche davon dann eigene Klassen sind, hängt vom konkreten Anwendungsfall ab, und auch ein bisschen von den eigenen Vorlieben. Das lässt sich so pauschal nicht sagen.
nixfindus
User
Beiträge: 1
Registriert: Sonntag 26. Juli 2015, 21:36

Hallo,
ein sehr interessanter Beitrag der einige lesenswerte Informationen enthält.

Der Beitrag ist zwar schon etwas her, aber das Endresultat würde mich interessieren. Denn ich überlege gerade, wie ich es am sinnvollsten anstelle, Daten von der seriellen Schnittstelle zu lesen um diese zyklisch mit Tkinter als Zahlenwert darzustellen.

Vielen Dank
Wolfgang
Antworten