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