1414import sys
1515import weakref
1616
17- import numpy as np
18- import PIL .Image
19-
2017import matplotlib as mpl
2118from matplotlib .backend_bases import (
2219 _Backend , FigureCanvasBase , FigureManagerBase ,
2320 GraphicsContextBase , MouseButton , NavigationToolbar2 , RendererBase ,
2421 TimerBase , ToolContainerBase , cursors ,
2522 CloseEvent , KeyEvent , LocationEvent , MouseEvent , ResizeEvent )
2623
27- from matplotlib import _api , cbook , backend_tools
24+ from matplotlib import _api , cbook , backend_tools , _c_internal_utils
2825from matplotlib ._pylab_helpers import Gcf
2926from matplotlib .path import Path
3027from matplotlib .transforms import Affine2D
3128
3229import wx
30+ import wx .svg
3331
3432_log = logging .getLogger (__name__ )
3533
@@ -45,6 +43,8 @@ def _create_wxapp():
4543 wxapp = wx .App (False )
4644 wxapp .SetExitOnFrameDelete (True )
4745 cbook ._setup_new_guiapp ()
46+ # Set per-process DPI awareness. This is a NoOp except in MSW
47+ _c_internal_utils .Win32_SetProcessDpiAwareness_max ()
4848 return wxapp
4949
5050
@@ -471,12 +471,12 @@ def __init__(self, parent, id, figure=None):
471471 """
472472
473473 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 )
475477 # 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
480480 self ._isDrawn = False
481481 self ._rubberband_rect = None
482482 self ._rubberband_pen_black = wx .Pen ('BLACK' , 1 , wx .PENSTYLE_SHORT_DASH )
@@ -512,6 +512,12 @@ def __init__(self, parent, id, figure=None):
512512 self .SetBackgroundStyle (wx .BG_STYLE_PAINT ) # Reduce flicker.
513513 self .SetBackgroundColour (wx .WHITE )
514514
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+
515521 def Copy_to_Clipboard (self , event = None ):
516522 """Copy bitmap of canvas to system clipboard."""
517523 bmp_obj = wx .BitmapDataObject ()
@@ -524,6 +530,12 @@ def Copy_to_Clipboard(self, event=None):
524530 wx .TheClipboard .Flush ()
525531 wx .TheClipboard .Close ()
526532
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+
527539 def draw_idle (self ):
528540 # docstring inherited
529541 _log .debug ("%s - draw_idle()" , type (self ))
@@ -631,7 +643,7 @@ def _on_size(self, event):
631643 In this application we attempt to resize to fit the window, so it
632644 is better to take the performance hit and redraw the whole window.
633645 """
634-
646+ self . _update_device_pixel_ratio ()
635647 _log .debug ("%s - _on_size()" , type (self ))
636648 sz = self .GetParent ().GetSizer ()
637649 if sz :
@@ -655,9 +667,10 @@ def _on_size(self, event):
655667 return # Empty figure
656668
657669 # Create a new, correctly sized bitmap
658- self .bitmap = wx .Bitmap (self ._width , self ._height )
659-
660670 dpival = self .figure .dpi
671+ if not wx .Platform == '__WXMSW__' :
672+ scale = self .GetDPIScaleFactor ()
673+ dpival /= scale
661674 winch = self ._width / dpival
662675 hinch = self ._height / dpival
663676 self .figure .set_size_inches (winch , hinch , forward = False )
@@ -712,7 +725,11 @@ def _mpl_coords(self, pos=None):
712725 else :
713726 x , y = pos .X , pos .Y
714727 # 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
716733
717734 def _on_key_down (self , event ):
718735 """Capture key press."""
@@ -898,8 +915,8 @@ def __init__(self, num, fig, *, canvas_class):
898915 # On Windows, canvas sizing must occur after toolbar addition;
899916 # otherwise the toolbar further resizes the canvas.
900917 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 ) ))
903920 self .canvas .SetFocus ()
904921
905922 self .Fit ()
@@ -1017,9 +1034,9 @@ def _set_frame_icon(frame):
10171034class NavigationToolbar2Wx (NavigationToolbar2 , wx .ToolBar ):
10181035 def __init__ (self , canvas , coordinates = True , * , style = wx .TB_BOTTOM ):
10191036 wx .ToolBar .__init__ (self , canvas .GetParent (), - 1 , style = style )
1037+ if wx .Platform == '__WXMAC__' :
1038+ self .SetToolBitmapSize (self .GetToolBitmapSize ()* self .GetDPIScaleFactor ())
10201039
1021- if 'wxMac' in wx .PlatformInfo :
1022- self .SetToolBitmapSize ((24 , 24 ))
10231040 self .wx_ids = {}
10241041 for text , tooltip_text , image_file , callback in self .toolitems :
10251042 if text is None :
@@ -1028,7 +1045,7 @@ def __init__(self, canvas, coordinates=True, *, style=wx.TB_BOTTOM):
10281045 self .wx_ids [text ] = (
10291046 self .AddTool (
10301047 - 1 ,
1031- bitmap = self ._icon (f"{ image_file } .png " ),
1048+ bitmap = self ._icon (f"{ image_file } .svg " ),
10321049 bmpDisabled = wx .NullBitmap ,
10331050 label = text , shortHelp = tooltip_text ,
10341051 kind = (wx .ITEM_CHECK if text in ["Pan" , "Zoom" ]
@@ -1054,9 +1071,7 @@ def _icon(name):
10541071 *name*, including the extension and relative to Matplotlib's "images"
10551072 data directory.
10561073 """
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 ()
10601075 try :
10611076 dark = wx .SystemSettings .GetAppearance ().IsDark ()
10621077 except AttributeError : # wxpython < 4.1
@@ -1068,11 +1083,9 @@ def _icon(name):
10681083 fg_lum = (.299 * fg .red + .587 * fg .green + .114 * fg .blue ) / 255
10691084 dark = fg_lum - bg_lum > .2
10701085 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 )
10761089
10771090 def _update_buttons_checked (self ):
10781091 if "Pan" in self .wx_ids :
@@ -1123,7 +1136,9 @@ def save_figure(self, *args):
11231136
11241137 def draw_rubberband (self , event , x0 , y0 , x1 , y1 ):
11251138 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 )
11271142 self .canvas .Refresh ()
11281143
11291144 def remove_rubberband (self ):
0 commit comments