Contenu | Rechercher | Menus

Annonce

La nouvelle clé USB Ubuntu-fr est en prévente
Rendez-vous sur la boutique En Vente Libre

Si vous avez des soucis pour rester connecté, déconnectez-vous puis reconnectez-vous depuis ce lien en cochant la case
Me connecter automatiquement lors de mes prochaines visites.

À propos de l'équipe du forum.

#1 Le 24/03/2019, à 14:11

kholo

[résolu] subtilité python ; image dans tkinter

Bonjour,
de la page de Fabrice Sincère
j'ai pris le code Exemple n°8 pour afficher des images pour en faire une class
qui affiche d'autres type d'images que les gif
donc j'ai ajouté PIL et nettoyé un peu le code
code origine :

 # script lecture_gif.py
#(C) Fabrice Sincère
from tkinter import *
import tkinter.messagebox
import tkinter.filedialog

def Ouvrir():
    Canevas.delete(ALL) # on efface la zone graphique

    filename = tkinter.filedialog.askopenfilename(title="Ouvrir une image",filetypes=[('gif files','.gif'),('all files','.*')])
    print(filename)

    photo = PhotoImage(file=filename)
    gifdict[filename] = photo  # référence
    print(gifdict)

    Canevas.create_image(0,0,anchor=NW,image=photo)
    Canevas.config(height=photo.height(),width=photo.width())

    Mafenetre.title("Image "+str(photo.width())+" x "+str(photo.height()))

def Fermer():
    Canevas.delete(ALL)
    Mafenetre.title("Image")

def Apropos():
    tkinter.messagebox.showinfo("A propos","Tutorial Python Tkinter\n(C) Fabrice Sincère")

# Main window
Mafenetre = Tk()
Mafenetre.title("Image")

# Création d'un widget Menu
menubar = Menu(Mafenetre)

menufichier = Menu(menubar,tearoff=0)
menufichier.add_command(label="Ouvrir une image",command=Ouvrir)
menufichier.add_command(label="Fermer l'image",command=Fermer)
menufichier.add_command(label="Quitter",command=Mafenetre.destroy)
menubar.add_cascade(label="Fichier", menu=menufichier)

menuaide = Menu(menubar,tearoff=0)
menuaide.add_command(label="A propos",command=Apropos)
menubar.add_cascade(label="Aide", menu=menuaide)

# Affichage du menu
Mafenetre.config(menu=menubar)

# Création d'un widget Canvas
Canevas = Canvas(Mafenetre)
Canevas.pack(padx=5,pady=5)

# Utilisation d'un dictionnaire pour conserver une référence
gifdict={}

Mafenetre.mainloop()

mon code issue de ce code

#!/usr/bin/env python3
# -*- coding: UTF8 -*-
"""
# script lecture_gif.py
#(C) Fabrice Sincère

modifié pour les images d'autres formats
"""

import tkinter as tk
import tkinter.messagebox
import tkinter.filedialog as fd

from PIL.ImageTk import PhotoImage 

class Display_Image(tk.Tk):
    """ afficheur d'image """
    def __init__(self):
        """ init """
        # tk.Tk.__init__(self)
        super().__init__()

        self.title("Image")
        self.affiche_menu()

        self.canevas = tk.Canvas(self)
        self.canevas.pack(padx=5,pady=5)

        self.imgdict = {} # dictionnaire pour conserver une référence
    def ouvrir_image(self):
        """ Ouvrir image """
        self.canevas.delete(tk.ALL) # on efface la zone graphique

        titre = "Ouvrir une image"
        fitres = [('all files','.*'), ('gif files','.gif')]
        filename = fd.askopenfilename(title=titre,filetypes=fitres)

        # photo = tk.PhotoImage(file=filename) # PhotoImage de tk
        photo = PhotoImage(file=filename) # PhotoImage de PIL
        self.imgdict[filename] = photo  # référence

        self.canevas.create_image(0,0,anchor=tk.NW,image=photo)
        self.canevas.config(height=photo.height(),width=photo.width())

        self.title("Image "+str(photo.width())+" x "+str(photo.height()))
    def fermer_image(self):
        """ Fermer image"""
        self.canevas.delete(tk.ALL)
        self.title("Image")
    def fenetre_a_propos(self):
        """ fenetre Apropos """
        titre, message = "A propos", "Tutorial Python Tkinter\n(C) Fabrice Sincère"
        tkinter.messagebox.showinfo(titre, message)
    def affiche_menu(self):
        """ Création d'un widget Menu """
        menubar = tk.Menu(self)

        menufichier = tk.Menu(menubar,tearoff=0)
        menufichier.add_command(label="Ouvrir une image",command=self.ouvrir_image)
        menufichier.add_command(label="Fermer l'image",command=self.fermer_image)
        menufichier.add_command(label="Quitter",command=self.destroy)
        menubar.add_cascade(label="Fichier", menu=menufichier)

        menuaide = tk.Menu(menubar,tearoff=0)
        menuaide.add_command(label="A propos",command=self.fenetre_a_propos)
        menubar.add_cascade(label="Aide", menu=menuaide)

        #Affichage du menu
        self.config(menu=menubar)
    def run(self):
        """ Main window """
        self.mainloop()

