Show More
@@ -993,6 +993,8 b' class TestBind(TestCase):' | |||||
993 | # Change one of the values to make sure they stay in sync. |
|
993 | # Change one of the values to make sure they stay in sync. | |
994 | a.value = 5 |
|
994 | a.value = 5 | |
995 | self.assertEqual(a.value, b.value) |
|
995 | self.assertEqual(a.value, b.value) | |
|
996 | b.value = 6 | |||
|
997 | self.assertEqual(a.value, b.value) | |||
996 |
|
998 | |||
997 | def test_bind_different(self): |
|
999 | def test_bind_different(self): | |
998 | """Verify two traitlets of different types can be bound together using bind.""" |
|
1000 | """Verify two traitlets of different types can be bound together using bind.""" | |
@@ -1014,6 +1016,8 b' class TestBind(TestCase):' | |||||
1014 | # Change one of the values to make sure they stay in sync. |
|
1016 | # Change one of the values to make sure they stay in sync. | |
1015 | a.value = 5 |
|
1017 | a.value = 5 | |
1016 | self.assertEqual(a.value, b.count) |
|
1018 | self.assertEqual(a.value, b.count) | |
|
1019 | b.count = 4 | |||
|
1020 | self.assertEqual(a.value, b.count) | |||
1017 |
|
1021 | |||
1018 | def test_unbind(self): |
|
1022 | def test_unbind(self): | |
1019 | """Verify two binded traitlets can be unbinded.""" |
|
1023 | """Verify two binded traitlets can be unbinded.""" | |
@@ -1032,3 +1036,40 b' class TestBind(TestCase):' | |||||
1032 | # Change one of the values to make sure they stay in sync. |
|
1036 | # Change one of the values to make sure they stay in sync. | |
1033 | a.value = 5 |
|
1037 | a.value = 5 | |
1034 | self.assertNotEqual(a.value, b.value) |
|
1038 | self.assertNotEqual(a.value, b.value) | |
|
1039 | ||||
|
1040 | def test_callbacks(self): | |||
|
1041 | """Verify two binded traitlets have their callbacks called once.""" | |||
|
1042 | ||||
|
1043 | # Create two simple classes with Int traitlets. | |||
|
1044 | class A(HasTraits): | |||
|
1045 | value = Int() | |||
|
1046 | class B(HasTraits): | |||
|
1047 | count = Int() | |||
|
1048 | a = A(value=9) | |||
|
1049 | b = B(count=8) | |||
|
1050 | ||||
|
1051 | # Register callbacks that count. | |||
|
1052 | callback_count = [] | |||
|
1053 | def a_callback(name, old, new): | |||
|
1054 | callback_count.append('a') | |||
|
1055 | a.on_trait_change(a_callback, 'value') | |||
|
1056 | def b_callback(name, old, new): | |||
|
1057 | callback_count.append('b') | |||
|
1058 | b.on_trait_change(b_callback, 'count') | |||
|
1059 | ||||
|
1060 | # Conenct the two classes. | |||
|
1061 | c = bind((a, 'value'), (b, 'count')) | |||
|
1062 | ||||
|
1063 | # Make sure b's count was set to a's value once. | |||
|
1064 | self.assertEqual(''.join(callback_count), 'b') | |||
|
1065 | del callback_count[:] | |||
|
1066 | ||||
|
1067 | # Make sure a's value was set to b's count once. | |||
|
1068 | b.count = 5 | |||
|
1069 | self.assertEqual(''.join(callback_count), 'ba') | |||
|
1070 | del callback_count[:] | |||
|
1071 | ||||
|
1072 | # Make sure b's count was set to a's value once. | |||
|
1073 | a.value = 4 | |||
|
1074 | self.assertEqual(''.join(callback_count), 'ab') | |||
|
1075 | del callback_count[:] |
@@ -202,13 +202,15 b' class bind(object):' | |||||
202 | if len(args) < 2: |
|
202 | if len(args) < 2: | |
203 | raise TypeError('At least two traitlets must be provided.') |
|
203 | raise TypeError('At least two traitlets must be provided.') | |
204 |
|
204 | |||
205 |
self.objects = |
|
205 | self.objects = {} | |
|
206 | initial = getattr(args[0][0], args[0][1]) | |||
206 | for obj,attr in args: |
|
207 | for obj,attr in args: | |
207 | obj.on_trait_change(self._update, attr) |
|
208 | if getattr(obj, attr) != initial: | |
|
209 | setattr(obj, attr, initial) | |||
208 |
|
210 | |||
209 | # Syncronize the traitlets initially. |
|
211 | callback = self._make_closure(obj,attr) | |
210 | initial = getattr(args[0][0], args[0][1]) |
|
212 | obj.on_trait_change(callback, attr) | |
211 | self._update(args[0][1], initial, initial) |
|
213 | self.objects[(obj,attr)] = callback | |
212 |
|
214 | |||
213 | @contextlib.contextmanager |
|
215 | @contextlib.contextmanager | |
214 | def _busy_updating(self): |
|
216 | def _busy_updating(self): | |
@@ -218,16 +220,23 b' class bind(object):' | |||||
218 | finally: |
|
220 | finally: | |
219 | self.updating = False |
|
221 | self.updating = False | |
220 |
|
222 | |||
221 | def _update(self, name, old, new): |
|
223 | def _make_closure(self, sending_obj, sending_attr): | |
|
224 | def update(name, old, new): | |||
|
225 | self._update(sending_obj, sending_attr, new) | |||
|
226 | return update | |||
|
227 | ||||
|
228 | def _update(self, sending_obj, sending_attr, new): | |||
222 | if self.updating: |
|
229 | if self.updating: | |
223 | return |
|
230 | return | |
224 | with self._busy_updating(): |
|
231 | with self._busy_updating(): | |
225 | for obj,attr in self.objects: |
|
232 | for obj,attr in self.objects.keys(): | |
226 | setattr(obj, attr, new) |
|
233 | if obj is not sending_obj or attr != sending_attr: | |
|
234 | setattr(obj, attr, new) | |||
227 |
|
235 | |||
228 | def unbind(self): |
|
236 | def unbind(self): | |
229 |
for |
|
237 | for key, callback in self.objects.items(): | |
230 | obj.on_trait_change(self._update, attr, remove=True) |
|
238 | (obj,attr) = key | |
|
239 | obj.on_trait_change(callback, attr, remove=True) | |||
231 |
|
240 | |||
232 | #----------------------------------------------------------------------------- |
|
241 | #----------------------------------------------------------------------------- | |
233 | # Base TraitType for all traits |
|
242 | # Base TraitType for all traits |
General Comments 0
You need to be logged in to leave comments.
Login now