Skip to content

Commit d866fa7

Browse files
committed
Merge pull request #84 from jdfreder/statepush
Push state in comm open msg
2 parents 5f5c9c2 + 98aff2c commit d866fa7

File tree

2 files changed

+80
-69
lines changed

2 files changed

+80
-69
lines changed

ipywidgets/static/widgets/js/manager.js

Lines changed: 36 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ define([
1818
*/
1919
WidgetManager._managers.push(this);
2020

21-
// Attach a comm manager to the
21+
// Attach a comm manager to the
2222
this.keyboard_manager = notebook.keyboard_manager;
2323
this.notebook = notebook;
2424
this.comm_manager = comm_manager;
@@ -61,14 +61,14 @@ define([
6161
WidgetManager._save_callback = null;
6262

6363
WidgetManager.register_widget_model = function (model_name, model_type) {
64-
/**
64+
/**
6565
* Registers a widget model by name.
6666
*/
6767
WidgetManager._model_types[model_name] = model_type;
6868
};
6969

7070
WidgetManager.register_widget_view = function (view_name, view_type) {
71-
/**
71+
/**
7272
* Registers a widget view by name.
7373
*/
7474
WidgetManager._view_types[view_name] = view_type;
@@ -77,7 +77,7 @@ define([
7777
WidgetManager.set_state_callbacks = function (load_callback, save_callback, options) {
7878
/**
7979
* Registers callbacks for widget state persistence.
80-
*
80+
*
8181
* Parameters
8282
* ----------
8383
* load_callback: function()
@@ -107,7 +107,7 @@ define([
107107

108108
// Use local storage to persist widgets across page refresh by default.
109109
// LocalStorage is per domain, so we need to explicitly set the URL
110-
// that the widgets are associated with so they don't show on other
110+
// that the widgets are associated with so they don't show on other
111111
// pages hosted by the noteboook server.
112112
var url = [window.location.protocol, '//', window.location.host, window.location.pathname].join('');
113113
var key = 'widgets:' + url;
@@ -129,11 +129,11 @@ define([
129129
*/
130130
var cell = this.get_msg_cell(msg.parent_header.msg_id);
131131
if (cell === null) {
132-
return Promise.reject(new Error("Could not determine where the display" +
132+
return Promise.reject(new Error("Could not determine where the display" +
133133
" message was from. Widget will not be displayed"));
134134
} else {
135135
return this.display_view_in_cell(cell, model)
136-
.catch(utils.reject('Could not display view', true));
136+
.catch(utils.reject('Could not display view', true));
137137
}
138138
};
139139

@@ -149,7 +149,7 @@ define([
149149
that._handle_display_view(view);
150150
view.trigger('displayed');
151151
return view;
152-
}).catch(utils.reject('Could not create or display view', true));
152+
}).catch(utils.reject('Could not create or display view', true));
153153
} else {
154154
return Promise.reject(new Error('Cell does not have a `widgetarea` defined'));
155155
}
@@ -163,24 +163,24 @@ define([
163163
*/
164164
if (this.keyboard_manager) {
165165
this.keyboard_manager.register_events(view.el);
166-
166+
167167
if (view.additional_elements) {
168168
for (var i = 0; i < view.additional_elements.length; i++) {
169169
this.keyboard_manager.register_events(view.additional_elements[i]);
170170
}
171-
}
171+
}
172172
}
173173
};
174-
174+
175175
WidgetManager.prototype.create_view = function(model, options) {
176176
/**
177177
* Creates a promise for a view of a given model
178178
*
179-
* Make sure the view creation is not out of order with
179+
* Make sure the view creation is not out of order with
180180
* any state updates.
181181
*/
182182
model.state_change = model.state_change.then(function() {
183-
183+
184184
return utils.load_class(model.get('_view_name'), model.get('_view_module'),
185185
WidgetManager._view_types).then(function(ViewType) {
186186

@@ -199,7 +199,7 @@ define([
199199
});
200200
var id = utils.uuid();
201201
model.views[id] = model.state_change;
202-
model.state_change.then(function(view) {
202+
model.state_change.then(function(view) {
203203
view.once('remove', function() {
204204
delete view.model.views[id];
205205
}, this);
@@ -228,8 +228,8 @@ define([
228228
return callbacks.iopub.get_cell();
229229
}
230230
}
231-
232-
// Not triggered by a cell or widget (no get_cell callback
231+
232+
// Not triggered by a cell or widget (no get_cell callback
233233
// exists).
234234
return null;
235235
};
@@ -280,11 +280,26 @@ define([
280280
/**
281281
* Handle when a comm is opened.
282282
*/
283-
return this.create_model({
284-
model_name: msg.content.data.model_name,
285-
model_module: msg.content.data.model_module,
286-
comm: comm,
287-
}).catch(utils.reject("Couldn't create a model.", true));
283+
// For < 4.x
284+
var model;
285+
if (msg.content.data.model_name) {
286+
model = this.create_model({
287+
model_name: msg.content.data.model_name,
288+
model_module: msg.content.data.model_module,
289+
comm: comm,
290+
});
291+
} else {
292+
model = this.create_model({
293+
model_name: msg.content.data._model_name,
294+
model_module: msg.content.data._model_module,
295+
comm: comm,
296+
}).then(function(model) {
297+
model.set_state(msg.content.data);
298+
return model;
299+
});
300+
}
301+
302+
return model.catch(utils.reject("Couldn't create a model.", true));
288303
};
289304

290305
WidgetManager.prototype.create_model = function (options) {

ipywidgets/widgets/widget.py

Lines changed: 44 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def _json_to_widget(x, obj):
4848
class CallbackDispatcher(LoggingConfigurable):
4949
"""A structure for registering and running callbacks"""
5050
callbacks = List()
51-
51+
5252
def __call__(self, *args, **kwargs):
5353
"""Call all of the registered callbacks."""
5454
value = None
@@ -74,7 +74,7 @@ def register_callback(self, callback, remove=False):
7474
Method to be registered or unregistered.
7575
remove=False: bool
7676
Whether to unregister the callback."""
77-
77+
7878
# (Un)Register the callback.
7979
if remove and callback in self.callbacks:
8080
self.callbacks.remove(callback)
@@ -96,7 +96,7 @@ def m(self, *args, **kwargs):
9696

9797

9898
def register(key=None):
99-
"""Returns a decorator registering a widget class in the widget registry.
99+
"""Returns a decorator registering a widget class in the widget registry.
100100
If no key is provided, the class name is used as a key. A key is
101101
provided for each core IPython widget so that the frontend can use
102102
this key regardless of the language of the kernel"""
@@ -141,29 +141,29 @@ def handle_comm_opened(comm, msg):
141141
# Traits
142142
#-------------------------------------------------------------------------
143143
_model_module = Unicode(None, allow_none=True, help="""A requirejs module name
144-
in which to find _model_name. If empty, look in the global registry.""")
145-
_model_name = Unicode('WidgetModel', help="""Name of the backbone model
146-
registered in the front-end to create and sync this widget with.""")
144+
in which to find _model_name. If empty, look in the global registry.""", sync=True)
145+
_model_name = Unicode('WidgetModel', help="""Name of the backbone model
146+
registered in the front-end to create and sync this widget with.""", sync=True)
147147
_view_module = Unicode(help="""A requirejs module in which to find _view_name.
148148
If empty, look in the global registry.""", sync=True)
149149
_view_name = Unicode(None, allow_none=True, help="""Default view registered in the front-end
150150
to use to represent the widget.""", sync=True)
151151
comm = Instance('ipykernel.comm.Comm', allow_none=True)
152-
153-
msg_throttle = Int(3, sync=True, help="""Maximum number of msgs the
152+
153+
msg_throttle = Int(3, sync=True, help="""Maximum number of msgs the
154154
front-end can send before receiving an idle msg from the back-end.""")
155-
155+
156156
version = Int(0, sync=True, help="""Widget's version""")
157157
keys = List()
158158
def _keys_default(self):
159159
return [name for name in self.traits(sync=True)]
160-
160+
161161
_property_lock = Dict()
162162
_holding_sync = False
163163
_states_to_send = Set()
164164
_display_callbacks = Instance(CallbackDispatcher, ())
165165
_msg_callbacks = Instance(CallbackDispatcher, ())
166-
166+
167167
#-------------------------------------------------------------------------
168168
# (Con/de)structor
169169
#-------------------------------------------------------------------------
@@ -187,8 +187,7 @@ def open(self):
187187
"""Open a comm to the frontend if one isn't already open."""
188188
if self.comm is None:
189189
args = dict(target_name='ipython.widget',
190-
data={'model_name': self._model_name,
191-
'model_module': self._model_module})
190+
data=self.get_state())
192191
if self._model_id is not None:
193192
args['comm_id'] = self._model_id
194193
self.comm = Comm(**args)
@@ -198,12 +197,9 @@ def _comm_changed(self, name, new):
198197
if new is None:
199198
return
200199
self._model_id = self.model_id
201-
200+
202201
self.comm.on_msg(self._handle_msg)
203202
Widget.widgets[self.model_id] = self
204-
205-
# first update
206-
self.send_state()
207203

208204
@property
209205
def model_id(self):
@@ -220,7 +216,7 @@ def __setattr__(self, name, value):
220216
"""Overload of HasTraits.__setattr__to handle read-only-ness of widget
221217
attributes """
222218
if (self._read_only_enabled and self.has_trait(name) and
223-
self.trait_metadata(name, 'read_only')):
219+
self.trait_metadata(name, 'read_only')):
224220
raise TraitError('Widget attribute "%s" is read-only.' % name)
225221
else:
226222
super(Widget, self).__setattr__(name, value)
@@ -236,7 +232,7 @@ def close(self):
236232
Widget.widgets.pop(self.model_id, None)
237233
self.comm.close()
238234
self.comm = None
239-
235+
240236
def send_state(self, key=None):
241237
"""Sends the widget state, or a piece of it, to the front-end.
242238
@@ -316,9 +312,9 @@ def on_msg(self, callback, remove=False):
316312
----------
317313
callback: callable
318314
callback will be passed three arguments when a message arrives::
319-
315+
320316
callback(widget, content, buffers)
321-
317+
322318
remove: bool
323319
True if the callback should be unregistered."""
324320
self._msg_callbacks.register_callback(callback, remove=remove)
@@ -330,9 +326,9 @@ def on_displayed(self, callback, remove=False):
330326
----------
331327
callback: method handler
332328
Must have a signature of::
333-
329+
334330
callback(widget, **kwargs)
335-
331+
336332
kwargs from display are passed through without modification.
337333
remove: bool
338334
True if the callback should be unregistered."""
@@ -356,7 +352,7 @@ def _lock_property(self, **properties):
356352
The value should be the JSON state of the property.
357353
358354
NOTE: This, in addition to the single lock for all state changes, is
359-
flawed. In the future we may want to look into buffering state changes
355+
flawed. In the future we may want to look into buffering state changes
360356
back to the front-end."""
361357
self._property_lock = properties
362358
try:
@@ -373,7 +369,7 @@ def _allow_write(self):
373369
self._read_only_enabled = False
374370
yield
375371
finally:
376-
self._read_only_enabled = True
372+
self._read_only_enabled = True
377373

378374
@contextmanager
379375
def hold_sync(self):
@@ -385,7 +381,7 @@ def hold_sync(self):
385381
self._holding_sync = True
386382
yield
387383
finally:
388-
self._holding_sync = False
384+
self._holding_sync = False
389385
self.send_state(self._states_to_send)
390386
self._states_to_send.clear()
391387

@@ -400,7 +396,7 @@ def _should_send_property(self, key, value):
400396
return False
401397
else:
402398
return True
403-
399+
404400
# Event handlers
405401
@_show_traceback
406402
def _handle_msg(self, msg):
@@ -479,7 +475,7 @@ class DOMWidget(Widget):
479475
visible = Bool(True, allow_none=True, help="Whether the widget is visible. False collapses the empty space, while None preserves the empty space.", sync=True)
480476
_css = Tuple(sync=True, help="CSS property list: (selector, key, value)")
481477
_dom_classes = Tuple(sync=True, help="DOM classes applied to widget.$el.")
482-
478+
483479
width = CUnicode(sync=True)
484480
height = CUnicode(sync=True)
485481
# A default padding of 2.5 px makes the widgets look nice when displayed inline.
@@ -493,33 +489,33 @@ class DOMWidget(Widget):
493489
border_width = CUnicode(sync=True)
494490
border_radius = CUnicode(sync=True)
495491
border_style = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_border-style.asp
496-
'none',
497-
'hidden',
498-
'dotted',
499-
'dashed',
500-
'solid',
501-
'double',
502-
'groove',
503-
'ridge',
504-
'inset',
505-
'outset',
506-
'initial',
492+
'none',
493+
'hidden',
494+
'dotted',
495+
'dashed',
496+
'solid',
497+
'double',
498+
'groove',
499+
'ridge',
500+
'inset',
501+
'outset',
502+
'initial',
507503
'inherit', ''],
508504
default_value='', sync=True)
509505

510506
font_style = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_font_font-style.asp
511-
'normal',
512-
'italic',
513-
'oblique',
514-
'initial',
515-
'inherit', ''],
507+
'normal',
508+
'italic',
509+
'oblique',
510+
'initial',
511+
'inherit', ''],
516512
default_value='', sync=True)
517513
font_weight = CaselessStrEnum(values=[ # http://www.w3schools.com/cssref/pr_font_weight.asp
518-
'normal',
519-
'bold',
520-
'bolder',
514+
'normal',
515+
'bold',
516+
'bolder',
521517
'lighter',
522-
'initial',
518+
'initial',
523519
'inherit', ''] + list(map(str, range(100,1000,100))),
524520
default_value='', sync=True)
525521
font_size = CUnicode(sync=True)

0 commit comments

Comments
 (0)