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

Tkinter Python Discussion :

Attendre résultat d'un callback


Sujet :

Tkinter Python

  1. #1
    Nouveau membre du Club
    Attendre résultat d'un callback
    Bonjour à tous

    Je ne sais pas si tout ce que je vais dire est correct mais bon.

    Lorsqu'on démarre une fonction en "callback" dans un une fenêtre Tkinter, comme dans le code ci-dessous par le clic du bouton.
    Tk se charge de l’exécution en parallèle de la fonction action() et la main est rendue à la fonction appelante (dans l'exemple start())
    Comment attendre la fin de la fonction action() avant de reprendre l’exécution de la fonction start()

    Par exemp
    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
    from tkinter import *
    import time
     
    def start():
        fen1.after(1000, action)
     
    def action():
        print("Je suis dans action")
     
    fen1 = Tk()
    txt1 = Label(fen1, text="exemple")
    txt1.grid(row=1)
    bouton1 = Button(fen1, text='Démarrer', command=lambda: start())
    bouton1.grid(row=2)
     
    fen1.mainloop()

  2. #2
    Expert éminent sénior
    Salut,

    Citation Envoyé par tm68780 Voir le message
    Comment attendre la fin de la fonction action() avant de reprendre l’exécution de la fonction start()
    La fonction start est terminée bien avant que le callback action ne démarre.
    Pour le visualiser pour pourriez ajouter des "print"...

    Donc on ne peut pas...

    Citation Envoyé par tm68780 Voir le message
    Tk se charge de l’exécution en parallèle de la fonction action() et la main est rendue à la fonction appelante (dans l'exemple start())
    Tout l'intérêt de la programmation évènementielle est justement de ne rien avoir qui se déroule en parallèle mais de garantir qu'un callback ne sera appelé qu'après la fin du callback précédent.

    Cela peut vous paraître futile mais c'est ce qui garantit l'intégrité de l'état de l'application. Lorsqu'un un callback démarre, s'il teste ou met à jour une variable (non locale), rien d'autre n'ira la changer...

    Intégrité qu'on peut mettre en défaut avec l'utilisation intempestive d'update dans un callback...

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #3
    Membre actif
    Bonjour,
    J'utiliserai un 'flag' pour executer le after de start() sous condition...

    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
    from tkinter import *
     
    porte = {'flag':True} 
    def start():
        if porte['flag']:
            porte['flag'] = False
            fen1.after(1000, action)
     
    def action():
        print("Je suis dans action")
        porte['flag'] = True
     
    fen1 = Tk()
    txt1 = Label(fen1, text="exemple")
    txt1.grid(row=1)
    bouton1 = Button(fen1, text='Démarrer', command=lambda: start())
    bouton1.grid(row=2)
     
    fen1.mainloop()

  4. #4
    Nouveau membre du Club
    Bonjour à tous

    Je reprends depuis le début.

    Lorsqu'on démarre une fonction en "callback" dans un une fenêtre Tkinter, par le clic du bouton.
    Tk se charge de l’exécution de la fonction action() et la main est rendue à la fonction appelante.

    Comment attendre la fin de la fonction action() avant de reprendre l’exécution de la fonction start().
    OK ça on ne peut pas mais

    Pourquoi cette question:
    Je suis dans une fenêtre Tkinter et sur le clic bouton je démarre une fonction d'animation.
    Cette animation doit être terminée avant de poursuivre mon programme.
    Ne pourrais-je pas utiliser "bind" pour attendre un évènement dans la fenêtre (genre prise de focus d'un widget) pour poursuivre mon programme ?

  5. #5
    Expert éminent sénior
    Salut,

    Citation Envoyé par tm68780 Voir le message
    Cette animation doit être terminée avant de poursuivre mon programme.
    Le clic sur le bouton démarre l'animation, la fin de l'animation démarre la suite... c'est quoi le soucis? D'autant que dans votre exemple, rien ne représente ce qui vient après...

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  6. #6
    Nouveau membre du Club
    Citation Envoyé par wiztricks Voir le message
    Salut,
    Le clic sur le bouton démarre l'animation, la fin de l'animation démarre la suite... c'est quoi le soucis?
    - W
    Bonjour,

    Le soucis et vous l'avez dit aussi Start continue de s'executer sans attendre que action soit terminée

    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
    from tkinter import *
    import time
     
     
    def start():
        fen1.after(1000, action)
     
        # Je veux continuer ici
        # A moins d'utiliser une nouvelle construction de ce type fen1.after(1000, nouvelleAction)
        # C'est pas très propre non plus aec tous ces déalais
     
    def action():
        print("Je suis dans action")
     
     
    fen1 = Tk()
    txt1 = Label(fen1, text="exemple")
    txt1.grid(row=1)
    bouton1 = Button(fen1, text='Démarrer', command=lambda: start())
    bouton1.grid(row=2)
     
    fen1.mainloop()

  7. #7
    Nouveau membre du Club
    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
    28
    from tkinter import *
    import time
     
    def maFonctionEvent(event):
        print("event")
        action2()           #action2() ne démarre que à la fin de action1()
     
    def start():
        fen1.after(10, action1)
        #action2()            #action2 démarre avant la fin de action1()
     
    def action1():
        print("Je suis dans action 1")
        txt1.destroy()
     
    def action2():
        print("Je suis dans action 2")
     
    fen1 = Tk()
     
    txt1 = Label(fen1, text='Text 1')
    txt1.grid(row=1)
    txt1.bind('<Destroy>', maFonctionEvent)
     
    bouton1 = Button(fen1, text='Démarrer', command=start)
    bouton1.grid(row=2)
     
    fen1.mainloop()

  8. #8
    Expert éminent sénior
    Salut,

    Citation Envoyé par tm68780 Voir le message
    Le soucis et vous l'avez dit aussi Start continue de s'executer sans attendre que action soit terminée
    Mettre les instructions à exécuter "après" dans une fonction g appelée lorsqu'action se termine est une division du travail entre fonctions que je suppose maîtrisée avant de se lancer dans l'aventure tkinter.

    Vous pouvez même passer g et un contexte en paramètre d'action.

    Je ne vois pas la difficulté que vous avez à écrire juste çà.
    D'autant que qui dit contexte dit matière à penser lorsqu'on apprend les classes et qu'on compare les intérêts d'un "g" fonction à un "g" méthode.

    Après si la question est de savoir si on peut le faire "sans" avec les fonctionnalités de tkinter. C'est de la "programmation avancée" où on peut tirer profit de ce que sont variables et fenêtres pour "attendre".

    Mais vous en êtes encore à essayer d'apprendre à faire avec ce que vous connaissez (la frustration quotidienne du programmeur...)

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  9. #9
    Nouveau membre du Club
    Citation Envoyé par wiztricks Voir le message
    Salut,
    Mettre les instructions à exécuter "après" dans une fonction g appelée lorsqu'action se termine
    - W
    Désolé je ne comprends pas cette phrase


    J'essaie simplement de comprendre les outils dont je pourrais avoir besoin plus tard

    A savoir:

    1/ je déclenche une animation dans une fenêtre, qui sera déclenchée par un callback
    2/ A l'issu de cette animation je veux continuer par un traitement X

    Je ne veux pas dans mes lignes de code rajouter ce traitement X dans ce 1er callback
    et je ne veux pas démarrer une nouvelle fonction "traitement X" avec encore une fois de plus un "after" qui prend en compte le délai d’exécution du 1er callback

  10. #10
    Expert éminent sénior
    Salut,

    Citation Envoyé par tm68780 Voir le message
    Désolé je ne comprends pas cette phrase
    Plutôt qu'écrire:
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    def start():
        fen1.after(10, action)
        g()


    vous pouvez écrire:
    Code :Sélectionner tout -Visualiser dans une fenêtre à part
    1
    2
    3
    4
    def start():
        fen1.after(10, action, g)
    def action(g):
        g()


    Citation Envoyé par tm68780 Voir le message
    J'essaie simplement de comprendre les outils dont je pourrais avoir besoin plus tard
    Vous êtes supposé réaliser le comportement souhaité avec les outils que vous connaissez: fonction, variable globales,...

    Dit autrement quand vous dites:
    Citation Envoyé par tm68780 Voir le message
    Je ne veux pas dans mes lignes de code rajouter ce traitement X dans ce 1er callback
    et je ne veux pas démarrer une nouvelle fonction "traitement X" avec encore une fois de plus un "after" qui prend en compte le délai d’exécution du 1er callback
    vous devez faire la différence entre ce que l'application fait et le code que vous arrivez écrire pour que çà le fasse...

    Le premier jet n'est pas "joli", ok, mais s'il fonctionne, c'est pas si mal... quite à découvrir plus tard d'autres façons de réaliser la même chose et vous questionner alors sur ce qui est mieux en fonction du contexte.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  11. #11
    Nouveau membre du Club
    Merci beaucoup pour la réponse et votre patience.

    Je comprends bien le fonctionnement de votre code.

    Mais ce qui paradoxal c'est que la programmation sous Tkinter est basée sur l'évènementiel (et on me l'a rappelé plusieurs fois) alors que cette dernière démarche est séquentielle
    dans le sens où l'on demande à une fonction callback d'exécuter séquentiellement plus qu'un ensemble fonctionnel...
    La démarche séquentielle serait à mon idée de pouvoir accéder au gestionnaire d'évènement et de déclencher d'autres fonctions sous conditions, à savoir si les tâches précédentes ont bien été effectuées et sans erreurs.

  12. #12
    Expert éminent sénior
    Salut,

    Citation Envoyé par tm68780 Voir le message
    Mais ce qui paradoxal c'est que la programmation sous Tkinter est basée sur l'évènementiel (et on me l'a rappelé plusieurs fois) alors que cette dernière démarche est séquentielle
    dans le sens où l'on demande à une fonction callback d'exécuter séquentiellement plus qu'un ensemble fonctionnel...
    Ce n'est pas comme si "start" n'était pas déjà un callback (qui devait appeler g en attendant que...).
    Déplacer l'appel de g dans un autre callback ne change pas grand chose à ce que çà fait.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  13. #13
    Nouveau membre du Club
    Pas trop satisfait

    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
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    from tkinter import *
     
    """ Fonctionne mais problème de timing
     
    def start():
        can2.after(1000, allume)
        can2.after(2000, etteind, suite)
     
    def allume():
        can2.itemconfigure(ampoule, fill='red')
     
    def etteind(laSuite):
        can2.itemconfigure(ampoule, fill='grey')
        can2.after(3000, laSuite())
     
    def suite():
        txt2 = Label(can3, text='Texte 2', bg="#41B77F")
        txt2.grid(row=1, pady=10, sticky=E)
    """
    #  Donc préférence à ça:
     
    def start():
        can2.after(1000, allume)
        can2.after(2000, etteind)
        can2.after(3000, suite)
     
    def allume():
        can2.itemconfigure(ampoule, fill='red')
     
    def etteind():
        can2.itemconfigure(ampoule, fill='grey')
     
    def suite():
        txt2 = Label(can3, text='Texte 2', bg="#41B77F")
        txt2.grid(row=1, pady=10, sticky=E)
     
     
    fen1 = Tk()
    fen1.option_add('*font', ('', 15))
    fen1.geometry("1000x750")
    fen1.config(background="#41B77F")
     
    can1 = Canvas(fen1, bg="#41B77F", height=330, width=460)
    can1.grid(row=0, column=0)
    can1.grid_propagate(0)
    can2 = Canvas(fen1, bg="#41B77F", height=330, width=530)
    can2.grid_propagate(0)
    can2.grid(row=0, column=1)
    can3 = Canvas(fen1, bg="#41B77F", height=330, width=460)
    can3.grid_propagate(0)
    can3.grid(row=1, column=0)
     
    txt1 = Label(can1, text='Texte 1', bg="#41B77F")
    txt1.grid(row=1, pady=10, sticky=E)
     
    bouton1 = Button(can1, text='Démarrer', width=8, command=start)
    bouton1.grid(row=6, pady=20, sticky=E)
     
    ampoule = can2.create_oval(30, 30, 60, 60, width=4, fill='grey')
     
    fen1.mainloop()

  14. #14
    Nouveau membre du Club
    Bonjour à tous

    Lorsque j'ai écrit : pas trop satisfait
    Je parlais de mon code :-)

    et non pas de vos réponses

  15. #15
    Expert éminent sénior
    Salut,

    Citation Envoyé par tm68780 Voir le message
    Lorsque j'ai écrit : pas trop satisfait
    Je parlais de mon code :-)
    Toujours à vouloir inventer vos propres exercices plutôt qu'apprendre patiemment par étapes en suivant ce que propose un tuto?

    Il y a différentes façons de structurer et réaliser un code qui affiche 4 carreaux, un Label et un Button qui démarre une animation qui se termine par...

    Normalement, après avoir pris le temps d'apprendre, vous devriez pouvoir envisager plusieurs solutions et argumenter le choix de l'une parmi les autres.

    Sinon vous assemblez des lignes en espérant que çà tombe en marche et en doutant toujours de ce que vous avez écrit car n'ayant pas appris, il y a peut être mieux...

    Vous débutez, l'important n'est pas dans le code que vous avez écrit mais dans tout ce que vous avez pu découvrir en essayant de le faire fonctionner.

    Lorsque vous aurez acquis ces réflexes de bases, vous avez plein de sources (dans les bibliothèques Python, sur Internet) à étudier pour voir ce qui se fait...

    Et vu la quantité de connaissances que vous devez acquérir, pas la peine de vous presser: donnez vous de objectifs pour progresser pas à pas et tenir dans la durée.

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  16. #16
    Nouveau membre du Club
    Bonjour,

    Dans le principe vous n'avez pas tord loin de là, mais c'est une démarche idéaliste.
    Aujourd'hui dans le monde du travail et ça a été pour moi le quotidien, on vous demande de réaliser quelque chose sans vous laisser le temps d’approfondir.
    De plus prenez l'exemple du médecin généraliste de l'Égypte ancienne qui maîtrisait tout et le spécialiste presque rien...

    Pour moi, avec python, c'est encore différent, je veux faire plaisir à mon petit-fils pendant qu'il en est encore temps...

###raw>template_hook.ano_emploi###