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 5bf037e

Browse filesBrowse files
committed
Merge pull request #5383 from mdboom/hidpi-nbagg
Handle HiDPI displays in WebAgg/NbAgg backends
1 parent 507aca5 commit 5bf037e
Copy full SHA for 5bf037e

File tree

Expand file treeCollapse file tree

4 files changed

+50
-12
lines changed
Filter options
Expand file treeCollapse file tree

4 files changed

+50
-12
lines changed
+5Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
Support for HiDPI (Retina) displays in the NbAgg and WebAgg backends
2+
--------------------------------------------------------------------
3+
4+
The NbAgg and WebAgg backends will now use the full resolution of your
5+
high-pixel-density display.

‎lib/matplotlib/backends/backend_webagg_core.py

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/backend_webagg_core.py
+19-2Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ def __init__(self, *args, **kwargs):
159159
# to the connected clients.
160160
self._current_image_mode = 'full'
161161

162+
# Store the DPI ratio of the browser. This is the scaling that
163+
# occurs automatically for all images on a HiDPI display.
164+
self._dpi_ratio = 1
165+
162166
def show(self):
163167
# show the figure window
164168
from matplotlib.pyplot import show
@@ -334,7 +338,7 @@ def handle_event(self, event):
334338

335339
def handle_resize(self, event):
336340
x, y = event.get('width', 800), event.get('height', 800)
337-
x, y = int(x), int(y)
341+
x, y = int(x) * self._dpi_ratio, int(y) * self._dpi_ratio
338342
fig = self.figure
339343
# An attempt at approximating the figure size in pixels.
340344
fig.set_size_inches(x / fig.dpi, y / fig.dpi, forward=False)
@@ -351,6 +355,17 @@ def handle_send_image_mode(self, event):
351355
# The client requests notification of what the current image mode is.
352356
self.send_event('image_mode', mode=self._current_image_mode)
353357

358+
def handle_set_dpi_ratio(self, event):
359+
dpi_ratio = event.get('dpi_ratio', 1)
360+
if dpi_ratio != self._dpi_ratio:
361+
# We don't want to scale up the figure dpi more than once.
362+
if not hasattr(self.figure, '_original_dpi'):
363+
self.figure._original_dpi = self.figure.dpi
364+
self.figure.dpi = dpi_ratio * self.figure._original_dpi
365+
self._dpi_ratio = dpi_ratio
366+
self._force_full = True
367+
self.draw_idle()
368+
354369
def send_event(self, event_type, **kwargs):
355370
self.manager._send_event(event_type, **kwargs)
356371

@@ -436,7 +451,9 @@ def _get_toolbar(self, canvas):
436451
return toolbar
437452

438453
def resize(self, w, h):
439-
self._send_event('resize', size=(w, h))
454+
self._send_event(
455+
'resize',
456+
size=(w / self.canvas._dpi_ratio, h / self.canvas._dpi_ratio))
440457

441458
def set_window_title(self, title):
442459
self._send_event('figure_label', label=title)

‎lib/matplotlib/backends/web_backend/mpl.js

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/web_backend/mpl.js
+22-8Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* Put everything inside the global mpl namespace */
22
window.mpl = {};
33

