You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将DynamicMap设为Selection1D流源以响应散点点击事件?

问题描述

在以下最小可复现示例(MWE)中,点击散点图的点应在“debug”文本框中显示其索引。当布局为scatter * dmap时功能正常,但仅使用dmap作为布局时无法生效。实际Selection1D流的源为scatter,选中的是scatter的点而非DynamicMap的点,设置source=dmap也无法解决问题。由于实际场景需要在首次点击后更新散点图(后续需响应第二次点击),必须使用DynamicMap,需要解决如何移除布局中的scatter,让dmap成为实际的事件源。

原始代码:

import numpy as np
import holoviews as hv
import panel as pn

data = np.random.randn(100, 2)
scatter = hv.Scatter(
    data
).opts(
    tools = ["tap"]
)

debug = pn.widgets.TextInput(name="debug", value="")

def on_click(index):
    if index is None:
        return scatter
    debug.value = str(index)
    
    return scatter

stream = hv.streams.Selection1D(source=scatter)

dmap = hv.DynamicMap(on_click, streams=[stream])

#layout = scatter * dmap  # works
layout = dmap             # doesn’t work
pn.Column(
    debug,
    layout
)
解决方案

问题核心在于:全局定义的scatter对象并没有被dmap渲染到页面中(仅用dmap作为布局时,原始scatter不在DOM里),导致绑定到它的Selection1D流无法捕获点击事件。要让dmap成为事件源,需要让流绑定到dmap返回的散点图实例,而非全局的scatter。

修改思路

  1. 移除全局scatter定义,在on_click函数内部生成散点图(支持后续更新数据的需求)。
  2. 使用Holoviews的link_selections工具,自动将选择事件绑定到DynamicMap返回的图表元素上,无需手动指定流的源。
  3. 确保每次返回的散点图都启用tap工具,保证点击事件可被捕获。

修改后的代码

import numpy as np
import holoviews as hv
import panel as pn

hv.extension('bokeh')
pn.extension()

data = np.random.randn(100, 2)
debug = pn.widgets.TextInput(name="debug", value="")

def on_click(index):
    # 可根据index更新数据,此处保留原逻辑,实际场景可替换为更新后的数据
    current_data = data if index is None else data
    # 每次生成新的散点图实例
    scatter = hv.Scatter(current_data).opts(tools=["tap"])
    if index is not None:
        debug.value = str(index)
    return scatter

# 创建Selection1D流,暂不指定源
stream = hv.streams.Selection1D(source=None)
dmap = hv.DynamicMap(on_click, streams=[stream])

# 关键:用link_selections确保点击事件传递到流
dmap = hv.link_selections(dmap)

# 直接使用dmap作为布局即可正常触发点击事件
pn.Column(debug, dmap).servable()

补充说明

如果需要在点击后更新散点图数据,只需修改on_click中的current_data逻辑即可。例如点击某个点后,高亮该点或者更新数据集:

def on_click(index):
    if index is not None:
        debug.value = str(index)
        # 示例:将选中点设为红色,其余为蓝色
        colors = ['red' if i == index[0] else 'blue' for i in range(len(data))]
        scatter = hv.Scatter(data).opts(tools=["tap"], color=colors)
    else:
        scatter = hv.Scatter(data).opts(tools=["tap"])
    return scatter

内容的提问来源于stack exchange,提问作者Skippy le Grand Gourou

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 04:05:42