Goswin hat geschrieben: Mein Algorithmus im Hauptmodul macht Berechnungen, die viel Zeit in Anspruch nehmen. Aus diesem Grund ist es wichtig sich laufend über dessen Fortschritt zu informieren und Zwischenergebnisse auszuwerten. Das geht natürlich auch schriftlich über das Terminal, oder durch die Ausgabe verschiedener Dateien, die man einsehen kann. Aber in einigen Fällen ist dieser Fortschritt inuitiver auszuwerten, wenn er grafisch dargestellt wird, und genau das möchte ich manchmal machen.
Das Algorithmus ist eindeutig die Hauptsache, und kann auch ohne grafische Darstellung durchlaufen und vernünftige Ergebnisse liefern. Die grafische Darstellung ist ohne den Algorithmus sinnlos, sie soll ja nur berichten, was dieser gerade macht. Deshalb halte ich es für "gesund" wenn der Algorithmus die grafische Darstellung von Zeit zu Zeit aufruft und nicht umgekehrt. Außerdem könnten je nach Verlauf manchmal verschiedene grafische Darstellungen angezeigt werden.
Für die grafische Darstellung wäre ein Canvas-Widget in Tkinter geqeignet. Aber wie mache ich es, dass Tkinter in einem parallelen Strang läuft, der immer dann reagiert, wenn er vom Algorithmus aufgerufen wird?
Ich bin mir nicht sicher, ob ich die Antwort von Blackjack richtig verstanden habe, wenn ich herauslese, dass das was ich ich möchte mit Tkinter so nicht zu machen ist. Insbesondere scheint er zu sagen, dass irgendwo im Programm eine Hauptschleife laufen muss, und laut den Tutorials die ich finde, wird diese mit ".mainloop()" angestoßen. Zumindest das letztere scheint mir ein Mythos zu sein, denn mein Fenster ist auch ohne ".mainloop()"-Anweisung zu sehen. Der folgende Beispielscode vergleicht verschiedene Rundwege zwischen den Besucherorten eines Handelsreisenden-Problems (traveling salesman problem), und zeichnet jedesmal, wenn er einen kürzeren findet, diesen in ein Canvas-Widget eines Tkinter-Begleitfensters, welches während des gesamten Verlaufs offen bleibt. Natürlich ist dieser Algorithmus grottenschlecht und dient nur dazu, die Erzeugung eines Begleitfensters zu illustrieren.BlackJack hat geschrieben: Das Problem ist halt, dass grafische Systeme das schlicht so nicht vorsehen. Die haben eine Hauptschleife und die muss auch laufen, sonst friert die GUI ein und das System fragt den Benutzer ob er die Anwendung killen möchte, weil sie nicht mehr reagiert.
Lang laufende Berechnungen kann man in einem eigenen Thread laufen lassen und ab und zu Daten in eine Queue schreiben lassen. Die kann man dann regelmässig von der GUI-Seite aus abfragen und eventuell vorhandene Daten/Zwischenergebnisse visualisieren. Oder man informiert den GUI-Thread über ein benutzerdefiniertes Ereignis das Daten in der Queue vorliegen. Die Daten selber kann man bei Tkinter leider nicht mit dem Ereignis übertragen.
Wie so etwas zu machen ist habe ich großen Teils durch Zufall herausgefunden (indem ich anderswo vergaß, ein "mainloop()" einzufügen). Aber ich verstehe leider immer noch nicht, warum so etwas funktioniert bzw funktionieren soll, und bin für jede Erklärung dankbar. Auch ist es gut möglich, dass das angezeigte Pattern verbessert werden kann oder vielleicht auch Probleme aufweist, die mir nicht aufgefallen sind.
Code: Alles auswählen
import tkinter as t
from time import sleep
#
class Begleitbild(object):
def __init__(self):
t.Tk().withdraw()
def oeffne(self):
self._fenster = fenster = t.Toplevel()
(weit,hoch) = (400,400)
fenster.geometry("{}x{}+{}+{}".format(weit+20,hoch+20,300,0))
fenster.overrideredirect(True)
rahmen = t.Frame(fenster,borderwidth=10)
rahmen.grid()
self._cv = t.Canvas(rahmen,bg='white',width=weit,height=hoch)
self._cv.grid()
self.zeichne_startbild()
def schliesse(self):
self._fenster.destroy()
def zeichne_startbild(self):
#leeres Startbild
cv = self._cv
self._koord = [-1,-1]
self._weg = self._cv.create_polygon( *self._koord,
outline='red',fill='white', width=2 )
def zeige_weg(self,koord=None):
#Bild aktualisieren
if not koord: koord = self._koord
self._cv.coords(self._weg,*koord)
self._fenster.update_idletasks()
sleep(1)
import math
import random as r
#
class Handelsreisender(object):
def __init__(self,bild):
koordwerte = list(range(10,400,10))
#
ortz = 12
ort_lst = []
for _ in range(ortz):
ort = ( r.choice(koordwerte), r.choice(koordwerte) )
ort_lst.append(ort)
print("\nDer Handelsreisender besucht folgende Orte:\n{}"
.format(ort_lst))
input("\nZufallswege im Begleitfenster sehen? ")
bild.oeffne()
#
#Schlechtmöglichster Algorithmus für diese Aufgabe;
#NUR um Begleitbilder zu illustrieren.
versuchz = 100000
minlaenge = 10e9
for versuch in range(versuchz):
#
(xx,yy) = ort_lst[-1]
laenge = 0
koord = []
for ort in ort_lst:
koord.extend(ort)
laenge += math.hypot(xx-ort[0],yy-ort[1])
(xx,yy) = ort
#
if laenge<minlaenge:
minlaenge = laenge
bild.zeige_weg(koord)
print("\nbisher kürzeste gefundene Weglänge ist {:.3f}"
.format(minlaenge))
#weiter = input("besseren Weg suchen? ")
#if weiter=='nein': exit()
r.shuffle(ort_lst)
print("\nMehr als {} Fälle untersuche ich nicht!".format(versuchz))
#
input("Begleitfenster schließen?")
bild.schliesse()
input("\ngenug?")
bild = Begleitbild()
Handelsreisender(bild)