tkinter.TclError: image "pyimage1" doesn't exist

Fragen zu Tkinter.
Antworten
tomZ
User
Beiträge: 9
Registriert: Samstag 16. September 2023, 20:16

Hallo,

ich will eine Anwendung schreiben bei der in einem tkinter Fenster durch einen Button ein zweites tkinter Fenster geöffnet werden soll - und in diesem zweiten Fenster soll unter anderem ein png Bild dargestellt werden.
Wichtig dabei ist, dass das erste Fenster aktiv bleibt und man dort weiterarbeiten kann.

Bis auf das Bild funktioniert auch alles wie gewollt, sobald ich
self.pictureout = Label(master = self.frame, image = self.picture)
einkommentiere kommt diese Fehlermeldung:
Exception in Tkinter callback
Traceback (most recent call last):
File "/usr/lib/python3.10/tkinter/__init__.py", line 1921, in __call__
return self.func(*args)
File "/home/thomas/Programme/Python/testumgebung/simple_starter.py", line 9, in start
a = frontend()
File "/home/thomas/Programme/Python/testumgebung/tk_fe_test_simple.py", line 16, in __init__
self.pictureout = Label(master = self.frame, image = self.picture)
File "/usr/lib/python3.10/tkinter/__init__.py", line 3187, in __init__
Widget.__init__(self, master, 'label', cnf, kw)
File "/usr/lib/python3.10/tkinter/__init__.py", line 2601, in __init__
self.tk.call(
_tkinter.TclError: image "pyimage1" doesn't exist
Wenn ich allerdings bei dem zweiten Script die letzte Zeile einkommentiere und dieses Scipt direkt starte funktioniert das Bild.

Hier das Starter Script:

Code: Alles auswählen

#!/usr/bin/python3

from tkinter import *
from tk_fe_test_simple import frontend

def main():
    
    def start():
        a = frontend()
    
    window = Tk()
    window.geometry('500x500')
            
    button = Button(master = window, text = "Start", width = 20, command = start)
    button.pack(anchor = 'w')
    
    mainloop()
    
main()
Und hier das (2.) Script dass das Bild ausgeben soll:

Code: Alles auswählen

#!/usr/bin/python3

from tkinter import *

class frontend:
    def __init__(self):
                
        self.window = Tk()
        self.window.geometry('1200x500')
        self.frame = Frame(master = self.window)
        self.frame.pack( side = RIGHT,anchor = 'ne', padx = 8, pady = 8 )
        
        self.picture = PhotoImage(file = "leer.png")
        print (type(self.picture))
        self.pictureout = Label(master = self.frame, image = self.picture) 
        self.pictureout.pack (anchor = 'w')
    
        self.window.mainloop()
        
#a = frontend()
Beide Scipte sind um alles was funktioniert ( Button, Radiobutton, Textfelder, Labels, etc.) gekürzt.
Beide Scripte und auch das Bild sind im gleichen Verzeichnis.
Der Typ von self.picture (print) ist in beiden Fällen <class 'tkinter.PhotoImage'>

Kann mir da jemand helfen oder einen Rat geben ?

Vielen Dank im Voraus
tom

P.S. Ich verwende Python 3.10.12 auf Linux Mint 21.2
__deets__
User
Beiträge: 14545
Registriert: Mittwoch 14. Oktober 2015, 14:29

Edit: ich habe Quatsch geredet, zu flüchtig gewesen. Sirius3 hat’s natürlich richtig benannt.
Sirius3
User
Beiträge: 18034
Registriert: Sonntag 21. Oktober 2012, 17:20

Da sind so einige Probleme: man benutzt keine *-Importe, weil man damit unkontrolliert Namen in den eigenen Namensraum lädt.
Für so ein kurzes Skript ist es unnötig. das in mehrere Dateien aufzuteilen.
Beim Aufruf von main fehlt der if-__name__-Part.
Innerhalb einer Funktion definiert man normalerweise keine weiteren Funktionen. Ist ja auch völlig unnötig, weil man ja frontend direkt aufrufen könnte. `mainloop` ruft man normalerweise auf dem Hauptfenster auf. Eine feste Fenstergröße gibt man nicht an, weil der Inhalt die Größe bestimmt.
Weiter geht es mit frontend: Klassen schreibt man groß: Frontend.
Es darf im gesamten Programm nur ein Exemplar von Tk geben, weitere Fenster macht man mit Toplevel. Auch hier gibt man keine feste Größe vor.
Für die anchor-Angaben benutzt man die tk-Konstanten tk.NE
Es darf insgesamt nur einen mainloop-Aufruf geben. Und in __init__ dürfen sowieso keine Langlaufenden Aufrufe vorkommen.

Code: Alles auswählen

#!/usr/bin/python3
import tkinter as tk

class Frontend:
    def __init__(self):
        self.window = tk.Toplevel()
        self.frame = tk.Frame(master=self.window)
        self.frame.pack(side=tk.RIGHT, anchor=tk.NE, padx=8, pady=8)
        self.picture = tk.PhotoImage(file="leer.png")
        self.pictureout = tk.Label(master=self.frame, image=self.picture) 
        self.pictureout.pack(anchor=tk.W)
        self.window.show()
        
def main():
    window = tk.Tk()
    button = tk.Button(master=window, text="Start", width=20, command=Frontend)
    button.pack(anchor=tk.W)
    window.mainloop()
    
if __name__ == "__main__":
    main()
Und das Problem kommt daher, dass Du mehrere Tk-Instanzen erzeugt hast, was ja, wie oben geschrieben, wegen solcher Fehler verboten ist.
Benutzeravatar
__blackjack__
User
Beiträge: 13466
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@tomZ: Sternchen-Importe sind Böse™. Da holt man sich gerade bei `tkinter` fast 140 Namen ins Modul von denen nur ein kleiner Bruchteil verwendet wird. Auch Namen die gar nicht in `tkinter` definiert werden, sondern ihrerseits von woanders importiert werden. Das macht Programme unnötig unübersichtlicher und fehleranfälliger und es besteht die Gefahr von Namenskollisionen.

Es darf nur *ein* `Tk`-Objekt geben. Das ist *das* Hauptfenster, wo auch der Tcl-Interpreter dran hängt. Für zusätzliche Fenster ist `Toplevel` da.

Klassennamen werden MixedCase geschrieben, also mit einem grossen Anfangsbuchstaben.

Die `start()`-Funktion ist so wie sie da steht überflüssig, die ruft ja nur `frontend` auf. Da kann man auch einfach ``command=frontend`` schreiben.

Aber die `__init__()` sollte nicht in einem `mainloop()`-Aufruf enden. Eine `__init__()` initialisiert ein Objekt und kehrt dann zurück, damit der Aufrufer ein initialisiertes Objekt bekommt mit er etwas machen *bevor* da alles abgelaufen ist. Zudem läuft ja bereits eine Hauptschleife, das macht also wenig Sinn noch eine zu starten.

Wenn es Konstanten für Zeichenketten mit besonderen Bedeutungen im `tkinter`-Modul gibt, dann sollte man die benutzen, statt literale Zeichenketten mit den gleichen Werten.

Code: Alles auswählen

- (void)countSheep {
    unsigned int sheep = 0;
    while ( ! [self isAsleep]) { ++sheep; }
}
tomZ
User
Beiträge: 9
Registriert: Samstag 16. September 2023, 20:16

ich danke euch sehr für die erhellenden Antworten.
Ich muss da wohl generell einen anderen Ansatz wählen um das Beabsichtigte umzusetzen ;)

Viele Grüße
tom
Antworten