Seite 2 von 3

Re: canvas und button

Verfasst: Dienstag 15. Oktober 2019, 14:14
von __blackjack__
@reinerdoll: Das klingt sehr danach dass Du Dir erst einmal objektorientierte Programmierung anschauen solltest. Denn wenn es mehrere dieser Stopper geben soll, dann will man da ja nicht `stop1_auf()`/`stop1_zu()` bis `stop10_auf()`/`stop10_zu()` haben sondern sich eine `Stopper`-Klasse schreiben und davon dann 10 Exemplare zum Beispiel in eine Liste stecken. Auf und Zu möchte man vielleicht auch nicht mit relativen Bewegungen machen bzw. wenn doch, dann auch den Zustand merken. Denn momentan kann man 5 mal `stop_zu()` aufrufen und der Stopper wandert dann fröhlich durch das Laufband 100 Einheiten nach unten statt nur die 20 die er sollte.

Re: canvas und button

Verfasst: Dienstag 15. Oktober 2019, 14:37
von Sirius3
Wenne es Dir um den Lerneffekt geht, wäre das ein typisches Beispiel, wie man Logik und GUI sauber trennen kann, in dem die Anlage abstrakt mit ihrem Zustand und den Übergängen als Methoden in einer Klasse definiert wird und Du in der GUI per Knopf oder Timer die Übergänge aufrufen kannst und dann jeweils den neuen Zustand grafisch darstellst.

Der Zustand sollte dann auch schön in Listen oder anderen Datenstrukturen möglichst allgemeingültig beschrieben sein, so dass es einfach möglich ist, Anzahl der Stopper oder Teile zu ändern.

Re: canvas und button

Verfasst: Mittwoch 16. Oktober 2019, 03:01
von reinerdoll
das tut jetzt so ziemlich was es soll (die main macht einen beispielablauf)
aber ist die struktur jetzt in ordnung ? passt die trennung von klasse (=grafik) und funktion (=main) ??

Code: Alles auswählen

import tkinter as tk

class DigitalTwin(tk.Tk):
    def __init__(self):
        super().__init__()
        self.twin = tk.Canvas(self, width=700, height=300)
        self.twin.create_line(50, 100, 600, 100)
        self.twin.create_line(50, 120, 600, 120)
        self.teile = {}     #liste der teile auf dem band
        self.stopper = {}   #liste der stoppstellen vor kolben am band
        self.kolben = {}    #liste der stopperkolben
        self.twin.create_rectangle(150,80,160,100,fill = 'grey')
        self.kolben[1]=self.twin.create_rectangle(153,80,157,100,fill = 'grey')
        self.twin.create_rectangle(250,80,260,100,fill = 'grey')
        self.kolben[2]=self.twin.create_rectangle(253,80,257,100,fill = 'grey')
        self.twin.grid(row=1, column=0)
        self.nr=0

    def erzeuge_teil(self):
        self.nr=self.nr+1
        self.teile[self.nr] = self.twin.create_oval(50,100,70,120,fill="blue")

    def stop_zu(self,snr):
        self.twin.move(self.kolben[snr],0,20)
        if snr==1:
            self.stopper[snr]=153
        if snr==2:
            self.stopper[snr]=253

    def stop_auf(self,snr):
        self.twin.move(self.kolben[snr],0,-20)
        self.stopper[snr]=2000
        
    def bewege_band(self):
        for self.teilnr in range(1,self.nr+1):      #für jedes teil
            touch = 0
            for self.stoppnr in range(1,2+1):       #an jedem stopper
                if self.twin.coords(self.teile[self.teilnr])[2] == self.stopper[self.stoppnr]: #wenn es stopper berührt:
                    touch = 1
            if touch == 0:                          #wenn kein stopper berührt :
                if self.teilnr == 1:                #nur ein teil : 
                    self.twin.move(self.teile[self.teilnr],1,0)     #bewegen !                
                if self.teilnr > 1:                                 #ab zweitem teil : prüfe ob kollision !
                    if self.twin.coords(self.teile[self.teilnr])[2] < self.twin.coords(self.teile[self.teilnr-1])[0]:
                        self.twin.move(self.teile[self.teilnr],1,0) #nur bewegen, wenn keine kollision mit vordermann
              


