Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 0ced8c1

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

File tree

Expand file treeCollapse file tree

6 files changed

+191
-355
lines changed
Filter options
Expand file treeCollapse file tree

6 files changed

+191
-355
lines changed

‎lib/matplotlib/backends/backend_nbagg.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_nbagg.py
+1-1Lines 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

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_webagg_core.py
+1-1Lines 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

+189-142Lines 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 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.