Wie bekommt man ein PhotoImage in ein image Canvas item?

Fragen zu Tkinter.
Antworten
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Das Problem bei PhotoImages ist ja, dass die Config Option 'image' nicht als Referenz ausreicht, warum auch immer. Daher legt man am Besten bei einem Objekt eine Membervariable zusätzlich an, die auf das Photoimage verweist.

Nur das Canvas item 'image' gehört keiner Klasse an und ist daher kein Objekt. Extra dafür eine Klasse machen, möchte ich eigentlich nicht. Es soll ein Canvas item bleiben.

Wäre das eine Idee? Mit 'tag_bind' kann man ein Event an ein Canvas Item binden. Es sollte da auch ein virtuelles Event, das man nicht benützt, möglich sein. Den Callack läßt man dann auf eine Methode eines Objektes zeigen, welche die Referenz(en) für die PhotoImages enthält.

Wenn man mit delete das Canvas Item löscht, sollte das Event gelöscht werden und dann das referenzierte Objekt mit Referenzen für die Photoimages auch beseitigt werden.

Ist das eine gute Idee, oder gibt es bessere Ideen?

Ach so, kann man abfragen, welcher Callback an ein Event gebunden ist, ohne dass man extra event_generate ausführen muss? Ein Funktionsaufruf, mit dem ich etwas darüfer erfahren könnte, wäre nützlich.


Ich brauche zusätzliche Daten für ein Canvas Item, speziell für 'image', ohne dass ich dafür eine eigene Klasse hernehmen möchte.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Ach ich glaube jetzt, dass mir da eine Lösung eingefallen ist. Für den GuiDesigner brauche ich auch die File Angabe 'photoimage'. Kann es ja so machen, dass bei DynTkInter ein zusätzliches Objekt einer Klasse erzeugt wird, die dieses liefert.

Und wenn ich bei Canvas 'delete' überschreibe, wird auch dieses gelöscht, das heißt, aus einem Dictionary beseitigt.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Also, das mit tag_bind funktioniert:

Code: Alles auswählen

import tkinter as tk
from tkinter import PhotoImage

def create_canvas_photoimage(parent,x,y,**kwargs):
    filename = kwargs.pop('photoimage') if 'photoimage' in kwargs else None
    item = parent.create_image(x,y,**kwargs)
    if filename != None:
        photoimage = PhotoImage(file = filename)
        parent.itemconfig(item,image = photoimage)
        parent.tag_bind(item,'<<PhotoImage>>',photoimage)
    return item
         
root = tk.Tk()

canvas = tk.Canvas(root)
canvas.pack()
image_item = create_canvas_photoimage(canvas,20,50,anchor='w',photoimage = 'guidesigner/images/split1.gif')

root.mainloop()
Man müßte natürlich noch activeimage und disabledimage mit einbauen.

Das mit delete überschreiben ist doch keine gute idee, denn delete geht auch auf tags, evtl. gar auf ein tuple von tags und ids. Statt delete nachzubauen, wäre die Frage, kann man zusätzliche Daten auf diese Art und Weise an an canvas item binden, aber so, dass man diese wieder abfragen kann?

Für den GuiDesigner würde ich mir nämlich gerne auch die Filenamen merken, ohne einen eigenen GuiDesigner Modus mit zusätzlich dazu verwalteten Daten implementieren zu müssen.
Sirius3
User
Beiträge: 17754
Registriert: Sonntag 21. Oktober 2012, 17:20

"pop" hat ein default-Argument, so dass die komplizierte if-Abfrage unnötig ist, aber warum überhaupt? photoimage kann doch gleich ein default-Argument der Funktion sein:

Code: Alles auswählen

def create_canvas_photoimage(parent, x, y, photoimage=None, **kwargs):
    item = parent.create_image(x, y, **kwargs)
    if photoimage is not None:
        photoimage = PhotoImage(file=photoimage)
        parent.itemconfig(item, image=photoimage)
        parent.tag_bind(item, '<<PhotoImage>>', photoimage)
    return item
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@Sirius3 Ja keine schlechte Idee. Wo Du recht hast, hast Du recht. Wichtig dabei war, wie man das PhotoImage bekommt, ohne dass es in den Garbage Collector wandert.

Weiss jemand, warum man da doppelt referenzieren muß? Also normal ist das ja nicht! Ein Objekt, das nur einmal referenziert wirtd, sollte doch auch nicht in den Garbage Collector wandern. Was hat da tkinter gemacht, und gibt es da einen Grund dafür?
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Ist vielleicht doch keine gute Idee, es auf diese Art und Weise zu tun. Hatte nochmals etwas getestet, nämlich ob man auch andere Daten auf diese Art und Weise anbinden kann und dann Zugriff auf diese durch die __del__ Methode erhält, etwa:

Code: Alles auswählen

canvas.tag_bind(item, '<<AddData>>',CanvasItemData(canvas,item,photoimage=photoimage))
canvas.tag_bind(item, '<<AddData>>',None)
Aber so geht es nicht. Die __del__ Methode wurde nicht aufgerufen. Auch beim Beseitigen der <<PhotoImage>> Referenz blieb das Image weiterhin sichtbar. Nicht mal delete führte zum Aufruf der __del__ Methode. Was da tkinter oder der Canvas von tkinter tut, ist ziemlich unvorhersehbar. Zugriff auf zusätzliche Daten ist anscheinend nicht möglich. Am Besten dann doch zusätzliche Daten und Referenzen extra verwalten und die delete Methode erweitern. Sie normal ausführen lassen, aber hinterher ein Update von zusätzlichen Daten ausführen, nämlich herauslöschen, wofür kein Canvas Item mehr vorhanden ist.
Antworten