def main():
    count = 0
    twin = DigitalTwin()
    twin.erzeuge_teil()
    twin.stop_zu(1)
    twin.stop_zu(2)
    while 1==1:
        count = count + 1
        if count==50:
            twin.erzeuge_teil()
        if count==100:
            twin.erzeuge_teil()
            twin.stop_auf(1)
        if count ==170:
            twin.stop_zu(1)
        twin.bewege_band()
        twin.after(50)
        twin.update()

 

    twin.mainloop()
    
main()
    

Re: canvas und button

Verfasst: Mittwoch 16. Oktober 2019, 03:13
von reinerdoll
und dann blamier ich mich halt nochmal :

ich brauche super(), um tk.canvas instanzieren zu können, oder ?

Re: canvas und button

Verfasst: Mittwoch 16. Oktober 2019, 07:59
von __deets__
Nein. Super braucht man, damit man eine Klasse vernünftig ableiten kann. In deinem Fall DigitalTwin und seine super-Klasse (daher der Name von super) tk.Tk. Die hat jede Menge Dinge bei ihrer Konstruktion zu tun, und mit super() sorgst du dafür, dass das passiert.

Und dein Code oben trennt nicht Funktion von GUI. Auch wenn main eine Funktion ist. Hier geht es um die Funktion im allgemeineren Sinne. Die Anlage sollte man als ein eigenes Objekt, mit verschiedenen Unterobjekten wie Förderband, Stoppern etc modellieren. Die durchzurechnen sollte erstmal gar nichts mit der GUI zu tun haben. Die stellt dann zu guter Letzt nur eine oder sogar mehrere Ansichten des Modells dar.

Re: canvas und button

Verfasst: Mittwoch 16. Oktober 2019, 08:11
von Sirius3
Ich habe nur ein Wörterbuch benutzt, weil ich nicht wußte, was die Teile bedeuten. Jetzt hast Du aber eindeutig eine Liste, weil Du ja neben den aufsteigenden Teile-Nummern auch noch die Gesamtzahl speicherst. Im (eigentlich überflüssigen) Kommentar schreibst Du ja auch von einer Liste.

Magische Werte sollte man nicht benutzen. Was passiert, wenn Dein Band mal 2000 Einheiten lang ist? Dann ist der Wert für `stopper_zu` plötzlich ein gültiger Wert. Du prüfst immer noch nicht, ob der Kolben bereits zu ist.

Ein for-Schleifenindex ist nie ein Instanzattribut. Wenn Du eine if-Abfrage hast und die ist genau das Gegenteil der vorherigen, dann benutzt man else. Für boolsche Werte benutzt man True und False statt 1 und 0.

Jetzt hast Du die Ereignisschleife schon wieder händisch programmiert. Benutze after.

Code: Alles auswählen

import tkinter as tk

