Bild Wird nicht auf der Canvas angezeigt.

Fragen zu Tkinter.
Antworten
Zuzu_Typ

Ich habe ein paar Probleme mit Tkinter verbunden mit PIL (Python Imaging Library):
Ich versuche auf möglichst einfachem Wege ein Bild darzustellen:

Code: Alles auswählen

     canvas = Canvas(self.root,width=32,height=32)
     canvas.place(x=0,y=0)
     image = Image.open("Data/SystemExclamation.png")
     photoimage = ImageTk.PhotoImage(image)
     canvas.create_image(0,0,anchor=NW,image=photoimage)
Allerdings erzeugt das einen Abbruch:
Exception in Tkinter callback
Traceback (most recent call last):
...
TclError: image "pyimage1" doesn't exist

Ich hatte dieses Problem vorher noch nie, da ich auf diese Weise schon mehrmals Bilder angezeigt habe. Ich benutze Windows 7 x64bit, Python 2.7.8 x32bit und PIL 1.1.7
Hat jemand eine Idee?
Vielen dank für jegliche Antwort.
-Zuzu_Typ-
Zuletzt geändert von Zuzu_Typ am Mittwoch 23. Juli 2014, 16:35, insgesamt 1-mal geändert.
Grund: Quelltext in Python-Code-Tags gesetzt.
BlackJack

@Zuzu_Typ: Ich tippe mal darauf dass das Problem erst auftaucht wenn die Methode abgearbeitet ist (den Teil hast Du aus dem Traceback ja leider entfernt), und dass es daran liegt, dass man auf das Bildobjekt auf Python-Seite eine Referenz behalten muss, weil Python nicht weiss welche Bilder Tk intern noch benötigt.

Warum ein Canvas wenn's einfach sein soll? Label wäre IMHO einfacher.
Zuzu_Typ

Hier nochmal die ganze Exception:

Code: Alles auswählen

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 1486, in __call__
    return self.func(*args)
  File "I:\...\Scripter.py", line 85, in add_connection
    canvas.create_image(0,0,anchor=NW,image=photoimage)
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 2279, in create_image
    return self._create('image', args, kw)
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 2270, in _create
    *(args + self._options(cnf, kw))))
TclError: image "pyimage1" doesn't exist
Ein Label wäre zwar einfacher, es hat allerdings 2 Nachteile:
Es gibt keine Transparenz, Es ist ein 2 Pixel breiter, weißer Rand um das Bild (Ich weiß allerdings nicht ob das noch aktuell ist). Ich habe den Teil, der das Problem auslöst schon rausgesucht:
...\Python27\Lib\lib-tk\Tkinter.py ab linie 2260

Code: Alles auswählen

def _create(self, itemType, args, kw): # Args: (val, val, ..., cnf={})
        """Internal function."""
        args = _flatten(args)
        cnf = args[-1]
        if type(cnf) in (DictionaryType, TupleType):
            args = args[:-1]
        else:
            cnf = {}
        return getint(self.tk.call(
            self._w, 'create', itemType,
            *(args + self._options(cnf, kw))))
Zuletzt geändert von Zuzu_Typ am Mittwoch 23. Juli 2014, 17:53, insgesamt 1-mal geändert.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

BlackJack hat dir die Lösung schon genannt: Du must die Referenz auf das Bildobjekt selbst noch speichern, Tkinter macht das nicht für dich. Deine rausgesuchte "Fehlerstelle" hat damit nichts zu tun, bzw. nur sehr indirekt.

Das Forum unterstüzt übrigens Code-Tags, der Button dazu befindet sich direkt neben denen für "fett" und "rekursiv". Dann kann man deinen Code auch vernünftig lesen.
Das Leben ist wie ein Tennisball.
Zuzu_Typ

Ich will ja nicht nerven, aber wenn du mit
Du musst die Referenz auf das Bildobjekt selbst noch speichern
meinst, dass ich die Variable zu der Canvas Klasse hinzufügen soll, dann hilft mir das nicht weiter, denn auch dann zeigt python den Abbruch an. Wenn du damit etwas anderes meinst, dann entschuldige bitte (Ich bin noch Anfänger).
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Nee, du musst das image-Objekt aus deinem ersten Stück Code so lange unter Python halten, wie es von Tkinter benötigt wird. Wie genau das in deinem Fall aussehen muss, das hängt von deinem Code ab, vielleicht zeigst du den einfach mal. Sollte sich dein gezeigter Code in der __init__-Methode befinden oder in einer Methode, welche von dieser aufgerufen wird, dann genügt es, wenn du das Bild an das Objekt bindest. Statt ``image`` also ``self.image`` verwendest.
Das Leben ist wie ein Tennisball.
Zuzu_Typ

