IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Python Discussion :

aide pour un programme avec PIL et Tkinter


Sujet :

Python

  1. #1
    Membre du Club
    Homme Profil pro
    Enseignant
    Inscrit en
    Mars 2013
    Messages
    38
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2013
    Messages : 38
    Points : 47
    Points
    47
    Par défaut aide pour un programme avec PIL et Tkinter
    Bonjour,

    c'est une question de débutant. Mon script est censé alterner l'affichage de deux images qui sont à la racine de mon projet ( j'arrive à les charger dans d'autres scipts).
    Le canevas s'affiche mais aucune des deux images ne s'affiche.
    Merci d'avance pour votre aide.

    Rafoim.

    le scipt:

    from PIL import Image, ImageTk
    import Tkinter as Tk


    def afficheImage(im):
    can.delete(all)
    photo=ImageTk.PhotoImage(im)
    can.create_image(0,0, anchor = Tk.NW, image=photo)

    ##### Programme principal : ############
    im1=Image.open("image2.jpg")
    im2=Image.open("image3.jpg")
    fen = Tk.Tk()
    can = Tk.Canvas(fen, width =500, height =500, bg ='black')
    can.pack()
    afficheImage(im1)
    b1 = Tk.Button(fen, text ='image1', command =afficheImage(im1))
    b1.pack()
    b2 = Tk.Button(fen, text ='image2', command =afficheImage(im2))
    b2.pack()
    fen.mainloop()

  2. #2
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 442
    Points : 37 034
    Points
    37 034
    Par défaut
    Salut,
    Le problème est ici:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    def afficheImage(im):
       can.delete(all)
       photo=ImageTk.PhotoImage(im)
       can.create_image(0,0, anchor = Tk.NW, image=photo)
    Au retour de la fonction l'objet référencé par "photo" étant détruit, impossible de l'afficher.
    Ajoutez juste:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    def afficheImage(im):
       can.delete(all)
       photo=ImageTk.PhotoImage(im)
       can.photo = photo
       can.create_image(0,0, anchor = Tk.NW, image=photo)
    - W

  3. #3
    Expert éminent

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 302
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 302
    Points : 6 782
    Points
    6 782
    Par défaut
    Et le prog tourne ?

    Je pensais qu'on ne pouvait pas mettre d'argument avec la commande:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    b1 = Tk.Button(fen, text ='image1', command =afficheImage(im1))
    je me gourre ?

  4. #4
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 940
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 940
    Points : 7 350
    Points
    7 350
    Par défaut
    On peut mais avec le mot clé lambda, je pense que c'est de cela dont tu parlais.

    Voici un autre exemple fonctionnel

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    from tkinter import *
     
    def func(param):
        print(param)
     
    class Execute:
        def __init__(self, f, arg):
            self.f = f
            self.arg = arg
        def __call__(self):
            return self.f(self.arg)
     
    root = Tk()
     
    b = Button(root, text="GO", command=Execute(func, "Hello World!"))
    b.pack()
     
    root.mainloop()

  5. #5
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonsoir,

    La réponse étant donnée par wiztricks, et sans vouloir revenir sur cette histoire de classe, une réponse plus simple (avec juste un zest d'infos en plus) pour le PO
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    from PIL import Image, ImageTk
    import Tkinter as Tk
     
     
    def afficheImage():
        if can.im == im1:
            can.itemconfigure(photo, image=im2)
            can.im = im2
        else:
            can.itemconfigure(photo, image=im1)
            can.im = im1
        # can.update() sans doute optionnel. A tester suivant les os.
     
    ##### Programme principal : ############
     
    fen = Tk.Tk()
    im1 = ImageTk.PhotoImage(Image.open("image2.jpg"))
    im2 = ImageTk.PhotoImage(Image.open("image3.jpg"))
    can = Tk.Canvas(fen, width=500, height=500, bg='black')
    photo = can.create_image(0, 0, anchor=Tk.NW, image=im1)
    can.pack()
    can.im = im1
    b1 = Tk.Button(fen, text='image1', command=afficheImage)
    b1.pack()
    b2 = Tk.Button(fen, text='image2', command=afficheImage)
    b2.pack()
    fen.mainloop()
    @+

  6. #6
    Membre du Club
    Homme Profil pro
    Enseignant
    Inscrit en
    Mars 2013
    Messages
    38
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Côte d'Or (Bourgogne)

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Service public

    Informations forums :
    Inscription : Mars 2013
    Messages : 38
    Points : 47
    Points
    47
    Par défaut merci à tous
    Merci pour vos réponses. Je profite de votre disponibilité qui m'impressionne ( c'est ma première fois sur un forum d'info).

    En lisant des docs sur Tkinter avant de poser ma question, je n'ai trouvé aucune information quant aux nuances pour moi assez pointues que vous apportez.

    Les docs que j'ai trouvées donnent des exemples tous faits où un grand nombre d'options, de précisions sont passées sous silence. En lisant, on trouve très simple. Et en refaisant, il y a sans cesse des bugs difficiles à cerner ( je n'ai pas eu ce sentiment en étudiant les généralités sur Python mais précisément Tkinter)

    Dans quelle doc aurais je pu trouver les précisions que vous apportez? Sur la question de la destruction de l'objet après retour de la fonction par exemple.

    Merci encore.

  7. #7
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonjour,

    Pour ce qui est de command (ce qui est aussi valable pour tout ce qui utilise une fonction) vous ne trouverez pas d'explication dans Tkinter car c'est une généralité Python : Si le nom d'une fonction est suivit de (), (arguments) dans notre cas, la fonction est exécutée et retourne quelque chose, None ou ce qui est spécifier par l’expression return (je ne parlerais pas de yield ici).
    Lors de la construction du Widget ce qui suit command= est évaluer et stocké.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    >>> def Foo():
    ...     print("Foo !")
    ... 
    >>> command=Foo()
    Foo !
    >>> command
    >>> print(command)
    None
    >>> command=Foo
    >>> command
    <function Foo at 0xb76d1764>
    >>> command()
    Foo !
    fred1599 'contourne' la chose en utilisant une classe comme callback. Lors de l'évaluation de command= l'__init__ de celle ci est lu et __init__ retourne une instance de la classe. C'est donc ici une instance de Execute qui est stockée.
    La solution habituellement employée est d'utiliser des fonctions lambda.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    >>> def Foo(val):
    ...     print(val)
    ... 
    >>> command = lambda : Foo("Foo !")
    >>> command
    <function <lambda> at 0xb76d1e2c>
    >>> command()
    Foo !
    Pour ce qui est de la perte de référence aux images par contre il est vrais que les avertissements sont donnés (keep a reference!) mais que ce n'est pas, de mémoire, documenté.

    @+

  8. #8
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 442
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 442
    Points : 37 034
    Points
    37 034
    Par défaut
    Salut,
    Citation Envoyé par VinsS Voir le message
    Et le prog tourne ?
    Ca ne fait pas ce que le PO attend mais çà répond a sa question tout en laissant un espace pour le vrai sujet que sont les "callbacks".
    Cet espace a été rempli par différents points de vue sur la question.

    Si j'avais proposé directement:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Tk.Button(fen, text ='image1', command = lambda: afficheImage(im1))
    la discussion aurait peut être été moins "riche".
    - W

  9. #9
    Expert éminent

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 302
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 302
    Points : 6 782
    Points
    6 782
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Salut,

    Ca ne fait pas ce que le PO attend ...
    Oui, ça m'est revenu à l'esprit après coup, ça ne lève pas d'erreur, les commandes sont simplement exécutées au démarrage. Et comme dans ce cas ci les images disparaissaient aussitôt créées, on ne se rend compte de rien.

    ...

  10. #10
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Que nenni : Il ne faut pas confondre perte de référence à une image Tk et les callbacks.

    La seule réponse donné dans le post pour ce qui est de l'image (question du PO) est de garder une référence, ce qui est donné par wiztricks dans son exemple (et qu'importe la boite : sans plagia... ou presque).

    Tout le reste de la discussion ne concerne que les callbacks sous tkinter (command, bind et autre)
    Citation Envoyé par wiztricks Voir le message
    Ca ne fait pas ce que le PO attend mais çà répond a sa question tout en laissant un espace pour le vrai sujet que sont les "callbacks".
    Cet espace a été rempli par différents points de vue sur la question.

    Si j'avais proposé directement:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Tk.Button(fen, text ='image1', command = lambda: afficheImage(im1))
    la discussion aurait peut être été moins "riche".
    - W
    @+

  11. #11
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Citation Envoyé par VinsS Voir le message
    Oui, ça m'est revenu à l'esprit après coup, ça ne lève pas d'erreur, les commandes sont simplement exécutées au démarrage. Et comme dans ce cas ci les images disparaissaient aussitôt créées, on ne se rend compte de rien.

    ...
    Méa culpa : Je n'avais pas bien compris la réponse.
    C'est bien cela : Le garbage collector ne comprend pas le lien entre l'image et Tkinter et détruit celle ci dès que l'objet est créé.
    En voici l'exemple typique:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    fen = Tk.Tk()
    l = Tk.Label(fen, image=ImageTk.PhotoImage(Image.open("Zen.jpg")))
    l.pack()
    Tk.Button(fen, text='Quit', command=fen.destroy).pack()
    fen.mainloop()
    Ici une fois l'objet l créé le garbage collector détruit l'image.

    Il est donc conseillé de 'garder une référence' à l'image, soit dans une variable globale, un attribut de l'objet ou encore un conteneur quelconque (liste, dico...).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    photo = PhotoImage(...)
    l = Label(image=photo)
    l.image = photo
    l.pack()
    Ceci dit le code suivant fonctionne comme voulu
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    fen = Tk.Tk()
    im = ImageTk.PhotoImage(Image.open("Zen.jpg"))
    l = Tk.Label(fen, image=im)
    l.pack()
    Tk.Button(fen, text='Quit', command=fen.destroy).pack()
    fen.mainloop()
    Car im est une variable globale.
    Mais rien n’empêche d'écrire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    l = Tk.Label(fen, image=im)
    l.im = im
    l.pack()
    De manière préventive face au garbage collector.

    En fait dans
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    from PIL import Image, ImageTk
    import Tkinter as Tk
     
     
    def afficheImage():
        if can.im == im1:
            can.itemconfigure(photo, image=im2)
            can.im = im2
        else:
            can.itemconfigure(photo, image=im1)
            can.im = im1
        # can.update() sans doute optionnel. A tester suivant les os.
     
    ##### Programme principal : ############
     
    fen = Tk.Tk()
    im1 = ImageTk.PhotoImage(Image.open("image2.jpg"))
    im2 = ImageTk.PhotoImage(Image.open("image3.jpg"))
    can = Tk.Canvas(fen, width=500, height=500, bg='black')
    photo = can.create_image(0, 0, anchor=Tk.NW, image=im1)
    can.pack()
    can.im = im1
    b1 = Tk.Button(fen, text='image1', command=afficheImage)
    b1.pack()
    b2 = Tk.Button(fen, text='image2', command=afficheImage)
    b2.pack()
    fen.mainloop()
    can.im n'est qu'un flag et im1/im2 étant des variables globales ceci fonctionne aussi:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    from PIL import Image, ImageTk
    import Tkinter as Tk
     
     
    def afficheImage():
        if can.im == 1:
            can.itemconfigure(photo, image=im2)
            can.im = 2
        else:
            can.itemconfigure(photo, image=im1)
            can.im = 1
        # can.update() sans doute optionnel. A tester suivant les os.
     
    ##### Programme principal : ############
     
    fen = Tk.Tk()
    im1 = ImageTk.PhotoImage(Image.open("image2.jpg"))
    im2 = ImageTk.PhotoImage(Image.open("image3.jpg"))
    can = Tk.Canvas(fen, width=500, height=500, bg='black')
    photo = can.create_image(0, 0, anchor=Tk.NW, image=im1)
    can.pack()
    can.im = 1
    Tk.Button(fen, text='Change image', command=afficheImage).pack()
    fen.mainloop()
    De même, pour en revenir au code du PO
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    from PIL import Image, ImageTk
    import Tkinter as Tk
     
     
    def afficheImage(im):
        can.itemconfigure(photo, image=im)
     
    ##### Programme principal : ############
     
    fen = Tk.Tk()
    im1 = ImageTk.PhotoImage(Image.open("Zen.jpg"))
    im2 = ImageTk.PhotoImage(Image.open("Certif_heredite.jpg"))
    can = Tk.Canvas(fen, width=500, height=500, bg='black')
    photo = can.create_image(0, 0, anchor=Tk.NW, image=im1)
    can.pack()
    Tk.Button(fen, text='image1', command=lambda: afficheImage(im1)).pack()
    Tk.Button(fen, text='image2', command=lambda: afficheImage(im2)).pack()
    fen.mainloop()
    Fonctionne pour la même raison.

    @+

  12. #12
    Expert confirmé Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Points : 4 005
    Points
    4 005
    Par défaut
    Un autre exemple au passage :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from Tkinter import *
     
    def Affiche():
        im = PhotoImage(file="Zen.gif")
        l.config(image=im)
     
    fen = Tk()
    l = Label(fen)
    l.pack()
    Button(fen, text='Affiche', command=Affiche).pack()
    fen.mainloop()
    Ici im est une variable locale et l'image est détruite

    On pourrais faire comme ceci
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from Tkinter import *
     
    def Affiche():
        global im
        im = PhotoImage(file="Zen.gif")
        l.config(image=im)
     
    fen = Tk()
    l = Label(fen)
    l.pack()
    Button(fen, text='Affiche', command=Affiche).pack()
    fen.mainloop()
    im devenant une variable globale l'image est conservée.

    Mais il est plus souvent conseillé d'en faire un attribut de l'objet
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    from Tkinter import *
     
    def Affiche():
        im = PhotoImage(file="Zen.gif")
        l.config(image=im)
        l.image = im
     
    fen = Tk()
    l = Label(fen)
    l.pack()
    Button(fen, text='Affiche', command=Affiche).pack()
    fen.mainloop()
    D'une part cela ne pollue pas l'espace de nom et d'autre part son 'espérance de vie' est celle de l'objet en question.

    @+

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Python 3.X] Aide pour un programme Scrabble tkinter
    Par yohan69 dans le forum Tkinter
    Réponses: 8
    Dernier message: 28/04/2015, 10h08
  2. Aide pour un warning avec RegSetValueEx
    Par Pierre.g dans le forum Windows
    Réponses: 4
    Dernier message: 24/08/2006, 15h46
  3. [Free Pascal] Besoin d'aide pour un programme
    Par ricomix dans le forum Free Pascal
    Réponses: 3
    Dernier message: 04/06/2006, 18h01
  4. aide pour un programme a compiler
    Par letombeur dans le forum C++
    Réponses: 8
    Dernier message: 11/01/2006, 22h53
  5. [PHP-JS] besoin d'aide pour menu déroulant avec lien
    Par Damarus dans le forum Langage
    Réponses: 3
    Dernier message: 06/10/2005, 19h43

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo