11import math
22import sys
33
4- from matplotlib .backend_bases import FigureCanvasBase , RendererBase
4+ from matplotlib .backend_bases import RendererBase
55from matplotlib .figure import Figure
66from matplotlib .path import Path
77from matplotlib .transforms import Affine2D
1212
1313
1414class Chart (Widget ):
15- """Create new chart.
16-
17- Args:
18- id (str): An identifier for this widget.
19- style (:obj:`Style`): An optional style object. If no
20- style is provided then a new one will be created for the widget.
21- on_resize (:obj:`callable`): Handler to invoke when the chart is resized.
22- The default resize handler will draw the chart on every resize;
23- generally, you won't need to override this default behavior.
24- on_draw (:obj:`callable`): Handler to invoke when the chart needs to be
25- drawn.
26- factory (:obj:`module`): A python module that is capable to return a
27- implementation of this class with the same name. (optional &
28- normally not needed)
29- """
30-
31- def __init__ (self , id = None , style = None , on_resize = None , on_draw = None , factory = None ):
15+ def __init__ (
16+ self ,
17+ id : str = None ,
18+ style = None ,
19+ on_resize : callable = None ,
20+ on_draw : callable = None ,
21+ ):
22+ """Create a new matplotlib chart.
23+
24+ :param id: An identifier for this widget.
25+ :param style: An optional style object. If no style is provided then a new one
26+ will be created for the widget.
27+ :param on_resize: Handler to invoke when the chart is resized. The default
28+ resize handler will draw the chart on every resize; generally, you won't
29+ need to override this default behavior.
30+ :param on_draw: Handler to invoke when the chart needs to be drawn. This
31+ performs the matplotlib drawing operations that will be displayed on the
32+ chart.
33+ """
3234 self .on_draw = on_draw
3335 if on_resize is None :
3436 on_resize = self ._on_resize
3537
36- self .canvas = Canvas (style = style , on_resize = on_resize , factory = factory )
38+ # The Chart widget that the user interacts with is a subclass of Widget, not
39+ # Canvas; this subclass acts as a facade over the underlying Canvas
40+ # implementation (mostly so that the redraw() method of the Chart is independent
41+ # of the Canvas redraw() method). The _impl of the Chart is set to the Canvas
42+ # _impl so that functionally, the widget behaves as a Canvas.
43+ self .canvas = Canvas (style = style , on_resize = on_resize )
44+
45+ super ().__init__ (id = id , style = style )
3746
38- super ().__init__ (id = id , style = style , factory = factory )
3947 self ._impl = self .canvas ._impl
4048
41- def _set_app (self , app ):
49+ @Widget .app .setter
50+ def app (self , app ):
51+ # Invoke the superclass property setter
52+ Widget .app .fset (self , app )
53+ # Point the canvas to the same app
4254 self .canvas .app = app
4355
44- def _set_window (self , window ):
56+ @Widget .window .setter
57+ def window (self , window ):
58+ # Invoke the superclass property setter
59+ Widget .window .fset (self , window )
60+ # Point the canvas to the same window
4561 self .canvas .window = window
4662
4763 @property
@@ -52,22 +68,19 @@ def layout(self):
5268 def layout (self , value ):
5369 self .canvas .layout = value
5470
55- def _draw (self , figure ):
56- """Draws the matplotlib figure onto the canvas
71+ def _draw (self , figure : Figure ):
72+ """Draw the matplotlib figure onto the canvas.
5773
58- Args:
59- figure (figure): matplotlib figure to draw
74+ :param figure: The matplotlib figure to draw
6075 """
6176 l , b , w , h = figure .bbox .bounds
62- matplotlib_canvas = MatplotlibCanvasProxy (figure = figure , canvas = self .canvas )
63- renderer = ChartRenderer (matplotlib_canvas , w , h )
77+ renderer = ChartRenderer (self .canvas , w , h )
6478
65- # Invoke the on_draw handler (if present) .
79+ # Invoke the on_draw handler.
6680 # This is where the user adds the matplotlib draw instructions
6781 # to construct the chart, so it needs to happen before the
6882 # figure is rendered onto the canvas.
69- if self .on_draw :
70- self .on_draw (self , figure = figure )
83+ self .on_draw (figure = figure )
7184
7285 figure .draw (renderer )
7386
@@ -79,66 +92,32 @@ def redraw(self):
7992 # 100 is the default DPI for figure at time of writing.
8093 dpi = 100
8194 figure = Figure (
82- figsize = (self .layout .content_width / dpi , self .layout .content_height / dpi )
95+ figsize = (
96+ self .layout .content_width / dpi ,
97+ self .layout .content_height / dpi ,
98+ ),
8399 )
84100 self ._draw (figure )
85101
86102 @property
87- def on_draw (self ):
88- """The handler to invoke when the canvas needs to be drawn.
89-
90- Returns:
91- The handler that is invoked on canvas draw.
92- """
103+ def on_draw (self ) -> callable :
104+ """The handler to invoke when the canvas needs to be drawn."""
93105 return self ._on_draw
94106
95107 @on_draw .setter
96- def on_draw (self , handler ):
97- """Set the handler to invoke when the canvas is drawn.
98-
99- Args:
100- handler (:obj:`callable`): The handler to invoke when the canvas is drawn.
101- """
108+ def on_draw (self , handler : callable ):
102109 self ._on_draw = wrapped_handler (self , handler )
103110
104111
105- class MatplotlibCanvasProxy (FigureCanvasBase ):
106- def __init__ (self , figure , canvas : Canvas ):
107- super ().__init__ (figure )
108- self .canvas = canvas
109-
110- def fill (self , color ):
111- return self .canvas .fill (color = color )
112-
113- def stroke (self , color , line_width , line_dash ):
114- return self .canvas .stroke (
115- color = color , line_width = line_width , line_dash = line_dash
116- )
117-
118- def measure_text (self , text , font ):
119- return self .canvas .measure_text (text = text , font = font )
120-
121- def translate (self , tx , ty ):
122- return self .canvas .translate (tx , ty )
123-
124- def rotate (self , radians ):
125- return self .canvas .rotate (radians )
126-
127- def reset_transform (self ):
128- return self .canvas .reset_transform ()
129-
130-
131112class ChartRenderer (RendererBase ):
132- """
133- The renderer handles drawing/rendering operations.
134-
135- Args:
136- canvas (:obj:`Canvas`): canvas to render onto
137- width (int): width of canvas
138- height (int): height of canvas
139- """
113+ def __init__ (self , canvas : Canvas , width : int , height : int ):
114+ """
115+ The matplotlib handler for drawing/rendering operations.
140116
141- def __init__ (self , canvas , width , height ):
117+ :param canvas: The canvas to render onto
118+ :param width: Width of canvas
119+ :param height: height of canvas
120+ """
142121 self .width = width
143122 self .height = height
144123 self ._canvas = canvas
@@ -157,25 +136,30 @@ def draw_path(self, gc, path, transform, rgbFace=None):
157136 color = parse_color (rgba (r * 255 , g * 255 , b * 255 , a ))
158137
159138 if rgbFace is not None :
160- stroke_fill_context = self ._canvas .fill (color = color )
139+ stroke_fill_context = self ._canvas .context . Fill (color = color )
161140 else :
162141 offset , sequence = gc .get_dashes ()
163- stroke_fill_context = self ._canvas .stroke (
164- color = color , line_width = gc .get_linewidth (), line_dash = sequence
142+ stroke_fill_context = self ._canvas .context .Stroke (
143+ color = color ,
144+ line_width = gc .get_linewidth (),
145+ line_dash = sequence ,
165146 )
166147
167148 transform = transform + Affine2D ().scale (1.0 , - 1.0 ).translate (0.0 , self .height )
168149
169150 with stroke_fill_context as context :
170- with context .context () as path_segments :
151+ with context .Context () as path_segments :
171152 for points , code in path .iter_segments (transform ):
172153 if code == Path .MOVETO :
173154 path_segments .move_to (points [0 ], points [1 ])
174155 elif code == Path .LINETO :
175156 path_segments .line_to (points [0 ], points [1 ])
176157 elif code == Path .CURVE3 :
177158 path_segments .quadratic_curve_to (
178- points [0 ], points [1 ], points [2 ], points [3 ]
159+ points [0 ],
160+ points [1 ],
161+ points [2 ],
162+ points [3 ],
179163 )
180164 elif code == Path .CURVE4 :
181165 path_segments .bezier_curve_to (
@@ -187,7 +171,7 @@ def draw_path(self, gc, path, transform, rgbFace=None):
187171 points [5 ],
188172 )
189173 elif code == Path .CLOSEPOLY :
190- path_segments .closed_path (points [0 ], points [1 ])
174+ path_segments .ClosedPath (points [0 ], points [1 ])
191175
192176 def draw_image (self , gc , x , y , im ):
193177 pass
@@ -217,12 +201,14 @@ def draw_text(self, gc, x, y, s, prop, angle, ismath=False, mtext=None):
217201 gc .set_linewidth (0.75 )
218202 self .draw_path (gc , path , transform , rgbFace = color )
219203 else :
220- self ._canvas .translate (x , y )
221- self ._canvas .rotate (- math .radians (angle ))
222- with self ._canvas .fill (color = self .to_toga_color (* gc .get_rgb ())) as fill :
204+ self ._canvas .context .translate (x , y )
205+ self ._canvas .context .rotate (- math .radians (angle ))
206+ with self ._canvas .context .Fill (
207+ color = self .to_toga_color (* gc .get_rgb ())
208+ ) as fill :
223209 font = self .get_font (prop )
224210 fill .write_text (s , x = 0 , y = 0 , font = font )
225- self ._canvas .reset_transform ()
211+ self ._canvas .context . reset_transform ()
226212
227213 def flipy (self ):
228214 return True
@@ -231,23 +217,16 @@ def get_canvas_width_height(self):
231217 return self .width , self .height
232218
233219 def get_text_width_height_descent (self , s , prop , ismath ):
234- """
235- get the width and height in display coords of the string s
236- with FontPropertry prop
220+ """Get the width and height in display coords of the string s
221+ with FontProperty prop
237222 """
238223 font = self .get_font (prop )
239224 w , h = self ._canvas .measure_text (s , font )
240225 return w , h , 1
241226
242227 def get_font (self , prop ):
243- if prop .get_family ()[0 ] == SANS_SERIF :
244- font_family = SANS_SERIF
245- elif prop .get_family ()[0 ] == CURSIVE :
246- font_family = CURSIVE
247- elif prop .get_family ()[0 ] == FANTASY :
248- font_family = FANTASY
249- elif prop .get_family ()[0 ] == MONOSPACE :
250- font_family = MONOSPACE
228+ if prop .get_family ()[0 ] in {SANS_SERIF , CURSIVE , FANTASY , MONOSPACE }:
229+ font_family = prop .get_family ()[0 ]
251230 else :
252231 font_family = SERIF
253232
0 commit comments