@@ -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
203
233
# sort bars according to bar containers
@@ -310,83 +340,6 @@ def draw_bar(self, coll):
310
340
"assuming data redundancy, not plotting."
311
341
)
312
342
313
- def draw_legend_shapes (self , mode , shape , ** props ):
314
- """Create a shape that matches lines or markers in legends.
315
-
316
- Main issue is that path for circles do not render, so we have to use 'circle'
317
- instead of 'path'.
318
- """
319
- for single_mode in mode .split ("+" ):
320
- x = props ["data" ][0 ][0 ]
321
- y = props ["data" ][0 ][1 ]
322
- if single_mode == "markers" and props .get ("markerstyle" ):
323
- size = shape .pop ("size" , 6 )
324
- symbol = shape .pop ("symbol" )
325
- # aligning to "center"
326
- x0 = 0
327
- y0 = 0
328
- x1 = size
329
- y1 = size
330
- markerpath = props ["markerstyle" ].get ("markerpath" )
331
- if markerpath is None and symbol != "circle" :
332
- self .msg += (
333
- "not sure how to handle this marker without a valid path\n "
334
- )
335
- return
336
- # marker path to SVG path conversion
337
- path = " " .join (
338
- [f"{ a } { t [0 ]} ,{ t [1 ]} " for a , t in zip (markerpath [1 ], markerpath [0 ])]
339
- )
340
-
341
- if symbol == "circle" :
342
- # symbols like . and o in matplotlib, use circle
343
- # plotly also maps many other markers to circle, such as 1,8 and p
344
- path = None
345
- shape_type = "circle"
346
- x0 = - size / 2
347
- y0 = size / 2
348
- x1 = size / 2
349
- y1 = size + size / 2
350
- else :
351
- # triangles, star etc
352
- shape_type = "path"
353
- legend_shape = go .layout .Shape (
354
- type = shape_type ,
355
- xref = "paper" ,
356
- yref = "paper" ,
357
- x0 = x0 ,
358
- y0 = y0 ,
359
- x1 = x1 ,
360
- y1 = y1 ,
361
- xsizemode = "pixel" ,
362
- ysizemode = "pixel" ,
363
- xanchor = x ,
364
- yanchor = y ,
365
- path = path ,
366
- ** shape ,
367
- )
368
-
369
- elif single_mode == "lines" :
370
- mode = "line"
371
- x1 = props ["data" ][1 ][0 ]
372
- y1 = props ["data" ][1 ][1 ]
373
-
374
- legend_shape = go .layout .Shape (
375
- type = mode ,
376
- xref = "paper" ,
377
- yref = "paper" ,
378
- x0 = x ,
379
- y0 = y + 0.02 ,
380
- x1 = x1 ,
381
- y1 = y1 + 0.02 ,
382
- ** shape ,
383
- )
384
- else :
385
- self .msg += "not sure how to handle this element\n "
386
- return
387
- self .plotly_fig .add_shape (legend_shape )
388
- self .msg += " Heck yeah, I drew that shape\n "
389
-
390
343
def draw_marked_line (self , ** props ):
391
344
"""Create a data dict for a line obj.
392
345
@@ -502,7 +455,7 @@ def draw_marked_line(self, **props):
502
455
self .msg += " Heck yeah, I drew that line\n "
503
456
elif props ["coordinates" ] == "axes" :
504
457
# dealing with legend graphical elements
505
- self .draw_legend_shapes ( mode = mode , shape = shape , ** props )
458
+ self .msg += " Using native legend \n "
506
459
else :
507
460
self .msg += " Line didn't have 'data' coordinates, " "not drawing\n "
508
461
warnings .warn (
@@ -668,6 +621,10 @@ def draw_text(self, **props):
668
621
self .draw_title (** props )
669
622
else : # just a regular text annotation...
670
623
self .msg += " Text object is a normal annotation\n "
624
+ # Skip creating annotations for legend text when using native legend
625
+ if self ._processing_legend and self ._legend_visible and props ["coordinates" ] == "axes" :
626
+ self .msg += " Skipping legend text annotation (using native legend)\n "
627
+ return
671
628
if props ["coordinates" ] != "data" :
672
629
self .msg += (
673
630
" Text object isn't linked to 'data' " "coordinates\n "
0 commit comments