Ok Hier kommt der gesamte Code:

Code: Alles auswählen

# -*- coding: cp1252 -*-
from Tkinter import *
from PIL import Image, ImageTk
import string
import random
import time
import winsound

"""Dieses Programm wird später dafür da sein, für Spiele ein Script zu erstellen"""
class Scripter:
    def __init__(self):
        try:
            open_ = open("Data/Script.zts","r") #Öffnet das zu erstellende Script
            self.raw_script = open_.readlines()
            open_.close()
        except IOError:
            open("Data/Script.zts","w").close() #Erstellt das Script, falls es noch nicht existiert
            self.raw_script = False
    def save_file(self): #Speichert später das Script
        print "File saved"
    def main(self): #Hauptmethode
        self.mode = "NORMAL"
        #Haupt-Fenster
        self.root = Tk()
        self.root.geometry("800x600")
        self.root.title("ZuzuTypScripter")

        #Haupt-Menü
        self.menubar = Menu(self.root)
        self.menu_file = Menu(self.menubar,tearoff=0)
        def add_room(): #Neuer Raum für das Spiel
            try:
                self.room_number = self.room_number
                print "1"
            except (NameError,AttributeError):
                self.room_number = "0"
                print "2"
            try:
                self.rooms = self.rooms
            except (NameError,AttributeError):
                self.rooms = []
            self.room_root = Tk()
            self.room_root.geometry("200x100")
            self.room_root.title("Raum erzeugen")
            self.room_label = Label(self.room_root,text="Gib die Einstellungen des Raumes an")
            self.room_label.place(x=5,y=1)
            self.room_entry_name = Entry(self.room_root,width=20,fg="gray50")
            self.room_entry_name.place(x=5,y=21)
            self.room_entry_name.insert(END,"Name")
            def func_name(event):
                self.room_entry_name.config(fg="black")
                self.room_entry_name.unbind("<Button-1>")
                self.room_entry_name.delete(0,END)
            self.room_entry_name.bind("<Button-1>",func_name)
            self.room_entry_directions = Entry(self.room_root,width=20,fg="gray50")
            self.room_entry_directions.place(x=5,y=41)
            self.room_entry_directions.insert(END,"Anzahl d. Richtungen")
            def func_directions(event):
                self.room_entry_directions.config(fg="black")
                self.room_entry_directions.unbind("<Button-1>")
                self.room_entry_directions.delete(0,END)
            self.room_entry_directions.bind("<Button-1>",func_directions)
            def room_finish():
                self.room_info = "[ROOM_"+self.room_number+":{NAME='"+self.room_entry_name.get()+"'},{DIRECTIONS="+self.room_entry_directions.get()+"}]"
                self.room_number = str(int(self.room_number)+1)
                self.room_root.destroy()
                self.rooms.append(self.room_info)
            def room_abort():
                self.room_root.destroy()
            self.room_button_ok = Button(self.room_root,text="  OK  ",command=room_finish)
            self.room_button_abort = Button(self.room_root,text=" Abbrechen ",command=room_abort)
            self.room_button_ok.place(x=50,y=70)
            self.room_button_abort.place(x=100,y=70)
            self.room_root.mainloop()
        def add_connection(): #Eine Verbindung zwischen Räumen erstellen
            if self.mode == "ROOM":
                pass #Wenn der richtige Modus aktiviert wird, startet das Programm (zukünftig)
            else:
                self.connection_root = Tk() #Abbruchfenster wird geöffnet
                self.connection_root.geometry("300x100")
                self.connection_root.title("Falscher Ansichtsmodus")
                self.connection_root.wm_iconbitmap(bitmap = "Data/SystemExclamation.ico")
                self.connection_canvas = Canvas(self.connection_root,width=32,height=32)
                self.connection_canvas.place(x=0,y=0)
                self.connection_image = Image.open("SystemExclamation.png")
                self.connection_photoimage = ImageTk.PhotoImage(self.connection_image)
                self.connection_canvas.create_image(0,0,anchor=NW,image=self.image)
                #winsound.PlaySound('SystemExclamation', winsound.SND_ALIAS) #Spielt den SystemExclamation Sound ab
                self.connection_root.mainloop()
        def add_item():
            print "new item"
        def add_action():
            print "new action"
        def add_background():
            print "new background"
        def change_room():
            print "room changed"
        def change_connection():
            print "connection changed/deleted"
        def change_direction():
            print "direction changed"
        def change_item():
            print "item changed"
        def change_action():
            print "action changed"
        def change_background():
            print "background changed"
        def sight_normal():
            print "sight changed"
        def sight_room():
            print "sight changed"
        def sight_script():
            print "sight changed"
        def change_resolution():
            print "resolution changed"
        def coding():
            print "coding changed"
        def autosave():
            print "autosave changed"
        #Menü-Reiter
        self.menu_file.add_command(label="Neuer Raum",command=add_room)
        self.menu_file.add_command(label="Neue Verbindung",command=add_connection)
        self.menu_file.add_command(label="Neuer Gegenstand",command=add_item)
        self.menu_file.add_command(label="Neue Aktion",command=add_action)
        self.menu_file.add_command(label="Neuer Hintergrund",command=add_background)
        self.menubar.add_cascade(menu=self.menu_file,label="Neu")
        self.menu_edit = Menu(self.menubar,tearoff=0)
        self.menu_edit.add_command(label="Raum wechseln",command=change_room)
        self.menu_edit.add_command(label="Verbindung ändern/trennen",command=change_connection)
        self.menu_edit.add_command(label="Richtung wechseln",command=change_direction)
        self.menu_edit.add_command(label="Gegenstand ändern",command=change_item)
        self.menu_edit.add_command(label="Aktion ändern",command=change_action)
        self.menu_edit.add_command(label="Hintergrund ändern",command=change_background)
        self.menu_sight = Menu(self.menubar,tearoff=0)
        self.menu_sight.add_command(label="Normal",command=sight_normal)
        self.menu_sight.add_command(label="Raumübersicht",command=sight_room)
        self.menu_sight.add_command(label="Scriptübersicht",command=sight_script)
        self.menu_options = Menu(self.menu_file,tearoff=0)
        self.menu_options.add_command(label="Auflösung",command=change_resolution)
        self.menu_options.add_command(label="Codierung",command=coding)
        self.menu_options.add_command(label="Auto-Sichern",command=autosave)
        self.menubar.add_cascade(menu=self.menu_edit,label="Bearbeiten")
        self.menubar.add_cascade(menu=self.menu_sight,label="Ansicht")
        self.menubar.add_cascade(menu=self.menu_options,label="Optionen")
        self.menubar.add_command(label="Sichern",command=self.save_file)
        self.root.config(menu=self.menubar)

        #Fensteraufbau
        self.label_welcome = Label(self.root,text="Willkommen bei ZTScripter!",font="Helvetica 14")
        self.label_welcome.place(x=10,y=2)
        self.root.mainloop()
