Seite 1 von 1

"jpg" - Bild anzeigen

Verfasst: Montag 13. Mai 2019, 22:41
von DB7WN
Also ich hab mir schon die Finger wundgeklickt. Kann man wirklich nicht jpg-Bilder anzeigen? Alles was ich finde läuft immer auf "png", "bmp"... hinaus.

Re: "jpg" - Bild anzeigen

Verfasst: Montag 13. Mai 2019, 22:51
von __deets__
Du kannst Pillow benutzen. Damit geht’s.

Re: "jpg" - Bild anzeigen

Verfasst: Mittwoch 15. Mai 2019, 10:19
von DB7WN
o.k., danke. Ich versuchs mal.

Re: "jpg" - Bild anzeigen

Verfasst: Mittwoch 15. Mai 2019, 11:46
von wuf
Hi DB7WN

Hier eine sofortige Lösung damit du deine Finger ein wenig schonen kannst:

Code: Alles auswählen

import tkinter as tk
from PIL import Image, ImageTk
        
main_win = tk.Tk()
tk_image = ImageTk.PhotoImage(Image.open('my.jpg')) 
tk.Label(main_win, image=tk_image).pack(expand=True)    
main_win.mainloop()
Gruss wuf :-)

Re: "jpg" - Bild anzeigen

Verfasst: Donnerstag 16. Mai 2019, 21:29
von DB7WN
Ich hatte einen Teilerfolg! Als lib habe ich Pillow installiert.
Dieser Code macht was er soll und zeigt "Kira_Exif.jpg" im Label "Photoanzeige an:
from tkinter import *
from PIL import Image, ImageTk

fenster = Tk()
fenster.geometry("650x500")
photoanzeige = Label(fenster)
photoanzeige.place( x=0, y= 0)
path = "z://python_workspace/FOTO_Exif/Kira_Exif.jpg"
i = Image.open(path)
i = i.resize((400,300))
bild = ImageTk.PhotoImage(i)
photoanzeige.config(image=bild)

fenster.mainloop()
Nun möchte ich aber das Bild nicht sofort, sondern erst nach Drücken eines Button anzeigen. Dieser Code funktioniert nicht. Wer weiß warum?
Das hat wohl weniger etwas mit der lib Pillow zu tun, als mit einer anderen Eigenart von Tkinter, die ich hier übersehe.
from tkinter import *
from PIL import Image, ImageTk

fenster = Tk()
fenster.geometry("650x500")
photoanzeige = Label(fenster)
photoanzeige.place( x=0, y= 0)
los=Button(fenster,bg="yellow",text="Los!", command=start)
los.place(x=600, y=10)

fenster.mainloop()

def start():
path = "z://python_workspace/FOTO_Exif/Kira_Exif.jpg"
i = Image.open(path)
i = i.resize((400,300))
bild = ImageTk.PhotoImage(i)
photoanzeige.config(image=bild)

Re: "jpg" - Bild anzeigen

Verfasst: Donnerstag 16. Mai 2019, 23:07
von __blackjack__
@DB7WN: Du musst dafür sorgen, dass das Python-Objekt erreichbar bleibt, solange es angezeigt werden soll. In diesem Fall ist es an den Namen `bild` gebunden und wenn `start()` zuende abgearbeitet ist, dann verschwindet der Name und mit ihm die letzte Referenz auf das Objekt. Python hat keine Möglichkeit festzustellen ob Tk das noch braucht oder nicht. Du kannst es beispielsweise als Attribut auf dem Label setzen.

Wobei der Code eigentlich darüber stolpern müsste, dass `start` an der Stelle wo Du es verwendest, noch gar nicht definiert ist.

Der Sternchen-Import und die ganzen manuellen Pixelgenauen Einstellungen sind auch nicht gut. Also weder das `geometry()` noch die `place()`-Aufrufe sollten verwendet werden. `i` ist auch kein guter Name für ein Bild.

Re: "jpg" - Bild anzeigen

Verfasst: Freitag 17. Mai 2019, 08:53
von DB7WN
Danke blackjack. Ich weiß, daß "Sternchenimporte" Probleme machen können, aber ich mache eigentlich immer so kleine Codes, dass ich mir das aus Bequemlichkeit leisten will. Warum sollte man keine pixelgenauen Platzierungen machen? "pack" bringt mir immer Überraschungen und mit "place" klappt das ganz gut.

