This is honestly the shortest I can get it while still being runnable. I’ve commented ‘why does this work on its own’ and ‘but this just glitches?’ at the relevant lines and included the data table visual to hopefully clarify my issue with an absolute minimum data source. Thanks again if you get a chance to look at it.
from bokeh.plotting import figure, show, Column
from bokeh.io import curdoc
from bokeh.models import ColumnDataSource, CDSView, IndexFilter, Tool, PointDrawTool, DataTable, TableColumn, EditTool, \
Drag, LineEditTool
from bokeh.models.callbacks import CustomJS
from bokeh.events import PointEvent, Tap, MouseMove
from bokeh.core.properties import Instance, Enum
from bokeh.util.compiler import TypeScript
from bokeh.models.renderers import GlyphRenderer
from bokeh.core.enums import Dimensions
from bokeh.core.validation import error
TS_CODE = """
import {LineEditTool, LineEditToolView} from "models/tools/edit/line_edit_tool"
import {UIEvent} from "core/ui_events"
import {GlyphRenderer} from "models/renderers/glyph_renderer"
import {XYGlyph} from "models/glyphs/xy_glyph"
import {Dimensions} from "core/enums"
import * as p from "core/properties"
export interface HasXYGlyph {
glyph: XYGlyph
}
export class TestToolView extends LineEditToolView {
model: TestTool
_drag_points(ev: UIEvent, renderers: (GlyphRenderer & HasXYGlyph)[], dim: Dimensions = "both"): void {
if (this._basepoint == null)
return
const [bx, by] = this._basepoint
for (const renderer of renderers) {
const basepoint = this._map_drag(bx, by, renderer)
const point = this._map_drag(ev.sx, ev.sy, renderer)
if (point == null || basepoint == null) {
continue
}
const [x, y] = point
const [px, py] = basepoint
const [dx, dy] = [x-px, y-py]
// Type once dataspecs are typed
const glyph: any = renderer.glyph
const cds = renderer.data_source
const [xkey, ykey] = [glyph.x.field, glyph.y.field]
for (const index of cds.selected.indices) {
if (xkey && (dim == "width" || dim == "both")) {
cds.data[xkey][index] += dx
}
if (ykey && (dim == "height" || dim == "both")) {
cds.data[ykey][index] += dy //why does this work on its own
cds.data['y1'][2] = 5 //but this just glitches?
}
}
cds.change.emit()
}
this._basepoint = [ev.sx, ev.sy]
}
}
export namespace TestTool {
export type Attrs = p.AttrsOf<Props>
export type Props = LineEditTool.Props
}
export interface TestTool extends TestTool.Attrs {}
export class TestTool extends LineEditTool {
properties: TestTool.Props
__view_type__: TestToolView
constructor(attrs?: Partial<TestTool.Attrs>) {
super(attrs)
}
tool_name = "Test Tool"
static init_TestTool(): void {
this.prototype.default_view = TestToolView
this.define<TestTool.Props>({
})
}
}
"""
class TestTool(LineEditTool):
__implementation__ = TypeScript(TS_CODE)
plot = figure()
x = [1, 2, 3]
y1 = [1, 2, 3]
y2 = [3, 2, 1]
data = {'x': x, 'y1': y1, 'y2': y2}
source = ColumnDataSource(data)
testLine1 = plot.line(x = 'x', y = 'y1', source = source)
testLine2 = plot.line(x = 'x', y = 'y2', source = source)
circleGlyph = plot.circle([], [], size = 10)
testTool = TestTool(renderers = [testLine1, testLine2], intersection_renderer = circleGlyph)
plot.add_tools(testTool)
columns = [TableColumn(field='x', title='x'),
TableColumn(field='y1', title='y1'),
TableColumn(field='y2', title='y2')]
table = DataTable(source=source, columns=columns, editable=True, height=200)
show(Column(plot, table))