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