使用ipywidget替换Plotly图例项
我正在尝试用ipywidgets替换掉Plotly自带的图例,想要创建一个自定义的图例。下面是一个简单的例子,我为每个数据线添加了一个复选框,可以用来“高亮”显示对应的数据线。
这个方法似乎运行得不错,但我想做得更像Plotly通常显示的那样。我可以获取到所有需要的信息,比如线的颜色、虚线类型、标记符号等等,但我找不到简单的方法来制作一个图例的图形,让它看起来像Plotly生成的那样。
这是我目前的进展,应该是一个完整的例子:
import ipywidgets as widgets
from ipywidgets import interactive, fixed
import plotly.graph_objects as go
line = {'name': 'line','data': ((1,1), (2,2), (3,3)), 'color':'red', 'dash':'solid', 'symbol':'circle'}
square = {'name': 'square','data': ((1,1), (2,4), (3,9)), 'color':'blue', 'dash':'dash', 'symbol':'square'}
traces = (line, square)
def get_values(func, index):
return [e[index] for e in func['data']]
def toggle_highlight(highlight, fig, index):
new_width = 4 if fig.data[index].line.width == 2 else 2
fig.data[index].line.width = new_width
fig = go.FigureWidget()
legend_items = []
for i, t in enumerate(traces):
highlight_chk = interactive(toggle_highlight, highlight=False, fig=fixed(fig), index=fixed(i)).children[0]
item = widgets.HBox([widgets.Label(value=f"{t['name']}, c = {t['color']}, d = {t['dash']}, s = {t['symbol']}"), highlight_chk])
s = go.Scatter(name=t['name'], x=get_values(t, 0), y=get_values(t, 1), line=dict(width=2, color=t['color'], dash=t['dash']), marker=dict(symbol=t['symbol']), customdata=[{'index':i}])
fig.add_trace(s)
legend_items.append(item)
legend = widgets.VBox(legend_items)
display(legend)
display(fig)
有没有什么想法可以创建这样的“标签”?
1 个回答
0
我通过一个包含SVG线条的HTML小部件达成了我的目标。下面是一个例子,我用小部件替换了Plotly的图例,这样我就可以把任何数据线设置为“正常”、“高亮”或者隐藏。
from ipywidgets import interactive, fixed
import plotly.graph_objects as go
import re
line = {'name': 'line','data': ((1,1), (2,2), (3,3)), 'color':'red', 'dash':'solid'}
squared = {'name': 'squared','data': ((1,1), (2,2**2), (3,3**2)), 'color':'blue', 'dash':'4,4'}
cubed = {'name': 'cubed','data': ((1,1), (2,2**3), (3,3**3)), 'color':'green', 'dash':'solid'}
n4 = {'name': 'n4','data': ((1,1), (2,2**4), (3,3**4)), 'color':'purple', 'dash':'solid'}
traces = (line, squared, cubed, n4)
def get_values(func, index):
return [e[index] for e in func['data']]
def mode_fn(mode, fig, legend, index):
html_value = legend[index].children[1].value
if mode == 0:
fig.data[index].line.width = 2
fig.data[index].visible = True
legend[index].children[1].value = re.sub(r'stroke-width:\d+', 'stroke-width:2', html_value)
elif mode == 1:
fig.data[index].line.width = 6
fig.data[index].visible = True
legend[index].children[1].value = re.sub(r'stroke-width:\d+', 'stroke-width:6', html_value)
else:
fig.data[index].line.width = 2
fig.data[index].visible = False
legend[index].children[1].value = re.sub(r'stroke-width:\d+', 'stroke-width:0', html_value)
fig = go.FigureWidget()
legend_items = []
for i, t in enumerate(traces):
mode = interactive(mode_fn, mode=widgets.Dropdown(options=[("Normal", 0), ("Highlight", 1), ("Invisible", 2)], value=0, rows=1), fig=fixed(fig), legend=fixed(legend_items), index=fixed(i)).children[0]
mode.layout.width = "max-content"
mode.description = ""
html = widgets.HTML(
value = f'<svg height="15" width="30"><line x1="0" y1="10" x2="30" y2="10" stroke-dasharray="{t["dash"]}" style="stroke:{t["color"]};stroke-width:2" /></svg>'
)
item = widgets.HBox(height="15px",spacing=0, children=[mode, html, widgets.Label(height="15px", value=f"{t['name']}")])
s = go.Scatter(mode="lines", name=t['name'], x=get_values(t, 0), y=get_values(t, 1), line=dict(width=2, color=t['color'], dash=t['dash']), customdata=[{'index':i}])
fig.add_trace(s)
legend_items.append(item)
fig.layout.showlegend=False
legend = widgets.VBox(legend_items)
display(legend)
display(fig)
这里有一张截图,展示了它的样子。