Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0ced8c1

Browse files
authored
Merge pull request #9534 from tacaswell/fix_webagg
Fix webagg
2 parents d78fb0e + 86ac6ce commit 0ced8c1

File tree

6 files changed

+191
-355
lines changed

6 files changed

+191
-355
lines changed

lib/matplotlib/backends/backend_nbagg.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,7 @@ def get_javascript(cls, stream=None):
125125
super(FigureManagerNbAgg, cls).get_javascript(stream=output)
126126
with io.open(os.path.join(
127127
os.path.dirname(__file__),
128-
"web_backend",
128+
"web_backend", 'js',
129129
"nbagg_mpl.js"), encoding='utf8') as fd:
130130
output.write(fd.read())
131131
if stream is None:

lib/matplotlib/backends/backend_webagg_core.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -468,7 +468,7 @@ def get_javascript(cls, stream=None):
468468

469469
with io.open(os.path.join(
470470
os.path.dirname(__file__),
471-
"web_backend",
471+
"web_backend", "js",
472472
"mpl.js"), encoding='utf8') as fd:
473473
output.write(fd.read())
474474

Lines changed: 189 additions & 142 deletions
Original file line numberDiff line numberDiff line change
@@ -1,164 +1,211 @@
1+
var comm_websocket_adapter = function(comm) {
2+
// Create a "websocket"-like object which calls the given IPython comm
3+
// object with the appropriate methods. Currently this is a non binary
4+
// socket, so there is still some room for performance tuning.
5+
var ws = {};
6+
7+
ws.close = function() {
8+
comm.close()
9+
};
10+
ws.send = function(m) {
11+
//console.log('sending', m);
12+
comm.send(m);
13+
};
14+
// Register the callback with on_msg.
15+
comm.on_msg(function(msg) {
16+
//console.log('receiving', msg['content']['data'], msg);
17+
// Pass the mpl event to the overriden (by mpl) onmessage function.
18+
ws.onmessage(msg['content']['data'])
19+
});
20+
return ws;
21+
}
122

