Tkinter GUI aktualisieren

Fragen zu Tkinter.
Antworten
M4rcde
User
Beiträge: 16
Registriert: Sonntag 21. September 2014, 19:36

Hallo Profis,

ich weiß jetzt schon, dass dieser Thread sicherlich schon mehrfach erstellt wurde, habe ihn aber leider nicht gefunden (unterschiedliche Definitionen des Problems).
Ich schreibe gerade mein erstes Programm in Tkinter.
Ich habe schon die Canvas erstellt und auch schon Kreise an bestimmten Punkten "ausgegeben".
Mein Programm soll allerdings diesen Kreis (hängt von Radius r ab) alle 2 Sekunden aktualisieren. Das würde ich mit einer while-Schleife und time.sleep() realisieren.
Dennoch stell sich die Frage, wie kann ich die GUI verändern (alten Kreis löschen, neuen mit anderem Radius anzeigen), wenn bereits diese Tkinter.mainloop() ausgeführt wurde?
Hab mir schon diverses Zeug zu text updates durchgelesen, beidenen einfach der Text verändert wird, aber wie verändere ich einen Kreis?

Danke euch im vorraus für Antworten und Tipps.

Marc
BlackJack

@M4rcde: Mit Schleife und `sleep()` geht das nicht weil die `mainloop()` ja schon läuft und auch laufen muss damit die GUI ordentlich dargestellt wird.

Du müsstest mit der `after()`-Methode auf Widgets arbeiten und immer wieder eine Funktion/Methode registrieren die in x Sekunden ausgeführt werden soll, dann kurz etwas macht, und die Kontrolle wieder an die GUI-Hauptschleife zurück gibt.

Löschen brauchst Du die Kreise nicht, wenn Du Dir die IDs merkst, kannst Du die Eigenschaften der Grafikelemente darüber später auch wieder ändern.
M4rcde
User
Beiträge: 16
Registriert: Sonntag 21. September 2014, 19:36

Damn, ich bekomm after() net hin....
haste ein simples Beispiel parat für Kreise?


Danke schonmal ;)
Marc
M4rcde
User
Beiträge: 16
Registriert: Sonntag 21. September 2014, 19:36

Gerade sieht der Code so aus:

Code: Alles auswählen

import Tkinter
import time

master = Tkinter.Tk()

canvas_width = 900
canvas_height = 400
w = Tkinter.Canvas(master, 
           width=canvas_width,
           height=canvas_height)


def circle(canvas,x,y, r):
   id = canvas.create_oval(x-r,y-r,x+r,y+r)
   return id


circle(w,0,0, 50)
w.pack()
Tkinter.mainloop()
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Benutze einfach mal die Forumsuche und suche nach Tkinter und after. Zu dem Thema gibt es hier wahrscheinlich alle zwei Wochen eine Frage.
Das Leben ist wie ein Tennisball.
M4rcde
User
Beiträge: 16
Registriert: Sonntag 21. September 2014, 19:36

habe ich wie gesagt schon mehrfach durchsucht. Kann mit den Codes leider wenig anfangen und die entsprechen auch nicht den Anwendungszwecken von mir.
Da muss es doch ein ganz simples Review geben. Das ist doch was ganz simples... An dem Problem sitz ich den ganzen Tag...
Unglaublich, alles was ich probier klappt net...
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@M4rcde: was probierst Du denn, und wie äußert sich das "klappt net"?
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi M4rcde

Hier etwas zum ausprobieren mit 'widget.after()':

Code: Alles auswählen

import Tkinter as tk

CANVAS_WIDTH = 400
CANVAS_HEIGHT = 400

CIRCLE_XORG = 50
CIRCLE_YORG = 50
CIRCLE_RADIUS = 50

STEP_TIME = 30 # Milliseconds
XSTEP = 1
YSTEP = 1

def move_circle():
    canvas.move("MyCircle", XSTEP, YSTEP)
    x0, y0, x1, y1 = canvas.bbox("MyCircle")
    if x1 > CANVAS_WIDTH: return
    canvas.after(STEP_TIME, move_circle)
    
def circle(x, y, r):
    canvas.create_oval(x-r, y-r, x+r, y+r, tag="MyCircle", fill='yellow')

