Hallo, ich habe mal eine Anfängerfrage zu Tkinter und dort speziell zum Umgang mit der Klasse Canvas.
Ich möchte mir gerne die laufenden Zwischenergebnisse einer langwierigen Berechnung eines Python-Programms grafisch anzeigen lassen, z.B. in Form eines Punktes oder eines kleinen Kreises, der über ein Koordinatensystem wandert (also während der Berechnung seine Position verändert). Dazu habe ich mir mit Tk() ein Objekt instanziert, mit Canvas( ... ) die Eigenschaften des Fensters festgelegt und mir mit create_line( ... ) eine Koordinatensystem zusammen gebastelt und mit create_oval( ... ) einen kleinen Kreis in den Koordinatenursprung gelegt.
Alle diese Sachen werden aber erst nach dem Aufruf von mainloop() sichtbar. Und nach dem Aufruf kann ich die Position der Objekte (z.B. des kleinen Kreises) nicht mehr ändern, jedenfalls finde ich dazu keine Möglichkeit. Wie könnte ich das denn machen?
Ich nehme an, dass ich hier irgendetwas sehr simples übersehe bzw. meine Herangehensweise irgendwie nicht stimmt. Für einen kleien Tipp wäre ich deshalb sehr dankbar.
Nachträgliche Änderung von Objekten der Klasse Canvas
Hi Foxxi
Meinst du sowas?:
Gruß wuf 
Meinst du sowas?:
Code: Alles auswählen
#!/usr/bin/python
# -*- coding: utf-8 -*-
import Tkinter as tk
def move():
canvas.move('bubbles', 4, 4)
app.win.after(500, move)
class App(object):
def __init__(self):
self.win = tk.Tk()
self.win.geometry('{0}x{1}+{2}+{3}'.format(300, 300, 50, 50))
self.move_flag = False
app = App()
app.win.title('Move canvas object')
canvas = tk.Canvas(app.win)
canvas.pack()
canvas.create_oval(10, 10, 50, 50, fill='green', tag='bubbles')
canvas.create_oval(80, 80, 100, 100, fill='yellow', tag='bubbles')
move()
app.win.mainloop()

Take it easy Mates!
Hallo wuf,
vielen Dank für die schnelle Antwort. Wenn ich das richtig sehe, dann bewegen sich die beiden Objekte, weil sich die Methode move() in der Zeile app.win.after(500, move) rekursiv mit einer Zeitverzögerung selbst aufruft.
Mein Problem ist etwas anders gelagert, weil ich Daten aus einer Berechnung, die in einem anderen Modul stattfindet benutzen möchte, um daraus die Koordinaten der grafischen Objekte zu berechnen und diese Objekte dann entsprechend zu bewegen. Wie kriege ich es hin, Objekte zu bewegen, obwohl die Zeile whatever.mainloop() schon ausgeführt wurde (weil sonst ja gar kein Fenster sichtbar wäre). Oder geht das gar nicht? :K
Im Grunde brauche ich einen Datenplotter, der On-the-fly arbeitet, indem er eine Methode bereit stellt, an die man die gewünschten Koordinaten übergeben kann und die dann eben ein Objekt bewegt (oder von mir aus auch eine Kurve plottet, also immer einfach einen Pixel bei den neuen Koordinaten farblich ändert). Ich dachte, ich könne mir sowas vielleicht "mal eben schnell" selbst basteln.
Da fällt mir auf: vielleicht sollte ich danach mal im Allgemein-Thread fragen. Vielleicht gibt es so etwas irgendwo standardmäßig.
Grüße
Foxxi
vielen Dank für die schnelle Antwort. Wenn ich das richtig sehe, dann bewegen sich die beiden Objekte, weil sich die Methode move() in der Zeile app.win.after(500, move) rekursiv mit einer Zeitverzögerung selbst aufruft.
Mein Problem ist etwas anders gelagert, weil ich Daten aus einer Berechnung, die in einem anderen Modul stattfindet benutzen möchte, um daraus die Koordinaten der grafischen Objekte zu berechnen und diese Objekte dann entsprechend zu bewegen. Wie kriege ich es hin, Objekte zu bewegen, obwohl die Zeile whatever.mainloop() schon ausgeführt wurde (weil sonst ja gar kein Fenster sichtbar wäre). Oder geht das gar nicht? :K
Im Grunde brauche ich einen Datenplotter, der On-the-fly arbeitet, indem er eine Methode bereit stellt, an die man die gewünschten Koordinaten übergeben kann und die dann eben ein Objekt bewegt (oder von mir aus auch eine Kurve plottet, also immer einfach einen Pixel bei den neuen Koordinaten farblich ändert). Ich dachte, ich könne mir sowas vielleicht "mal eben schnell" selbst basteln.
Da fällt mir auf: vielleicht sollte ich danach mal im Allgemein-Thread fragen. Vielleicht gibt es so etwas irgendwo standardmäßig.
Grüße
Foxxi
OK Foxxi
Vielleicht kannst du ein kleines Datenpacket in Form einer Liste, welches das Objekt auf der Canvas bewegen soll zusammenstellen.
z.B.
Gruß wuf 
Vielleicht kannst du ein kleines Datenpacket in Form einer Liste, welches das Objekt auf der Canvas bewegen soll zusammenstellen.
z.B.
Code: Alles auswählen
data_points = [[x0,y0], [x1, y1], [x2, y2], . . . . [xx, yx]]