Also zum Hauptproblem: ich habe das so verstanden, dass die variable "bild" in der TK-Schleife nicht bekannt ist, weil sie in der Funktion "start" definiert ist. Ist das richtig? Was heißt "als Attribut auf dem Label setzen"?

Re: "jpg" - Bild anzeigen

Verfasst: Freitag 17. Mai 2019, 11:25
von __deets__
Und eines Tages werden aus kleinen große codes, und dann sind die ranziger als nötig. Vor allem aber übst du dadurch nix.

Und pack ist nicht überraschend, man muss es einmal verstanden haben. Überraschend ist, was mit deinen places passiert, wenn sich etwas ändert wie zb Bildschirmauflösung oder Schriftgrösse.

Last but not least: bild wird garbage collected. Du musst es an photoanzeige binden.

photoanzeige.bild = bild

ACHTUNG! Das ersetzt NICHT das config! Das ist einfach nur ein quirk,von Tkinter.

Re: "jpg" - Bild anzeigen

Verfasst: Freitag 17. Mai 2019, 21:13
von DB7WN
So, jetzt habe ich das rein funktional hingekriegt.
import tkinter as tk
from PIL import Image, ImageTk

def start():
path = "z://python_workspace/FOTO_Exif/Kira_Exif.jpg"
i = Image.open(path)
i = i.resize((400,300))
bild = ImageTk.PhotoImage(i)
photoanzeige.bind=bild
photoanzeige.config(image=bild)

fenster = tk.Tk()
fenster.geometry("650x500")

photoanzeige = tk.Label(fenster)
photoanzeige.pack(side="left")

los=tk.Button(fenster,bg="yellow",text="Los!", command=start)
los.pack(side = "left")

fenster.mainloop()
Das Positionieren der Widgets mit "pack" hat hier den Nachteil, dass z.B. bei Programmaufruf der Button an der linken Seite klebt, weil ja das Label noch ohne Inhalt ist. Wird das Bild in das Label eingefügt, springt der Button nach rechts. Das ist natürlich nix. Die Positionierung über Pixel hat den Vorteil, dass die Positionen absolut sind und nicht relativ zu den andere Widgets - oder kann man das mit "pack" auch machen?

Re: "jpg" - Bild anzeigen

Verfasst: Freitag 17. Mai 2019, 23:00
von __blackjack__
@DB7WN: Du hast das nicht funktional hingekriegt. Das ist bei Sachen mit Zuständen nur bedingt möglich, aber so ein Mindeststandard wie nicht irgendwelche globalen Variablen benutzen kann man auch in Python hinbekommen. Also alles was eine Funktion ausser Konstanten benötigt, sollte als Argument(e) übergeben werden. Das ist auch nötig wenn das Hauptprogramm in einer Funktion verschwindet um die globalen Variablen los zu werden. Man braucht also mindestens mal `functools.partial()` oder einen ``lambda``-Ausdruck um das soweit wie's halt geht funktional zu machen.

Da der `Button` nicht mehr verwendet wird, nach dem er angeordnet wurde, muss man da keinen Namen für verwenden.

`i` ist kein guter Name für etwas anderes als ganze Zahlen die als Zähler oder Index verwendet werden. Zumal man den Namen auch gar nicht braucht wenn man diese Zwischenergebnisse nicht unbedingt an etwas binden möchte.

Die Positionierung über Pixel hat nicht den Vorteil, sondern den *Nachteil* das die Positionierungen absolut sind und sich nicht darum scheren was andere Widgets an Platz belegen/benötigen. Da Du die Grösse des Bildes ja kennst – Du `resize()`\d das ja explizit, kannst Du am Anfang ganz einfach ein leeres Bild mit der gleichen Grösse erzeugen. Die Masse sollte man dann als Konstanten definieren, damit man sie leicht ändern kann ohne das überall im Code suchen und machen zu müssen:

Code: Alles auswählen

#!/usr/bin/env python3
import tkinter as tk
from functools import partial

from PIL import Image, ImageTk

