Open wochner opened 5 years ago
I would propose to add a property to legend that governs the interactive policy:
click
toggles hide/mute state based on explicit clicks on legend items (current behavior, would remain default)
hover
when the legend as a whole is hovered over, all items are automatically hidden/muted, except the one item that is hovered over
Click policy could potentially also be differentiated:
click_toggle
(current behaviour)
click select
similar to hover, mutes/hides everything except what was clicked.
The issue to solve with click_select
is how do users get back to the original state, where nothing is muted/hidden?
An alternative to click select
could be to give an option to
grey out the legend (current behavior)
put the legend text in bold (new option)
The glyphs can already be modified to be highlighted or muted, by setting different muted_glyph
properties. As example, we can set alpha=0.5
and muted_alpha=1
.
@wochner sorry I don't quite understand the relation to my question, which is really a UX one. If we have a mode where clicking on one item in a legend automatically mutes all the others, how do we get back to a state with nothing muted? We can't do it by clicking on any individual item, that will just repeat the process with muting everything else. So then... double clicking? right clicking? clicking outside the legend (but then what about interfering with taps)? some explicit button added to legends? Concerned specifically about UI discoverability.
I think a new property highlight
could be added to the existing click_policy properties hide
and mute
. Then same behaviour as with mute, just inverted. You click on one element, others get muted, you click on same element again, others come back.
This should also work for multiple selctions. We would have to deselect all legend entries to get back to the natural state, but this is the same as we currently do with muted.
I might be ok with that for single selections, where the meaning of a click can always be expressed uniformly as: "toggle the muted status of everything besides this item". But admitting multiple selections means having to compose and reason about inverse sets and that gets complicated quickly and I don't think the meaning of any individual click in that case can be expressed succinctly (which leads me to be -1 on it) i.e. sometimes a click toggles everything else, sometimes it doesn't, it just depends on what you've clicked already up to that point.
I agree that the “toggle everything else” may become complex to implement and have side effects. Hence the suggestion to simply change the selected legend entry text to bold when ‘click_property = highlight’
I'd like to implement something like this, if even for my personal use.
How would I go about extending the Legend model or making a new version that could achieve this?
I'm having trouble combing through the source code and actually finding where the "legend" code hooks into the "figure" code, such as in the kwargs for the "line" function in this example:
https://bokeh.pydata.org/en/latest/docs/user_guide/interaction/legends.html
@bodacious-bill Almost all the work anywhere in Bokeh is actually done in BokehJS, which is written in TypeScript, e.g.
https://github.com/bokeh/bokeh/blob/master/bokehjs/src/lib/models/annotations/legend.ts
Typically on the Python side there is nothing more than defining properties on the classes. These properties get mirrored to BokehJS, and BokehJS uses their values to do whatever is needed in the browser. The User's Guide section on extending Bokeh is the best place to look for information about the relationship between Bokeh and BokehJS:
https://bokeh.pydata.org/en/latest/docs/user_guide/extensions.html
Then you would need to start going through the Developer's Guide to get set up and building BokehJS from source as a start:
Regarding the click select
concept, could the reset
button not be used to restore initial state where nothing is muted?
Perhaps an initial click could mute all but the entry which was clicked. Subsequent clicks could then be used to unmute specific muted entries? Although I have no idea how difficult this might be to accomplish.
The reset
button seems reasonable all the same?
Regards,
@tony-bony do you have any thoughts on this question:
The issue to solve with click_select is how do users get back to the original state, where nothing is muted/hidden?
A discoverable way to get back to the start state in the "inverse selection" mode is really all that is needed for someone to start working on this.
This was proposed:
The reset button seems reasonable all the same?
However I don't see how that can work as there is no mandate or guarantee that a reset tool be present, in general, and this kind of legend may be desired on a plot with no toolbar visible at all.
Good discussion.
I agree that new changes to user interaction with Legend
should not rely on presence of the Reset
toolbar button to restore the original state. Also restoring the original state should not come at an expensive cost of clicking on tens of legend items.
The most important is backwards compatibility
So the changes should just extend the current functionality, providing default values to new features so that if those are not used everything works as it is now.
New options could be possibly added to 'click_policy' like e.g. 'highlight' in combination with the existing two ones so we would end up e.g. with something like this:
click_policy = ['none', 'mute', 'hide', 'highlight_mute', 'highlight_hide']
So highlight the item that was clicked and then mute or hide the others.
The two new options sound pretty self-contradicting (highlight ? or mute?) so maybe it would be better to call it highlight_mute_others
and highlight_hide_others
? So far the options was referring only to the legend item and corresponding glyph while new options try also to describe what happens to others...
Additionally a new hover_policy
could be added to handle the mouseover interaction with exactly the same options:
hover_policy = ['none', 'mute', 'hide', 'highlight_mute', 'highlight_hide']
The user can hover over a legend item and click on it to freeze the current view. The default value would be none
For multi-selections on click user could use CTRL / CMD which would toggle the legend item that was clicked together with corresponding glyph.
It could be also nice if clicking on the glyph itself had the same effect as clicking on the corresponding legend item (this is what I currently do in my plots via a JS callback)
For restoring the original state a double click on the plot or on the legend could be used (my current JS callback implementation). Of course clicking the Reset
button (if present) should do the job as well.
Easier said than done. With some assistance I could try to do implement this changes in BokehJS...
I think that it could be even better if we could just add a callbacks, like JS js_on_click(), js_on_event() and js_on_change() to a Legend
and / or LegendItem
models to make it more consistent with other Bokeh models. This could allow the user to add a custom behaviours on events like mouseenter
, mouseleave
, mousemove
, wheel
etc...
That also seems like a reasonable thing to add though it might make sense to split off into a separate issue. The inverse hover/select (this original issue) should also just work out of the box.
this would be a really awesome feature to have, as it aligns the interactive legend usage with most commercial viz software tools out there
Also, what is the easiest way to reset the selection for the legend?
p.js_on_event(DoubleTap, CustomJS(args=dict(p=p), code='p.reset.emit()'))
does not seem to reset the legend
Hey all, is this issue still being worked on?
No one has ever done any implementation work yet, AFAIK.
When there are many lines glyphs in one plot, it is hard to discern them. We can already highlight the glyphs making use of the
click_policy
andmuted_glyph
. However clicking through a long list of legend entries is tidious and it would be much more elegant to hover over them.When hovering over legend entries, the corresponding glyph should be highlighted. @birdsarah has provided an example in a previous request.