kein Bild bei Label(parent, image=p)

Fragen zu Tkinter.
Antworten
sedi
User
Beiträge: 104
Registriert: Sonntag 9. Dezember 2007, 19:22

Hallo,

ich wollte in einem ersten Versuch im Umgang mit Bilder und Tkinter diese schlicht anzeigen lassen. Dazu habe ich das Modul tkimg.py erstellt:

Code: Alles auswählen

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


import os
import Tkinter as tk
from fnmatch import fnmatch
from PIL import Image, ImageTk


def convert_to_tk(image):
    if isinstance(image, ImageTk.PhotoImage):
        return image
    elif isinstance(image, (str, unicode)):
        return ImageTk.PhotoImage(Image.open(image))


if __name__ == "__main__":

    def test_image(window, *bilder):
        kw = {"relief": "groove"}
        pw = {"side": "left"}
        pictures = [convert_to_tk(b) for b in bilder]
        [tk.Label(window, image=p, **kw).pack(**pw) for p in pictures]

    window = tk.Tk()
    window.title("Images")

    bilderpfad = "."
    bilder = [p for p in os.listdir(".") if fnmatch(p, "*.png")]

    test_image(window, *bilder)

    kw = {"relief": "groove"}
    pw = {"side": "left"}
    pictures = [convert_to_tk(b) for b in bilder]
    [tk.Label(window, image=p, **kw).pack(**pw) for p in pictures]

    window.mainloop()
Rufe ich es über

Code: Alles auswählen

 python tkimg.py
auf, so erwartete ich, dass jedes Bild zwei mal im Fenster angezeigt wird, da es in der Funktion ``test_image`` und außerhalb der Funktion ins Programmfenster eingefügt wird.

Jedes Bild wird aber nur 1x angzeigt und zwar nur durch den Bereich außerhalb der Funktion!

Wie kann das sein?
CU sedi
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
BlackJack

@sedi: Weil nur im „Hauptprogramm” auch Referenzen auf die `PhotoImage`-Objekte behalten werden. Die Speicherverwaltung von Python weiss nichts davon das die Daten in Tk noch gebraucht werden und deshalb muss man solange sie dort benötigt werden auf Python-Seite die Objekte aufheben.

An dem Programm ist einiges unschön.

„List comprehensions” sind zum Erstellen von Listen gedacht, nicht um als einzeilige ``for``-Schleifen missbraucht zu werden in dem man Listen voller Werte erzeugt die überhaupt nicht gebraucht werden.

`convert_to_tk()` unterläuft das „duck typing” und vermischt zwei verschiedene Funktionen. Eigentlich sogar drei wenn man den Filtereffekt mit zählt den die Funktion hat wenn das Argument weder ein `PhotoImage` noch eine Zeichenkette ist.

Bei `test_image()` wird unnötigerweise ``*``-Magie verwendet, die nur dazu führt das man das beim Aufruf auch machen muss. Das Programm wäre einfacher wenn man es an beiden Stellen weg lassen würde.

Was soll `pw` beweuten? Weisst Du warum `kw` so heisst? Warum werden diese beiden Wörterbücher überhaupt angelegt?

`bilderpfad` ist unbenutzt.

Statt `fnmatch` hätte man hier gleich das `glob`-Modul verwenden können statt es nachzubauen.

Man sollte keine Sprachen mischen. Das es hier `bilder` und `pictures` gibt, die als Worte das gleiche bedeuten, die aber an Werte gebunden sind, die nicht die gleiche Bedeutung haben, ist verwirrend.
sedi
User
Beiträge: 104
Registriert: Sonntag 9. Dezember 2007, 19:22

In Zeile 23 holt sich doch die Funktion die Bildreferenzen, das Hauptprogramm in Zeile 36:

Code: Alles auswählen

pictures = [convert_to_tk(b) for b in bilder]
Beide Programmteile, Haupt- und Unterprogramm haben doch somit Referenzen auf die Bilder. Und in beiden Programmteilen werden zugehörige Labels erzeugt? Was versteh' ich denn da nicht???

Die Labels werden im Haupt- und Unterprogramm erzeugt und angezeigt. Die Bilder nicht??? Mir is das gar nich klar :(

Dann mal zum unschönen Programm:

1) ListComprehensions als for-Schleife missbrauchen - sieht nicht gut aus - stimmt!

2) "*" - Magie bei ``*bilder** weglassen - ok!

3) pw und kw sind Relikte einer erweiterten Version. Wurden lediglich nicht aus der Forumsversion genommen. Das betrifft auch den bilderpfad.

4) Sprachen mischen - unschön - yes ;)

4) Duck Typing:
`convert_to_tk()` unterläuft das „duck typing” und vermischt zwei verschiedene Funktionen. Eigentlich sogar drei wenn man den Filtereffekt mit zählt den die Funktion hat wenn das Argument weder ein `PhotoImage` noch eine Zeichenkette ist.
Diese Funktion war eigentlich nur aus der Hüfte geschossen, ohne wirklich Aussicht auf Übernahme in das Endprodukt, aber weil wir das schon mal zum Thema haben: Warum sollte man das DMn eben nicht so machen? Was spricht gegen die Funktion ``convert_to_tk``?
Wenn ich sichergehen will, dass ich eine für Tkinter passende Bildreferenz erhalte, dann erfüllt die Funktion (wenn sie noch weitere Prüfungszweige bekommt) dies! Klar - nach Duck Typing wäre im Hauptprogramm eine Prüfung des Objekts nötig, ob es tatsächlich ein PhotoImage ist, bentöigt ein ``try - except``... Aber warum nicht vorab eine Funktion, die sicherstellt, dass tatsächlich ein PhotoImage geliefert wird?
CU sedi
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
BlackJack

@sedi: Die Bildobjekte werden zwar in der Funktion in einer Liste gespeichert und an den Namen `pictures` gebunden, aber eine Zeile später ist die Funktion dann zuende und dann verschwinden alle lokalen Namen und damit ist dann die Liste nicht mehr erreichbar und kann von der automatischen Speicherbereinigung abgeräumt werden. Und ohne die Liste sind die Bildobjekte nicht mehr erreichbar und damit fallen dann auch die Bilddaten der Speicherbereinigung zum Opfer.

Ad 4) Wie schon gesagt, das läuft dem „duck typing” zuwieder wenn man mit `isinstance()` den konkreten Typ prüft. Und zwar grundsätzlich das prüfen, es wäre also auch keine gute Option das zum Aufrufer zu verschieben. Ich sehe auch den Sinn nicht. Wenn man das braucht dann doch eigentlich nur weil man schon an anderer Stelle unsauber gearbeitet hat und zum Beispiel eine Funktion hat, die entweder `PhotoImage`-Objekte oder Dateinamen zurück gibt, also zwei Typen die nicht als der selbe „duck type” durchgehen. Wenn man so etwas erst mal anfängt, zieht sich das schnell durch das ganze Programm das man plötzlich Fallunterscheidungen aufgrund des Typs braucht. Wenn Du sicherstellen willst das in der Liste `PhotoImage`-Objekte sind, dann erstell einfach eine Liste mit `PhotoImage`-Objekten. Also genau das was Du da gerade machst.
sedi
User
Beiträge: 104
Registriert: Sonntag 9. Dezember 2007, 19:22

Ahh - Danke - nun ist selbst mir klar :oops:
CU sedi
----------------------------------------------------------
Python 3.5; Python 3.6
LinuxMint18
Antworten