Take it easy Mates!
Ja, wenn die Berechnung fertig ist, dann kann ich das natürlich machen. Dann könnte ich diese Daten ähnlich wie in deinem Beispielcode in eine Bewegung umsetzten und somit darstellen lassen. In deinem Beispiel werden einfach bei jedem Schritt 4 Pixel hinzu genommen. Stattdessen könnten dort auch die Koordinaten abgearbeitet werden.
Ich wollte die Daten nur halt gerne während der laufenden Berechnung in einzelnen Koordinatenpaaren übergeben, weil die Berechnung sehr langwierig ist und man dann auch gleich den Berechnungsfortschritt anhand der Grafik sehen könnte. Also, jedenfalls dann, wenn man die Anzahl der Rechenschritte über die X-Achse laufen läßt.
Es würde auch funktionieren, wenn die Berechnung innerhalb der Codesequenz stattfindet, die das Objekt bewegt, also z.B. in move() in deinem Beispiel. Immer einen Rechenschritt ausführen, das Objekt bewegen und dann die Methode erneut aufrufen. Aber es wäre ja nun reichlich schlechter Stil, mitunter sehr große Codepakete in so ein Tool zu pasten, nur damit die Berechnung visualisiert werden kann. Nun gut, es müßte nicht der Code selbst dort sein, es würde reichen, die Berechnung von move() aus zu starten und die Zwischenergebnisse zurück liefern zu lassen. Aber das kann doch nicht der Sinn der Sache sein.
Was mich verwirrt ist, dass es Methoden wie move() oder coords() gibt, die man aber "von außen" praktisch nicht nutzen kann. Es muss doch irgendwie möglich sein, von außerhalb der Grafikobjekte eine Veränderung (z.B. eine Bewegung der Objekte) zu veranlassen. In Turbo Pascal habe ich solche kleinen Tools vor vielen Jahren oft auf die Schnelle zusammengetippt. O.k., das war noch nicht objektorientiert. Aber hier fühle ich mich im Moment, als hätte ich das totale Brett vor dem Kopf.
Grüße
Foxxi
Ich wollte die Daten nur halt gerne während der laufenden Berechnung in einzelnen Koordinatenpaaren übergeben, weil die Berechnung sehr langwierig ist und man dann auch gleich den Berechnungsfortschritt anhand der Grafik sehen könnte. Also, jedenfalls dann, wenn man die Anzahl der Rechenschritte über die X-Achse laufen läßt.
Es würde auch funktionieren, wenn die Berechnung innerhalb der Codesequenz stattfindet, die das Objekt bewegt, also z.B. in move() in deinem Beispiel. Immer einen Rechenschritt ausführen, das Objekt bewegen und dann die Methode erneut aufrufen. Aber es wäre ja nun reichlich schlechter Stil, mitunter sehr große Codepakete in so ein Tool zu pasten, nur damit die Berechnung visualisiert werden kann. Nun gut, es müßte nicht der Code selbst dort sein, es würde reichen, die Berechnung von move() aus zu starten und die Zwischenergebnisse zurück liefern zu lassen. Aber das kann doch nicht der Sinn der Sache sein.
Was mich verwirrt ist, dass es Methoden wie move() oder coords() gibt, die man aber "von außen" praktisch nicht nutzen kann. Es muss doch irgendwie möglich sein, von außerhalb der Grafikobjekte eine Veränderung (z.B. eine Bewegung der Objekte) zu veranlassen. In Turbo Pascal habe ich solche kleinen Tools vor vielen Jahren oft auf die Schnelle zusammengetippt. O.k., das war noch nicht objektorientiert. Aber hier fühle ich mich im Moment, als hätte ich das totale Brett vor dem Kopf.
Grüße
Foxxi
Hi Foxxi
Die Grafik-Methoden:
move() und coords() zum bewegen und canvas.create_line(), _rectangle(), _oval()_arc(), _oval() usw. zum zeichnen.
Hier noch das zeichnen einer Funktion. Sie wird nicht in Schritten ausgeführt sondern mit Koordinaten die vor dem zeichnen in eine Liste gepackt wurden:
Gruß wuf 
Tut mir leid hier verstehe ich dich nicht ganz. Was heisst bei dir genau von aussen. Meinst du damit von einer anderen laufenden Anwendung? Ich gehe davon aus deine Plotausgabe erfolgt auf einer Canvas im Hauptfenster deiner Anwendung und die Berechnungen führst du in der gleichen Anwendung durch. Dein Vorhaben sollte von mir aus absolut kein Problem sein.Foxxi hat geschrieben:Was mich verwirrt ist, dass es Methoden wie move() oder coords() gibt, die man aber "von außen" praktisch nicht nutzen kann.
Die Grafik-Methoden:
move() und coords() zum bewegen und canvas.create_line(), _rectangle(), _oval()_arc(), _oval() usw. zum zeichnen.
Hier noch das zeichnen einer Funktion. Sie wird nicht in Schritten ausgeführt sondern mit Koordinaten die vor dem zeichnen in eine Liste gepackt wurden:
Code: Alles auswählen
import tkinter as tk
from math import pow
from functools import partial
def draw_function(func_data):
# draw function
graph_plane.create_line(func_data, fill='red')
app_win = tk.Tk()
xrange = 800
yrange = 800
# REMARK: Function is starting at x=0 and y=0 which ist the upper left corner
# of the graph plane !
# Calculate the yscale to fit the function into the given graph plane
ymax = pow(yrange, 2)
yscale = yrange / ymax
# construct graph plane
graph_plane = tk.Canvas(app_win, width=xrange, height=yrange)
graph_plane.pack()
# calculate function
func_data = [[x, pow(x, 2)*yscale] for x in range(0, xrange)]
call_draw_func = partial(draw_function, func_data)
tk.Button(app_win, text='Zeichen Funktion', command=call_draw_func).pack()
app_win.mainloop()

