Hello,
I’m working on a project that involves lots of panning and box selecting on my Bokeh plot and it would significantly help the process if the user could toggle between the two tools on the keyboard instead of selecting and reselecting the pan and box select tools from the toolbar each time.
An idea I had was to make the box select get triggered on Alt+drag so the user can switch between the two tools just based on whether they are holding down the ‘Alt’ key or not. Since there doesn’t seem to be a way to define a keymodifier for the BoxSelectTool
, I was able to achieve this by adding event listeners for keydown and keyup using CustomJS
such that if the key is ‘Alt’, it sets fig.toolbar.active_drag = box_select
(or pan
on keyup).
However, as soon as the event listener gets triggered (by pressing the ‘Alt’ key), right clicking on the plot no longer shows Bokeh’s toolbar context_menu
and the console starts giving this error:
Below is a small test case that captures what I’m trying to do on a much simpler plot, but it does generate the same error:
"""
MINIMAL test case for Bokeh race condition bug report
Error shown in console: "Uncaught TypeError: Cannot read properties of undefined (reading 'is_empty')"
"""
from bokeh.plotting import figure, show
from bokeh.models import CustomJS, PanTool, BoxSelectTool
pan = PanTool()
box_select = BoxSelectTool()
p = figure(width=300, height=300, tools=[pan, box_select])
p.circle([1, 2, 3], [1, 2, 3], radius=0.1)
# The problematic code - rapid toolbar.active_drag switching seems to be causing the error
callback = CustomJS(
args=dict(p=p, pan=pan, box_select=box_select),
code="""
// Rapid tool switching causes race condition
window.addEventListener('keydown', function(event) {
if (event.key === 'Alt') {
p.toolbar.active_drag = box_select; // This line...
}
});
window.addEventListener('keyup', function(event) {
if (event.key === 'Alt') {
p.toolbar.active_drag = pan; // ...and this line cause the error
}
});
console.log("Press and release Alt key, then right click to show the error in console instead of the toolbar's context_menu");
""",
)
p.js_on_event("mouseenter", callback)
show(p)
Specifically, when the plot gets created for the first time and user mouses over the plot (triggering the callback) the context_menu
still gets displayed with no errors when I right click on the plot, it’s only when I actually click ‘Alt’ that it no longer wants to show the context_menu
on right click and instead throws the above error.
At first I thought it had something to do with my javascript event listener interfering with plot interactions, I’ve tried doing document.addEventListener()
instead and it gives the same error. I then realized it might actually have to do with how I have the eventlistener constantly set and reset the toolbar.active_drag
.
In an effort to try to debug this I removed those lines and replaced them with console.log print statements, and clicking ‘Alt’ and then right clicking on the plot showed the context_menu
with no issue:
# Changing this section to the below code was not throwing the error anymore
window.addEventListener('keydown', function(event) {
if (event.key === 'Alt') {
// fig.toolbar.active_drag = box_select;
console.log("keydown");
}
});
window.addEventListener('keyup', function(event) {
if (event.key === 'Alt') {
// fig.toolbar.active_drag = pan;
console.log("keyup");
}
});
Seems like the constant setting and resetting of the toolbar.active_drag
causes a race condition that causes an is_empty
somewhere is my best guess? But I’m also not sure either as i have no idea what happens under the covers.
I was wondering if this is an known error? Is there a way around this? Can I achieve switching to box select just by doing ‘Alt’+drag a different way? Could this error cause other stuff to break later on?
Any assistance on this is very much appreciated, thanks!