Skip to content

Commit 1b50814

Browse files
committed
Add support for High DPI displays to wxAgg backend
1 parent 5f785e3 commit 1b50814

File tree

2 files changed

+34
-32
lines changed

2 files changed

+34
-32
lines changed

lib/matplotlib/backends/backend_wx.py

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,6 @@
1414
import sys
1515
import weakref
1616

17-
import numpy as np
18-
import PIL.Image
19-
2017
import matplotlib as mpl
2118
from matplotlib.backend_bases import (
2219
_Backend, FigureCanvasBase, FigureManagerBase,
@@ -30,6 +27,7 @@
3027
from matplotlib.transforms import Affine2D
3128

3229
import wx
30+
import wx.svg
3331

3432
_log = logging.getLogger(__name__)
3533

@@ -473,10 +471,8 @@ def __init__(self, parent, id, figure=None):
473471
FigureCanvasBase.__init__(self, figure)
474472
w, h = map(math.ceil, self.figure.bbox.size)
475473
# Set preferred window size hint - helps the sizer, if one is connected
476-
wx.Panel.__init__(self, parent, id, size=wx.Size(w, h))
477-
# Create the drawing bitmap
478-
self.bitmap = wx.Bitmap(w, h)
479-
_log.debug("%s - __init__() - bitmap w:%d h:%d", type(self), w, h)
474+
wx.Panel.__init__(self, parent, id, size=parent.FromDIP(wx.Size(w, h)))
475+
self.bitmap = None
480476
self._isDrawn = False
481477
self._rubberband_rect = None
482478
self._rubberband_pen_black = wx.Pen('BLACK', 1, wx.PENSTYLE_SHORT_DASH)
@@ -524,6 +520,12 @@ def Copy_to_Clipboard(self, event=None):
524520
wx.TheClipboard.Flush()
525521
wx.TheClipboard.Close()
526522

523+
def _update_device_pixel_ratio(self, *args, **kwargs):
524+
# We need to be careful in cases with mixed resolution displays if
525+
# device_pixel_ratio changes.
526+
if self._set_device_pixel_ratio(self.GetDPIScaleFactor()):
527+
self.draw()
528+
527529
def draw_idle(self):
528530
# docstring inherited
529531
_log.debug("%s - draw_idle()", type(self))
@@ -631,7 +633,7 @@ def _on_size(self, event):
631633
In this application we attempt to resize to fit the window, so it
632634
is better to take the performance hit and redraw the whole window.
633635
"""
634-
636+
self._update_device_pixel_ratio()
635637
_log.debug("%s - _on_size()", type(self))
636638
sz = self.GetParent().GetSizer()
637639
if sz:
@@ -655,9 +657,10 @@ def _on_size(self, event):
655657
return # Empty figure
656658

657659
# Create a new, correctly sized bitmap
658-
self.bitmap = wx.Bitmap(self._width, self._height)
659-
660660
dpival = self.figure.dpi
661+
if not wx.Platform == '__WXMSW__':
662+
scale = self.GetDPIScaleFactor()
663+
dpival /= scale
661664
winch = self._width / dpival
662665
hinch = self._height / dpival
663666
self.figure.set_size_inches(winch, hinch, forward=False)
@@ -712,7 +715,11 @@ def _mpl_coords(self, pos=None):
712715
else:
713716
x, y = pos.X, pos.Y
714717
# flip y so y=0 is bottom of canvas
715-
return x, self.figure.bbox.height - y
718+
if not wx.Platform == '__WXMSW__':
719+
scale = self.GetDPIScaleFactor()
720+
return x*scale, self.figure.bbox.height - y*scale
721+
else:
722+
return x, self.figure.bbox.height - y
716723

717724
def _on_key_down(self, event):
718725
"""Capture key press."""
@@ -898,8 +905,8 @@ def __init__(self, num, fig, *, canvas_class):
898905
# On Windows, canvas sizing must occur after toolbar addition;
899906
# otherwise the toolbar further resizes the canvas.
900907
w, h = map(math.ceil, fig.bbox.size)
901-
self.canvas.SetInitialSize(wx.Size(w, h))
902-
self.canvas.SetMinSize((2, 2))
908+
self.canvas.SetInitialSize(self.FromDIP(wx.Size(w, h)))
909+
self.canvas.SetMinSize(self.FromDIP(wx.Size(2, 2)))
903910
self.canvas.SetFocus()
904911

905912
self.Fit()
@@ -1017,9 +1024,6 @@ def _set_frame_icon(frame):
10171024
class NavigationToolbar2Wx(NavigationToolbar2, wx.ToolBar):
10181025
def __init__(self, canvas, coordinates=True, *, style=wx.TB_BOTTOM):
10191026
wx.ToolBar.__init__(self, canvas.GetParent(), -1, style=style)
1020-
1021-
if 'wxMac' in wx.PlatformInfo:
1022-
self.SetToolBitmapSize((24, 24))
10231027
self.wx_ids = {}
10241028
for text, tooltip_text, image_file, callback in self.toolitems:
10251029
if text is None:
@@ -1028,7 +1032,7 @@ def __init__(self, canvas, coordinates=True, *, style=wx.TB_BOTTOM):
10281032
self.wx_ids[text] = (
10291033
self.AddTool(
10301034
-1,
1031-
bitmap=self._icon(f"{image_file}.png"),
1035+
bitmap=self._icon(f"{image_file}.svg"),
10321036
bmpDisabled=wx.NullBitmap,
10331037
label=text, shortHelp=tooltip_text,
10341038
kind=(wx.ITEM_CHECK if text in ["Pan", "Zoom"]
@@ -1054,9 +1058,7 @@ def _icon(name):
10541058
*name*, including the extension and relative to Matplotlib's "images"
10551059
data directory.
10561060
"""
1057-
pilimg = PIL.Image.open(cbook._get_data_path("images", name))
1058-
# ensure RGBA as wx BitMap expects RGBA format
1059-
image = np.array(pilimg.convert("RGBA"))
1061+
svg = cbook._get_data_path("images", name).read_bytes()
10601062
try:
10611063
dark = wx.SystemSettings.GetAppearance().IsDark()
10621064
except AttributeError: # wxpython < 4.1
@@ -1068,11 +1070,9 @@ def _icon(name):
10681070
fg_lum = (.299 * fg.red + .587 * fg.green + .114 * fg.blue) / 255
10691071
dark = fg_lum - bg_lum > .2
10701072
if dark:
1071-
fg = wx.SystemSettings.GetColour(wx.SYS_COLOUR_WINDOWTEXT)
1072-
black_mask = (image[..., :3] == 0).all(axis=-1)
1073-
image[black_mask, :3] = (fg.Red(), fg.Green(), fg.Blue())
1074-
return wx.Bitmap.FromBufferRGBA(
1075-
image.shape[1], image.shape[0], image.tobytes())
1073+
svg = svg.replace(b'fill:black;', b'fill:white;')
1074+
toolbarIconSize = wx.ArtProvider().GetDIPSizeHint(wx.ART_TOOLBAR)
1075+
return wx.BitmapBundle.FromSVG(svg, toolbarIconSize)
10761076

10771077
def _update_buttons_checked(self):
10781078
if "Pan" in self.wx_ids:

lib/matplotlib/backends/backend_wxagg.py

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@ def draw(self, drawDC=None):
1212
Render the figure using agg.
1313
"""
1414
FigureCanvasAgg.draw(self)
15-
self.bitmap = _rgba_to_wx_bitmap(self.get_renderer().buffer_rgba())
15+
self.bitmap = self._create_bitmap()
1616
self._isDrawn = True
1717
self.gui_repaint(drawDC=drawDC)
1818

1919
def blit(self, bbox=None):
2020
# docstring inherited
21-
bitmap = _rgba_to_wx_bitmap(self.get_renderer().buffer_rgba())
21+
bitmap = self._create_bitmap()
2222
if bbox is None:
2323
self.bitmap = bitmap
2424
else:
@@ -31,11 +31,13 @@ def blit(self, bbox=None):
3131
srcDC.SelectObject(wx.NullBitmap)
3232
self.gui_repaint()
3333

34-
35-
def _rgba_to_wx_bitmap(rgba):
36-
"""Convert an RGBA buffer to a wx.Bitmap."""
37-
h, w, _ = rgba.shape
38-
return wx.Bitmap.FromBufferRGBA(w, h, rgba)
34+
def _create_bitmap(self):
35+
"""Create a wx.Bitmap from the renderer RGBA buffer"""
36+
rgba = self.get_renderer().buffer_rgba()
37+
h, w, _ = rgba.shape
38+
bitmap = wx.Bitmap.FromBufferRGBA(w, h, rgba)
39+
bitmap.SetScaleFactor(self.GetDPIScaleFactor())
40+
return bitmap
3941

4042

4143
@_BackendWx.export

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy