Tkinter erste animationen

Fragen zu Tkinter.
Antworten
BerryBlue
User
Beiträge: 41
Registriert: Mittwoch 9. November 2011, 19:57

Hallo Pythonforum,

Nach einigem rumprobieren mit Tkinter (python 2.7) schaff ich es einfach nicht, ein Bild einzufügen und dieses nach einer gewissen Zeit, ohne das man irgendeinen Knopf drückt das bild wechselt, also ein neues drübergezeichnet wir.

Wäre sehr dankbar über Lösungsvorshlage bzw. hilfen und Ideen.

MFG BerryBlue

PS: also sowas wie Animationen
Ich weis erst was ich denke, wenn ich höre was ich sage!
BlackJack

@BerryBlue: Man nimmt sich ein Widget welches ein Bild darstellen kann, zum Beispiel ein `Label` und benutzt die `after()`-Methode um das Bild durch ein anderes zu ersetzen.
BerryBlue
User
Beiträge: 41
Registriert: Mittwoch 9. November 2011, 19:57

Vielen dank für die schnelle Antwort, könnten Sie mir aber bitte ein Beispiel geben, da ich es iw. nicht verstehen.

MFG BerryBlue
Ich weis erst was ich denke, wenn ich höre was ich sage!
Benutzeravatar
StefanLawl
User
Beiträge: 92
Registriert: Donnerstag 7. Juni 2012, 20:23

Code: Alles auswählen

after(ms,func[,argl[,..]])

Code: Alles auswählen

