diff --git a/IPython/html/widgets/widget.py b/IPython/html/widgets/widget.py
index a215b57..f39c754 100644
--- a/IPython/html/widgets/widget.py
+++ b/IPython/html/widgets/widget.py
@@ -13,11 +13,12 @@ in the IPython notebook front-end.
# Imports
#-----------------------------------------------------------------------------
from contextlib import contextmanager
+import collections
from IPython.core.getipython import get_ipython
from IPython.kernel.comm import Comm
from IPython.config import LoggingConfigurable
-from IPython.utils.traitlets import Unicode, Dict, Instance, Bool, List, Tuple, Int
+from IPython.utils.traitlets import Unicode, Dict, Instance, Bool, List, Tuple, Int, Set
from IPython.utils.py3compat import string_types
#-----------------------------------------------------------------------------
@@ -110,7 +111,8 @@ class Widget(LoggingConfigurable):
return [name for name in self.traits(sync=True)]
_property_lock = Tuple((None, None))
-
+ _send_state_lock = Int(0)
+ _states_to_send = Set(allow_none=False)
_display_callbacks = Instance(CallbackDispatcher, ())
_msg_callbacks = Instance(CallbackDispatcher, ())
@@ -174,12 +176,12 @@ class Widget(LoggingConfigurable):
Parameters
----------
- key : unicode (optional)
- A single property's name to sync with the front-end.
+ key : unicode, or iterable (optional)
+ A single property's name or iterable of property names to sync with the front-end.
"""
self._send({
"method" : "update",
- "state" : self.get_state()
+ "state" : self.get_state(key=key)
})
def get_state(self, key=None):
@@ -187,10 +189,17 @@ class Widget(LoggingConfigurable):
Parameters
----------
- key : unicode (optional)
- A single property's name to get.
+ key : unicode or iterable (optional)
+ A single property's name or iterable of property names to get.
"""
- keys = self.keys if key is None else [key]
+ if key is None:
+ keys = self.keys
+ elif isinstance(key, string_types):
+ keys = [key]
+ elif isinstance(key, collections.Iterable):
+ keys = key
+ else:
+ raise ValueError("key must be a string, an iterable of keys, or None")
state = {}
for k in keys:
f = self.trait_metadata(k, 'to_json')
@@ -255,10 +264,29 @@ class Widget(LoggingConfigurable):
finally:
self._property_lock = (None, None)
+ @contextmanager
+ def hold_sync(self):
+ """Hold syncing any state until the context manager is released"""
+ # We increment a value so that this can be nested. Syncing will happen when
+ # all levels have been released.
+ self._send_state_lock += 1
+ try:
+ yield
+ finally:
+ self._send_state_lock -=1
+ if self._send_state_lock == 0:
+ self.send_state(self._states_to_send)
+ self._states_to_send.clear()
+
def _should_send_property(self, key, value):
"""Check the property lock (property_lock)"""
- return key != self._property_lock[0] or \
- value != self._property_lock[1]
+ if (key == self._property_lock[0] and value == self._property_lock[1]):
+ return False
+ elif self._send_state_lock > 0:
+ self._states_to_send.add(key)
+ return False
+ else:
+ return True
# Event handlers
@_show_traceback