最近使用pyecharts绘图遇到了需要给绘制出的图形添加自定义标签功能,查阅有关文档后成功解决了该问题,现在分享出来。使用本文的方法可以实现双击图形上的数据点,为该点创建一个标签,再次双击该标签,标签销毁。
pyecharts的图形对象都有一个add_js_funcs方法,通过该方法可以在html文件中嵌入js脚本,增加html的交互功能。
以line图形举例,下面是示例代码,该代码会创建一个折线图对象,并打开。
import os
import pyecharts.options as opts
from pyecharts.charts import Line
x_data = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
y_data = [820, 932, 901, 934, 1290, 1330, 1320]
line=Line()
line.set_global_opts(
tooltip_opts=opts.TooltipOpts(is_show=False),
xaxis_opts=opts.AxisOpts(type_="category"),
yaxis_opts=opts.AxisOpts(
type_="value",
axistick_opts=opts.AxisTickOpts(is_show=True),
splitline_opts=opts.SplitLineOpts(is_show=True),
),
)
line.add_xaxis(xaxis_data=x_data)
line.add_yaxis(
series_name="",
y_axis=y_data,
symbol="emptyCircle",
is_symbol_show=True,
label_opts=opts.LabelOpts(is_show=False),
)
line.render("basic_line_chart.html")
os.startfile("basic_line_chart.html")
现在增加js_func,具体内容如下,其中chart_id是绘制图形的唯一id,通过
chart_id=line.chart_id获取。
js_func = f"""
chart_{chart_id}.on('dblclick', function(params) {{
var opts=option_{chart_id};
if(params.componentType=="series") {{
var seriesIndex=params.seriesIndex;
if (!('markPoint' in opts.series[seriesIndex])) {{
var markPoint = {{
label: {{
show: true,
}},
data: []
}};
opts.series[seriesIndex].markPoint = markPoint;
}}
var markData={{ name:seriesIndex, coord: params.value, value: params.value[params.value.length-1] }}
opts.series[seriesIndex].markPoint.data.push(markData);
chart_{chart_id}.setOption(opts);
}} else if(params.componentType=="markPoint") {{
var seriesIndex=params.seriesIndex;
var coord=params.data.coord;
var idxToRemove=opts.series[seriesIndex].markPoint.data.findIndex(function(item) {{
return item.name == seriesIndex && item.coord[0] === coord[0] && item.coord[1] === coord[1];
}});
if (idxToRemove !== -1) {{
opts.series[seriesIndex].markPoint.data.splice(idxToRemove, 1);
chart_{chart_id}.setOption(opts);
}}
}}
}});
"""
解释一下上述js_func,该js函数使用了echarts的鼠标事件api接口,dblclick表示双击鼠标触发。当然你也可以修改为click,表示单击触发。
chart_{chart_id}.on('dblclick', function(params)
首先定义变量opts,并将图形的opts赋值给opts。
var opts=option_{chart_id};
对鼠标点击事件的点击对象进行判断,
如果点击对象为series,则获取点击对象的seriesindex,同时检查当前的绘图配置项里面有没有配置标记点,如果没有就需要初始化一下,这里需要构造一个markpoint空字典,将它添加到绘图配置项里,然后定义一个markdata,markdata的name属性设置为seriesindex,coord设置为点击对象的coord,值设置为点击对象的coord的最后一维的值。使用push办法将这个markdata添加到markpoint的data里面,最后通过chart.setOption方法设置markpoint。
当然如果你想自定义markpoint的样式,包括颜色,字体,大小什么的设置,请参照下面的官方文档链接,修改js_func中的markPoint变量。
var markPoint = {{
label: {{
show: true,
}},
data: []
}};
如果点击的对象是markpoint,则去寻找当前绘图设置项里面的markpoint.data有没有这个markpoint,如果存在,则删除它,实现双击markpoint可删除的效果。这里就用到了上面为啥将markdata的name属性设置为seriesindex,因为遍历当前绘图设置项里面的markpoint.data里面的item的时候,它是只有name,coord这些属性的,将name设置为seriesindex,实现了对唯一系列项的唯一坐标的校核。
完整代码如下
import os
import pyecharts.options as opts
from pyecharts.charts import Line
x_data = ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"]
y_data = [820, 932, 901, 934, 1290, 1330, 1320]
line = Line()
line.set_global_opts(
tooltip_opts=opts.TooltipOpts(is_show=False),
xaxis_opts=opts.AxisOpts(type_="category"),
yaxis_opts=opts.AxisOpts(
type_="value",
axistick_opts=opts.AxisTickOpts(is_show=True),
splitline_opts=opts.SplitLineOpts(is_show=True),
),
)
line.add_xaxis(xaxis_data=x_data)
line.add_yaxis(
series_name="",
y_axis=y_data,
symbol="emptyCircle",
is_symbol_show=True,
label_opts=opts.LabelOpts(is_show=False),
)
chart_id = line.chart_id
js_func = f"""
chart_{chart_id}.on('dblclick', function(params) {{
var opts=option_{chart_id};
if(params.componentType=="series") {{
var seriesIndex=params.seriesIndex;
if (!('markPoint' in opts.series[seriesIndex])) {{
var markPoint = {{
label: {{
show: true,
}},
data: []
}};
opts.series[seriesIndex].markPoint = markPoint;
}}
var markData={{ name:seriesIndex, coord: params.value, value: params.value[params.value.length-1] }}
opts.series[seriesIndex].markPoint.data.push(markData);
chart_{chart_id}.setOption(opts);
}} else if(params.componentType=="markPoint") {{
var seriesIndex=params.seriesIndex;
var coord=params.data.coord;
var idxToRemove=opts.series[seriesIndex].markPoint.data.findIndex(function(item) {{
return item.name == seriesIndex && item.coord[0] === coord[0] && item.coord[1] === coord[1];
}});
if (idxToRemove !== -1) {{
opts.series[seriesIndex].markPoint.data.splice(idxToRemove, 1);
chart_{chart_id}.setOption(opts);
}}
}}
}});
"""
line.add_js_funcs(js_func)
line.render("basic_line_chart.html")
os.startfile("basic_line_chart.html")