@@ -60,6 +60,8 @@ def __init__(self):
60
60
self .mpl_x_bounds = (0 , 1 )
61
61
self .mpl_y_bounds = (0 , 1 )
62
62
self .msg = "Initialized PlotlyRenderer\n "
63
+ self ._processing_legend = False
64
+ self ._legend_visible = False
63
65
64
66
def open_figure (self , fig , props ):
65
67
"""Creates a new figure by beginning to fill out layout dict.
@@ -108,7 +110,6 @@ def close_figure(self, fig):
108
110
fig -- a matplotlib.figure.Figure object.
109
111
110
112
"""
111
- self .plotly_fig ["layout" ]["showlegend" ] = False
112
113
self .msg += "Closing figure\n "
113
114
114
115
def open_axes (self , ax , props ):
@@ -198,6 +199,35 @@ def close_axes(self, ax):
198
199
self .msg += " Closing axes\n "
199
200
self .x_is_mpl_date = False
200
201
202
+ def open_legend (self , legend , props ):
203
+ """Enable Plotly's native legend when matplotlib legend is detected.
204
+
205
+ This method is called when a matplotlib legend is found. It enables
206
+ Plotly's showlegend only if the matplotlib legend is visible.
207
+
208
+ Positional arguments:
209
+ legend -- matplotlib.legend.Legend object
210
+ props -- legend properties dictionary
211
+ """
212
+ self .msg += " Opening legend\n "
213
+ self ._processing_legend = True
214
+ self ._legend_visible = props .get ("visible" , True )
215
+ if self ._legend_visible :
216
+ self .msg += " Enabling native plotly legend (matplotlib legend is visible)\n "
217
+ self .plotly_fig ["layout" ]["showlegend" ] = True
218
+ else :
219
+ self .msg += " Not enabling legend (matplotlib legend is not visible)\n "
220
+
221
+ def close_legend (self , legend ):
222
+ """Finalize legend processing.
223
+
224
+ Positional arguments:
225
+ legend -- matplotlib.legend.Legend object
226
+ """
227
+ self .msg += " Closing legend\n "
228
+ self ._processing_legend = False
229
+ self ._legend_visible = False
230
+
201
231
def draw_bars (self , bars ):
202
232
# sort bars according to bar containers
203
233
mpl_traces = []
@@ -309,83 +339,6 @@ def draw_bar(self, coll):
309
339
"assuming data redundancy, not plotting."
310
340
)
311
341
312
- def draw_legend_shapes (self , mode , shape , ** props ):
313
- """Create a shape that matches lines or markers in legends.
314
-
315
- Main issue is that path for circles do not render, so we have to use 'circle'
316
- instead of 'path'.
317
- """
318
- for single_mode in mode .split ("+" ):
319
- x = props ["data" ][0 ][0 ]
320
- y = props ["data" ][0 ][1 ]
321
- if single_mode == "markers" and props .get ("markerstyle" ):
322
- size = shape .pop ("size" , 6 )
323
- symbol = shape .pop ("symbol" )
324
- # aligning to "center"
325
- x0 = 0
326
- y0 = 0
327
- x1 = size
328
- y1 = size
329
- markerpath = props ["markerstyle" ].get ("markerpath" )
330
- if markerpath is None and symbol != "circle" :
331
- self .msg += (
332
- "not sure how to handle this marker without a valid path\n "
333
- )
334
- return
335
- # marker path to SVG path conversion
336
- path = " " .join (
337
- [f"{ a } { t [0 ]} ,{ t [1 ]} " for a , t in zip (markerpath [1 ], markerpath [0 ])]
338
- )
339
-
340
- if symbol == "circle" :
341
- # symbols like . and o in matplotlib, use circle
342
- # plotly also maps many other markers to circle, such as 1,8 and p
343
- path = None
344
- shape_type = "circle"
345
- x0 = - size / 2
346
- y0 = size / 2
347
- x1 = size / 2
348
- y1 = size + size / 2
349
- else :
350
- # triangles, star etc
351
- shape_type = "path"
352
- legend_shape = go .layout .Shape (
353
- type = shape_type ,
354
- xref = "paper" ,
355
- yref = "paper" ,
356
- x0 = x0 ,
357
- y0 = y0 ,
358
- x1 = x1 ,
359
- y1 = y1 ,
360
- xsizemode = "pixel" ,
361
- ysizemode = "pixel" ,
362
- xanchor = x ,
363
- yanchor = y ,
364
- path = path ,
365
- ** shape ,
366
- )
367
-
368
- elif single_mode == "lines" :
369
- mode = "line"
370
- x1 = props ["data" ][1 ][0 ]
371
- y1 = props ["data" ][1 ][1 ]
372
-
373
- legend_shape = go .layout .Shape (
374
- type = mode ,
375
- xref = "paper" ,
376
- yref = "paper" ,
377
- x0 = x ,
378
- y0 = y + 0.02 ,
379
- x1 = x1 ,
380
- y1 = y1 + 0.02 ,
381
- ** shape ,
382
- )
383
- else :
384
- self .msg += "not sure how to handle this element\n "
385
- return
386
- self .plotly_fig .add_shape (legend_shape )
387
- self .msg += " Heck yeah, I drew that shape\n "
388
-
389
342
def draw_marked_line (self , ** props ):
390
343
"""Create a data dict for a line obj.
391
344
@@ -501,7 +454,7 @@ def draw_marked_line(self, **props):
501
454
self .msg += " Heck yeah, I drew that line\n "
502
455
elif props ["coordinates" ] == "axes" :
503
456
# dealing with legend graphical elements
504
- self .draw_legend_shapes ( mode = mode , shape = shape , ** props )
457
+ self .msg += " Using native legend \n "
505
458
else :
506
459
self .msg += " Line didn't have 'data' coordinates, not drawing\n "
507
460
warnings .warn (
@@ -667,6 +620,10 @@ def draw_text(self, **props):
667
620
self .draw_title (** props )
668
621
else : # just a regular text annotation...
669
622
self .msg += " Text object is a normal annotation\n "
623
+ # Skip creating annotations for legend text when using native legend
624
+ if self ._processing_legend and self ._legend_visible and props ["coordinates" ] == "axes" :
625
+ self .msg += " Skipping legend text annotation (using native legend)\n "
626
+ return
670
627
if props ["coordinates" ] != "data" :
671
628
self .msg += " Text object isn't linked to 'data' coordinates\n "
672
629
x_px , y_px = (
0 commit comments