Open droumis opened 4 weeks ago
Should be tested and work with the HoloViews doc's eeg example
See Bokeh PR for incoming 3.5 functionality and description of behavior: https://github.com/bokeh/bokeh/pull/13826
https://github.com/bokeh/bokeh/assets/6613202/9bc53421-937c-47aa-9bcc-23dc4b7ec94c
Should be tested and work with the HoloViews doc's eeg example
See Bokeh PR for incoming 3.5 functionality and description of behavior: https://github.com/bokeh/bokeh/pull/13826
Pure Bokeh example
```python from bokeh.layouts import column from bokeh.plotting import figure, curdoc import numpy as np from bokeh.core.properties import field from bokeh.layouts import column, row from bokeh.models import (ColumnDataSource, CustomJS, Div, FactorRange, HoverTool, Range1d, Switch, WheelZoomTool, ZoomInTool, ZoomOutTool, GroupByModels) from bokeh.palettes import Category10 from bokeh.plotting import figure n_eeg_channels = 7 n_pos_channels = 3 n_channels = n_eeg_channels + n_pos_channels n_seconds = 15 total_samples = 512*n_seconds time = np.linspace(0, n_seconds, total_samples) data = np.random.randn(n_channels, total_samples).cumsum(axis=1) channels = [f"EEG {i}" for i in range(n_eeg_channels)] + [f"POS {i}" for i in range(n_pos_channels)] hover = HoverTool(tooltips=[ ("Channel", "$name"), ("Time", "$x s"), ("Amplitude", "$y μV"), ]) x_range = Range1d(start=time.min(), end=time.max()) y_range = FactorRange(factors=channels) p = figure(x_range=x_range, y_range=y_range, lod_threshold=None, tools="pan,reset,xcrosshair") source = ColumnDataSource(data=dict(time=time)) eeg_renderers = [] pos_renderers = [] for i, channel in enumerate(channels): is_eeg = channel.startswith('EEG') xy = p.subplot( x_source=p.x_range, y_source=Range1d(start=data[i].min(), end=data[i].max()), x_target=p.x_range, y_target=Range1d(start=i, end=i + 1), ) source.data[channel] = data[i] if is_eeg: line = xy.line(field("time"), field(channel), color='black', source=source, name=channel) eeg_renderers.append(line) else: line = xy.line(field("time"), field(channel), color=Category10[10][i], source=source, name=channel) pos_renderers.append(line) all_renderers = eeg_renderers + pos_renderers level = 1 hit_test = True behavior = GroupByModels(groups=[eeg_renderers, pos_renderers]) ywheel_zoom = WheelZoomTool(renderers=all_renderers, level=level, hit_test=hit_test, hit_test_mode="hline", hit_test_behavior=behavior, dimensions="height") xwheel_zoom = WheelZoomTool(renderers=all_renderers, level=level, dimensions="width") zoom_in = ZoomInTool(renderers=all_renderers, level=level, dimensions="height") zoom_out = ZoomOutTool(renderers=all_renderers, level=level, dimensions="height") p.add_tools(ywheel_zoom, xwheel_zoom, zoom_in, zoom_out, hover) p.toolbar.active_scroll = ywheel_zoom level_switch = Switch(active=level == 1) level_switch.js_on_change("active", CustomJS( args=dict(tools=[ywheel_zoom, zoom_in, zoom_out]), code=""" export default ({tools}, obj) => { const level = obj.active ? 1 : 0 for (const tool of tools) { tool.level = level } } """)) hit_test_switch = Switch(active=hit_test) hit_test_switch.js_on_change("active", CustomJS( args=dict(tool=ywheel_zoom), code=""" export default ({tool}, obj) => { tool.hit_test = obj.active } """)) layout = column( row(Div(text="Zoom sub-coordinates:"), level_switch), row(Div(text="Zoom hit-tested:"), hit_test_switch), p, ) curdoc().add_root(layout) ```https://github.com/bokeh/bokeh/assets/6613202/9bc53421-937c-47aa-9bcc-23dc4b7ec94c