class DigitalTwin(tk.Tk):
    def __init__(self):
        super().__init__()
        self.twin = tk.Canvas(self, width=700, height=300)
        self.twin.create_line(50, 100, 600, 100)
        self.twin.create_line(50, 120, 600, 120)
        self.teile = []
        self.stopper_position = [None, None]
        self.kolben = []
        self.twin.create_rectangle(150,80,160,100,fill = 'grey')
        self.kolben.append(self.twin.create_rectangle(153,80,157,100,fill = 'grey'))
        self.twin.create_rectangle(250,80,260,100,fill = 'grey')
        self.kolben.append(self.twin.create_rectangle(253,80,257,100,fill = 'grey'))
        self.twin.grid(row=1, column=0)
        self.time = 0

    def erzeuge_teil(self):
        self.teile.append(self.twin.create_oval(50,100,70,120,fill="blue"))

    def stop_zu(self,snr):
        self.twin.move(self.kolben[snr],0,20)
        if snr==0:
            self.stopper_position[snr] = 153
        if snr==1:
            self.stopper_position[snr] = 253

    def stop_auf(self,snr):
        self.twin.move(self.kolben[snr],0,-20)
        self.stopper_position[snr] = None
        
    def aktualisiere_band(self):
        vorheriges_teil = None
        for teil in self.teile:
            touch = False
            for position in self.stopper_position:
                if self.twin.coords(teil)[2] == position: #wenn es stopper berührt:
                    touch = True
            if not touch:                          #wenn kein stopper berührt :
                if vorheriges_teil is None:
                    self.twin.move(teil,1,0)     #bewegen !                
                else:
                    if self.twin.coords(teil)[2] < self.twin.coords(vorheriges_teil)[0]:
                        self.twin.move(teil,1,0) #nur bewegen, wenn keine kollision mit vordermann
            vorheriges_teil = teil

    def bewege_band(self):
        self.time += 1
        if self.time in (50, 100): 
            self.erzeuge_teil()
        if self.time == 100:
            self.stop_auf(0)
        if self.time == 170:
            self.stop_zu(0)
        self.aktualisiere_band()
        self.after(50, self.bewege_band)

def main():
    twin = DigitalTwin()
    twin.erzeuge_teil()
    twin.stop_zu(0)
    twin.stop_zu(1)
    twin.after(50, twin.bewege_band)
    twin.mainloop()
    
if __name__ == '__main__':
    main()
Jetzt ist das alles noch sehr statisch, Stopper hinzuzufügen ist kompliziert. Positionen werden an verschiedenen Stellen als magische Werte benutzt.

Code: Alles auswählen

import tkinter as tk

class DigitalTwin(tk.Tk):
    STOPPER_POSITIONEN = [153, 253]
    def __init__(self):
        super().__init__()
        self.twin = tk.Canvas(self, width=700, height=300)
        self.twin.create_line(50, 100, 600, 100)
        self.twin.create_line(50, 120, 600, 120)
        self.teile = []
        self.stopper_offen = []
        self.kolben = []
        for position in self.STOPPER_POSITIONEN:
            self.stopper_offen.append(False)
            self.twin.create_rectangle(position - 3, 80, position + 7, 100, fill='grey')
            self.kolben.append(self.twin.create_rectangle(position, 100, position + 4, 120, fill='grey'))
        self.twin.grid(row=1, column=0)
        self.time = 0

    def erzeuge_teil(self):
        self.teile.append(self.twin.create_oval(50,100,70,120,fill="blue"))

    def stop_zu(self,snr):
        if self.stopper_offen[snr]:
            self.twin.move(self.kolben[snr],0,20)
            self.stopper_offen[snr] = False

    def stop_auf(self,snr):
        if not self.stopper_offen[snr]:
            self.twin.move(self.kolben[snr],0,-20)
            self.stopper_offen[snr] = True
        
    def aktualisiere_band(self):
        vorheriges_teil = None
        for teil in self.teile:
            ypos = self.twin.coords(teil)[2]
            touch = any(not offen and ypos == position
                for position, offen in zip(self.STOPPER_POSITIONEN, self.stopper_offen))
            if not touch:
                if vorheriges_teil is None or ypos < self.twin.coords(vorheriges_teil)[0]:
                    self.twin.move(teil, 1, 0)
            vorheriges_teil = teil

    def bewege_band(self):
        self.time += 1
        if self.time in (50, 100): 
            self.erzeuge_teil()
        if self.time == 100:
            self.stop_auf(0)
        if self.time == 170:
            self.stop_zu(0)
        self.aktualisiere_band()
        self.after(50, self.bewege_band)

def main():
    twin = DigitalTwin()
    twin.erzeuge_teil()
    twin.after(50, twin.bewege_band)
    twin.mainloop()
    
if __name__ == '__main__':
    main()

Re: canvas und button

Verfasst: Mittwoch 16. Oktober 2019, 08:40
von reinerdoll
ich denke ich sehe licht am ende des tunnels.
jedenfalls verstehe ich deine ratschläge auf anhieb.

