Plotly(离线)用于 Python 点击事件



是否可以将点击事件添加到 Plotly 散点图(Python 中的离线模式)?

例如,我想在单击时更改一组散点的形状。

到目前为止我尝试过什么

从网站上阅读其他问题(没有明确的答案)中,我的理解是我可能必须生成 html,然后在事后通过输入 javascript 代码对其进行编辑?所以我可以写一个javascript函数,将其保存到my_js.js然后从html链接到它?

我一直在情节中做一些离线情节的工作,并遇到了同样的挑战。

这是我想出的一个技巧,我的证明是其他人的灵感来源。

一些限制:

  • 假设您在单个 html 文件中具有单个绘图的脱机输出。
  • 假定 on 事件的名称与事件处理程序的名称相同。
  • 需要美汤 4.
  • 假设您已安装 lxml。
  • 使用 Plotly 2.2.2 开发

代码片段:

import bs4
def add_custom_plotly_events(
    filename, 
    events = {
        "plotly_click": "function plotly_click(data) { console.log(data); }",
        "plotly_hover": "function plotly_hover(data) { console.log(data); }"
    },
    prettify_html = True
):
    # what the value we're looking for the javascript
    find_string = "Plotly.newPlot"
    # stop if we find this value
    stop_string = "then(function(myPlot)"
    def locate_newplot_script_tag(soup):    
        scripts = soup.find_all('script')
        script_tag = soup.find_all(string=re.compile(find_string))
        if len(script_tag) == 0:
            raise ValueError("Couldn't locate the newPlot javascript in {}".format(filename))
        elif len(script_tag) > 1:
            raise ValueError("Located multiple newPlot javascript in {}".format(filename))
        if script_tag[0].find(stop_string) > -1:
            raise ValueError("Already updated javascript, it contains:", stop_string)
        return script_tag[0]
    def split_javascript_lines(new_plot_script_tag):
        return new_plot_script_tag.string.split(";")
    def find_newplot_creation_line(javascript_lines):
        for index, line in enumerate(javascript_lines):
            if line.find(find_string) > -1:
                return index, line
        raise ValueError("Missing new plot creation in javascript, couldn't find:", find_string)
    def join_javascript_lines(javascript_lines):
        # join the lines with javascript line terminator ;    
        return ";".join(javascript_lines)
    def register_on_events(events):
        on_events_registration = []
        for function_name in events:
            on_events_registration.append("myPlot.on('{}', {})".format(
                function_name, function_name
            ))
        return on_events_registration
    # load the file
    with open(filename) as inf:
        txt = inf.read()
        soup = bs4.BeautifulSoup(txt, "lxml")
    new_plot_script_tag = locate_newplot_script_tag(soup)
    javascript_lines = split_javascript_lines(new_plot_script_tag)
    line_index, line_text = find_newplot_creation_line(javascript_lines)    
    on_events_registration = register_on_events(events)
    # replace whitespace characters with actual whitespace
    # using + to concat the strings as {} in format
    # causes fun times with {} as the brackets in js
    # could possibly overcome this with in ES6 arrows and such
    line_text = line_text + ".then(function(myPlot) { " + join_javascript_lines(on_events_registration) +"  })".replace('n', ' ').replace('r', '')
    # now add the function bodies we've register in the on handles
    for function_name in events:
        javascript_lines.append(events[function_name])
    # update the specific line
    javascript_lines[line_index] = line_text
    # update the text of the script tag
    new_plot_script_tag.string.replace_with(join_javascript_lines(javascript_lines))
    # save the file again
    with open(filename, "w") as outf:
        # tbh the pretty out is still ugly af
        if prettify_html:
            for line in soup.prettify(formatter = None):
                outf.write(str(line))
        else:
            outf.write(str(soup))

根据 Python 离线模式下的点击事件? 在 Plotly 的社区网站上,这不受支持,至少截至 2015 年 12 月。

这篇文章确实包含一些关于如何自己实现此功能的提示,如果你喜欢冒险的话。

最新更新