23+
mpl.mpl_figure_comm = function(comm, msg) {
24+
// This is the function which gets called when the mpl process
25+
// starts-up an IPython Comm through the "matplotlib" channel.
226

3-
define(['jupyter-js-widgets', '/nbextensions/matplotlib/mpl.js'], function(widgets, mpl) {
4-
5-
var MPLCanvasView = widgets.WidgetView.extend({
6-
7-
render: function() {
8-
var that = this;
9-
10-
var id = this.model.get('_id');
11-
12-
var element = this.$el;
13-
14-
this.ws_proxy = this.comm_websocket_adapter(this.model.comm);
15-
16-
function ondownload(figure, format) {
17-
window.open(figure.imageObj.src);
18-
}
19-
20-
mpl.toolbar_items = this.model.get('_toolbar_items')
21-
22-
var fig = new mpl.figure(id, this.ws_proxy,
23-
ondownload,
24-
element.get(0));
25-
26-
// Call onopen now - mpl needs it, as it is assuming we've passed it a real
27-
// web socket which is closed, not our websocket->open comm proxy.
28-
this.ws_proxy.onopen();
29-
30-
fig.parent_element = element.get(0);
31-
32-
// subscribe to incoming messages from the MPLCanvasWidget
33-
this.model.on('msg:custom', this.ws_proxy.onmessage, this);
34-
35-
this.send(JSON.stringify({ type: 'initialized' }));
36-
},
37-
38-
comm_websocket_adapter: function(comm) {
39-
// Create a "websocket"-like object which calls the given IPython comm
40-
// object with the appropriate methods. Currently this is a non binary
41-
// socket, so there is still some room for performance tuning.
42-
var ws = {};
43-
var that = this;
27+
var id = msg.content.data.id;
28+
// Get hold of the div created by the display call when the Comm
29+
// socket was opened in Python.
30+
var element = $("#" + id);
31+
var ws_proxy = comm_websocket_adapter(comm)
4432

45-
ws.close = function() {
46-
comm.close()
47-
};
48-
ws.send = function(m) {
49-
that.send(m);
50-
};
51-
return ws;
52-
}
33+
function ondownload(figure, format) {
34+
window.open(figure.imageObj.src);
35+
}
5336

54-
});
37+
var fig = new mpl.figure(id, ws_proxy,
38+
ondownload,
39+
element.get(0));
5540

56-
mpl.figure.prototype.handle_close = function(fig, msg) {
57-
var width = fig.canvas.width/mpl.ratio
58-
fig.root.unbind('remove')
41+
// Call onopen now - mpl needs it, as it is assuming we've passed it a real
42+
// web socket which is closed, not our websocket->open comm proxy.
43+
ws_proxy.onopen();
5944

60-
// Re-enable the keyboard manager in IPython - without this line, in FF,
61-
// the notebook keyboard shortcuts fail.
62-
IPython.keyboard_manager.enable()
63-
fig.close_ws(fig, msg);
45+
fig.parent_element = element.get(0);
46+
fig.cell_info = mpl.find_output_cell("<div id='" + id + "'></div>");
47+
if (!fig.cell_info) {
48+
console.error("Failed to find cell for figure", id, fig);
49+
return;
6450
}
6551

66-
mpl.figure.prototype.close_ws = function(fig, msg){
67-
fig.send_message('closing', msg);
68-
// fig.ws.close()
52+
var output_index = fig.cell_info[2]
53+
var cell = fig.cell_info[0];
54+
55+
};
56+
57+
mpl.figure.prototype.handle_close = function(fig, msg) {
58+
var width = fig.canvas.width/mpl.ratio
59+
fig.root.unbind('remove')
60+
61+
// Update the output cell to use the data from the current canvas.
62+
fig.push_to_output();
63+
var dataURL = fig.canvas.toDataURL();
64+
// Re-enable the keyboard manager in IPython - without this line, in FF,
65+
// the notebook keyboard shortcuts fail.
66+
IPython.keyboard_manager.enable()
67+
$(fig.parent_element).html('<img src="' + dataURL + '" width="' + width + '">');
68+
fig.close_ws(fig, msg);
69+
}
70+
71+
mpl.figure.prototype.close_ws = function(fig, msg){
72+
fig.send_message('closing', msg);
73+
// fig.ws.close()
74+
}
75+
76+
mpl.figure.prototype.push_to_output = function(remove_interactive) {
77+
// Turn the data on the canvas into data in the output cell.
78+
var width = this.canvas.width/mpl.ratio
79+
var dataURL = this.canvas.toDataURL();
80+
this.cell_info[1]['text/html'] = '<img src="' + dataURL + '" width="' + width + '">';
81+
}
82+
83+
mpl.figure.prototype.updated_canvas_event = function() {
84+
// Tell IPython that the notebook contents must change.
85+
IPython.notebook.set_dirty(true);
86+
this.send_message("ack", {});
87+
var fig = this;
88+
// Wait a second, then push the new image to the DOM so
89+
// that it is saved nicely (might be nice to debounce this).
90+
setTimeout(function () { fig.push_to_output() }, 1000);
91+
}
92+
93+
mpl.figure.prototype._init_toolbar = function() {
94+
var fig = this;
95+
96+
var nav_element = $('<div/>')
97+
nav_element.attr('style', 'width: 100%');
98+
this.root.append(nav_element);
99+
100+
// Define a callback function for later on.
101+
function toolbar_event(event) {
102+
return fig.toolbar_button_onclick(event['data']);
69103
}
70-
71-
mpl.figure.prototype.updated_canvas_event = function() {
72-
// Tell IPython that the notebook contents must change.
73-
IPython.notebook.set_dirty(true);
74-
this.send_message("ack", {});
104+
function toolbar_mouse_event(event) {
105+
return fig.toolbar_button_onmouseover(event['data']);
75106
}
76107

77-
mpl.figure.prototype._init_toolbar = function() {
78-
var fig = this;
79-
80-
var nav_element = $('<div/>')
81-
nav_element.attr('style', 'width: 100%');
82-
this.root.append(nav_element);
83-
84-
// Define a callback function for later on.
85-
function toolbar_event(event) {
86-
return fig.toolbar_button_onclick(event['data']);
87-
}
88-
function toolbar_mouse_event(event) {
89-
return fig.toolbar_button_onmouseover(event['data']);
90-
}
108+
for(var toolbar_ind in mpl.toolbar_items){
109+
var name = mpl.toolbar_items[toolbar_ind][0];
110+
var tooltip = mpl.toolbar_items[toolbar_ind][1];
111+
var image = mpl.toolbar_items[toolbar_ind][2];
112+
var method_name = mpl.toolbar_items[toolbar_ind][3];
91113

92-
for(var toolbar_ind in mpl.toolbar_items){
93-
var name = mpl.toolbar_items[toolbar_ind][0];
94-
var tooltip = mpl.toolbar_items[toolbar_ind][1];
95-
var image = mpl.toolbar_items[toolbar_ind][2];
96-
var method_name = mpl.toolbar_items[toolbar_ind][3];
114+
if (!name) { continue; };
97115

98-
if (!name) { continue; };
99-
100-
var button = $('<button class="btn btn-default" href="#" title="' + name + '"><i class="fa ' + image + ' fa-lg"></i></button>');
101-
button.click(method_name, toolbar_event);
102-
button.mouseover(tooltip, toolbar_mouse_event);
103-
nav_element.append(button);
104-
}
105-
106-
// Add the status bar.
107-
var status_bar = $('<span class="mpl-message" style="text-align:right; float: right;"/>');
108-
nav_element.append(status_bar);
109-
this.message = status_bar[0];
110-
111-
// Add the close button to the window.
112-
var buttongrp = $('<div class="btn-group inline pull-right"></div>');
113-
var button = $('<button class="btn btn-mini btn-primary" href="#" title="Stop Interaction"><i class="fa fa-power-off icon-remove icon-large"></i></button>');
114-
button.click(function (evt) { fig.handle_close(fig, {}); } );
115-
button.mouseover('Stop Interaction', toolbar_mouse_event);
116-
buttongrp.append(button);
117-
var titlebar = this.root.find($('.ui-dialog-titlebar'));
118-
titlebar.prepend(buttongrp);
116+
var button = $('<button class="btn btn-default" href="#" title="' + name + '"><i class="fa ' + image + ' fa-lg"></i></button>');
117+
button.click(method_name, toolbar_event);
118+
button.mouseover(tooltip, toolbar_mouse_event);
119+
nav_element.append(button);
119120
}
120121

121-
mpl.figure.prototype._root_extra_style = function(el){
122-
var fig = this
123-
el.on("remove", function(){
124-
fig.close_ws(fig, {});
125-
});
126-
}
127-
128-
mpl.figure.prototype._canvas_extra_style = function(el){
129-
// this is important to make the div 'focusable
130-
el.attr('tabindex', 0)
131-
// reach out to IPython and tell the keyboard manager to turn it's self
132-
// off when our div gets focus
122+
// Add the status bar.
123+
var status_bar = $('<span class="mpl-message" style="text-align:right; float: right;"/>');
124+
nav_element.append(status_bar);
125+
this.message = status_bar[0];
126+
127+
// Add the close button to the window.
128+
var buttongrp = $('<div class="btn-group inline pull-right"></div>');
129+
var button = $('<button class="btn btn-mini btn-primary" href="#" title="Stop Interaction"><i class="fa fa-power-off icon-remove icon-large"></i></button>');
130+
button.click(function (evt) { fig.handle_close(fig, {}); } );
131+
button.mouseover('Stop Interaction', toolbar_mouse_event);
132+
buttongrp.append(button);
133+
var titlebar = this.root.find($('.ui-dialog-titlebar'));
134+
titlebar.prepend(buttongrp);
135+
}
136+
137+
mpl.figure.prototype._root_extra_style = function(el){
138+
var fig = this
139+
el.on("remove", function(){
140+
fig.close_ws(fig, {});
141+
});
142+
}
133143

134-
// location in version 3
135-
if (IPython.notebook.keyboard_manager) {
136-
IPython.notebook.keyboard_manager.register_events(el);
137-
}
138-
else {
139-
// location in version 2
140-
IPython.keyboard_manager.register_events(el);
141-
}
144+
mpl.figure.prototype._canvas_extra_style = function(el){
145+
// this is important to make the div 'focusable
146+
el.attr('tabindex', 0)
147+
// reach out to IPython and tell the keyboard manager to turn it's self
148+
// off when our div gets focus
142149

150+
// location in version 3
151+
if (IPython.notebook.keyboard_manager) {
152+
IPython.notebook.keyboard_manager.register_events(el);
143153
}
144-
145-
mpl.figure.prototype._key_event_extra = function(event, name) {
146-
var manager = IPython.notebook.keyboard_manager;
147-
if (!manager)
148-
manager = IPython.keyboard_manager;
149-
150-
// Check for shift+enter
151-
if (event.shiftKey && event.which == 13) {
152-
this.canvas_div.blur();
153-
event.shiftKey = false;
154-
// select the cell after this one
155-
var index = IPython.notebook.find_cell_index(this.cell_info[0]);
156-
IPython.notebook.select(index + 1); }
154+
else {
155+
// location in version 2
156+
IPython.keyboard_manager.register_events(el);
157157
}
158158

159-
mpl.figure.prototype.handle_save = function(fig, msg) {
160-
fig.ondownload(fig, null);
159+
}
160+
161+
mpl.figure.prototype._key_event_extra = function(event, name) {
162+
var manager = IPython.notebook.keyboard_manager;
163+
if (!manager)
164+
manager = IPython.keyboard_manager;
165+
166+
// Check for shift+enter
167+
if (event.shiftKey && event.which == 13) {
168+
this.canvas_div.blur();
169+
event.shiftKey = false;
170+
// Send a "J" for go to next cell
171+
event.which = 74;
172+
event.keyCode = 74;
173+
manager.command_mode();
174+
manager.handle_keydown(event);
175+
}
176+
}
177+
178+
mpl.figure.prototype.handle_save = function(fig, msg) {
179+
fig.ondownload(fig, null);
180+
}
181+
182+
183+
mpl.find_output_cell = function(html_output) {
184+
// Return the cell and output element which can be found *uniquely* in the notebook.
185+
// Note - this is a bit hacky, but it is done because the "notebook_saving.Notebook"
186+
// IPython event is triggered only after the cells have been serialised, which for
187+
// our purposes (turning an active figure into a static one), is too late.
188+
var cells = IPython.notebook.get_cells();
189+
var ncells = cells.length;
190+
for (var i=0; i<ncells; i++) {
191+
var cell = cells[i];
192+
if (cell.cell_type === 'code'){
193+
for (var j=0; j<cell.output_area.outputs.length; j++) {
194+
var data = cell.output_area.outputs[j];
195+
if (data.data) {
196+
// IPython >= 3 moved mimebundle to data attribute of output
197+
data = data.data;
198+
}
199+
if (data['text/html'] == html_output) {
200+
return [cell, data, j];
201+
}
202+
}
203+
}
161204
}
205+
}
162206

163-
return {MPLCanvasView: MPLCanvasView}
164-
});
207+
// Register the function which deals with the matplotlib target/channel.
208+
// The kernel may be null if the page has been refreshed.
209+
if (IPython.notebook.kernel != null) {
210+
IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);
211+
}

0 commit comments

Comments
 (0)