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 :

Où trouver de l'info pour comprendre le threading avec Python ?


Sujet :

Python

  1. #21
    Membre confirmé
    Avatar de vincent.mbg
    Homme Profil pro
    Développeur Python
    Inscrit en
    Décembre 2007
    Messages
    327
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Développeur Python

    Informations forums :
    Inscription : Décembre 2007
    Messages : 327
    Points : 618
    Points
    618
    Par défaut
    IDLE c'est du Tkinter non ?
    Oui c'est pour ça qu'on a pas besoin de mettre mainloop() pour faire tourner un programme
    sous IDLE.
    Ton code fonctionne très bien sous Linux
    Je suis sous Linux et le script du mouvement brownien ne démarre pas
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    Exception in thread Thread-1:
    Traceback (most recent call last):
      File "/usr/lib64/python2.6/threading.py", line 525, in __bootstrap_inner
        self.run()
      File "/usr/lib64/python2.6/threading.py", line 477, in run
        self.__target(*self.__args, **self.__kwargs)
      File "/home/maillol/Bureau/toto.py", line 29, in particle
        canvas.move( oval, dx, dy)
      File "/usr/lib64/python2.6/lib-tk/Tkinter.py", line 2258, in move
        self.tk.call((self._w, 'move') + args)
    TclError: out of stack space (infinite loop?)
    et le script de calogero n'est pas animé j'ai 4 barres immobiles rien ne se passe après lancement des threads. En même temps mon tk ne souligne pas les fonts

    Ce que j'aimerais savoir : est-ce que les plantages que nous rencontrons tous sont dû à :

    - Tkinter ?
    - Tkinter couplé aux threads ?
    - Aux threads seuls (dans ce cas, ça se saurait si Python n'était pas performant de ce point de vue là, non) ?
    Je pense que cela viens d'un conflit entre les threads tcl et Python
    Tkinter fonctionne très bien et les Threads pythons aussi mais pas ensemble.
    Il y a un package multithreading pour tcl/tk mais il n'est pas embarqué dans Python ça aurais pu être intéressant.

    wxPython bonne idée, il utilise directement les composants du système, il ne devrait pas avoir de problèmes.
    Mon guide pour apprendre Tkinter - N'oubliez pas de consulter les FAQ Python ou de visiter mon blog

  2. #22
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 781
    Points
    36 781
    Par défaut
    Bonsoir

    Vous avez assez peu de bibliothèques graphiques qui soit "threads safe" (*): Tkinter, WxWorks, QT, GTK,... ne le sont pas.

    Dans la pratique, ca ve dire que les primitives graphiques doivent être appelées dans le thread principale - celle qui contient la main loop du GUI - et que pour poster des évènement asynchrones qui seront affichés, il faut passer par des messages.

    L'URL montre comment faire cela.

    Bon courage
    - W

    (*) Ce n'est pas parce que çà fonctionne que c'est stable... une modif de code ici ou la et patatras "race condition" entraine corruption mémoire et n'importe quoi.
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  3. #23
    Futur Membre du Club
    Profil pro
    Inscrit en
    Janvier 2009
    Messages
    7
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2009
    Messages : 7
    Points : 5
    Points
    5
    Par défaut
    Merci

  4. #24
    Membre confirmé Avatar de calogerogigante
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    602
    Détails du profil
    Informations personnelles :
    Âge : 53
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2003
    Messages : 602
    Points : 497
    Points
    497
    Par défaut
    Citation Envoyé par wiztricks Voir le message
    Vous avez assez peu de bibliothèques graphiques qui soit "threads safe" (*): Tkinter, WxWorks, QT, GTK,... ne le sont pas.
    Triste nouvelle ça... Je ne comprends pas bien l'utilité des Threads, alors, si ça doit planter dès qu'on veut utiliser la concurrence par le moyen d'une belle interface graphique ?

    Même wxPython génèrerait des plantages avec les threads ??? PPFffff... Quel tristesse d'apprendre ça...

    Wiztricks, j'ai vu l'exemple que tu mets dans l'URL : ça a l'air super intéressant, mais je ne pige pas tout ce qui est dans ce code, malheureusement...

    Quelqu'un peut-il faire une adaptation de mon code (avec 4 barres tournantes sur un Canvas) pour me permettre de comprendre ce que cela signifie de communiquer "par envoi de messages" ???

    Je suis de plus en plus largué à chaque intervention des nouveaux intervenants !!

  5. #25
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 781
    Points
    36 781
    Par défaut
    Salut,

    Wiztricks, j'ai vu l'exemple que tu mets dans l'URL : ça a l'air super intéressant, mais je ne pige pas tout ce qui est dans ce code, malheureusement...
    Oui, je sais... vous ne venez pas d'un monde "geeks".

    Le soucis est qu'une programmation multi-threads plus sophistiquée que le modèle Workers où on fractionne le problème en plusieurs morceaux, mais on reste dans un modèle symétrique... demande une appropriation des paradigmes de programmation réseau - échanges de messages, asymétrie.

    Une bibliothèque graphique n'étant pas ré-entrante, l'utiliser dans un contexte multithreads signifie mettre en branle un modèle asymétrique et des échanges de messages. Beurk...

    Le seul langage de programmation ou l'écriture de ce genre de chose est "simple" est hélas l'assembleur... et Python.
    Pourquoi? Parce que çà a à voir avec la maîtrise du standard d'appel - vous savez la mécanique sous jacente aux appels de fonction qui sauvegardent et restorent le contexte de l'appelant.

    Pourquoi Python rend la vie aussi simple que l'assembleur dans ce cas, ben parce que le standard d'appel est callable(*args, **kwds) avec callable dans fonction ou méthode.... et que de là, le message devient le uplet (callable, *args, **kwds) autrement dit, une variable.

    Si mes neurones veulent bien bosser ce week end, j'essaierai de faire quelque chose de comestible...

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

  6. #26
    Membre extrêmement actif
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    1 418
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 1 418
    Points : 1 658
    Points
    1 658
    Par défaut
    Je suis de plus en plus largué à chaque intervention des nouveaux intervenants !!
    Moi aussi !



    vous savez la mécanique sous jacente aux appels de fonction qui sauvegardent et restorent le contexte de l'appelant.
    Ben non, je ne sais pas.



    Si mes neurones veulent bien bosser ce week end, j'essaierai de faire quelque chose de comestible...
    Ce serait fort méritoire à vous, Grand Manitou, et satisfaisant pour nous, vu que cette file prolifère comme un champignon et pousse ses mycéliums dans divers concepts se cachant sous l’écorce......

  7. #27
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 781
    Points
    36 781
    Par défaut Standard d'appel == calling standard.
    En attendant, regardez ce que fait la fonction "processor" ci dessous.

    Code python : 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
    def processor(call, *args, **kwds):
        try:
            results = call(*args, **kwds)
        except:
            print "exception in call to %s" % call.__name__
            raise
        return results
     
    def decorator():
        def processed_callable(call):
            def wrapped_callable(*args, **kwds):
                result = processor(call, *args, **kwds)
                return result
            return wrapped_callable
        return processed_callable
     
    @decorator()
    def example(a, b, c):
        print a, b, c
        return True, a, b, c
     
    print example(1, 2, 3)

    example(1, 2, 3) est de la forme callable(*args, **kwds) avec

    • callable = example
    • *args = [ 1, 2, 3 ]
    • **kwds = None


    Le code transforme example(1, 2, 3) en uplet (example, [ 1, 2, 3 ], None).
    Et processor l'exécute...

    Grosso merdo, ce code est nul: il ne fait rien sinon ajouter de l'overhead autour d'"example" qui n'a rien demandé...
    Et cela à l'insu de l'appelant, magie du décorateur...

    Tout le truc va être de passer de:
    callable(*args, **kwds)
    devient: uplet: (callable, *args, **kwds)
    devient: message!!!
    qu'on acheminera et qu'on exécutera (méthode) dans le contexte de la cible.

    Faisons apparaître Message dans cette boue:

    Code python : 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
    class Message():
        def __init__(self, call, *args, **kwds):
            # pack
            self._prototype = (call, args, kwds)
     
        def invoke(self):
            # unpack
            method, args, kwds =  self._prototype
            try:
                results = method (*args, **kwds)
            except:
                print "exception in call to %s" % method.__name__
                raise
            return results        
     
    def processor(m):
        return  m.invoke()
     
    def decorator():
        def processed_callable(call):
            def wrapped_callable(*args, **kwds):
                m = Message(call, *args, **kwds)
                result = processor(m)
                return result
            return wrapped_callable
        return processed_callable
     
    @decorator()
    def example(a, b, c):
        print a, b, c
        return True, a, b, c
     
    print example(1, 2, 3)

    Notre poovre example n'a toujours rien vu...
    Cà se passe derrière la scène, dans l'univers étrange des meta.

    Jusqu'à présent nous avons construit sous "example" tout un échafaudage qui joue sur l'équivalence:
    callable(*args, **kwds) <=> uplet: (callable, *args, **kwds) <=> message

    Laissez reposer les neurones là dessus pendant 24heures.

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

  8. #28
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 781
    Points
    36 781
    Par défaut Digression sur l'orthogonalité.
    Salut,

    Il y a encore des choses à raconter sur:
    callable(*args, **kwds) <=> uplet: (callable, *args, **kwds)

    Sur Wikipedia, vous trouverez cette définition de l'orthogonalité:
    Le jeu d'instructions d'un ordinateur est dit orthogonal lorsque (presque) toutes les instructions peuvent s'appliquer à tous les types de données. Un jeu d'instruction orthogonal simplifie la tâche du compilateur puisqu'il y a moins de cas particuliers à traiter : les opérations peuvent être appliquées telles quelles à n'importe quel type de donnée. Un exemple typique est le VAX ou le PDP-10.
    Le uplet: (callable, *args, **kwds) permet "d'orthogonaliser" les traitements qu'on va pouvoir faire dessus.
    => nous pourrons traiter cela comme n'importe quelles variables...

    Sans descendre dans l'architecture des jeux d'instructions machine...
    Vous avez un avantage de l'orthogonalité dans UNIX/Linux qui se réduit à:
    Code C : Sélectionner tout - Visualiser dans une fenêtre à part
    int main(int argc, char** argv, char** envp) { exit(0); }
    i.e. le point d'entrée de tout programme C (ou autres) qui permet de le lancer depuis la ligne de commande (shell) via
    Que ce soit un script Bash, un programme C ou autre... à partir du moment ou ce standard d'appel est respecté, nous pouvons "invoquer" programme ou script.

    Ce que doivent être le contenu de p1, p2, p3 est important mais c'est une autre histoire. L'API permet de réduire le nombre de cas particuliers à traiter pour appeler n'importe quel script ou programme...

    Notez aussi la similitude entre callable(*args, **kwds) et main(int argc, char** argv, char** envp).
    Dans les deux cas, les paramètres sont une d'arguments de position (p1, p2, p3,...) et un dictionnaire.

    Entrer un nom de fichier suivi de chaines de caractères séparées par des espaces voilà qui est minimaliste...
    Difficile de faire moins, est-il possible de mettre "plus" sans casser la généralité?
    C'est ce minimum qui fait généralité et... orthogonalité.

    En C, main(int argc, char** argv, char** envp) n'est que le prototype de la fonction main... (pour les autres débrouillez vous)
    En Python, callable(*args, **kwds) est le prototype de n'importe quel callable... (*)

    - W

    (*) Et qu'écrire en C la transformation callable(*args, **kwds) <=> (callable, *args, **kwds) est un exercice que je ne suis jamais arrivé à faire sans lire une documentation ou pomper du code déjà fait
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  9. #29
    Membre confirmé Avatar de calogerogigante
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    602
    Détails du profil
    Informations personnelles :
    Âge : 53
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2003
    Messages : 602
    Points : 497
    Points
    497
    Par défaut
    Salut Wiztricks,

    Je te remercie de tant de prouesses dans tes explications, mais je t'avoue que je n'y comprends rien... Je ne vois pas le lien direct (ni indirect) avec les choses que tu décris, et mon petit soucis double :

    - comprendre le threading en général

    - ne pas me mordre les doigts de voir python se planter quand les threads fonctionnent au sein d'une bibliothèque graphique...

    A ce stade, je suis un peu dégouté d'apprendre que Thread et Tkinter ne font pas bon ménage, et les solutions qui me sont proposées sont hors de ma portée.

    Je crois que je vais pour l'instant laisser tomber définitivement l'étude des threads, et gérer les animations contrôlées à la façon de ce qui est décrit ici :

    http://www.developpez.net/forums/d85...de-classe-gui/

  10. #30
    Membre confirmé Avatar de calogerogigante
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    602
    Détails du profil
    Informations personnelles :
    Âge : 53
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2003
    Messages : 602
    Points : 497
    Points
    497
    Par défaut
    Pour animer des objets sur des canvas, je me baserais sur une structure moins chaotique que les threads. Un exemple simple de contrôle DEMARRER - PAUSE - REPRENDRE- STOPPER sans threading :

    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
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    #!/usr/local/bin/python
    # -*- coding:utf-8 -*-
    from Tkinter import *
    from time import *
    import sys
     
    class Application:
        def __init__(self):
            self.Terminated = False
            self.root=Tk()
            self.root.title('Barre qui avance sans thread...')
            self.root.geometry('600x160')
            self.root.protocol("WM_DELETE_WINDOW", self.finirapplication)
     
            self.valeur = 1
     
            self.c1 = Canvas(self.root, bg="white")
            self.c1.pack(side=LEFT, expand=YES, fill=BOTH)
     
            self.barre = self.c1.create_rectangle( 10, 50, 11, 100, fill="yellow")
     
            #Boutons du 4éme canvas :
            bouton_animate   = Button(self.root, text="Démarrer",      command=self.ianimate)
            bouton_stop      = Button(self.root, text="Arrêt",         command=self.stop)
            bouton_reprendre = Button(self.root, text="Reprendre",     command=self.reprendre)
            bouton_reset     = Button(self.root, text="Remise à zéro", command=self.reset)
            bouton_animate.pack(side=TOP, expand=YES)
            bouton_stop.pack(side=TOP, expand=YES)
            bouton_reprendre.pack(side=TOP, expand=YES)
            bouton_reset.pack(side=TOP, expand=YES)
     
            self.root.mainloop()
     
        def stop(self):
            #Thread Stop :
            self.Terminated = True
     
        def redessiner(self,x):
            #Mise a jour de la barre :
            self.valeur = x
     
            self.c1.coords(self.barre, 10, 50, 10+x, 100)
     
        def redessinertout(self, event=None):
            #Mise a jour de la barre avec la valeur donnée par self.valeur :
            self.redessiner(self.valeur)
     
        def reset(self, event=None):
            if self.Terminated == False:
                self.Terminated = True
            self.redessiner(1)
     
        def reprendre(self):
            #Reprise de l'animation :
            self.animate()
     
        def ianimate(self):
            #Lancement animation 0-500 :
            self.valeur = 0
            self.animate()
     
        def animate(self):
            #Lancement de l'animation
            if self.Terminated == True:
                self.Terminated = False
     
            while not self.Terminated:
                for i in range(500):
                    if self.Terminated == True:
                        return
                    if i >= self.valeur:
                        self.valeur = i
                        sleep(0.01)
                        self.redessiner(i)
                        self.root.update()
                #update du controle
                self.valeur = 0
     
        def finirapplication(self):
            self.Terminated = True
            self.stop()
            sys.exit()
     
    # Lancement programme principal :
    if __name__ == "__main__":
        app = Application()
    Les deux soucis avec cette façon de faire :

    - c'est plus difficile de gérer des objets différents indépendamment (mais il y a moyen)

    - le déroulement du programme est moins optimisé de la sorte mais tant pis, je ne compte pas encore refaire les SIMS en 3D...

  11. #31
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 781
    Points
    36 781
    Par défaut gui_main.py
    Programme principal.

    J'ai remanié un peu le code original mais, il ne comprend que quelques lignes en plus - soulignées en rouge. Mais d'autres emballages.

    - W
    edit: 07/02: aligné avec "sans les threads"

    Code python : 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
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
     
    #! /usr/bin/python
    # -*- coding:utf-8 -*-
     
    THREADED = True
    import Tkinter
    import math
    import time
     
     
    from  gui_threads import *
     
     
    class States:
        """folds state into variable just to track typos you get using raw string"""
        def __init__(self, name):
            self._name = name
        def __str__(self):
            return self._name
     
    st_init  = States('init')
    st_pause = States('pause')
    st_run   = States('run')
    st_quit  = States('quit')
     
     
    ObjectProperties = {
    #          x    y   dx  dy  color
         1 : [ 50, 100,  5, 30, "red"],
         2 : [150, 100,  5, 30, "green"],
         3 : [250, 100,  5, 30, "blue"],
         4 : [350, 100,  5, 30, "black"],
    }
     
     
    class Rectangle(object):
     
        def __init__(self, canvas, x, y, dx, dy, color):
            self.xy = [(x, y), (x + dx, y), (x + dx, y + dy), (x, y + dy) ]
            self.drawn_item = canvas.create_polygon(self.xy, fill=color)
            self.center = complex( x + dx / 2, y + dy / 2)
            self.arc = 0
            self._step = (math.pi * 2) / 50
            self.canvas = canvas
     
        def rotate(self, **kwds):
            """ rotate polygons by self._step
                normally called in threads context"""
            if THREADED:
                gui_call = kwds.pop('gui_call')
            """compute angle and rotate each point of the polygon around its center
            """
            self.arc += self._step
            if self.arc >= math.pi * 2:
                self.arc = 0
            angle = complex(math.cos(self.arc), math.sin(self.arc))
            newxy = []
            for x, y in self.xy:
                v = angle * (complex(x, y) - self.center) + self.center
                newxy.append(v.real)
                newxy.append(v.imag)
            """send command for execution in gui_mainloop context"""
            if THREADED:
                gui_call(Command(self.canvas.coords, self.drawn_item, *newxy))
            else:
                self.canvas.coords(self.drawn_item, *newxy)
            """prepare for next iteration"""
     
     
     
    class GuiObject(GuiClient):
    #not THREADED:class GuiObject():
     
        button_names = ['start', 'pause', 'restart', 'quit']
        NORMAL = Tkinter.NORMAL
        DISABLED = Tkinter.DISABLED
        buttons_state = dict(
            init = [ NORMAL, DISABLED, DISABLED, DISABLED ],
            start = [ DISABLED, NORMAL, DISABLED, NORMAL ],
            pause = [ DISABLED, DISABLED, NORMAL, NORMAL ],
            restart = [ DISABLED, NORMAL, DISABLED, NORMAL ],
            quit = [ DISABLED, DISABLED, DISABLED, DISABLED ],
            )
     
     
        class GuiButton:
            def __init__(self, frame, name, command, row, column):
                self._button = Tkinter.Button(frame,
                                text="%s %d" % (name, row),
                                command=command)
                self._button.grid(row=row, column=column)
     
            def set_state(self, state):
                self._button.config(state=state)
     
     
        def set_buttons_state(self, b_event):
            b_states = self.buttons_state[b_event]
            for k, button_name in enumerate(self.button_names):
                self.buttons[button_name].set_state(b_states[k])
     
        def __init__(self, gui_server, name_id, frame, canvas, props):
    #not THREADED:    def __init__(self, name_id, frame, canvas, props):
            if THREADED:
                GuiClient.__init__(self, gui_server) # déclare client du server
     
            self._frame = frame
            self._name_id = name_id
            self._canvas = canvas
     
            self.buttons = dict()
            row = name_id #TODO: name_id should not be same as row
            for column, name in enumerate(self.button_names):
                self.buttons[name] = GuiObject.GuiButton(frame, name,
                                                getattr(self, 'command_%s' % name),
                                                row, column)
            self.rectangle = Rectangle(canvas, *props)
            self.set_buttons_state('init')
            self.state = st_init
     
     
        def command_start(self):
            assert self.state in [ st_init ]
            if THREADED:
                self.thread_start()
            self.set_buttons_state('start')
            self.state = st_run
     
        def command_quit(self):
            if THREADED:
                self.thread_quit()
            self.set_buttons_state('quit')
            self.state = st_quit
     
        def command_pause(self):
            assert self.state in [ st_run ]
            if self.state == st_run:
                if THREADED:
                    self.thread_pause()
                self.set_buttons_state('pause')
                self.state = st_pause
     
        def command_restart(self):
            assert self.state in [ st_pause ]
            if self.state == st_pause:
                if THREADED:
                    self.thread_restart()
                self.set_buttons_state('restart')
                self.state = st_run
     
        def visit_method(self, **kwds):
            """method called by thread.run
               kwds should contains gui_call
            """
            if THREADED:
                gui_call = kwds.pop('gui_call')
                self.rectangle.rotate(gui_call=gui_call)
            else:
                self.rectangle.rotate()
     
            time.sleep(0.02)
     
    gui_objects = dict()
    quit_event = False
     
    def on_delete_window():
        global quit_event
        if THREADED:
            quit_event = True
        for o in gui_objects.itervalues():
            if o.state != st_quit:
                o.command_quit()
        sys.exit()
     
    POLL_TIME = 200 # assume Ms
     
    def server_poller(**kwds):
        poll_timer = kwds.pop('poll_timer')
        if THREADED:
            poller_callback = kwds.pop('poller_callback')
        gui_root = kwds.pop('gui_root')
     
        def poller_routine():
            global quit_event
            if THREADED:
                quit_event = poller_callback()
            else:
                for o in gui_objects.itervalues():
                    if o.state == st_run:
                        o.visit_method()
            if not quit_event:
                gui_root.after(poll_timer, poller_routine)
     
        poller_routine()
     
    if __name__ == "__main__":
        root = Tkinter.Tk()
        root.title('Objets tournants avec threads - v04')
        root.geometry('700x200')
        root.protocol("WM_DELETE_WINDOW", on_delete_window)
     
        canvas = Tkinter.Canvas(root, bg="white")
        canvas.pack(side=Tkinter.LEFT, expand=Tkinter.YES, fill=Tkinter.BOTH)
     
        frame = Tkinter.Frame(root)
        if THREADED:
            gui_server = GuiServer(root,
                        poll_timer=POLL_TIME,
                        server_poller=server_poller)       # start server
        for id, props in ObjectProperties.iteritems():     # start clients
            print "props= %s" % props
            if THREADED:
                gui_objects[id] = GuiObject(gui_server, id, frame, canvas, props)
            else:
                gui_objects[id] = GuiObject(id, frame, canvas, props)
        frame.pack(side=Tkinter.RIGHT)
        if not THREADED:
            server_poller(poll_timer=POLL_TIME, gui_root=root)
        root.mainloop()
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  12. #32
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 781
    Points
    36 781
    Par défaut gui_threads.py
    Tout ce que j'ai caché dessous...
    - W
    edit 07/02: quelques modifications et corrections de typos

    Code python : 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
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    #! /usr/bin/python
    # -*- coding:utf-8 -*-
     
    import threading
    import Queue
    import time
     
    __all__ = [ 'GuiClient', 'GuiServer', 'Command']
    #DONE: provide global event to be used by default
    #DONE: add callback interface to gui_call
    #DONE: push poller into guiPart
    class Message(object):
        def __init__(self, *args, **kwds):
            self._message = (args, kwds)
     
     
    class Command(Message):
        def __init__(self, call, *args, **kwds):
            Message.__init__(self, *args, **kwds)
            self._method = call
     
        def invoke(self):
            # unpack
            args, kwds =  self._message
            try:
                results = self._method (*args, **kwds)
            except:
                print "exception in call to %s" % self._method.__name__
                raise
            return results
     
     
     
    class GuiClient(threading.Thread):
     
        def __init__(self, gui_server):
            threading.Thread.__init__(self)
            self._gui_server = gui_server
            self._quitEvent = threading.Event()
            self._runEvent = threading.Event()
            self.thread_restart()
            self._callEvent = threading.Event()
     
        def thread_pause(self):
             self._runEvent.clear()
     
        def thread_quit(self):
            self._quitEvent.set()
     
        def thread_restart(self):
            self._runEvent.set()
     
        def thread_start(self):
            self.thread_restart()
            self.start()
            time.sleep(0.0) # resched
     
        def wait_restart(self):
            self._runEvent.wait()
     
        def is_state_pause(self):
            return not self._runEvent.is_set()
     
        def is_state_quit(self):
            return self._quitEvent.is_set()
     
        def gui_call(self, gui_command, **kwds):
            server = self._gui_server
            ident = server.request.post(gui_command, event=self._callEvent)
            result = server.response.wait(ident)
            return result
     
        def run(self):
           while True:
                if self.is_state_quit():
                    break
                if self.is_state_pause():
                    self.wait_restart()
                self.visit_method(gui_call=self.gui_call)
     
     
     
    class GuiServer:
     
        class RRContext(object):
     
            def __init__(self, command, **kwds):
                self.ident = id(self)
                self.command = command
                self.response = None
                self.event   = kwds.get('event', None)
                if not self.event:
                    self.event = threading.Event()
    #            self.event.clear()
     
        class Request:
            def __init__(self, server, queue, mapper, **kwds):
                self._server = server
                self._queue  = queue
                self.map = mapper.map
     
            def poller_callback(self):
                while not self._queue.empty():
                    ident = self._queue.get(block=False)
                    self.handle(ident)
                return False
    #            self._server.gui_root.after(self._poll_time, self.poller)   ##
     
            def start_poller(self, **kwds):
                server_poller = kwds.pop('server_poller')
                kwds['poller_callback'] = self.poller_callback
                server_poller(**kwds)
     
            def handle(self, ident):
                rrcontext = self.map.get(ident)
                results = rrcontext.command.invoke()
                self.map.response(ident, results)
     
            def post(self, request, **kwds):
                ident = self.map.request(request, **kwds)
                self._queue.put(ident)
                return ident
     
        class Response:
            def __init__(self, server, mapper):
                self._server = server
                self.map = mapper.map
     
            def get(self, ident):
                rrcontext = self.map.clear(ident)
                return rrcontext.response
     
            def wait(self, ident):
                rrcontext = self.map.get(ident)
                while True:
                    rrcontext.event.wait()
                    if rrcontext.response != None:
                        break
                    rrcontext.event.clear()
                    if rrcontext.response != None:
                        break
                return self.get(ident)
     
     
        class Mapper:
     
            class Map:
                def __init__(self, parent, map):
                    self._parent = parent
                    self._map = map
     
                def request(self, request, **kwds):
                    event = kwds.pop('event', None)
                    rrcontext = GuiServer.RRContext(request, event=event)
                    ident = rrcontext.ident # assume is message
                    parent = self._parent
     
                    parent.lock.acquire()
                    self._map[ident] = rrcontext
                    parent.lock.release()
     
                    return ident
     
                def response(self, ident, response):
                    parent = self._parent
                    assert ident in self._map
                    parent.lock.acquire()
                    self._map[ident].response = response
                    parent.lock.release()
                    self._map[ident].event.set()
                    return ident
     
                def get(self, ident):
                    parent = self._parent
                    if not ident in self._map:
                        return None
                    parent.lock.acquire()
                    rrcontext = self._map[ident]
                    parent.lock.release()
                    return rrcontext
     
                def clear(self, ident):
                    parent = self._parent
                    if not ident in self._map:
                        return None
                    parent.lock.acquire()
                    rrcontext = self._map[ident]
                    del self._map[ident]
                    parent.lock.release()
                    return rrcontext
     
     
            def __init__(self, server, map):
                self._server = server
                self.map = GuiServer.Mapper.Map(self, map)
                self.lock = threading.RLock()
     
     
        def __init__(self, gui_root, **kwds):
            poll_timer = kwds.pop('poll_timer', 200)
            server_poller = kwds.pop('server_poller')
     
            self.gui_root = gui_root
            self._requests = Queue.Queue()
            self._rrmap = dict()
     
            self.mapper = GuiServer.Mapper(self, self._rrmap)
            self.request = GuiServer.Request(self, self._requests,
                            self.mapper)
            self.response = GuiServer.Response(self, self.mapper)
     
            self.request.start_poller(
                            poll_timer=poll_timer,
                            gui_root=gui_root,
                            server_poller=server_poller)
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  13. #33
    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
    Merci wiztricks,
    J'avais tester plus simple avec Queue et autre mais la j'adore.
    A rajouter dans l'import this (pas vu, pas pris)
    Merci d'utiliser le forum pour les questions techniques.

  14. #34
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 781
    Points
    36 781
    Par défaut
    Salut,

    Citation Envoyé par PauseKawa Voir le message
    Merci wiztricks,
    J'avais tester plus simple avec Queue et autre mais la j'adore.
    Merci...
    Il faut que je retravaille cela.
    C'est pas encore assez transparent: au départ, je ne voulais pas passer par un "visitor". Mais, je préparais un truc du type "et sans les threads"... que je n'ai pas fini

    A rajouter dans l'import this (pas vu, pas pris)
    Faut que tu m'expliques, Python moi je débute

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

  15. #35
    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,

    Comme je vais passer pas mal de temps a 'digérer' ce code je me permets une première réponse (bien que cela n'apporte rien à calogerogigante, sorry).

    En tant que néophyte/autodidacte (Oui, cela fais beaucoup ) je découvre Queue.
    J'ai tenter de l'utiliser au travers du callback des variables (IntVar()) tkinter (mavariable.set(1)) sur l'exemple donné (utiliser le def processIncoming(self):
    pour faire un set de la variable) ou lancer une procédure de GuiPart à partir de la class ThreadedClient.
    Rien au niveau du code présenté juste avant.

    Pour ce qui est de la proposition de modification du Zen ce n'est qu'une montée de testostérone. Ménopause ? non pas pour de suite, juste de l'emballement
    Merci d'utiliser le forum pour les questions techniques.

  16. #36
    Membre confirmé Avatar de calogerogigante
    Profil pro
    Inscrit en
    Avril 2003
    Messages
    602
    Détails du profil
    Informations personnelles :
    Âge : 53
    Localisation : Belgique

    Informations forums :
    Inscription : Avril 2003
    Messages : 602
    Points : 497
    Points
    497
    Par défaut
    Salut Wiztricks,

    Je vois dans le code que tu as posté des import Tkinter et des import Threading : chouette !!

    Bon, je vais tenter dans les heures qui viennent (peut-être les jours qui viennent plutôt) de digérer ce code, et essayer d'en comprendre un peu le contenu...

    Mais quelque chose me dit que ce sera pas simple...

    First step : tenter de faire fonctionner ton code...

  17. #37
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 781
    Points
    36 781
    Par défaut
    Citation Envoyé par calogerogigante Voir le message
    Salut Wiztricks,
    Je vois dans le code que tu as posté des import Tkinter et des import Threading : chouette !!
    Je suis parti de ton code initial et auquel j'ai appliqué plusieurs factoring/refactoring. Avant de séparer les deux modules, je suis tombé dans un truc étrange: id est redéfinit quelque part et ident = id(object) plante en ralant que id est un integer!!!!
    N'ayant pas trouvé ou je pouvais écraser cela dans mon code, j'ai remplacé
    les from xxxx import * par des import xxxx... au cas où.
    La solution temporaire à été d'utiliser id sous sa forme __builtins__.id

    Bon, je vais tenter dans les heures qui viennent (peut-être les jours qui viennent plutôt) de digérer ce code, et essayer d'en comprendre un peu le contenu...
    Il faut que je retravaille l'interface entre les deux modules pour te simplifier la vie. Je n'ai pas eu le temps de "refactorer" la chose après la séparation.

    Il faut que j'assouplisse cette liaison:
    Code Python : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    # synchronous mode: send command, wait response...
                ident = server.request.post(Command(self.visit_method))
                server.response.wait(ident)

    Mais quelque chose me dit que ce sera pas simple...

    First step : tenter de faire fonctionner ton code...
    Le faire fonctionner ne devrait pas poser de problème.
    Pour comprendre, tu risques d'y passer du temps mais nous sommes par là pour essayer de t'apporter des réponses.

    Si tu t'en sers pour faire bouger des objets, mets cela dans le corps de "visit_method". Lorsque j'aurais retravaillé l'interface, tu devrais pouvoir remplacer les appels à la bibliothèque graphique de la forme:
    result = gui_command(p1, p2,... )
    par un truc de la forme:
    result = gui_server.call(gui_command, p1, p2,... )

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

  18. #38
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    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 287
    Points : 36 781
    Points
    36 781
    Par défaut sans les threads?!?
    Salut,

    Si vous avez compris comment fonctionne le code précédent, vous pouvez vous demandez à quoi peuvent bien servir les threads dans notre cas.

    En effet, les threads executent visit_method qui en retour poste un message qui sera dépilé dans le contexte du serveur pour être in fine exécuté dans le corps de la routine poller_routine --- Ouf!

    Et pourquoi ne pas appeler "directement" visit_method depuis poller_routine?
    Ci dessous, le même code que le précédent où les activités dans le contexte "THREADED" sont mises en évidence par des if.. else...

    - W

    Nota:
    - le passage de l'un à l'autre est presque simple, parce que le code à été organisé "pour". La clé étant le pattern Visitor.
    - GIL empêche l'utilisation de plusieurs CPU, mais nous pourrions avoir besoin d'une version multiprocessing s'il y avait beaucoup de calcul a effectuer.
    - bibliothèque non ré-entrante & threading... tant que les traitements sont non bloquants (une lecture sur le terminal est bloquante) et peuvent se faire dans poller_routine
    - l'ordonnanceur est dans ce cas non préemptif.

    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
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    #! /usr/bin/python
    # -*- coding:utf-8 -*-
     
    THREADED = False
    import Tkinter
    import math
    import time
     
     
    #THREADED: from  gui_threads import *
     
     
    class States:
        """folds state into variable just to track typos you get using raw string"""
        def __init__(self, name):
            self._name = name
        def __str__(self):
            return self._name
     
    st_init  = States('init')
    st_pause = States('pause')
    st_run   = States('run')
    st_quit  = States('quit')
     
     
    ObjectProperties = {
    #          x    y   dx  dy  color
         1 : [ 50, 100,  5, 30, "red"],
         2 : [150, 100,  5, 30, "green"],
         3 : [250, 100,  5, 30, "blue"],
         4 : [350, 100,  5, 30, "black"],
    }
     
     
    class Rectangle(object):
     
        def __init__(self, canvas, x, y, dx, dy, color):
            self.xy = [(x, y), (x + dx, y), (x + dx, y + dy), (x, y + dy) ]
            self.drawn_item = canvas.create_polygon(self.xy, fill=color)
            self.center = complex( x + dx / 2, y + dy / 2)
            self.arc = 0
            self._step = (math.pi * 2) / 50
            self.canvas = canvas
     
        def rotate(self, **kwds):
            """ rotate polygons by self._step
                normally called in threads context"""
            if THREADED:
                gui_call = kwds.pop('gui_call')
            """compute angle and rotate each point of the polygon around its center
            """
            self.arc += self._step
            if self.arc >= math.pi * 2:
                self.arc = 0
            angle = complex(math.cos(self.arc), math.sin(self.arc))
            newxy = []
            for x, y in self.xy:
                v = angle * (complex(x, y) - self.center) + self.center
                newxy.append(v.real)
                newxy.append(v.imag)
            """send command for execution in gui_mainloop context"""
            if THREADED:
                gui_call(Command(self.canvas.coords, self.drawn_item, *newxy))
            else:
                self.canvas.coords(self.drawn_item, *newxy)
            """prepare for next iteration"""
     
     
     
    #THREADED:class GuiObject(GuiClient):
    class GuiObject():
     
        button_names = ['start', 'pause', 'restart', 'quit']
        NORMAL = Tkinter.NORMAL
        DISABLED = Tkinter.DISABLED
        buttons_state = dict(
            init = [ NORMAL, DISABLED, DISABLED, DISABLED ],
            start = [ DISABLED, NORMAL, DISABLED, NORMAL ],
            pause = [ DISABLED, DISABLED, NORMAL, NORMAL ],
            restart = [ DISABLED, NORMAL, DISABLED, NORMAL ],
            quit = [ DISABLED, DISABLED, DISABLED, DISABLED ],
            )
     
     
        class GuiButton:
            def __init__(self, frame, name, command, row, column):
                self._button = Tkinter.Button(frame,
                                text="%s %d" % (name, row),
                                command=command)
                self._button.grid(row=row, column=column)
     
            def set_state(self, state):
                self._button.config(state=state)
     
     
        def set_buttons_state(self, b_event):
            b_states = self.buttons_state[b_event]
            for k, button_name in enumerate(self.button_names):
                self.buttons[button_name].set_state(b_states[k])
     
    #THREADED:    def __init__(self, gui_server, name_id, frame, canvas, props):
        def __init__(self, name_id, frame, canvas, props):
            if THREADED:
                GuiClient.__init__(self, gui_server) # déclare client du server
     
            self._frame = frame
            self._name_id = name_id
            self._canvas = canvas
     
            self.buttons = dict()
            row = name_id #TODO: name_id should not be same as row
            for column, name in enumerate(self.button_names):
                self.buttons[name] = GuiObject.GuiButton(frame, name,
                                                getattr(self, 'command_%s' % name),
                                                row, column)
            self.rectangle = Rectangle(canvas, *props)
            self.set_buttons_state('init')
            self.state = st_init
     
     
        def command_start(self):
            assert self.state in [ st_init ]
            if THREADED:
                self.thread_start()
            self.set_buttons_state('start')
            self.state = st_run
     
        def command_quit(self):
            if THREADED:
                self.thread_quit()
            self.set_buttons_state('quit')
            self.state = st_quit
     
        def command_pause(self):
            assert self.state in [ st_run ]
            if self.state == st_run:
                if THREADED:
                    self.thread_pause()
                self.set_buttons_state('pause')
                self.state = st_pause
     
        def command_restart(self):
            assert self.state in [ st_pause ]
            if self.state == st_pause:
                if THREADED:
                    self.thread_restart()
                self.set_buttons_state('restart')
                self.state = st_run
     
        def visit_method(self, **kwds):
            """method called by thread.run
               kwds should contains gui_call
            """
            if THREADED:
                gui_call = kwds.pop('gui_call')
                self.rectangle.rotate(gui_call=gui_call)
            else:
                self.rectangle.rotate()
     
            time.sleep(0.02)
     
    gui_objects = dict()
    quit_event = False
     
    def on_delete_window():
        global quit_event
        if THREADED:
            quit_event = True
        for o in gui_objects.itervalues():
            if o.state != st_quit:
                o.command_quit()
        sys.exit()
     
    POLL_TIME = 200 # assume Ms
     
    def server_poller(**kwds):
        poll_timer = kwds.pop('poll_timer')
        if THREADED:
            poller_callback = kwds.pop('poller_callback')
        gui_root = kwds.pop('gui_root')
     
        def poller_routine():
            global quit_event
            if THREADED:
                quit_event = poller_callback()
            else:
                for o in gui_objects.itervalues():
                    if o.state == st_run:
                        o.visit_method()
            if not quit_event:
                gui_root.after(poll_timer, poller_routine)
     
        poller_routine()
     
    if __name__ == "__main__":
        root = Tkinter.Tk()
        root.title('Objets tournants avec threads - v04')
        root.geometry('700x200')
        root.protocol("WM_DELETE_WINDOW", on_delete_window)
     
        canvas = Tkinter.Canvas(root, bg="white")
        canvas.pack(side=Tkinter.LEFT, expand=Tkinter.YES, fill=Tkinter.BOTH)
     
        frame = Tkinter.Frame(root)
        if THREADED:
            gui_server = GuiServer(root,
                        poll_timer=POLL_TIME,
                        server_poller=server_poller)       # start server
        for id, props in ObjectProperties.iteritems():     # start clients
            print "props= %s" % props
            if THREADED:
                gui_objects[id] = GuiObject(gui_server, id, frame, canvas, props)
            else:
                gui_objects[id] = GuiObject(id, frame, canvas, props)
        frame.pack(side=Tkinter.RIGHT)
        if not THREADED:
            server_poller(poll_timer=POLL_TIME, gui_root=root)
        root.mainloop()
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

Discussions similaires

  1. [PHP 5.0] Mon sujet pour comprendre la POO avec PHP
    Par Le nettoyeur dans le forum Langage
    Réponses: 3
    Dernier message: 09/07/2014, 18h05
  2. Trouver infos pour une liste dans une base de donnees
    Par clairejp dans le forum Requêtes et SQL.
    Réponses: 1
    Dernier message: 29/07/2011, 18h26
  3. 5 minutes pour comprendre le développement avec Kinect
    Par Gordon Fowler dans le forum Débuter
    Réponses: 10
    Dernier message: 07/07/2011, 11h18
  4. [Info] Nouvel Ubuntu 11.04 avec Python 2.7.1
    Par tyrtamos dans le forum Général Python
    Réponses: 11
    Dernier message: 04/05/2011, 10h48
  5. cmt se connecté a oracle pour faire une requete avec python
    Par dipajero dans le forum Bibliothèques tierces
    Réponses: 6
    Dernier message: 28/12/2005, 20h22

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