scripter = Scripter()
scripter.main()
            
Ich hoffe, er ist übersichtlich genug.
Ich habe den Code, den ich bisher gepostet habe etwas gekürzt, um es übersichtlicher zu machen.
Hier ist nocheinmal der originale Exclamation code:

Code: Alles auswählen

Exception in Tkinter callback
Traceback (most recent call last):
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 1486, in __call__
    return self.func(*args)
  File "I:\Entkomme dem Kaufhaus\Scripter.py", line 87, in add_connection
    self.connection_canvas.create_image(0,0,anchor=NW,image=self.connection_photoimage)
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 2279, in create_image
    return self._create('image', args, kw)
  File "C:\Python27\lib\lib-tk\Tkinter.py", line 2270, in _create
    *(args + self._options(cnf, kw))))
TclError: image "pyimage1" doesn't exist
BlackJack

@Zuzu_Typ: Es darf immer nur *ein* `Tk`-Objekt geben. Das ist das Hauptfenster und daran hängt auch die ganze Laufzeitumgebung von Tk. Wenn man mehr davon erstellt können komische Sachen passieren weil dann nicht mehr eindeutig ist zu welchem Tk-Interpreter welches Datum gehört. Wenn Du zusätzliche Fenster haben möchtest, musst Du das mit `Toplevel` machen.
Zuzu_Typ

Ah, interesting.
Vielen Dank, das wusste ich noch nicht. :D
Antworten