master = tk.Tk()
master.title("Moving Circle")
master.geometry("+{}+{}".format(20,20))
 
canvas = tk.Canvas(master, width=CANVAS_WIDTH, height=CANVAS_HEIGHT)
canvas.pack() 
 
circle(CIRCLE_XORG, CIRCLE_YORG, CIRCLE_RADIUS)

move_circle()

master.mainloop()
Gruss wuf :wink:
Take it easy Mates!
BlackJack

@wuf: Das mit den Tag finde ich ungünstig. Damit ist man entweder auf diesen einen Kreis beschränkt, beziehungsweise auf eine Gruppe von Kreisen die man immer zusammen ansprechen muss. Dann sollte das Tag 'MyCircles' heissen. Warum nicht einfach die ID des Kreises zurückgeben lassen statt ein Tag zu setzen?
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi BlackJack

Da hast du natürlich recht. Mein Versuchsskript ist nur für ein Canvasobjekt "MyCircle" gedacht sonst ergibt sich für mehrere Canvasobjekte folgendes Verhalten:

Code: Alles auswählen

import Tkinter as tk

CANVAS_WIDTH = 400
CANVAS_HEIGHT = 400

CIRCLE_XORG = 50
CIRCLE_YORG = 50
CIRCLE_RADIUS = 50

STEP_TIME = 30 # Milliseconds
XSTEP = 1
YSTEP = 1

def move_circle():
    canvas.move("MyCircle", XSTEP, YSTEP)
    x0, y0, x1, y1 = canvas.bbox("MyCircle")
    if x1 > CANVAS_WIDTH: return
    canvas.after(STEP_TIME, move_circle)
    
def circle(x, y, r):
    canvas.create_oval(x-r, y-r, x+r, y+r, tag="MyCircle", fill='yellow')

master = tk.Tk()
master.title("Moving Circle")
master.geometry("+{}+{}".format(20,20))
 
canvas = tk.Canvas(master, width=CANVAS_WIDTH, height=CANVAS_HEIGHT)
canvas.pack() 
 
circle(CIRCLE_XORG, CIRCLE_YORG, CIRCLE_RADIUS)
circle(CIRCLE_XORG+50, CIRCLE_YORG+50, CIRCLE_RADIUS)

move_circle()

master.mainloop()
Für mehrere Canvasobjekte muss deren Tag erweitert werden.

Gruss wuf :wink:
Take it easy Mates!
M4rcde
User
Beiträge: 16
Registriert: Sonntag 21. September 2014, 19:36

Ich danke euch, habe also nun after() zum laufen gebracht. Allerdings funktioniert after() offensichtlich nicht in einer while-schleife, aber des bekomm ich noch hin :) Danke
BlackJack

@M4rcde: Die Funktion bei `after()` darf nicht lange laufen, wie jede Rückruffunktion bei GUIs weil die GUI-Hauptschleife laufen muss wenn die GUI nicht einfrieren soll.

Man muss die Schleifenschritte in Aufrufe umorganisieren, wie das Beispiel von wuf ja zeigt. Also keine Schleife in der der Kreis in jedem durchlauf bewegt/verändert wird, sondern ein Funktionsaufruf in dem das gemacht wird was sonst in einem Schleifendurchlauf passiert und diese funktion wird dann Regelmässig von der GUI aufgerufen wenn man sie immer wieder mit `after()` dazu bringt.
M4rcde
User
Beiträge: 16
Registriert: Sonntag 21. September 2014, 19:36

BlackJack hat geschrieben:@M4rcde: Die Funktion bei `after()` darf nicht lange laufen, wie jede Rückruffunktion bei GUIs weil die GUI-Hauptschleife laufen muss wenn die GUI nicht einfrieren soll.

Man muss die Schleifenschritte in Aufrufe umorganisieren, wie das Beispiel von wuf ja zeigt. Also keine Schleife in der der Kreis in jedem durchlauf bewegt/verändert wird, sondern ein Funktionsaufruf in dem das gemacht wird was sonst in einem Schleifendurchlauf passiert und diese funktion wird dann Regelmässig von der GUI aufgerufen wenn man sie immer wieder mit `after()` dazu bringt.
Ich danke vielmals! Werde mich da wie gesagt nun mal einlesen :) Dankeschön!
Antworten