Salome HOME
Add COPYING license file
[tools/eficas.git] / Ihm / CONNECTOR.py
1 #i -*- coding: utf-8 -*-
2 # Copyright (C) 2007-2021   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 from __future__ import absolute_import
38 from __future__ import print_function
39 import traceback
40 from copy import copy
41 import weakref
42
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         if idx in self.connections :
57             channels = self.connections[idx]
58         else:
59             channels = self.connections[idx] = {}
60
61         #if channels.has_key(channel):
62         if channel in channels :
63             receivers = channels[channel]
64         else:
65             receivers = channels[channel] = []
66
67         for funct,fargs in receivers[:]:
68             if funct() is None:
69                 receivers.remove((funct,fargs))
70             elif (function,args) == (funct(),fargs):
71                 receivers.remove((funct,fargs))
72
73         receivers.append((ref(function),args))
74
75
76     def Disconnect(self, object, channel, function, args):
77         try:
78             receivers = self.connections[id(object)][channel]
79         except KeyError:
80             raise ConnectorError (
81                  'no receivers for channel %s of %s' % (channel, object))
82
83         for funct,fargs in receivers[:]:
84             if funct() is None:
85                 receivers.remove((funct,fargs))
86
87         for funct,fargs in receivers:
88             if (function,args) == (funct(),fargs):
89                 receivers.remove((funct,fargs))
90                 if not receivers:
91                     # the list of receivers is empty now, remove the channel
92                     channels = self.connections[id(object)]
93                     del channels[channel]
94                     if not channels:
95                         # the object has no more channels
96                         del self.connections[id(object)]
97                 return
98
99         raise ConnectorError(
100               'receiver %s%s is not connected to channel %s of %s' \
101               % (function, args, channel, object))
102
103
104
105     def Emit(self, object, channel, *args):
106         #print "Emit",object, channel, args
107         try:
108             receivers = self.connections[id(object)][channel]
109         except KeyError:
110             return
111         #print "Emit",object, channel, receivers
112         # Attention : copie pour eviter les pbs lies aux deconnexion reconnexion
113         # pendant l'execution des emit
114         for rfunc, fargs in copy(receivers):
115             try:
116                 func=rfunc()
117                 if func:
118                     #print (func,args,fargs)
119                     #rint args + fargs
120                     #apply(func, args + fargs)
121                     if args + fargs == () : func()
122                     else : func ( args + fargs)
123                 else:
124                     # Le receveur a disparu
125                     if (rfunc,fargs) in receivers:receivers.remove((rfunc,fargs))
126             except:
127                 traceback.print_exc()
128
129 def ref(target,callback=None):
130     #if hasattr(target,"im_self"):
131     #   return BoundMethodWeakref(target)
132     if hasattr(target,"__self__"):
133         return BoundMethodWeakref(target)
134     else:
135         return weakref.ref(target,callback)
136
137 class BoundMethodWeakref(object):
138     def __init__(self,callable):
139             #self.Self=weakref.ref(callable.im_self)
140             #self.Func=weakref.ref(callable.im_func)
141         self.Self=weakref.ref(callable.__self__)
142         self.Func=weakref.ref(callable.__func__)
143
144     def __call__(self):
145         target=self.Self()
146         if not target:return None
147         func=self.Func()
148         if func:
149             return func.__get__(self.Self())
150
151 _the_connector =CONNECTOR()
152 Connect = _the_connector.Connect
153 Emit = _the_connector.Emit
154 Disconnect = _the_connector.Disconnect
155
156 if __name__ == "__main__":
157     class A:
158         pass
159
160     class B:
161         def add(self,a):
162             print(("--------------------------------add ", self , a))
163
164         def __del__(self):
165             print(("__del__", self))
166
167     def f(a):
168         print((f, a))
169
170     a=A()
171     b=B()
172     c=B()
173
174     Connect(a,"add",b.add,())
175     Connect(a,"add",b.add,())
176     Connect(a,"add",c.add,())
177     Connect(a,"add",f,())
178
179     Emit(a,"add",1)
180
181     print ("del b")
182     del b
183
184     Emit(a,"add",1)
185     print ("del f")
186     del f
187
188     Emit(a,"add",1)
189     Disconnect(a,"add",c.add,())
190     Emit(a,"add",1)