Canvas-Objekte

Fragen zu Tkinter.
Antworten
Raininger
User
Beiträge: 11
Registriert: Mittwoch 23. Oktober 2013, 12:13
Wohnort: München

Hallo,

Mit der Canvas-Grafik habe ich ein Problem, bei dem mir vielleicht jemand helfen kann.
Ich möchte Objekte (hier Kreise) kontinuierlich mit move bewegen. Das Programm habe ich stark vereinfacht unten angehängt. Beim betätigen der Taste durchläuft es alle 10 Schritte und dann machen die Objekte einen großen Sprung.
Wie kann man erreichen, dass die Kreise bei jedem Schritt sich ein kleines Stück bewegen?
Vielen Dank im Voraus.
Ein Hobbybastler


Code: Alles auswählen

import time; import tkinter
haupt= tkinter.Tk()

c= tkinter.Canvas(haupt,width="36c", height="22c")
c.pack()

c.create_oval(420,440,460,480,fill="lightblue") 
c.create_oval(620,445,650,475,fill="lightblue") 
c.create_line(200,460,900,460)

def Lauf():
    for nn in range(1,11,1):
        print("  Schritt ",nn)
        c.move(1,0,7)
        c.move(2,0,7)
        time.sleep(0.2)
        
tkinter.Button(haupt,text="  Lauf  ",command=Lauf,bd=6,
                       font=("Verdana",15)).pack(side="left")

haupt.mainloop()
Zuletzt geändert von Anonymous am Mittwoch 23. Oktober 2013, 12:33, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@Raininger: GUI-Programmierung ist ereignisorientiert und nicht linear wie man das sonst vielleicht gewohnt ist. Das heisst solange *Dein* Code läuft kann die GUI-Hauptschleife nicht laufen, und wenn die nicht läuft, dann wird die GUI auch nicht aktualisiert. Du kannst die 10 Schritte also nicht in einer Schleife machen, sondern musst jeden Schritt die Kontrolle an die GUI zurückgeben. Dazu gibt es bei Tk die `after()`-Methode mit der man eine Funktion zeitversetzt aufrufen lassen kann. Die macht dann einen Schritt und benutzt wieder die `after()`-Methode um den Aufruf für den nächsten Schritt vorzubereiten.

Dafür muss man sich über Aufrufe hinweg die Zahl der Schritte merken. An der Stelle kommt dann objektorientierte Programmierung ins Spiel. Die sollte man IMHO drauf haben bevor man mit GUIs anfängt.

Edit: Das die `move()`-Aufrufe funktionieren ist wohl auch eher Zufall. Man sollte da nicht einfach „magische” Zahlen für die Canvas-Objekte verwenden sondern den Rückgabewert von den Methoden die diese Objekte erzeugt haben an Namen binden und *die* dann verwenden. Und die Namen sollten nicht nur aus einem Buchstaben bestehen sondern dem Leser vermitteln was die Bedeutung des Wertes dahinter ist ohne das er suchen muss wo der Name das erste mal gebunden wird.

Edit2: Nur mit Funktionen:

Code: Alles auswählen

from __future__ import print_function
import Tkinter as tk
from functools import partial

OVAL_COLOUR = 'lightblue'


def move_ovals(button, canvas, ovals, step):
    print('  Schritt ', step)
    for oval in ovals:
        canvas.move(oval, 0, 7)
    if step < 10:
        canvas.after(200, move_ovals, button, canvas, ovals, step + 1)
    else:
        button['state'] = tk.NORMAL


def start_move(button, canvas, ovals):
    button['state'] = tk.DISABLED
    move_ovals(button, canvas, ovals, 1)


def main():
    root = tk.Tk()
     
    canvas = tk.Canvas(root, width='36c', height='22c')
    canvas.pack()
    
    ovals = [
        canvas.create_oval(*args, fill=OVAL_COLOUR)
        for args in [(420, 440, 460, 480), (620, 445, 650, 475)]
    ]
    canvas.create_line(200, 460, 900, 460)

    run_button = tk.Button(
        root, text='  Lauf  ', border=6, font=('Verdana', 15)
    )
    run_button['command'] = partial(start_move, run_button, canvas, ovals)
    run_button.pack(anchor=tk.W)
     
    root.mainloop()


if __name__ == '__main__':
    main()
So etwas skaliert aber nicht wirklich gut. Objektorientiert ist in Python besser.
Antworten