if __name__ == "__main__":
    fenetre = Display_Image()
    fenetre.run()

c'est comme ça que je me suis rendu compte que ce code utilise un dictionnaire qui me semblait il, ne sert à rien

self.imgdict = {} # dictionnaire pour conserver une référence

puis

self.imgdict[filename] = photo  # référence

donc je commente ces deux lignes et l'image ne s'affiche plus dans la fenêtre qui pourtant se met bien à la dimension
dans le code d'origine ce dico s'appelle gifdict mais cela ne change rien en soit
j'ai loupé un truc ?

Dernière modification par kholo (Le 26/03/2019, à 09:57)

Hors ligne

#2 Le 26/03/2019, à 09:56

kholo

Re : [résolu] subtilité python ; image dans tkinter

Voici ce qui doit être la réponse :
... j'ai eu grâce à Fabrice Sincère une partie voire la réponse à cette question... alors je la partage :

dans ma version avec le passage en classe j'ai utilisé une variable locale pour créer l'objet PhotoImage. Cette variable doit être détruite par le Garbage Collector à la fin de la fonction et l'image "disparaît" (ou plutôt sa référence).

Avec la variable locale, le fait de mettre l'image dans un objet en référence permet de conserver le référencement actif. Pour les versions en ligne ou en fonction, un objet liste ou dico conserve l'image
Pour ma version en Class, le passage de l'objet PhotoImage peut être simplement conservé en rendant la variable locale à la classe (self) plutôt qu'à la fonction.

Ainsi plus besoin de liste, de Dico ou même de variable globale ! Subtile ! 

je partage ici les codes de Fabrice Sincère que je remercie ici publiquement:
UNE solution en classe :

#!/usr/bin/env python3
# -*- coding: UTF8 -*-
"""
# script lecture_gif.py
# (C) Fabrice Sincère
# (C) kholo (encapsulation dans une classe)

modifié pour les images d'autres formats
"""

import tkinter as tk
import tkinter.messagebox
import tkinter.filedialog as fd

#from PIL.ImageTk import PhotoImage 

class Display_Image(tk.Tk):
    """ afficheur d'image """
    def __init__(self):
        """ init """
        # tk.Tk.__init__(self)
        super().__init__()

        self.title("Image")
        self.affiche_menu()

        self.canevas = tk.Canvas(self)
        self.canevas.pack(padx=5,pady=5)

    
    def ouvrir_image(self):
        """ Ouvrir image """
        self.canevas.delete(tk.ALL) # on efface la zone graphique

        titre = "Ouvrir une image"
        fitres = [('all files','.*'), ('gif files','.gif')]
        filename = fd.askopenfilename(title=titre,filetypes=fitres)

        self.photo = tk.PhotoImage(file=filename) # PhotoImage de tk
        #self.photo = PhotoImage(file=filename) # PhotoImage de PIL pour ouvrir plus de formats que tk
        
        self.canevas.create_image(0,0,anchor=tk.NW,image=self.photo)
        self.canevas.config(height=self.photo.height(),width=self.photo.width())

        self.title("Image "+str(self.photo.width())+" x "+str(self.photo.height()))
    def fermer_image(self):
        """ Fermer image"""
        self.canevas.delete(tk.ALL)
        self.title("Image")
    def fenetre_a_propos(self):
        """ fenetre Apropos """
        titre, message = "A propos", "Tutorial Python Tkinter\n(C) Fabrice Sincère ; Cyril Ury"
        tkinter.messagebox.showinfo(titre, message)
    def affiche_menu(self):
        """ Création d'un widget Menu """
        menubar = tk.Menu(self)

        menufichier = tk.Menu(menubar,tearoff=0)
        menufichier.add_command(label="Ouvrir une image",command=self.ouvrir_image)
        menufichier.add_command(label="Fermer l'image",command=self.fermer_image)
        menufichier.add_command(label="Quitter",command=self.destroy)
        menubar.add_cascade(label="Fichier", menu=menufichier)

        menuaide = tk.Menu(menubar,tearoff=0)
        menuaide.add_command(label="A propos",command=self.fenetre_a_propos)
        menubar.add_cascade(label="Aide", menu=menuaide)

        #Affichage du menu
        self.config(menu=menubar)
    def run(self):
        """ Main window """
        self.mainloop()