in super() muß ich mich einlesen, das geht nicht im vorbeigehen ...

Re: canvas und button

Verfasst: Donnerstag 17. Oktober 2019, 09:38
von reinerdoll
Auf meiner Reise durch die Abenteuer des Python-Landes bin ich jetzt auf ein neues Hindenis gestoßen.
DIe Grafik für meinen digitalen Zwilling läuft mit eurer Hilfe ganz gut, ich meine auch die meisten eurer tipps verstanden zu haben.

Jetzt möchte ich die Grafik animieren.

Hierzu sollen 4 Threads entstehen, die parallel zur Grafik laufend die SPS einer Anlage simulieren.
In Vb hatte ich das vor ein paar Jahren mal geschrieben, das läuft. In Python gibts ein (natürlich : Verständnis -) Problem.

Ich kann das Objekt Twin (siehe oben in den Codes) nicht benutzen :
Das "erzeuge_teil()" ist hier sinnfrei, einfach ein Beispiel für einen Methodenaufruf..)

(Ich weiß, das sind OO-Grundlagen, aber leider mangelt es da offenbar ...)

Code: Alles auswählen

  
#hier drüber, wie gehabt, die Klasse für die Grafik)

def modul1():
    while 1==1:     
        twin.erzeuge_teil()
        time.sleep(2)
        
def main():
    twin = DigitalTwin()
    wago = threading.Thread(target=modul1, args=())
    wago.start()
    twin.mainloop()
    
main()

Re: canvas und button

Verfasst: Donnerstag 17. Oktober 2019, 10:05
von Sirius3
@reinerdoll: jetzt mußt Du, wie schon oben beschrieben, eine klare Trennung zwischen Modell und GUI machen, bevor Du irgendetwas neues einbaust.

Dann ist ein Thread wahrscheinlich der falsche Weg und so wie Du es probierst auch falsch. Zum Triggern der Anlage würde ich immer noch after verwenden, um eine schöne Taktung zu haben. Dort kannst Du ja verschiedene Dinge des Models aufrufen.

`1==1` läßt sich leicht ausrechnen, das gibt `True` und ist damit lesbarer. Für den Rechner ist die Rechenaufgabe trivial, für den menschlichen Leser aber nur lästig.

Re: canvas und button

Verfasst: Donnerstag 17. Oktober 2019, 10:09
von __blackjack__
@reinerdoll: Du darfst in `modul1()` (schon wieder eine Nummer im Namen 🙄) nichts machen was die GUI verändert, also nicht `erzeuge_teil()` aufrufen. Wenn Du bei einer `tkinter`-GUI Threads verwendest, sollten die mit dem Hauptthread in dem die GUI-Hauptschleife läuft per Queue(s) kommunizieren, wobei die GUI-Seite regelmässig per `after()` nachschaut ob Daten in der Queue sind, mit denen etwas gemacht werden soll.

Am besten trennst Du wie vorgeschlagen die Programmlogik, also die Simulation, komplett von der GUI. Dabei ist es am einfachsten Verquickungen zu vermeiden in dem man erst die Programmlogik entwickelt und da dann nachher die GUI drauf setzt.

Re: canvas und button

Verfasst: Donnerstag 17. Oktober 2019, 10:23
von reinerdoll
@sirius3 :
die threads gefallen mir dehalb besonders, weil sie das gleiche problem bringen wie die unabhängig laufenden sps der anlage : bei zugriffen auf gemeinsame resourcen (da ist z.b. ein rfid-controller) gibt es konflikte. bei den threads wird das dann später mit semaphoren behandelt. die threads sind also "funktionell" den sps am ähnlichsten ...
wieso kann der thread hier aber das "twin"-objekt nicht nutzen, es ist doch schon vor aufruf des threads bekannt !?

