• [^] # Re: Une solution

    Posté par . En réponse au message PyGTK et les threads.. Évalué à 2.

    Effectivement, c'est un peu mieux, mais thor_tue, va lire la doc avant de te lancer là-dedans !!

    Tout d'abord, comme Achille l'a fait, pour une simple boucle, on ne sous-classe pas Thread. Ou alors, si tu veux le faire, lis la doc qui dit :

    There are two ways to specify the activity: by passing a callable object to the constructor, or by overriding the run() method in a subclass. No other methods (except for the constructor) should be overridden in a subclass. In other words, only override the __init__() and run() methods of this class.

    Dans l'exemple que tu donnes, thor_tue, tu n'as simplement jamais créé de threads ! Tout s'éxécutait dans le processus principal, d'où l'exclusivité mutuelle que tu observais ...

    Cependant, la solution d'Achille n'est pas la bonne non plus : tu utilises bien des threads, mais ils ne servent qu'à attendre 0.1s et lancer la mise à jour des labels ... dans le processus principal !

    La fonctions gobject.idle_add() sert à faire du « faux » multithread en exécutant une fonction dans le processus principal quand elle a le temps (entre deux mises à jours de widget). C'est pratique quand tu ne veux pas utiliser de threads, mais inutile dans notre cas.

    Ensuite, le gtk.main_iteration() c'est vraiment un hack : en général, quand tu l'utilises, c'est que tu as mal fait quelque chose. Ce n'est qu'à utiliser que dans des cas très particuliers.

    Les fonctions qui nous seront utiles sont gtk.gdk.threads_enter() et gtk.gdk.threads_leave(). Elles permettent à des threads en dehors du « principal », qui fait tourner la boucle d'évènements, d'aller tripoter la GUI sans interférer avec cette boucle, de manière thread-safe. En bref, elle va « juste » demander un lock à la boucle principale lui permettant de jouer avec la GUI jusqu'à ce qu'elle le relâche. On doit donc minimiser le temps passé entre en enter() et un leave(), car la boucle principale est bloquée pendant ce temps !

    En gros, je pense qu'il faut que vous saisissiez bien le principe d'une boucle d'évènements avant de vous lancer là-dedans. Désolé je n'ai pas de liens sous la main.

    Voici ma version :

    #!/usr/bin/env python
    # -*- coding: utf-8 -*-

    import threading, time
    import gobject
    import gtk
    import gtk.gdk

    class MyThread:
    def __init__(self, main, nb):
    self.main = main
    self.nb = nb
    self.is_running = False

    def start(self):
    print 'start thread', self.nb
    self.is_running = True
    self.th = threading.Thread(target = self.loop)
    self.th.start()

    def stop(self):
    print 'stop thread', self.nb
    self.is_running = False
    self.th.join()
    self.th = None

    def loop(self):
    while self.is_running == True:
    gtk.gdk.threads_enter()
    self.update_label()
    gtk.gdk.threads_leave()
    time.sleep(0.1)

    def update_label(self):
    # Le but de cette fonction est de montrer comment modifier
    # un objet gtk dans la boucle principale depuis un thread.
    self.main.labels[self.nb-1].set_text(str(time.time()))


    Enfin, comme dis Achille, tu peux toujours avoir un problème de race-condition sur ta variable is_running. Il vaut mieux utiliser des locks ou autre.
    Ah oui, au passage, la version d'Achille crée un thread à chaque start(). Si tu ne veux pas ce comportement, il faudra aussi jouer des lucks ou des variables de condition.