PyVRML97 2.3.4b3__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
vrml/csscolors.py ADDED
@@ -0,0 +1,177 @@
1
+ """Module to allow using strings to specify colors"""
2
+ def stringToColor( value ):
3
+ """Given a string/unicode value, determine appropriate color"""
4
+ value = value.lower()
5
+ possible = cssColors.get( value )
6
+ if possible:
7
+ return possible
8
+ if value and value[0] == '#':
9
+ # HTML-style #hex encoding
10
+ possible = int( value[1:],16)
11
+ return toFloat(possible,16),toFloat(possible,8), toFloat(possible,0)
12
+ else:
13
+ raise ValueError( """String %(value)r couldn't be recognised as a color name, or #FFFFFF style encoding"""%locals())
14
+ def toInt( value ):
15
+ """Take float value and give single-byte integer equivalent"""
16
+ return int(round(value*255,0))
17
+ def toFloat( value, shift=0 ):
18
+ """Take single-byte integer value return floating point equivalent"""
19
+ return ((value&(255<<shift))>>shift)/255.0
20
+
21
+
22
+ cssColors = {
23
+ 'black' : (0.0,0.0,0.0),
24
+ 'dimgray' : (0.4118,0.4118,0.4118),
25
+ 'gray' : (0.502,0.502,0.502),
26
+ 'darkgray' : (0.6627,0.6627,0.6627),
27
+ 'silver' : (0.7529,0.7529,0.7529),
28
+ 'lightgrey' : (0.8275,0.8275,0.8275),
29
+ 'gainsboro' : (0.8627,0.8627,0.8627),
30
+ 'whitesmoke' : (0.9608,0.9608,0.9608),
31
+ 'white' : (1.0,1.0,1.0),
32
+ 'snow' : (1.0,0.9804,0.9804),
33
+ 'rosybrown' : (0.7373,0.5608,0.5608),
34
+ 'lightcoral' : (0.9412,0.502,0.502),
35
+ 'indianred' : (0.8039,0.3608,0.3608),
36
+ 'brown' : (0.6471,0.1647,0.1647),
37
+ 'firebrick' : (0.698,0.1333,0.1333),
38
+ 'maroon' : (0.502,0.0,0.0),
39
+ 'darkred' : (0.5451,0.0,0.0),
40
+ 'red' : (1.0,0.0,0.0),
41
+ 'mistyrose' : (1.0,0.8941,0.8824),
42
+ 'salmon' : (0.9804,0.502,0.4471),
43
+ 'tomato' : (1.0,0.3882,0.2784),
44
+ 'darksalmon' : (0.9137,0.5882,0.4784),
45
+ 'coral' : (1.0,0.498,0.3137),
46
+ 'orangered' : (1.0,0.2706,0.0),
47
+ 'lightsalmon' : (1.0,0.6275,0.4784),
48
+ 'sienna' : (0.6275,0.3216,0.1765),
49
+ 'seashell' : (1.0,0.9608,0.9333),
50
+ 'chocolate' : (0.8235,0.4118,0.1176),
51
+ 'saddlebrown' : (0.5451,0.2706,0.0745),
52
+ 'sandybrown' : (0.9569,0.6431,0.3765),
53
+ 'peachpuff' : (1.0,0.8549,0.7255),
54
+ 'peru' : (0.8039,0.5216,0.2471),
55
+ 'linen' : (0.9804,0.9412,0.902),
56
+ 'bisque' : (1.0,0.8941,0.7686),
57
+ 'burlywood' : (0.8706,0.7216,0.5294),
58
+ 'darkorange' : (1.0,0.549,0.0),
59
+ 'antiquewhite' : (0.9804,0.9216,0.8431),
60
+ 'tan' : (0.8235,0.7059,0.549),
61
+ 'blanchedalmond' : (1.0,0.9216,0.8039),
62
+ 'navajowhite' : (1.0,0.8706,0.6784),
63
+ 'papayawhip' : (1.0,0.9373,0.8353),
64
+ 'moccasin' : (1.0,0.8941,0.7098),
65
+ 'oldlace' : (0.9922,0.9608,0.902),
66
+ 'wheat' : (0.9608,0.8706,0.702),
67
+ 'orange' : (1.0,0.6471,0.0),
68
+ 'floralwhite' : (1.0,0.9804,0.9412),
69
+ 'goldenrod' : (0.8549,0.6471,0.1255),
70
+ 'cornsilk' : (1.0,0.9725,0.8627),
71
+ 'gold' : (1.0,0.8431,0.0),
72
+ 'lemonchiffon' : (1.0,0.9804,0.8039),
73
+ 'palegoldenrod' : (0.9333,0.9098,0.6667),
74
+ 'khaki' : (0.9412,0.902,0.549),
75
+ 'darkkhaki' : (0.7412,0.7176,0.4196),
76
+ 'ivory' : (1.0,1.0,0.9412),
77
+ 'beige' : (0.9608,0.9608,0.8627),
78
+ 'lightyellow' : (1.0,1.0,0.8784),
79
+ 'lightgoldenrodyellow' : (0.9804,0.9804,0.8235),
80
+ 'darkgoldenrod' : (0.7216,0.7137,0.0431),
81
+ 'olive' : (0.502,0.502,0.0),
82
+ 'yellow' : (1.0,1.0,0.0),
83
+ 'yellowgreen' : (0.6627,0.8039,0.1961),
84
+ 'olivedrab' : (0.4196,0.5569,0.1373),
85
+ 'darkolivegreen' : (0.3333,0.4196,0.1843),
86
+ 'greenyellow' : (0.6784,1.0,0.1843),
87
+ 'chartreuse' : (0.498,1.0,0.0),
88
+ 'lawngreen' : (0.4863,0.9882,0.0),
89
+ 'honeydew' : (0.9412,1.0,0.9412),
90
+ 'darkseagreen' : (0.5608,0.7373,0.5608),
91
+ 'lightgreen' : (0.5647,0.9333,0.5647),
92
+ 'palegreen' : (0.5961,0.9843,0.5961),
93
+ 'forestgreen' : (0.1333,0.5451,0.1333),
94
+ 'limegreen' : (0.1961,0.8039,0.1961),
95
+ 'darkgreen' : (0.0,0.3922,0.0),
96
+ 'green' : (0.0,0.502,0.0),
97
+ 'lime' : (0.0,1.0,0.0),
98
+ 'mediumseagreen' : (0.2353,0.702,0.4431),
99
+ 'seagreen' : (0.1804,0.5451,0.3412),
100
+ 'mintcream' : (0.9608,1.0,0.9804),
101
+ 'springgreen' : (0.0,1.0,0.498),
102
+ 'mediumspringgreen' : (0.0,0.9804,0.6039),
103
+ 'mediumaquamarine' : (0.4,0.8039,0.6667),
104
+ 'aquamarine' : (0.498,1.0,0.8314),
105
+ 'turquoise' : (0.251,0.8784,0.8157),
106
+ 'mediumturquoise' : (0.2824,0.8196,0.8),
107
+ 'lightseagreen' : (0.1255,0.698,0.6667),
108
+ 'azure' : (0.9412,1.0,1.0),
109
+ 'lightcyan' : (0.8784,1.0,1.0),
110
+ 'paleturquoise' : (0.6863,0.9333,0.9333),
111
+ 'darkslategray' : (0.1843,0.3098,0.3098),
112
+ 'teal' : (0.0,0.502,0.502),
113
+ 'darkcyan' : (0.0,0.5451,0.5451),
114
+ 'aqua' : (0.0,1.0,1.0),
115
+ 'cyan' : (0.0,1.0,1.0),
116
+ 'cadetblue' : (0.3725,0.6196,0.6275),
117
+ 'darkturquoise' : (0.0,0.8078,0.8196),
118
+ 'powderblue' : (0.6902,0.8784,0.902),
119
+ 'lightblue' : (0.6784,0.8471,0.902),
120
+ 'deepskyblue' : (0.0,0.749,1.0),
121
+ 'skyblue' : (0.5294,0.8078,0.9294),
122
+ 'lightskyblue' : (0.5294,0.8078,0.9804),
123
+ 'steelblue' : (0.2745,0.5098,0.7059),
124
+ 'aliceblue' : (0.9412,0.9725,1.0),
125
+ 'slategray' : (0.4392,0.502,0.5647),
126
+ 'lightslategray' : (0.4667,0.5333,0.6),
127
+ 'dodgerblue' : (0.1176,0.5647,1.0),
128
+ 'lightsteelblue' : (0.6902,0.7686,0.8706),
129
+ 'cornflowerblue' : (0.3922,0.5843,0.9294),
130
+ 'royalblue' : (0.0157,0.0863,0.5647),
131
+ 'ghostwhite' : (0.9725,0.9725,1.0),
132
+ 'lavender' : (0.902,0.902,0.9804),
133
+ 'midnightblue' : (0.098,0.098,0.4392),
134
+ 'navy' : (0.0,0.0,0.502),
135
+ 'darkblue' : (0.0,0.0,0.5451),
136
+ 'mediumblue' : (0.0,0.0,0.8039),
137
+ 'blue' : (0.0,0.0,1.0),
138
+ 'darkslateblue' : (0.2824,0.2392,0.5451),
139
+ 'slateblue' : (0.4157,0.3529,0.8039),
140
+ 'mediumslateblue' : (0.4824,0.4078,0.9333),
141
+ 'mediumpurple' : (0.5765,0.4392,0.8588),
142
+ 'blueviolet' : (0.5412,0.1686,0.8863),
143
+ 'indigo' : (0.2941,0.0,0.5098),
144
+ 'darkorchid' : (0.6,0.1961,0.8),
145
+ 'darkviolet' : (0.5804,0.0,0.8275),
146
+ 'mediumorchid' : (0.7294,0.3333,0.8275),
147
+ 'thistle' : (0.8471,0.749,0.8471),
148
+ 'plum' : (0.8667,0.6275,0.8667),
149
+ 'violet' : (0.9333,0.5098,0.9333),
150
+ 'purple' : (0.502,0.0,0.502),
151
+ 'darkmagenta' : (0.5451,0.0,0.5451),
152
+ 'fuchsia' : (1.0,0.0,1.0),
153
+ 'magenta' : (1.0,0.0,1.0),
154
+ 'orchid' : (0.8549,0.4392,0.8392),
155
+ 'mediumvioletred' : (0.7804,0.0824,0.5216),
156
+ 'deeppink' : (1.0,0.0784,0.5765),
157
+ 'hotpink' : (1.0,0.4118,0.7059),
158
+ 'lavenderblush' : (1.0,0.9412,0.9608),
159
+ 'palevioletred' : (0.8588,0.4392,0.5765),
160
+ 'pink' : (1.0,0.7529,0.8039),
161
+ 'crimson' : (0.8627,0.0784,0.2353),
162
+ 'lightpink' : (1.0,0.7137,0.7569),
163
+ }
164
+
165
+ if __name__ == "__main__":
166
+ import unittest
167
+ from vrml import arrays
168
+ class ColorTests(unittest.TestCase):
169
+ def testForwardName (self):
170
+ for name, value in cssColors.items():
171
+ assert stringToColor( name ) == value
172
+ def testForwardHexidecimal (self):
173
+ for name, value in cssColors.items ():
174
+ representation = '#%02x%02x%02x'%tuple(map(toInt,value))
175
+ result = stringToColor( representation )
176
+ assert arrays.allclose( result, value, 0.001), """Expected %r, got %r"""%(value, result)
177
+ unittest.main ()
vrml/event.py ADDED
@@ -0,0 +1,24 @@
1
+ """Routable event base-class for VRML environments"""
2
+
3
+ class Event( object ):
4
+ """Routable event for VRML environments"""
5
+ def __init__( self ):
6
+ """Initialize the Event object"""
7
+ self.visitedNodes = {}
8
+ def visited (self, key, value = None):
9
+ """Check for or register visitation of the given key
10
+
11
+ key -- an opaque hashable value, normally the node and
12
+ field/event as a tuple.
13
+ value -- if provided, sets the current value, otherwise
14
+ signals that the current value should be returned
15
+
16
+ return value: previous key value (possibly None)
17
+ """
18
+ if value is None:
19
+ return self.visitedNodes.get(key)
20
+ else:
21
+ previousValue = self.visitedNodes.get(key)
22
+ self.visitedNodes[key] = value
23
+ return previousValue
24
+
vrml/field.py ADDED
@@ -0,0 +1,476 @@
1
+ """property sub-class providing VRML field semantics"""
2
+
3
+ from pydispatch import dispatcher, robustapply
4
+ import weakref
5
+ import sys
6
+ from vrml import protonamespace
7
+
8
+ # conditional import via package entry points
9
+ try:
10
+ from vrml_accelerate import fieldaccel2
11
+ except Exception as err:
12
+ fieldaccel2 = None
13
+
14
+ from ._bytes import bytes, unicode, long
15
+
16
+ baseFieldTypes = protonamespace.ProtoNamespace({})
17
+ baseEventTypes = protonamespace.ProtoNamespace({})
18
+
19
+ ### stuff used by the various field sub-types
20
+ NUMERIC_TYPES = (int, float, long)
21
+ SEQUENCE_TYPES = (tuple, list)
22
+ if sys.version_info[0] >= 3:
23
+ MAP_TYPE = type(map(int, [0]))
24
+ ZIP_TYPE = type(zip([], []))
25
+ RANGE_TYPE = type(range(3))
26
+ UNPACK_TYPES = (MAP_TYPE, ZIP_TYPE, RANGE_TYPE)
27
+ SEQUENCE_TYPES += UNPACK_TYPES
28
+ else:
29
+ MAP_TYPE = None
30
+ ZIP_TYPE = None
31
+ RANGE_TYPE = type(xrange(3))
32
+ UNPACK_TYPES = ()
33
+ _NULL = []
34
+
35
+
36
+ def register(cls):
37
+ """Register a new Field or Event class"""
38
+ name = typeName(cls)
39
+ if issubclass(cls, Event):
40
+ dictionary = baseEventTypes
41
+ else:
42
+ dictionary = baseFieldTypes
43
+ if name in dictionary:
44
+ print(
45
+ 'Warning: redefining field-type %s from %s to %s'
46
+ % (name, dictionary.get(name), cls)
47
+ )
48
+ dictionary[name] = cls
49
+
50
+
51
+ def typeName(cls):
52
+ """Get the name of a field/event"""
53
+ if hasattr(cls, 'fieldType'):
54
+ return cls.fieldType
55
+ else:
56
+ return cls.__name__.split('.')[-1]
57
+
58
+
59
+ def newField(name, dataType, exposure=1, default=_NULL):
60
+ """Create a new field with support for using strings to specify type
61
+
62
+ name -- string name
63
+ dataType -- string (or Field sub-class) specifying datatype
64
+ exposure -- boolean (0/1) indicating whether this is an exposed field
65
+ default -- default value for the field
66
+ """
67
+ if isinstance(dataType, (bytes, unicode)):
68
+ dataType = baseFieldTypes[dataType]
69
+ return dataType(name, exposure, default)
70
+
71
+
72
+ def newEvent(name, dataType, direction=1):
73
+ """Create a new event object (a specialised Field)
74
+ name -- string name
75
+ dataType --
76
+ direction -- 0 == in, 1 == out
77
+ """
78
+ if isinstance(dataType, (bytes, unicode)):
79
+ dataType = baseEventTypes[dataType]
80
+ return dataType(name, direction)
81
+
82
+
83
+ if fieldaccel2:
84
+ BaseField = fieldaccel2.BaseField
85
+ else:
86
+
87
+ class BaseField(object):
88
+ def __init__(self, name, default):
89
+ self.name = name
90
+ self.defaultobj = default
91
+ if hasattr(default, '__call__'):
92
+ self.call_default = True
93
+ else:
94
+ self.call_default = False
95
+
96
+ def __get__(self, client, cls=None):
97
+ """Retrieve value for given instance (or self for cls)"""
98
+ if client is None:
99
+ return self
100
+ idict = client.__dict__
101
+ current = idict.get(self.name, _NULL)
102
+ if current is _NULL:
103
+ return self.getDefault(client)
104
+ return current
105
+
106
+ fget = __get__
107
+
108
+ def __set__(self, client, value):
109
+ """Set value for given instance"""
110
+ value = self._set(client, value)
111
+ dispatcher.send(
112
+ ('set', self),
113
+ client,
114
+ value=value,
115
+ )
116
+ # return value
117
+
118
+ def fset(self, client, value, notify=True):
119
+ value = self._set(client, value)
120
+ if notify:
121
+ dispatcher.send(
122
+ ('set', self),
123
+ client,
124
+ value=value,
125
+ )
126
+ return value
127
+
128
+ def _set(self, client, value):
129
+ try:
130
+ value = self.coerce(value)
131
+ except ValueError as x:
132
+ raise ValueError(
133
+ """Field %s could not accept value %s (%s)""" % (self, value, x)
134
+ )
135
+ except TypeError as x:
136
+ raise ValueError(
137
+ """Field %s could not accept value %s of type %s (%s)"""
138
+ % (self, value, type(value), x)
139
+ )
140
+ if isinstance(client, type):
141
+ setattr(client, self.name, value)
142
+ else:
143
+ client.__dict__[self.name] = value
144
+ return value
145
+
146
+ def coerce(self, value):
147
+ """Coerce the given value to our type"""
148
+ return value
149
+
150
+ def check(self, value):
151
+ "Raise ValueError if isn't correct type"
152
+ return value
153
+
154
+ def getDefault(self, client=None):
155
+ """Get the default value of this field
156
+
157
+ if client, set client's attribute to default
158
+ without sending a notification event.
159
+ """
160
+ if self.call_default:
161
+ defaultobj = self.defaultobj()
162
+ else:
163
+ defaultobj = self.defaultobj
164
+ if client is not None:
165
+ defaultobj = self._set(client, defaultobj)
166
+ return defaultobj
167
+
168
+ def __delete__(self, client):
169
+ """Delete our value from client's dictionary"""
170
+ try:
171
+ client.__dict__[self.name]
172
+ except KeyError:
173
+ raise AttributeError(self.name)
174
+
175
+ def fdel(self, client, notify=True):
176
+ """Delete with notify"""
177
+ self.__delete__(client)
178
+ if notify:
179
+ dispatcher.send(
180
+ ('del', self),
181
+ client,
182
+ )
183
+
184
+
185
+ class Field(BaseField):
186
+ """Property sub-class with VRML field semantics
187
+
188
+ The field basically binds a name, a dataType, and
189
+ a default value (with some other meta-data that isn't
190
+ actually used by the current implementation).
191
+
192
+ Fields are normally accessed through the protofunctions
193
+ module, which retrieves field objects from node or
194
+ prototype objects.
195
+
196
+ The field offers vrml.dispatcher notification of
197
+ changes to values (see fget, fset and fdel methods).
198
+ Which allows code to watch for those changes, a
199
+ facility you can see in the OpenGLContext.scenegraph.cache
200
+ module.
201
+ """
202
+
203
+ nodes = 0
204
+ defaultDefault = None
205
+
206
+ def __init__(
207
+ self,
208
+ name,
209
+ exposure=1,
210
+ default=_NULL,
211
+ ):
212
+ """Initialise the field object
213
+
214
+ name -- string name
215
+ exposure -- boolean (0/1) indicating whether this is an exposed field
216
+ default -- default value for the field
217
+ """
218
+ self.exposure = exposure
219
+ if default is _NULL:
220
+ default = self.defaultDefault
221
+ super(Field, self).__init__(name, default)
222
+ setattr(self, "__doc__", str(self))
223
+
224
+ def fhas(self, client):
225
+ """Determine whether the client currently has a non-default value"""
226
+ if isinstance(client, type):
227
+ return hasattr(client, self.name)
228
+ elif self.name in client.__dict__:
229
+ return 1
230
+ else:
231
+ return 0
232
+
233
+ def copy(self, client=None, copier=None):
234
+ """Copy this property's value/definition for client node/proto
235
+
236
+ if client is a prototype, copy this field definition
237
+ for use in a new prototype.
238
+
239
+ if client is a node, and it has a set value for this
240
+ field, then returns self.copyValue( currentValue )
241
+
242
+ otherwise returns _NULL, a singleton object which
243
+ shouldn't turn up anywhere else.
244
+ """
245
+ if isinstance(client, type):
246
+ # copy the field definition itself...
247
+ return self.__class__(
248
+ self.name,
249
+ self.exposure,
250
+ self.copyValue(self.defaultobj, copier),
251
+ )
252
+ elif self.fhas(client):
253
+ return self.copyValue(self.fget(client), copier)
254
+ else:
255
+ return _NULL
256
+
257
+ def copyValue(self, value, copier=None):
258
+ """Copy a value for copier"""
259
+ return value
260
+
261
+ def typeName(self):
262
+ """Get the typeName of this field"""
263
+ return typeName(self.__class__)
264
+
265
+ def __str__(self):
266
+ """Get a human-friendly representation of the field"""
267
+ if self.exposure:
268
+ exposed = "exposedField"
269
+ else:
270
+ exposed = "field"
271
+ if self.defaultobj is list:
272
+ default = '[]'
273
+ else:
274
+ default = str(self.defaultobj)[:20]
275
+ return '%s %s %s %s' % (
276
+ exposed,
277
+ self.typeName(),
278
+ self.name,
279
+ default,
280
+ )
281
+
282
+ def vrmlstr(self, value, lineariser):
283
+ """Convert the given value to a VRML97 representation"""
284
+ return ""
285
+
286
+ def fieldVrmlstr(self, lineariser):
287
+ """Write the field's definition to the lineariser
288
+
289
+ Basically this gives you a VRML97 fragment
290
+ which can be used for creating a PROTO which
291
+ will have the equivalent of this field available.
292
+ """
293
+ if self.exposure:
294
+ exposed = "exposedField"
295
+ else:
296
+ exposed = "field"
297
+ lineariser.buffer.write(
298
+ '%s %s %s '
299
+ % (
300
+ exposed,
301
+ self.typeName(),
302
+ self.name,
303
+ )
304
+ )
305
+ result = self.vrmlstr(
306
+ # coerce is necessary because the
307
+ # default values are often not in
308
+ # the canonical representation
309
+ self.coerce(self.getDefault()),
310
+ lineariser,
311
+ )
312
+ if result:
313
+ lineariser.buffer.write(result)
314
+ return
315
+
316
+ def watch(self, node, receiver, signal=dispatcher.Any):
317
+ """Make receiver receive all update events for this field+node
318
+
319
+ receiver( signal, sender, value=None )
320
+
321
+ signal -- ('del',self), ('set',self) etc...
322
+ sender -- node
323
+ value -- new value set (for set values)
324
+ """
325
+ return dispatcher.connect(
326
+ receiver=receiver,
327
+ sender=node,
328
+ signal=signal,
329
+ )
330
+
331
+ def __lt__(self, other):
332
+ if isinstance(other, Field):
333
+ return (self.__class__.__name__, self.name) < (
334
+ other.__class__.__name__,
335
+ other.name,
336
+ )
337
+ return False
338
+
339
+
340
+ class WeakField(object):
341
+ """A Mix-in for fields which stores weak-references to values"""
342
+
343
+ def fset(self, client, value, notify=1):
344
+ """Set the client's value for this property
345
+
346
+ if notify is true send a notification event.
347
+ """
348
+ if isinstance(value, weakref.ReferenceType):
349
+ value = value()
350
+ if not value:
351
+ if not isinstance(client, type):
352
+ self.fdel(client, notify=notify)
353
+ return None
354
+ value = weakref.ref(value)
355
+ value = super(WeakField, self).fset(client, value, notify=notify)
356
+ return value()
357
+
358
+ def fget(self, client):
359
+ """Get the client's value for this property
360
+
361
+ if notify is true send a notification event.
362
+ """
363
+ value = super(WeakField, self).fget(client)
364
+ if not value:
365
+ # is already default value, since refs are always non-null
366
+ return value
367
+ if isinstance(value, weakref.ReferenceType):
368
+ value = value()
369
+ # value now really is the ref'd value, or None
370
+ if value is None:
371
+ if not isinstance(client, type):
372
+ self.fdel(client, notify=0)
373
+ return None # super( WeakField, self).fget( client )
374
+ return value
375
+
376
+
377
+ class Event(object):
378
+ """An Event-handling Port definition
379
+
380
+ The event is currently non-functional, it's just
381
+ here to allow VRML content to parse and be represented
382
+ in-memory.
383
+ """
384
+
385
+ def __init__(self, name, direction=1):
386
+ """Initialise the field object
387
+
388
+ name -- string name
389
+ direction -- 0 == in, 1 == out
390
+ """
391
+ self.name = name
392
+ self.direction = direction
393
+
394
+ def __set__(self, client, value, notify=1):
395
+ """Set an event value"""
396
+ method = getattr(client, 'on_%s' % (self.name,), None)
397
+ if method is not None:
398
+ robustapply.robustApply(
399
+ method,
400
+ value,
401
+ event=self,
402
+ )
403
+ # now set as a normal property...
404
+ client.__dict__[self.name] = value
405
+ # and then send event letting world know...
406
+ if notify:
407
+ dispatcher.send(('set', self), client, value=value)
408
+
409
+ def __get__(self, client=None, cls=None):
410
+ """Get an event's last value"""
411
+ if client is None:
412
+ return self
413
+ try:
414
+ return client.__dict__[self.name]
415
+ except KeyError:
416
+ raise AttributeError(
417
+ """Event %s doesn't have a value for %s"""
418
+ % (
419
+ self.name,
420
+ client,
421
+ )
422
+ )
423
+
424
+ def clone(self, name=None, direction=None):
425
+ """Clone this property"""
426
+ if name is None:
427
+ name = self.name
428
+ if direction is None:
429
+ direction = self.direction
430
+ return self.__class__(name, direction)
431
+
432
+ def typeName(self):
433
+ """Get the typeName of this field"""
434
+ return typeName(self.__class__)
435
+
436
+ def __str__(self):
437
+ """Get a human-friendly representation of the event"""
438
+ if self.direction:
439
+ exposed = "eventOut"
440
+ else:
441
+ exposed = "eventIn"
442
+ return '%s %s %s' % (exposed, self.typeName(), self.name)
443
+
444
+ def eventVrmlstr(self, lineariser):
445
+ """Write the event's definition to the lineariser
446
+
447
+ Basically this gives you a VRML97 fragment
448
+ which can be used for creating a PROTO which
449
+ will have the equivalent of this event available.
450
+ """
451
+ if self.direction:
452
+ exposed = "eventOut"
453
+ else:
454
+ exposed = "eventIn"
455
+ base = '%s %s %s' % (
456
+ exposed,
457
+ self.typeName(),
458
+ self.name,
459
+ )
460
+ lineariser.buffer.write(base)
461
+ return base
462
+
463
+ def watch(self, node, receiver, signal=dispatcher.Any):
464
+ """Make receiver receive all update events for this field+node
465
+
466
+ receiver( signal, sender, value=None )
467
+
468
+ signal -- ('del',self), ('set',self) etc...
469
+ sender -- node
470
+ value -- new value set (for set values)
471
+ """
472
+ return dispatcher.connect(
473
+ receiver=receiver,
474
+ sender=node,
475
+ signal=signal,
476
+ )