Add interactions to your graph plot
In this example you will see, how to register interactions with your graph plot. This tutorial will make use of the more basic Interaction Interface. If you just want to move nodes check out the Predefined Interactions. The implementation of those is quit similar to what is shown in this tutorial.
We star with a simple wheel graph again. This time we use arrays for some attributes because we want to change them later in the interactions for individual nodes/edges.
using CairoMakie
using GraphMakie
using Graphs
using CairoMakie.Colors
g = wheel_graph(10)
f, ax, p = graphplot(g,
edge_width = [2.0 for i in 1:ne(g)],
edge_color = [colorant"gray" for i in 1:ne(g)],
node_size = [10 for i in 1:nv(g)],
node_color = [colorant"red" for i in 1:nv(g)])
hidedecorations!(ax); hidespines!(ax)
ax.aspect = DataAspect()
Later on we want to enable drag interactions, therefore we disable the default :rectanglezoom
interaction
deregister_interaction!(ax, :rectanglezoom)
Hover interactions
At first, let's add some hover interaction for our nodes using the NodeHoverHandler
constructor. We need to define a action function with the signature fun(state, idx, event, axis)
. We use the action to make the nodes bigger on hover events.
function node_hover_action(state, idx, event, axis)
p.node_size[][idx] = state ? 20 : 10
p.node_size[] = p.node_size[] # trigger observable
end
nhover = NodeHoverHandler(node_hover_action)
register_interaction!(ax, :nhover, nhover)
Please run the script locally with GLMakie.jl
if you want to play with the Graph 🙂 The edge hover interaction is quite similar:
function edge_hover_action(state, idx, event, axis)
p.edge_width[][idx]= state ? 5.0 : 2.0
p.edge_width[] = p.edge_width[] # trigger observable
end
ehover = EdgeHoverHandler(edge_hover_action)
register_interaction!(ax, :ehover, ehover)
Click interactions
In a similar fashion we might change the color of nodes and lines by click.
function node_click_action(idx, args...)
p.node_color[][idx] = rand(RGB)
p.node_color[] = p.node_color[]
end
nclick = NodeClickHandler(node_click_action)
register_interaction!(ax, :nclick, nclick)
function edge_click_action(idx, args...)
p.edge_color[][idx] = rand(RGB)
p.edge_color[] = p.edge_color[]
end
eclick = EdgeClickHandler(edge_click_action)
register_interaction!(ax, :eclick, eclick)
Drag interactions
function node_drag_action(state, idx, event, axis)
p[:node_pos][][idx] = event.data
p[:node_pos][] = p[:node_pos][]
end
ndrag = NodeDragHandler(node_drag_action)
register_interaction!(ax, :ndrag, ndrag)
The last example is not as straight forward. By dragging an edge we want to change the positions of both attached nodes. Therefore we need some more state inside the action. We can achieve this with a callable struct.
mutable struct EdgeDragAction
init::Union{Nothing, Point2f} # save click position
src::Union{Nothing, Point2f} # save src vertex position
dst::Union{Nothing, Point2f} # save dst vertex position
EdgeDragAction() = new(nothing, nothing, nothing)
end
function (action::EdgeDragAction)(state, idx, event, axis)
edge = collect(edges(g))[idx]
if state == true
if action.src===action.dst===action.init===nothing
action.init = event.data
action.src = p[:node_pos][][src(edge)]
action.dst = p[:node_pos][][dst(edge)]
end
offset = event.data - action.init
p[:node_pos][][src(edge)] = action.src + offset
p[:node_pos][][dst(edge)] = action.dst + offset
p[:node_pos][] = p[:node_pos][] # trigger change
elseif state == false
action.src = action.dst = action.init = nothing
end
end
edrag = EdgeDragHandler(EdgeDragAction())
register_interaction!(ax, :edrag, edrag)
This page was generated using Literate.jl.