14
14
import sys
15
15
import weakref
16
16
17
- import numpy as np
18
- import PIL.Image
19
-
20
17
import matplotlib as mpl
21
18
from matplotlib.backend_bases import (
22
19
_Backend, FigureCanvasBase, FigureManagerBase,
23
20
GraphicsContextBase, MouseButton, NavigationToolbar2, RendererBase,
24
21
TimerBase, ToolContainerBase, cursors,
25
22
CloseEvent, KeyEvent, LocationEvent, MouseEvent, ResizeEvent)
26
23
27
- from matplotlib import _api, cbook, backend_tools
24
+ from matplotlib import _api, cbook, backend_tools, _c_internal_utils
28
25
from matplotlib._pylab_helpers import Gcf
29
26
from matplotlib.path import Path
30
27
from matplotlib.transforms import Affine2D
31
28
32
29
import wx
30
+ import wx.svg
33
31
34
32
_log = logging.getLogger(__name__)
35
33
@@ -45,6 +43,8 @@ def _create_wxapp():
45
43
wxapp = wx.App(False)
46
44
wxapp.SetExitOnFrameDelete(True)
47
45
cbook._setup_new_guiapp()
46
+ # Set per-process DPI awareness. This is a NoOp except in MSW
47
+ _c_internal_utils.Win32_SetProcessDpiAwareness_max()
48
48
return wxapp
49
49
50
50
@@ -471,12 +471,12 @@ def __init__(self, parent, id, figure=None):
471
471
"""
472
472
473
473
FigureCanvasBase.__init__(self, figure)
474
- w, h = map(math.ceil, self.figure.bbox.size)
474
+ size = wx.Size(*map(math.ceil, self.figure.bbox.size))
475
+ if wx.Platform != '__WXMSW__':
476
+ size = parent.FromDIP(size)
475
477
# 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)
478
+ wx.Panel.__init__(self, parent, id, size=size)
479
+ self.bitmap = None
480
480
self._isDrawn = False
481
481
self._rubberband_rect = None
482
482
self._rubberband_pen_black = wx.Pen('BLACK', 1, wx.PENSTYLE_SHORT_DASH)
@@ -512,6 +512,12 @@ def __init__(self, parent, id, figure=None):
512
512
self.SetBackgroundStyle(wx.BG_STYLE_PAINT) # Reduce flicker.
513
513
self.SetBackgroundColour(wx.WHITE)
514
514
515
+ if wx.Platform == '__WXMAC__':
516
+ # Initial scaling. Other platforms handle this automatically
517
+ dpiScale = self.GetDPIScaleFactor()
518
+ self.SetInitialSize(self.GetSize()*(1/dpiScale))
519
+ self._set_device_pixel_ratio(dpiScale)
520
+
515
521
def Copy_to_Clipboard(self, event=None):
516
522
"""Copy bitmap of canvas to system clipboard."""
517
523
bmp_obj = wx.BitmapDataObject()
@@ -524,6 +530,12 @@ def Copy_to_Clipboard(self, event=None):
524
530
wx.TheClipboard.Flush()
525
531
wx.TheClipboard.Close()
526
532
533
+ def _update_device_pixel_ratio(self, *args, **kwargs):
534
+ # We need to be careful in cases with mixed resolution displays if
535
+ # device_pixel_ratio changes.
536
+ if self._set_device_pixel_ratio(self.GetDPIScaleFactor()):
537
+ self.draw()
538
+
527
539
def draw_idle(self):
528
540
# docstring inherited
529
541
_log.debug("%s - draw_idle()", type(self))
@@ -631,7 +643,7 @@ def _on_size(self, event):
631
643
In this application we attempt to resize to fit the window, so it
632
644
is better to take the performance hit and redraw the whole window.
633
645
"""
634
-
646
+ self._update_device_pixel_ratio()
635
647
_log.debug("%s - _on_size()", type(self))
636
648
sz = self.GetParent().GetSizer()
637
649
if sz:
@@ -655,9 +667,10 @@ def _on_size(self, event):
655
667
return # Empty figure
656
668
657
669
# Create a new, correctly sized bitmap
658
- self.bitmap = wx.Bitmap(self._width, self._height)
659
-
660
670
dpival = self.figure.dpi
671
+ if not wx.Platform == '__WXMSW__':
672
+ scale = self.GetDPIScaleFactor()
673
+ dpival /= scale
661
674
winch = self._width / dpival
662
675
hinch = self._height / dpival
663
676
self.figure.set_size_inches(winch, hinch, forward=False)
@@ -712,7 +725,11 @@ def _mpl_coords(self, pos=None):
712
725
else:
713
726
x, y = pos.X, pos.Y
714
727
# flip y so y=0 is bottom of canvas
715
- return x, self.figure.bbox.height - y
728
+ if not wx.Platform == '__WXMSW__':
729
+ scale = self.GetDPIScaleFactor()
730
+ return x*scale, self.figure.bbox.height - y*scale
731
+ else:
732
+ return x, self.figure.bbox.height - y
716
733
717
734
def _on_key_down(self, event):
718
735
"""Capture key press."""
@@ -898,8 +915,8 @@ def __init__(self, num, fig, *, canvas_class):
898
915
# On Windows, canvas sizing must occur after toolbar addition;
899
916
# otherwise the toolbar further resizes the canvas.
900
917
w, h = map(math.ceil, fig.bbox.size)
901
- self.canvas.SetInitialSize(wx.Size(w, h))
902
- self.canvas.SetMinSize(( 2, 2))
918
+ self.canvas.SetInitialSize(self.FromDIP( wx.Size(w, h) ))
919
+ self.canvas.SetMinSize(self.FromDIP(wx.Size( 2, 2) ))
903
920
self.canvas.SetFocus()
904
921
905
922
self.Fit()
@@ -1017,9 +1034,9 @@ def _set_frame_icon(frame):
1017
1034
class NavigationToolbar2Wx(NavigationToolbar2, wx.ToolBar):
1018
1035
def __init__(self, canvas, coordinates=True, *, style=wx.TB_BOTTOM):
1019
1036
wx.ToolBar.__init__(self, canvas.GetParent(), -1, style=style)
1037
+ if wx.Platform == '__WXMAC__':
1038
+ self.SetToolBitmapSize(self.GetToolBitmapSize()*self.GetDPIScaleFactor())
1020
1039
1021
- if 'wxMac' in wx.PlatformInfo:
1022
- self.SetToolBitmapSize((24, 24))
1023
1040
self.wx_ids = {}
1024
1041
for text, tooltip_text, image_file, callback in self.toolitems:
1025
1042
if text is None:
@@ -1028,7 +1045,7 @@ def __init__(self, canvas, coordinates=True, *, style=wx.TB_BOTTOM):
1028
1045
self.wx_ids[text] = (
1029
1046
self.AddTool(
1030
1047
-1,
1031
- bitmap=self._icon(f"{image_file}.png "),
1048
+ bitmap=self._icon(f"{image_file}.svg "),
1032
1049
bmpDisabled=wx.NullBitmap,
1033
1050
label=text, shortHelp=tooltip_text,
1034
1051
kind=(wx.ITEM_CHECK if text in ["Pan", "Zoom"]
@@ -1054,9 +1071,7 @@ def _icon(name):
1054
1071
*name*, including the extension and relative to Matplotlib's "images"
1055
1072
data directory.
1056
1073
"""
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"))
1074
+ svg = cbook._get_data_path("images", name).read_bytes()
1060
1075
try:
1061
1076
dark = wx.SystemSettings.GetAppearance().IsDark()
1062
1077
except AttributeError: # wxpython < 4.1
@@ -1068,11 +1083,9 @@ def _icon(name):
1068
1083
fg_lum = (.299 * fg.red + .587 * fg.green + .114 * fg.blue) / 255
1069
1084
dark = fg_lum - bg_lum > .2
1070
1085
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())
1086
+ svg = svg.replace(b'fill:black;', b'fill:white;')
1087
+ toolbarIconSize = wx.ArtProvider().GetDIPSizeHint(wx.ART_TOOLBAR)
1088
+ return wx.BitmapBundle.FromSVG(svg, toolbarIconSize)
1076
1089
1077
1090
def _update_buttons_checked(self):
1078
1091
if "Pan" in self.wx_ids:
@@ -1123,7 +1136,9 @@ def save_figure(self, *args):
1123
1136
1124
1137
def draw_rubberband(self, event, x0, y0, x1, y1):
1125
1138
height = self.canvas.figure.bbox.height
1126
- self.canvas._rubberband_rect = (x0, height - y0, x1, height - y1)
1139
+ sf = 1 if wx.Platform == '__WXMSW__' else self.GetDPIScaleFactor()
1140
+ self.canvas._rubberband_rect = (x0/sf, (height - y0)/sf,
1141
+ x1/sf, (height - y1)/sf)
1127
1142
self.canvas.Refresh()
1128
1143
1129
1144
def remove_rubberband(self):
0 commit comments