"jpg" - Bild anzeigen

Fragen zu Tkinter.
Antworten
DB7WN
User
Beiträge: 49
Registriert: Samstag 18. März 2017, 22:11

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.
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du kannst Pillow benutzen. Damit geht’s.
DB7WN
User
Beiträge: 49
Registriert: Samstag 18. März 2017, 22:11

o.k., danke. Ich versuchs mal.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

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 :-)
Take it easy Mates!
DB7WN
User
Beiträge: 49
Registriert: Samstag 18. März 2017, 22:11

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)
Benutzeravatar
__blackjack__
User
Beiträge: 13071
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@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.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
DB7WN
User
Beiträge: 49
Registriert: Samstag 18. März 2017, 22:11

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"?
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

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.
DB7WN
User
Beiträge: 49
Registriert: Samstag 18. März 2017, 22:11

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?
Benutzeravatar
__blackjack__
User
Beiträge: 13071
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@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()
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

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.
DB7WN
User
Beiträge: 49
Registriert: Samstag 18. März 2017, 22:11

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.
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du erzeugst damit eine Referenz auf das Bildobjekt. Dadurch wird das nicht zerstört.
DB7WN
User
Beiträge: 49
Registriert: Samstag 18. März 2017, 22:11

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.
__deets__
User
Beiträge: 14523
Registriert: Mittwoch 14. Oktober 2015, 14:29

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.
Benutzeravatar
__blackjack__
User
Beiträge: 13071
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@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.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
DB7WN
User
Beiträge: 49
Registriert: Samstag 18. März 2017, 22:11

Ich glaub, das lern ich nicht mehr.
Antworten