Makie provides a data inspection tool via
DataInspector(x) where x can be a figure, axis or scene. With it you get a floating tooltip with relevant information for various plots by hovering over one of its elements.
By default the inspector will be able to pick any plot other than
volume based plots. If you wish to ignore a plot, you can set its attribute
plot.inspectable = false. With that the next closest plot (in range) will be picked.
inspector = DataInspector(fig) contains the following attributes:
range = 10: Controls the snapping range for selecting an element of a plot.
enabled = true: Disables inspection of plots when set to false. Can also be adjusted with
text_padding = Vec4f0(5, 5, 3, 3): Padding for the box drawn around the tooltip text. (left, right, bottom, top)
text_align = (:left, :bottom): Alignment of text within the tooltip. This does not affect the alignment of the tooltip relative to the cursor.
textcolor = :black: Tooltip text color.
textsize = 20: Tooltip text size.
font = "Dejavu Sans": Tooltip font.
background_color = :white: Background color of the tooltip.
outline_color = :grey: Outline color of the tooltip.
outline_linestyle = nothing: Linestyle of the tooltip outline.
outline_linewidth = 2: Linewidth of the tooltip outline.
indicator_color = :red: Color of the selection indicator.
indicator_linewidth = 2: Linewidth of the selection indicator.
indicator_linestyle = nothing: Linestyle of the selection indicator
tooltip_align = (:center, :top): Default position of the tooltip relative to the cursor or current selection. The real align may adjust to keep the tooltip in view.
tooltip_offset = Vec2f0(20): Offset from the indicator to the tooltip.
depth = 9e3: Depth value of the tooltip. This should be high so that the tooltip is always in front.
priority = 100: The priority of creating a tooltip on a mouse movement or scrolling event.
The inspector implements tooltips for primitive plots and a few non-primitive (i.e. a recipe) plots. All other plots will fall back to tooltips of their hovered child. While this means that most plots have a tooltip it also means many may not have a fitting one. If you wish to implement a more fitting tooltip for non-primitive plot you may do so by creating a method
function show_data(inspector::DataInspector, my_plot::MyPlot, idx, primitive_child::SomePrimitive) ... end
my_plot is the plot you want to create a custom tooltip for,
primitive_child is one of the primitives your plot is made from and
idx is the index into that primitive plot. The latter two are the result from
pick_sorted at the mouseposition. In general you will need to adjust
idx to be useful for
Let's take a look at the
BarPlot overload, which also powers
hist. It contains two primitive plots -
idx from picking a
Mesh is based on vertices, which there are four per rectangle. From
Lines we get an index based on the end point of the line. To draw the outline of a rectangle we need 5 points and a seperator, totaling 6. We thus implement
import Makie: show_data function show_data(inspector::DataInspector, plot::BarPlot, idx, ::Lines) return show_barplot(inspector, plot, div(idx-1, 6)+1) end function show_data(inspector::DataInspector, plot::BarPlot, idx, ::Mesh) return show_barplot(inspector, plot, div(idx-1, 4)+1) end
to map the primitive
idx to one relevant for
BarPlot. With this we can now get the position of the hovered bar with
plot[idx]. To align the tooltip to the selection we need to compute the relevant position in screen space and update the tooltip position.
using Makie: parent_scene, shift_project, update_tooltip_alignment!, position2string function show_barplot(inspector::DataInspector, plot::BarPlot, idx) # All the attributes of DataInspector are here a = inspector.plot.attributes # Get the scene BarPlot lives in scene = parent_scene(plot) # Get the hovered world-space position pos = plot[idx] # project to screen space and shift it to be correct on the root scene proj_pos = shift_project(scene, to_ndim(Point3f0, pos, 0)) # anchor the tooltip at the projected position update_tooltip_alignment!(inspector, proj_pos) # Update the final text of the tooltip # position2string is just an `@sprintf` a._display_text = position2string(pos) # Show the tooltip a._visible = true # return true to indicate that we have updated the tooltip return true end
Next we need to mark the rectangle we are hovering. In this case we can use the rectangles which
BarPlot passes to
DataInspector contains some functionality for keeping track of temporary plots, so we can plot the indicator to the same
BarPlot uses. Doing so results in
using Makie: parent_scene, shift_project, update_tooltip_alignment!, position2string, clear_temporary_plots! function show_barplot(inspector::DataInspector, plot::BarPlot, idx) a = inspector.plot.attributes scene = parent_scene(plot) pos = plot[idx] proj_pos = shift_project(scene, to_ndim(Point3f0, pos, 0)) update_tooltip_alignment!(inspector, proj_pos) # Get the rectangle BarPlot generated for Poly # `_bbox2D` is a node meant for saving a `Rect2D` indicator. There is also # a `_bbox3D`. Here we keep `_bbox2D` updated and use it as a source for # our custom indicator. a._bbox2D = plot.plots[idx] a._model = plot.model # Only plot the indicator once. It'll be updated via `_bbox2D`. if inspector.selection != plot # Clear any old temporary plots (i.e. other indicators like this) # this also updates inspector.selection. clear_temporary_plots!(inspector, plot) # create the indicator using a bunch of the DataInspector attributes. # Note that temporary plots only cleared when a new one is created. To # control whether indicator is visible or not `a._bbox_visible` is set # instead, so it should be in any custom indicator like this. p = wireframe!( scene, a._bbox2D, model = a._model, color = a.indicator_color, strokewidth = a.indicator_linewidth, linestyle = a.indicator_linestyle, visible = a._bbox_visible, show_axis = false, inspectable = false ) # Make sure this draws on top translate!(p, Vec3f0(0, 0, a.depth)) # register this indicator for later cleanup. push!(inspector.temp_plots, p) end a._display_text = position2string(pos) a._visible = true # Show our custom indicator a._bbox_visible = true # Don't show the default screen space indicator a._px_bbox_visible = false return true end
which finishes the implementation of a custom tooltip for