@@ -60,6 +60,8 @@ def __init__(self):
6060 self .mpl_x_bounds = (0 , 1 )
6161 self .mpl_y_bounds = (0 , 1 )
6262 self .msg = "Initialized PlotlyRenderer\n "
63+ self ._processing_legend = False
64+ self ._legend_visible = False
6365
6466 def open_figure (self , fig , props ):
6567 """Creates a new figure by beginning to fill out layout dict.
@@ -108,7 +110,6 @@ def close_figure(self, fig):
108110 fig -- a matplotlib.figure.Figure object.
109111
110112 """
111- self .plotly_fig ["layout" ]["showlegend" ] = False
112113 self .msg += "Closing figure\n "
113114
114115 def open_axes (self , ax , props ):
@@ -198,6 +199,35 @@ def close_axes(self, ax):
198199 self .msg += " Closing axes\n "
199200 self .x_is_mpl_date = False
200201
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+ 201231 def draw_bars (self , bars ):
202232
203233 # sort bars according to bar containers
@@ -310,83 +340,6 @@ def draw_bar(self, coll):
310340 "assuming data redundancy, not plotting."
311341 )
312342
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- 390343 def draw_marked_line (self , ** props ):
391344 """Create a data dict for a line obj.
392345
@@ -502,7 +455,7 @@ def draw_marked_line(self, **props):
502455 self .msg += " Heck yeah, I drew that line\n "
503456 elif props ["coordinates" ] == "axes" :
504457 # dealing with legend graphical elements
505- self .draw_legend_shapes ( mode = mode , shape = shape , ** props )
458+ self .msg += " Using native legend \n "
506459 else :
507460 self .msg += " Line didn't have 'data' coordinates, " "not drawing\n "
508461 warnings .warn (
@@ -668,6 +621,10 @@ def draw_text(self, **props):
668621 self .draw_title (** props )
669622 else : # just a regular text annotation...
670623 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
671628 if props ["coordinates" ] != "data" :
672629 self .msg += (
673630 " Text object isn't linked to 'data' " "coordinates\n "
0 commit comments