IMAGE_SIZE = IMAGE_WIDTH, IMAGE_HEIGHT = (400, 300)


def start(photoanzeige):
    path = 'z://python_workspace/FOTO_Exif/Kira_Exif.jpg'
    bild = ImageTk.PhotoImage(Image.open(path).resize(IMAGE_SIZE))
    photoanzeige.bind = bild
    photoanzeige.config(image=bild)


def main():
    fenster = tk.Tk()

    empty_image = tk.PhotoImage(width=IMAGE_WIDTH, height=IMAGE_HEIGHT)
    photoanzeige = tk.Label(fenster, image=empty_image)
    photoanzeige.pack(side=tk.LEFT)

    tk.Button(
        fenster, bg='yellow', text='Los!', command=partial(start, photoanzeige)
    ).pack(side=tk.LEFT)

    fenster.mainloop()


if __name__ == '__main__':
    main()

Re: "jpg" - Bild anzeigen

Verfasst: Freitag 17. Mai 2019, 23:54
von __deets__
Bei pack springt es, wenn man’s falsch macht. Bei Place überlappt sich der Kram.

Wenn du sowas machen willst, dann kannst du zb einen Frame mit der späteren Größe des Bildes packen, und damit verrutscht dann nichts mehr.

Und du nennst die bild Eigenschaft “bind”. Das ist ne doofe Idee. Da gibt es auch als Methode, die du damit dann überbügelst.

Re: "jpg" - Bild anzeigen

Verfasst: Samstag 18. Mai 2019, 17:33
von DB7WN
Ich will ja mal Fotos aus einem Verzeichnis auswählen, das Foto anzeigen und dazu die EXIF-Daten. Das gibt zwei Listboxen für das Verzeichnis und für die EXIF-Daten und ein Label für das Foto nebeneinander. Die Fotos können natürlich unterschiedliche Formate haben, wodurch das Label in der Breite variiert.Ich werde mal versuchen sie in drei frames zu platzieren. Dann dürfte das "pack"-Problem gelöst sein.
Das "photoanzeige.bind (bild) hab ich einfach mal von dir übernommen. Warum beim Schreiben "bind" daraus geworden ist, war wohl ein Tippfehler, ist aber nicht aufgefallen, weils ja funktioniert hat.
Ich weiß allerdings nicht, was ich damit gemacht habe. Vielleicht bist du, oder auch jemand anderes, so freundlich mir das genauer zu erläutern.

Re: "jpg" - Bild anzeigen

Verfasst: Samstag 18. Mai 2019, 18:04
von __deets__
Du erzeugst damit eine Referenz auf das Bildobjekt. Dadurch wird das nicht zerstört.

Re: "jpg" - Bild anzeigen

Verfasst: Samstag 18. Mai 2019, 19:11
von DB7WN
Meine Unkenntnis liegt wohl viel tiefer.
Also wie ich das sehe (und das ist wohl nicht richtig):
"photoanzeige" habe ich ja als Label instanziert. Es hat also die Methoden eines Label. Das heißt für mich also es gibt z.B. "photoanzeige.pack()", "photoanzeige.config()", "photoanzeige.destroy()", etc. Aber was ist "photoanzeige.bild" und auch "photoanzeige.bind" macht das selbe. Macht "photoanzeige.wildsau" auch das selbe?
Ich tappe ziemlich im dunkeln.

Re: "jpg" - Bild anzeigen

Verfasst: Samstag 18. Mai 2019, 19:28
von __deets__
Ja. Macht dasselbe. Wie du auch einfach ausprobieren kannst. Normalerweise kann man an Python Objekte beliebige Attribute drandengeln. self.wildsau = irgendwas macht ja auch nix anderes.

Re: "jpg" - Bild anzeigen

Verfasst: Samstag 18. Mai 2019, 22:29
von __blackjack__
@DB7WN: Und Du musst das ja nur machen weil Du nicht objektorientiert arbeitest. Wenn Du eine Klasse hättest, könntest Du das Bild an Dein eigenes Objekt binden und müsstest es nicht irgend wo anders dran pappen.

Re: "jpg" - Bild anzeigen

Verfasst: Samstag 18. Mai 2019, 22:43
von DB7WN
Ich glaub, das lern ich nicht mehr.