if __name__ == "__main__":
    fenetre = Display_Image()
    fenetre.run()

ici, plus de variable englobante, la variable de Class jouant ce rôle

la version de Fabrice qui utilise une variable globale

# script lecture_gif.py
# python 3
#(C) Fabrice Sincère
from tkinter import *
import tkinter.messagebox
import tkinter.filedialog

def Ouvrir():
    # variable globale pour conserver une référence
    global photo
    
    Canevas.delete(ALL) # on efface la zone graphique

    filename = tkinter.filedialog.askopenfilename(title="Ouvrir une image",filetypes=[('gif files','.gif'),('all files','.*')])
    print(filename)

    photo = PhotoImage(file=filename)
    
    Canevas.create_image(0,0,anchor=NW,image=photo)
    Canevas.config(height=photo.height(),width=photo.width())

    Mafenetre.title("Image "+str(photo.width())+" x "+str(photo.height()))

def Fermer():
    Canevas.delete(ALL)
    Mafenetre.title("Image")

def Apropos():
    tkinter.messagebox.showinfo("A propos","Tutorial Python Tkinter\n(C) Fabrice Sincère")

# Main window
Mafenetre = Tk()
Mafenetre.title("Image")

# Création d'un widget Menu
menubar = Menu(Mafenetre)

menufichier = Menu(menubar,tearoff=0)
menufichier.add_command(label="Ouvrir une image",command=Ouvrir)
menufichier.add_command(label="Fermer l'image",command=Fermer)
menufichier.add_command(label="Quitter",command=Mafenetre.destroy)
menubar.add_cascade(label="Fichier", menu=menufichier)

menuaide = Menu(menubar,tearoff=0)
menuaide.add_command(label="A propos",command=Apropos)
menubar.add_cascade(label="Aide", menu=menuaide)

# Affichage du menu
Mafenetre.config(menu=menubar)

# Création d'un widget Canvas
Canevas = Canvas(Mafenetre)
Canevas.pack(padx=5,pady=5)

Mafenetre.mainloop()

ici la version qui utilise une liste qui, en fin de compte, joue le même rôle que la variable "global" précédente

# script lecture_gif.py
# python 3
#(C) Fabrice Sincère
from tkinter import *
import tkinter.messagebox
import tkinter.filedialog

def Ouvrir():
    Canevas.delete(ALL) # on efface la zone graphique

    filename = tkinter.filedialog.askopenfilename(title="Ouvrir une image",filetypes=[('gif files','.gif'),('all files','.*')])
    print(filename)

    photo = PhotoImage(file=filename)
    giflist.append(photo)  # référence
    print(giflist)

    Canevas.create_image(0,0,anchor=NW,image=photo)
    Canevas.config(height=photo.height(),width=photo.width())

    Mafenetre.title("Image "+str(photo.width())+" x "+str(photo.height()))

def Fermer():
    Canevas.delete(ALL)
    Mafenetre.title("Image")

def Apropos():
    tkinter.messagebox.showinfo("A propos","Tutorial Python Tkinter\n(C) Fabrice Sincère")

# Main window
Mafenetre = Tk()
Mafenetre.title("Image")

# Création d'un widget Menu
menubar = Menu(Mafenetre)

menufichier = Menu(menubar,tearoff=0)
menufichier.add_command(label="Ouvrir une image",command=Ouvrir)
menufichier.add_command(label="Fermer l'image",command=Fermer)
menufichier.add_command(label="Quitter",command=Mafenetre.destroy)
menubar.add_cascade(label="Fichier", menu=menufichier)

menuaide = Menu(menubar,tearoff=0)
menuaide.add_command(label="A propos",command=Apropos)
menubar.add_cascade(label="Aide", menu=menuaide)

# Affichage du menu
Mafenetre.config(menu=menubar)

# Création d'un widget Canvas
Canevas = Canvas(Mafenetre)
Canevas.pack(padx=5,pady=5)

# Utilisation d'une liste pour conserver une référence
giflist = []

Mafenetre.mainloop()

... et je passe fièrement en résolu !

Hors ligne