Bein.after(1000, treten)
Auto.after(20, bremsen)
Computer.after(340, ausschalten)
Label.after(500, ...
:mrgreen:
Man sagt uns wir sollen der Idee gedenken und nicht des Mannes. Denn ein Mensch kann versagen. Er kann gefangen werden. Er kann getötet und vergessen werden. Aber 400 Jahre später kann eine Idee immer noch die Welt verändern.
-V
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi BerryBlue

Hier eine konkrete Idee:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys

try:
    #~~ For Python 2.x
    import Tkinter as tk
except ImportError:
    #~~ For Python 3.x
    import tkinter as tk

class App(object):
    
    def __init__(self):
        self.win = tk.Tk()
        self.win.protocol("WM_DELETE_WINDOW", self.close)

    def projector(self):
        if self.picture_pointer == len(self.pictures):
            self.picture_pointer = 0
            
        self.picture_display.config(image=self.pictures[self.picture_pointer])
            
        self.picture_pointer += 1
        
        self.win.after(PAUSE, self.projector)
        
    def run(self):
        self.win.mainloop()
    
    def close(self):
        self.win.destroy()

APP_WIN_XPOS = 50
APP_WIN_YPOS = 50
SCRIPT_NAME = sys.argv[0]

IMAGES = ['Bild_01.gif', 'Bild_02.gif']
PAUSE = 500  # Milliseconds
    
app = App()
app.win.title(SCRIPT_NAME)
app.win.geometry('+{0}+{1}'.format(APP_WIN_XPOS, APP_WIN_YPOS))

app.pictures = [tk.PhotoImage(file=image) for image in IMAGES]
app.picture_pointer = 0

app.picture_display = tk.Label(app.win)
app.picture_display.pack()

app.projector()

app.run()
Gruß wuf :wink:
Take it easy Mates!
BlackJack

@wuf: Da ist jetzt aber einiges auf Modulebene was eigentlich in die `__init__()` gehört.

Und die Bahandlung von 'WM_DELETE_WINDOW' ist überflüssig solange man nicht noch etwas anderes dort machen möchte als nur das Fenster zu zerstören. Das passiert auch so.
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

ein schönes Beispiel für itertools:

Code: Alles auswählen

from itertools import cycle
[...]
    def projector(self):
        self.picture_display.config(image=self.pictures.next())
        self.win.after(PAUSE, self.projector)
[...]
app.pictures = cycle(tk.PhotoImage(file=image) for image in IMAGES)
Grüße
Sirius
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

OK BlackJack

Habe das Skript nach deinen Anregungen angepasst und den Tipp von Sirius3 einfliessen lassen:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
from itertools import cycle

try:
    #~~ For Python 2.x
    import Tkinter as tk
except ImportError:
    #~~ For Python 3.x
    import tkinter as tk

class App(object):
    
    def __init__(self, images):
        self.win = tk.Tk()
        self.win.geometry('+{0}+{1}'.format(APP_WIN_XPOS, APP_WIN_YPOS))
        self.win.protocol("WM_DELETE_WINDOW", self.close)
        self.win.title(SCRIPT_NAME)
        
        #self.pictures = [tk.PhotoImage(file=image) for image in images]
        self.pictures = cycle(tk.PhotoImage(file=image) for image in images)
        #self.picture_pointer = 0

        self.picture_display = tk.Label(self.win)
        self.picture_display.pack()

    #def projector(self):
        #if self.picture_pointer == len(self.pictures):
            #self.picture_pointer = 0
            
        #self.picture_display.config(image=self.pictures[self.picture_pointer])
            
        #self.picture_pointer += 1
        
        #self.win.after(PAUSE, self.projector)

    def projector(self):
        self.picture_display.config(image=self.pictures.next())
        self.win.after(PAUSE, self.projector)
                
    def run(self):
        self.win.mainloop()
    
    def close(self):
        print('Good Bye')
        self.win.destroy()

APP_WIN_XPOS = 50
APP_WIN_YPOS = 50
SCRIPT_NAME = sys.argv[0]

IMAGES = ['Bild_01.gif', 'Bild_02.gif']
PAUSE = 500  # Milliseconds
    
app = App(IMAGES)
app.projector()
app.close_button = tk.Button(app.win, text='Schliessen', command=app.close)
app.close_button.pack()
app.run()
BlackJack hat geschrieben:Und die Bahandlung von 'WM_DELETE_WINDOW' ist überflüssig solange man nicht noch etwas anderes dort machen möchte als nur das Fenster zu zerstören. Das passiert auch so.
Das nehme ich bei meinen Skripts als Standard rein im Falle man doch auf die Idee kommt vor der Vorhangschliessung des Auftritts noch etwas auszuführen.

@Sirius3: Danke für deinen Tipp mit der Methode 'cycle' des Moduls 'itertools'. Funktioniert unter Python 2.6 bestens aber unter Python 3.x wird folgende Exception geworfen:
Traceback (most recent call last):
File "label_dia_images_02.py", line 58, in <module>
app.projector()
File "label_dia_images_02.py", line 40, in projector
self.picture_display.config(image=self.pictures.next())
AttributeError: 'itertools.cycle' object has no attribute 'next'
Gruß wuf :wink:
Take it easy Mates!
BlackJack

@wuf: Iteratoren haben in Python 3 keine `next()`-Methode mehr, dafür gibt es eine `next()`-Funktion. Die gibt es schon ab Python 2.6, also wenn Du keine ältere Version unterstützen möchtest, kann man die entsprechende Zeile einfach so ändern:

Code: Alles auswählen

self.picture_display.config(image=next(self.pictures))
Edit: Da der OP Python 2.7 einsetzt ist das aber eigentlich auch kein Problem von Sirius3 sondern von Deiner zusätzlichen Anforderung, dass es auch mit Python 3 laufen soll.
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

@wuf:
(Python 3.3)

Code: Alles auswählen

>>> from itertools  import cycle
>>> seq = range(3)
>>> cs = cycle(seq)
>>> cs.next()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'itertools.cycle' object has no attribute 'next'
>>> next(cs)
0
>>> next(cs)
1
>>> next(cs)
2
>>> next(cs)
0
>>> cs.__next__()
1
>>> cs.__next__()
2
...
Siehe auch:
http://python3porting.com/differences.html#next

:wink:
yipyip
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi BlackJack & yipyip

Danke für eure hilfreichen Antworten und Links. Habe das Skript somit auf den neuesten Stand für Py2.6 plus und Py3.x gebracht:

Code: Alles auswählen

#!/usr/bin/env python
# -*- coding: utf-8 -*-

import sys
from itertools import cycle

try:
    #~~ For Python 2.x
    import Tkinter as tk
except ImportError:
    #~~ For Python 3.x
    import tkinter as tk

class App(object):
    
    def __init__(self, images):
        self.win = tk.Tk()
        self.win.geometry('+{0}+{1}'.format(APP_WIN_XPOS, APP_WIN_YPOS))
        self.win.protocol("WM_DELETE_WINDOW", self.close)
        self.win.title(SCRIPT_NAME)
        
        self.pictures = cycle(tk.PhotoImage(file=image) for image in images)

        self.picture_display = tk.Label(self.win)
        self.picture_display.pack()

    def projector(self):
        self.picture_display.config(image=next(self.pictures))
            
        self.win.after(PAUSE, self.projector)
                
    def run(self):
        self.win.mainloop()
    
    def close(self):
        print('Good Bye')
        self.win.destroy()

APP_WIN_XPOS = 50
APP_WIN_YPOS = 50
SCRIPT_NAME = sys.argv[0]

IMAGES = ['Bild_01.gif', 'Bild_02.gif']
PAUSE = 500  # Milliseconds
    
app = App(IMAGES)
app.projector()
app.close_button = tk.Button(app.win, text='Schliessen', command=app.close)
app.close_button.pack()
app.run()
BlackJack hat geschrieben:Edit: Da der OP Python 2.7 einsetzt ist das aber eigentlich auch kein Problem von Sirius3 sondern von Deiner zusätzlichen Anforderung, dass es auch mit Python 3 laufen soll.
Ist jetzt natürlich klar. Sorry an Sirius3 sollte er mich hier missverstanden haben.

Gruß wuf :wink:

Edit: Habe die Fallunterscheidung von 'next' für Python2.x & 3.x korrigiert.
Zuletzt geändert von wuf am Freitag 28. Dezember 2012, 21:01, insgesamt 1-mal geändert.
Take it easy Mates!
yipyip
User
Beiträge: 418
Registriert: Samstag 12. Juli 2008, 01:18

@wuf:
Du brauchst doch gar keine Fallunterscheidung mit try...except zu machen. Die 'next(<iterator>)' Funktion gibts doch fuer Python 2.6+ sowie Python 3 (wie BlackJack schon sagte).
Jetzt laeufts auch mit Python 2.5- !? :-)

:wink:
yipyip
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Danke yipyip

Da habe ich BlackJack falsch verstanden. Setzte das Modul 'itertools' bis jetzt noch nie ein. Das vereinfacht das Skript natürlich noch mehr.

Gruß wuf :wink:
Take it easy Mates!
Antworten