@blackjack :
modul1 heißt in der realen anlage die erste station .. da kommt das her mit der 1.
dann ist beim lesen deiner antwort eine sicherung im kopf durchgebrannt ...
die methode "erzeuge_teil()" ist doch gerade dafür da, in der laufenden gui ein neues teil zu machen.
wenn ich das in der main aufrufe, macht es genau was es soll, warum nicht im therad ?

deine argumentaion mit der queue-kommuniaktion kommt mir, obwohl ich sie zum größten teil nicht verstehe, logisch vor.
ich dachte bisher, solche grafiken reagieren nur auf events (einen button zum beispiel), wie das mit ner queue funktioniert ist mir unklar.
ich hab im web gesucht, von den da angebotenen erklärungen krieg ich kopfweh ;-(
ist die übergabe des wunsches des modul-threads, ein teil zu erzeugen (also eine methode der grafik zu benutzen) mit ein paar zeilen zu machen (vielleicht hab ich dann ne chance, das zu verstehen..)

Re: canvas und button

Verfasst: Donnerstag 17. Oktober 2019, 10:54
von __deets__
Threads dürfen nicht mit GUI-Elementen interagieren. Das ist eine Beschränkung der GUIs. Was passieren kann ist, das der Thread einen Button zb manipuliert, während gleichzeitig der Benutzer diesen drückt. Im Resultat arbeiten zwei threads auf den gleichen Daten rum. Mit unvorhersehbaren Konsequenzen, Programmabsturz inklusive. Und nein, dagegen kannst du dich NICHT mit semaphoren schützen. Denn an den Code im GUI Framework wo du das müsstest kommst du gar nicht ran. Und das ist übrigens beim Windows forms genauso: https://blogs.msdn.microsoft.com/make_i ... er-thread/

Die Losung besteht immer darin (auch bei forms), den GUI thread die Arbeit selbst machen zu lassen. Dazu muss er irgendwie benachrichtigt werden, und das geht zb mit Timern (after) und einer Queue, die man periodisch prüft.

Re: canvas und button

Verfasst: Donnerstag 17. Oktober 2019, 11:03
von reinerdoll
einverstanden. beim nachlesen hab ich selber erkannt, daß gui und ein selbstgebastelter thread nicht "threadsicher" sind.
races, deadlocks, was immer ...
da kann ich mit eigenen mitteln (semaphore und so) natürlich nicht eingreifen.

ich muß also eine "abgesicherte kommunikation" zwischen gui und meinen threads herstellen.

das mit der queue krieg ich insofern nicht auf die reihe, als ich mir den mechanismus nicht vorstellen kann, der aus einem meiner sps-threads dafür sorgt, daß eine gewünschte aktion in der gui (nehmen wir das ausfahren eines stoppers oder das erzeugen eines teils) abläuft. wie kann ich das mit hilfe einer warteschlange kommunizieren ?

Re: canvas und button

Verfasst: Donnerstag 17. Oktober 2019, 11:09
von __blackjack__
(Ja ich weiss, hier wiederholt sich einiges was bereits gesagt wurde, ich bin zu faul das jetzt rauszulöschen. 😎)

@reinerdoll: Du hast doch schon selbst von Problemen mit gemeinsam genutzen Ressourcen und Semaphoren geschrieben: Die GUI ist nicht thread-safe, da können nicht mehrere Threads gleichzeitig Veränderungen dran vornehmen. Das kann gut gehen und dadurch scheinbar funktionieren — aber halt nur solange bis irgendwann zwei Threads tatsächlich an den gleichen Daten rumfummeln. Das kann dann zu subtilen Problemchen führen, aber auch zum kompletten, harten Programmabsturz.

Du rufst da ja `create_oval()` auf, was irgendwo in Tk (in C) eine Datenstruktur ändert, der Daten für dieses neue Oval hinzugefügt werden. Wenn jetzt der Hauptthread gleichzeitig über diese Struktur geht um mit den Daten die GUI zu aktualisieren, kann es passieren das die Struktur, die ja gerade gleichzeitig von dem anderen Thread verändert wird, in einem inkonsistenten Zustand ist. Das könnte dazu führen das nicht initialsisierte Werte verwendet werden, zum Beispiel ein Zeiger der ”irgendwo” hin zeigt, und das ganze Programm dann mit einem Speicherzugriffsfehler auf die Nase fällt.

`after()` generiert ähnlich wie ein Button ja auch ein Event. Statt auf einen Klick etwas aufzurufen, wird nach einer festgelegten Zeit etwas aufgerufen. Und das kann man regelmässig machen in dem man als letzten Schritt der Rückruffunktion die Funktion selbst wieder per `after()` für einen Rückruf anmeldet. So kann man eine Queue regelmässig auf Daten prüfen. Am Allgemeinsten wäre es wenn man eine Queue hat in die man Tupel mit drei Elementen steckt: Aufrufbares Objekt, Sequenz mit Positionsargumenten, ein Wörterbuch mit Schlüsselwortargumenten. Damit kann man dann beliebige Aufrufe von einem Thread in einen anderen verlegen.

Erst mal noch zu der eigentlichen letzten Frage: `modul1()` kann auf `twin` nicht zugreifen weil das eine lokale Variable in `main()` ist. Stell Dir mal vor das ginge so einfach, das alle Funktionen auf alle Namen in anderen Funktionen zugreifen könnten – dann müsste ja *jeder* Name im *ganzen* Programm *einmalig* sein. Und nun stell Dir ein Programm mit 10K Zeilen vor. Funktionen haben ja gerade den Zweck eine „black box“ zu sein über die Daten als Argumente rein kommen und als Rückgabewerte raus kommen und was *in* der Funktion passiert, unter anderem auch welche Namen lokal verwendet werden, ist aussen nicht weiter sichtbar.

`modul1()` muss `twin` also als Argument übergeben bekommen. Und für eine Kommunikation per Queue auch die Queue(s) dafür:

Code: Alles auswählen

def modul1(call_queue, twin):
    while True:
        call_queue.put((twin.erzeuge_teil, [], {}))
        time.sleep(2)


def main():
    call_queue = Queue()
    twin = DigitalTwin(call_queue)
    wago = Thread(target=modul1, args=(twin, call_queue), daemon=True)
    wago.start()
    twin.mainloop()
Und in `DigitalTwin` kann man dann eine Methode zur Abarbeitung dieser Queue schreiben:

Code: Alles auswählen

class DigitalTwin(tk.Tk):
    
    ...
    
    def process_call_queue(self):
        try:
            function, args, kwargs = self.call_queue.get(block=False)
        except Empty:
            pass  # Nothing in queue = nothing to do.
        else:
            function(*args, **kwargs)
        finally:
            self.after(100, self.process_call_queue)

Re: canvas und button

Verfasst: Donnerstag 17. Oktober 2019, 11:16
von reinerdoll
schluck, das ist schwere kost. aber zugleich sehr nahrhafte ! (da muß ich jetzt erst mal ein wenig kauen ...)

das mit den threadproblemen hab ich kapiert, ist völlig klar.

danke für eure geduld !!

Re: canvas und button

Verfasst: Donnerstag 17. Oktober 2019, 11:35
von __blackjack__
Okay die Abarbeitung der Queue hatte ich zu einfach gedacht. Zweiter Versuch:

Code: Alles auswählen

class DigitalTwin(tk.Tk):

    ...

    def process_call_queue(self):
        while True:
            try:
                function, args, kwargs = self.call_queue.get(block=False)
            except Empty:
                break
            else:
                try:
                    function(*args, **kwargs)
                except Exception:
                    logging.exception(
                        "calling %r with %r and %r", function, args, kwargs
                    )

        self.after(100, self.process_call_queue)
Eventuell könnte man da noch `Queue.task_done()` einbauen wenn irgendwo `Queue.join()` verwendet werden soll um sicherzustellen das alle Aufrufe abgearbeitet wurden.

Re: canvas und button

Verfasst: Donnerstag 17. Oktober 2019, 11:58
von reinerdoll
oh weh, da verstehe ich viel nicht. das wird dauern ..

es sieht nun so aus :

Code: Alles auswählen

from queue import Queue
 ..
 gui..
 ..
 
    def queue_aufruf(self):		#(noch als methode in gui)
        while True:
            try:
                function, args, kwargs = self.call_queue.get(block=False)
            except Empty:
                break
            else:
                try:
                    function(*args, **kwargs)
                except Exception:
                    logging.exception("calling %r with %r and %r", function, args, kwargs)
        self.after(100, self.queue_aufruf)
        
def modul1(call_queue,twin):
    while True:
        call_queue.put((twin.erzeuge_teil, [], {}))
        time.sleep(2)
        
def main():
    call_queue = Queue()
    twin = DigitalTwin(call_queue)
    wago = threading.Thread(target=modul1, args=(twin, call_queue), daemon=True)
    wago.start()
    twin.mainloop()
 


er bemängelt, daß die parameterzahl in twin = DigitalTwin(call_queue) nicht stimmt. es ist aber doch bloß einer ?

Re: canvas und button

Verfasst: Donnerstag 17. Oktober 2019, 12:23
von __blackjack__
Jain, es sind zwei, `self` und die Queue. Die `__init__()` muss man natürlich auch noch anpassen das die die Queue nimmt und als Attribut speichert, damit man in der `queue_aufruf()` da dran kommt. Und die Methode muss man auch initial einmal aufrufen, zum Beispiel in der `__init__()`, damit die dann regelmässig läuft.

Re: canvas und button

Verfasst: Donnerstag 17. Oktober 2019, 12:25
von Sirius3
@__blackjack__: das ist jetzt aber keine schöne Trennung, wenn das Modell die GUI kennen muß und sogar deren Methoden.

@reinerdoll: die Denkweise, die GUI müßte ein Teil erzeugen, ist noch falsch. Du hast einen Zustand des Modells. Die Simulation erzeugt aus einem Zustand einen neuen. Und die GUI ist dazu da, einen beliebigen Zustand (meist den aktuellen) darzustellen.
Um das alles Ablaufen zu lassen, braucht es nur eine Ereignisschleife, die dafür sorgt, dass alles schön regelmäßig aktualisiert wird.
Als Pseudo-Code:

Code: Alles auswählen

class ModellZustand:
    # Teile-Liste mit Position, Schieber-Liste mit Zusänden,  etc.

class GUI:
    def stelle_zustand_dar(self, modell):
        # hier bräuchte es ein Mapping der Zustands-Teile zu Canvas-Objekten, damit man die verschieben kann

def ereignis_schritt(gui, modell, simulation):
    neues_modell = simulation(modell)
    gui.stelle_zustand_dar(neues_modell)
    gui.after(100, ereignis_schritt, gui, neues_modell, simulation)
Hier benutze ich die gui-Ereignisschleife, um das Modell regelmäßig aktualisieren zu lassen. Ich bleibe dabei, dass dafür ein Thread die Sache nur unnötig kompliziert macht.

Re: canvas und button

Verfasst: Donnerstag 17. Oktober 2019, 12:42
von reinerdoll
@sirius3:

ja, ich verstehe deine argumentaion.
ein digitaler zwilling in der anlagentechnik ist erst auch mal nur eine digitale darstellung aller anlagenzustände. die gui wird dann (wenn überhaupt nötig) nur draufgesetzt.
soweit möchte ich aber nicht gehen (jedenfalls nicht selber, es gibt schon arbeiten hier , die sich mit sowas beschäftigen, alerdings dann mit professioneller software).
du hat sicher schon gemerkt, daß ich als software-laie da massive probleme habe, auch ist der aufwand enorm.

ein ausweg ("workaround" oder ?) wäre vielleicht, die gui komplett zu vergewaltigen, und die sps-aktionen als methoden direkt reinzuschreiben ?
da graust es dir, schon klar, aber erreiche ich mein ziel (mit überschuabarem aufwand eine kleine simuation) so nicht auch ?