Skip to content

Commit 82734fe

Browse files
committed
Allow JS side to instigate widget creation
1 parent e24403c commit 82734fe

File tree

3 files changed

+139
-30
lines changed

3 files changed

+139
-30
lines changed

js/scripts/templates/js_wrapper.mustache

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,6 @@ var {{ modelName }} = {{ superClass.modelName }}.extend({
1818
defaults: function() {
1919
return _.extend({{ superClass.modelName }}.prototype.defaults.call(this), {
2020
21-
_model_name: '{{ modelName }}',
22-
2321
{{#each properties as |prop propName|}}
2422
{{ propName }}: {{{ prop.defaultJson }}},
2523
{{/each}}
@@ -70,15 +68,18 @@ var {{ modelName }} = {{ superClass.modelName }}.extend({
7068

7169
},
7270

73-
}{{#if serialized_props}}, {
71+
}, {
72+
73+
model_name: '{{ modelName }}',
7474
75+
{{#if serialized_props}}
7576
serializers: _.extend({
7677
{{#each serialized_props as |propName|}}
7778
{{ propName }}: { deserialize: widgets.unpack_models },
7879
{{/each}}
7980
}, {{ superClass.modelName }}.serializers),
80-
81-
}{{/if}});
81+
{{/if}}
82+
});
8283

8384
module.exports = {
8485
{{ modelName }}: {{ modelName }},

js/src/_base/Three.js

Lines changed: 49 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ var ndarray = require('ndarray');
66

77
var Enums = require('./enums');
88

9+
var version = require('../../package.json').version;
10+
911
var ThreeCache = {
1012
byId: {},
1113
byUuid: {},
@@ -17,7 +19,7 @@ var ThreeModel = widgets.WidgetModel.extend({
1719

1820
defaults: function() {
1921
return _.extend(widgets.WidgetModel.prototype.defaults.call(this), {
20-
_model_name: 'ThreeModel',
22+
_model_name: this.constructor.model_name,
2123
_model_module: 'jupyter-threejs',
2224
});
2325
},
@@ -27,6 +29,29 @@ var ThreeModel = widgets.WidgetModel.extend({
2729

2830
this.createPropertiesArrays();
2931

32+
if (options.three_obj) {
33+
// We are defining the object from a given THREE object!
34+
35+
// We need to push a default state first, as comm open does
36+
// not support buffers!
37+
this.save_changes();
38+
39+
var obj = options.three_obj;
40+
delete options.three_obj;
41+
42+
this.initPromise = Promise.resolve(obj).bind(this).then(this.processNewObj
43+
).then(function (obj) {
44+
45+
// sync in all the properties from the THREE object
46+
this.syncToModel(true);
47+
48+
// setup msg, model, and children change listeners
49+
this.setupListeners();
50+
51+
});
52+
return;
53+
}
54+
3055
// Instantiate Three.js object
3156
this.initPromise = this.createThreeObjectAsync().bind(this).then(function() {
3257

@@ -153,6 +178,24 @@ var ThreeModel = widgets.WidgetModel.extend({
153178

154179
},
155180

181+
processNewObj: function(obj) {
182+
183+
obj.ipymodelId = this.model_id; // brand that sucker
184+
obj.ipymodel = this;
185+
186+
var cacheDescriptor = this.getCacheDescriptor();
187+
if (!cacheDescriptor) {
188+
console.error('Model missing ID:', this);
189+
throw new Error('Model missing ID!');
190+
}
191+
192+
this.putThreeObjectIntoCache(cacheDescriptor, obj);
193+
194+
this.obj = obj;
195+
return obj;
196+
197+
},
198+
156199
createThreeObjectAsync: function() {
157200

158201
// try cache first
@@ -181,30 +224,7 @@ var ThreeModel = widgets.WidgetModel.extend({
181224
throw new Error('no THREE construct method exists: this.createThreeObjectAsync');
182225
}
183226

184-
return objPromise.bind(this).then(function(obj) {
185-
186-
obj.ipymodelId = this.model_id; // brand that sucker
187-
obj.ipymodel = this;
188-
189-
if (!cacheDescriptor) {
190-
cacheDescriptor = this.getCacheDescriptor();
191-
if (!cacheDescriptor) {
192-
console.error('Model missing ID:', this);
193-
throw new Error('Model missing ID!');
194-
}
195-
}
196-
197-
this.putThreeObjectIntoCache(cacheDescriptor, obj);
198-
199-
// pickers need access to the model from the three.js object
200-
// TODO: this.obj may not exist until after the update() call above
201-
// TODO: handle this now that it's in the model
202-
// this.obj.ipywidget_view = this;
203-
204-
this.obj = obj;
205-
return obj;
206-
207-
});
227+
return objPromise.bind(this).then(this.processNewObj);
208228

209229
},
210230

@@ -717,6 +737,10 @@ var ThreeModel = widgets.WidgetModel.extend({
717737
return "#" + c.getHexString();
718738
},
719739

740+
}, {
741+
model_module: 'jupyter-threejs',
742+
model_name: 'ThreeModel',
743+
model_module_version: version,
720744
});
721745

722746
module.exports = {

js/src/_base/utils.js

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
2+
var widgets = require("@jupyter-widgets/base");
3+
4+
5+
6+
/**
7+
* Work around for notebook issue #2730.
8+
*/
9+
var commOpenWithBuffers = function(comm, content, callbacks, metadata, buffers) {
10+
return comm.kernel.send_shell_message(
11+
"comm_open", content, callbacks, metadata, buffers);
12+
}
13+
14+
15+
16+
/**
17+
* Create a new model from the JS side.
18+
*
19+
* This will be pushed to the python side.
20+
*/
21+
var createModel = function(constructor, widget_manager, obj) {
22+
23+
var id = widgets.uuid();
24+
25+
let modelOptions = {
26+
widget_manager: widget_manager,
27+
model_id: id,
28+
three_obj: obj,
29+
}
30+
var attributes = { };
31+
let widget_model = new constructor(attributes, modelOptions);
32+
33+
widget_model.once('comm:close', () => {
34+
delete widget_manager._models[id];
35+
});
36+
37+
var data, buffers;
38+
widget_manager._models[id] = widget_model.initPromise.then(() => {
39+
var split = widgets.remove_buffers(
40+
widget_model.serialize(widget_model.get_state(true)));
41+
data = {
42+
state: _.extend({}, split.state, {
43+
_model_name: constructor.model_name,
44+
_model_module: constructor.model_module,
45+
_model_module_version: constructor.model_module_version,
46+
_view_name: null,
47+
_view_module: null,
48+
_view_module_version: '',
49+
}),
50+
buffer_paths: split.buffer_paths
51+
};
52+
buffers = split.buffers;
53+
54+
// Create un-opened comm:
55+
return widget_manager._create_comm(widget_manager.comm_target_name, id);
56+
57+
}).then(comm => {
58+
var content = {
59+
'comm_id': id,
60+
'target_name': widget_manager.comm_target_name,
61+
'data': data
62+
};
63+
var metadata = {version: widgets.PROTOCOL_VERSION};
64+
65+
commOpenWithBuffers(comm, content, null, metadata, buffers)
66+
67+
widget_model.comm = comm;
68+
69+
// Hook comm messages up to model.
70+
comm.on_close(_.bind(widget_model._handle_comm_closed, widget_model));
71+
comm.on_msg(_.bind(widget_model._handle_comm_msg, widget_model));
72+
73+
widget_model.comm_live = true;
74+
75+
return widget_model;
76+
});
77+
78+
return widget_manager._models[id];
79+
}
80+
81+
82+
module.exports = {
83+
createModel: createModel,
84+
}

0 commit comments

Comments
 (0)