diff --git a/lib/matplotlib/backend_bases.py b/lib/matplotlib/backend_bases.py index 8d70c5ca0841..e3f8865fe04f 100644 --- a/lib/matplotlib/backend_bases.py +++ b/lib/matplotlib/backend_bases.py @@ -3125,7 +3125,7 @@ def draw(self): for loc in locators: loc.refresh() - self.canvas.draw() + self.canvas.draw_idle() def _update_view(self): """Update the viewlim and position from the view and @@ -3146,7 +3146,7 @@ def _update_view(self): a.set_position(pos[i][0], 'original') a.set_position(pos[i][1], 'active') - self.draw() + self.draw_idle() def save_figure(self, *args): """Save the current figure""" diff --git a/lib/matplotlib/backends/backend_webagg.py b/lib/matplotlib/backends/backend_webagg.py index 931cddb900c2..5b73fcab9963 100644 --- a/lib/matplotlib/backends/backend_webagg.py +++ b/lib/matplotlib/backends/backend_webagg.py @@ -253,7 +253,6 @@ def handle_event(self, event): elif e_type == 'key_release': self.key_release_event(key) elif e_type == 'toolbar_button': - print('Toolbar button pressed: ', event['name']) # TODO: Be more suspicious of the input getattr(self.toolbar, event['name'])() elif e_type == 'refresh': @@ -296,8 +295,10 @@ def remove_web_socket(self, web_socket): self.web_sockets.remove(web_socket) def refresh_all(self): - for s in self.web_sockets: - s.send_image() + if self.web_sockets: + diff = self.canvas.get_diff_image() + for s in self.web_sockets: + s.send_diff_image(diff) def send_event(self, event_type, **kwargs): for s in self.web_sockets: @@ -473,6 +474,8 @@ def open(self, fignum): _, _, w, h = manager.canvas.figure.bbox.bounds manager.resize(w, h) self.on_message('{"type":"refresh"}') + if hasattr(self, 'set_nodelay'): + self.set_nodelay(True) def on_close(self): Gcf.get_fig_manager(self.fignum).remove_web_socket(self) @@ -484,6 +487,15 @@ def on_message(self, message): # whole. if message['type'] == 'supports_binary': self.supports_binary = message['value'] + elif message['type'] == 'ack': + # Network latency tends to decrease if traffic is + # flowing in both directions. Therefore, the browser + # sends back an "ack" message after each image frame + # is received. This could also be used as a simple + # sanity check in the future, but for now the + # performance increase is enough to justify it, even + # if the server does nothing with it. + pass else: canvas = Gcf.get_fig_manager(self.fignum).canvas canvas.handle_event(message) @@ -493,9 +505,7 @@ def send_event(self, event_type, **kwargs): payload.update(kwargs) self.write_message(json.dumps(payload)) - def send_image(self): - canvas = Gcf.get_fig_manager(self.fignum).canvas - diff = canvas.get_diff_image() + def send_diff_image(self, diff): if self.supports_binary: self.write_message(diff, binary=True) else: diff --git a/lib/matplotlib/backends/web_backend/mpl.js b/lib/matplotlib/backends/web_backend/mpl.js index 5056d8150b7b..f9d9761801cc 100644 --- a/lib/matplotlib/backends/web_backend/mpl.js +++ b/lib/matplotlib/backends/web_backend/mpl.js @@ -66,6 +66,11 @@ figure.prototype.finalize = function (canvas_id_prefix, toolbar_id_prefix, messa onload_creator = function(fig) {return function() {fig.context.drawImage(fig.imageObj, 0, 0);};}; this.imageObj.onload = onload_creator(fig); + + this.imageObj.onunload = function() { + this.ws.close(); + } + this.ws.onmessage = gen_on_msg_fn(this); }; @@ -88,11 +93,13 @@ function gen_on_msg_fn(fig) } fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL( evt.data); + fig.ws.send('{"type": "ack"}') return; } } else { if (evt.data.slice(0, 21) == "data:image/png;base64") { fig.imageObj.src = evt.data; + fig.ws.send('{"type": "ack"}') return; } } @@ -132,6 +139,9 @@ function gen_on_msg_fn(fig) fig.rubberband_canvas.width = size[0]; fig.rubberband_canvas.height = size[1]; fig.ws.send(JSON.stringify({type: 'refresh'})); + fig.ws.send(JSON.stringify( + {type: 'supports_binary', + value: fig.supports_binary})); } break;