4+
45
mpl.get_websocket_type = function() {
56
if (typeof(WebSocket) !== 'undefined') {
67
return WebSocket;
@@ -59,6 +60,9 @@ mpl.figure = function(figure_id, websocket, ondownload, parent_element) {
5960
this.ws.onopen = function () {
6061
fig.send_message("supports_binary", {value: fig.supports_binary});
6162
fig.send_message("send_image_mode", {});
63+
if (mpl.ratio != 1) {
64+
fig.send_message("set_dpi_ratio", {'dpi_ratio': mpl.ratio});
65+
}
6266
fig.send_message("refresh", {});
6367
}
6468

@@ -128,6 +132,15 @@ mpl.figure.prototype._init_canvas = function() {
128132
this.canvas = canvas[0];
129133
this.context = canvas[0].getContext("2d");
130134

135+
var backingStore = this.context.backingStorePixelRatio ||
136+
this.context.webkitBackingStorePixelRatio ||
137+
this.context.mozBackingStorePixelRatio ||
138+
this.context.msBackingStorePixelRatio ||
139+
this.context.oBackingStorePixelRatio ||
140+
this.context.backingStorePixelRatio || 1;
141+
142+
mpl.ratio = (window.devicePixelRatio || 1) / backingStore;
143+
131144
var rubberband = $('<canvas/>');
132145
rubberband.attr('style', "position: absolute; left: 0; top: 0; z-index: 1;")
133146

@@ -184,8 +197,9 @@ mpl.figure.prototype._init_canvas = function() {
184197
canvas_div.css('width', width)
185198
canvas_div.css('height', height)
186199

187-
canvas.attr('width', width);
188-
canvas.attr('height', height);
200+
canvas.attr('width', width * mpl.ratio);
201+
canvas.attr('height', height * mpl.ratio);
202+
canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');
189203

190204
rubberband.attr('width', width);
191205
rubberband.attr('height', height);
@@ -318,10 +332,10 @@ mpl.figure.prototype.handle_resize = function(fig, msg) {
318332
}
319333

320334
mpl.figure.prototype.handle_rubberband = function(fig, msg) {
321-
var x0 = msg['x0'];
322-
var y0 = fig.canvas.height - msg['y0'];
323-
var x1 = msg['x1'];
324-
var y1 = fig.canvas.height - msg['y1'];
335+
var x0 = msg['x0'] / mpl.ratio;
336+
var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;
337+
var x1 = msg['x1'] / mpl.ratio;
338+
var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;
325339
x0 = Math.floor(x0) + 0.5;
326340
y0 = Math.floor(y0) + 0.5;
327341
x1 = Math.floor(x1) + 0.5;
@@ -477,8 +491,8 @@ mpl.figure.prototype.mouse_event = function(event, name) {
477491
this.canvas_div.focus();
478492
}
479493

480-
var x = canvas_pos.x;
481-
var y = canvas_pos.y;
494+
var x = canvas_pos.x * mpl.ratio;
495+
var y = canvas_pos.y * mpl.ratio;
482496

483497
this.send_message(name, {x: x, y: y, button: event.button,
484498
step: event.step,

‎lib/matplotlib/backends/web_backend/nbagg_mpl.js

Copy file name to clipboardExpand all lines: lib/matplotlib/backends/web_backend/nbagg_mpl.js
+4-2Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ mpl.mpl_figure_comm = function(comm, msg) {
5555
};
5656

5757
mpl.figure.prototype.handle_close = function(fig, msg) {
58+
var width = fig.canvas.width/mpl.ratio
5859
fig.root.unbind('remove')
5960

6061
// Update the output cell to use the data from the current canvas.
@@ -63,7 +64,7 @@ mpl.figure.prototype.handle_close = function(fig, msg) {
6364
// Re-enable the keyboard manager in IPython - without this line, in FF,
6465
// the notebook keyboard shortcuts fail.
6566
IPython.keyboard_manager.enable()
66-
$(fig.parent_element).html('<img src="' + dataURL + '">');
67+
$(fig.parent_element).html('<img src="' + dataURL + '" width="' + width + '">');
6768
fig.close_ws(fig, msg);
6869
}
6970

@@ -74,8 +75,9 @@ mpl.figure.prototype.close_ws = function(fig, msg){
7475

7576
mpl.figure.prototype.push_to_output = function(remove_interactive) {
7677
// Turn the data on the canvas into data in the output cell.
78+
var width = this.canvas.width/mpl.ratio
7779
var dataURL = this.canvas.toDataURL();
78-
this.cell_info[1]['text/html'] = '<img src="' + dataURL + '">';
80+
this.cell_info[1]['text/html'] = '<img src="' + dataURL + '" width="' + width + '">';
7981
}
8082

8183
mpl.figure.prototype.updated_canvas_event = function() {

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.