Take it easy Mates!
@wuf: Hier berechnest Du aber wieder alles vorher. So wie ich das verstehe soll die Berechnung die *lange* dauert, zum Beispiel durch eine Schaltfläche gestartet werden und dann *während* die Berechnung läuft die Anzeige verändert werden. Was sich natürlich mit der `mainloop()` beisst, beziehungsweise dem Umstand, dass die GUI erst wieder aktiv wird wenn der Code hinter der Schaltfläche zurück kehrt.
@Foxxi: Du könntest die Berechnung in einem eigenen Thread starten und die Daten in eine `Queue` schreiben, die dann im GUI-Thread regelmässig abgefragt wird, zum Beispiel durch eine per `after()` ausgeführte Funktion. Was Du auf keinen Fall machen darfst ist aus einem anderen Thread etwas an der GUI ändern — das führt zu undefiniertem Programmverhalten bis hin zu harten Abstürzen.
@Foxxi: Du könntest die Berechnung in einem eigenen Thread starten und die Daten in eine `Queue` schreiben, die dann im GUI-Thread regelmässig abgefragt wird, zum Beispiel durch eine per `after()` ausgeführte Funktion. Was Du auf keinen Fall machen darfst ist aus einem anderen Thread etwas an der GUI ändern — das führt zu undefiniertem Programmverhalten bis hin zu harten Abstürzen.
Hallo allerseits,
danke, wuf, für den Code, aber BlackJack hat Recht. Die Anzeige soll während der Berechnung verändert werden, also zu einem Zeitpunkt, an dem die Liste mit den Koordinaten noch gar nicht fertig ist.
@BlackJack: Ja, an einen eigenen Thread habe ich auch schon mal gedacht, aber mir war nicht klar, wie ich das umsetzen kann. Kannst du etwas näher erklären, wie man eine 'Queue' beschreibt und wieder ausliest. Ach, ich sehe gerade, in den Python Docs gibt es einige Einträge unter multiprocessing. Das wird es bestimmt sein, nicht wahr?
Grüße
Foxxi
danke, wuf, für den Code, aber BlackJack hat Recht. Die Anzeige soll während der Berechnung verändert werden, also zu einem Zeitpunkt, an dem die Liste mit den Koordinaten noch gar nicht fertig ist.
@BlackJack: Ja, an einen eigenen Thread habe ich auch schon mal gedacht, aber mir war nicht klar, wie ich das umsetzen kann. Kannst du etwas näher erklären, wie man eine 'Queue' beschreibt und wieder ausliest. Ach, ich sehe gerade, in den Python Docs gibt es einige Einträge unter multiprocessing. Das wird es bestimmt sein, nicht wahr?
Grüße
Foxxi
@Foxxi: `multiprocessing` arbeitet mit Prozessen, das ist wenn man in CPython „echte” parallele Ausführung von Python-Bytecode benötigt das Mittel, aber hier vielleicht, oder ziemlich wahrscheinlich, übertrieben. Da würde das `threading`-Modul ausreichen. Und `Queue` ist auch ein Modul in der Standardbibliothek.
Es geht eventuell auch ohne diese Module. Lassen wir Foxxi zuerst einmal die Module 'Queue' & `threading` näher kennenlernen.BlackJack hat geschrieben: Da würde das `threading`-Modul ausreichen. Und `Queue` ist auch ein Modul in der Standardbibliothek.
Gruß wuf

Take it easy Mates!