Salome HOME
d369031737bba4f494877a85ecb445e92918d459
[modules/yacs.git] / src / pyqt / gui / CONNECTOR.py
1 # Copyright (C) 2006-2016  CEA/DEN, EDF R&D
2 #
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License, or (at your option) any later version.
7 #
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 # Lesser General Public License for more details.
12 #
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 #
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 #
19
20 # -*- coding: iso-8859-15 -*-
21 #
22 """
23   La classe CONNECTOR sert à enregistrer les observateurs d'objets et à délivrer
24   les messages émis à ces objets.
25
26   Le principe général est le suivant : un objet (subscriber) s'enregistre aupres du 
27   connecteur global (theconnector) pour observer un objet emetteur de messages (publisher) 
28   sur un canal donné (channel). Il demande à etre notifie par appel d'une fonction (listener).
29   La séquence est donc :
30
31      - enregistrement du subscriber pour le publisher : theconnector.Connect(publisher,channel,listener,args)
32      - émission du message par le publisher : theconnector.Emit(publisher,channel,cargs)
33
34   args et cargs sont des tuples contenant les arguments de la fonction listener qui sera appelée
35   comme suit::
36
37      listener(cargs+args)
38 """
39 import traceback
40 from copy import copy
41 import weakref
42
43 class ConnectorError(Exception):
44     pass
45
46 class CONNECTOR:
47
48   def __init__(self):
49     self.connections={}
50
51   def Connect(self, object, channel, function, args):
52     ###print "Connect",object, channel, function, args
53     idx = id(object)
54     if self.connections.has_key(idx):
55        channels = self.connections[idx]
56     else:
57        channels = self.connections[idx] = {}
58
59     if channels.has_key(channel):
60        receivers = channels[channel]
61     else:
62        receivers = channels[channel] = []
63
64     for funct,fargs in receivers[:]:
65         if funct() is None:
66            receivers.remove((funct,fargs))
67         elif (function,args) == (funct(),fargs):
68            receivers.remove((funct,fargs))
69
70     receivers.append((ref(function),args))
71     ###print "Connect",receivers
72     
73
74   def Disconnect(self, object, channel, function, args):
75     try:
76        receivers = self.connections[id(object)][channel]
77     except KeyError:
78        raise ConnectorError, \
79             'no receivers for channel %s of %s' % (channel, object)
80
81     for funct,fargs in receivers[:]:
82         if funct() is None:
83            receivers.remove((funct,fargs))
84
85     for funct,fargs in receivers:
86         if (function,args) == (funct(),fargs):
87            receivers.remove((funct,fargs))
88            if not receivers:
89               # the list of receivers is empty now, remove the channel
90               channels = self.connections[id(object)]
91               del channels[channel]
92               if not channels:
93                  # the object has no more channels
94                  del self.connections[id(object)]
95            return
96
97     raise ConnectorError,\
98           'receiver %s%s is not connected to channel %s of %s' \
99           % (function, args, channel, object)
100
101
102   def Emit(self, object, channel, *args):
103     ###print "Emit",object, channel, args
104     try:
105        receivers = self.connections[id(object)][channel]
106     except KeyError:
107        return
108     ###print "Emit",object, channel, receivers
109     # Attention : copie pour eviter les pbs lies aux deconnexion reconnexion
110     # pendant l'execution des emit
111     for rfunc, fargs in copy(receivers):
112        try:
113           func=rfunc()
114           if func:
115              apply(func, args + fargs)
116           else:
117              # Le receveur a disparu
118              if (rfunc,fargs) in receivers:receivers.remove((rfunc,fargs))
119        except:
120           traceback.print_exc()
121
122 def ref(target,callback=None):
123    if hasattr(target,"im_self"):
124       return BoundMethodWeakref(target)
125    else:
126       return weakref.ref(target,callback)
127
128 class BoundMethodWeakref(object):
129    def __init__(self,callable):
130        self.Self=weakref.ref(callable.im_self)
131        self.Func=weakref.ref(callable.im_func)
132
133    def __call__(self):
134        target=self.Self()
135        if not target:return None
136        func=self.Func()
137        if func:
138           return func.__get__(self.Self())
139
140 _the_connector =CONNECTOR()
141 Connect = _the_connector.Connect
142 Emit = _the_connector.Emit 
143 Disconnect = _the_connector.Disconnect
144
145 if __name__ == "__main__":
146    class A:pass
147    class B:
148      def add(self,a):
149        print "add",self,a
150      def __del__(self):
151        print "__del__",self
152
153    def f(a):
154      print f,a
155    print "a=A()"
156    a=A()
157    print "b=B()"
158    b=B()
159    print "c=B()"
160    c=B()
161    Connect(a,"add",b.add,())
162    Connect(a,"add",b.add,())
163    Connect(a,"add",c.add,())
164    Connect(a,"add",f,())
165    Emit(a,"add",1)
166    print "del b"
167    del b
168    Emit(a,"add",1)
169    print "del f"
170    del f
171    Emit(a,"add",1)
172    Disconnect(a,"add",c.add,())
173    Emit(a,"add",1)
174
175