Version: 8.3.0
CONNECTOR.py
Go to the documentation of this file.
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 
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