Plotting
Genie uses PlotlyJS to generate interactive plots in the browser. Particularly, it leverages the PlotlyJS.jl package and its syntax to declare plots in the Julia code.
A PLotlyJS plot has two components:
- Data: an array of trace objects, each representing a distinct set of data points and their graphical representation within the plot. A trace object specifies the type of plot (scatter, bar, pie, heatmap, etc.) and contains properties such as color, line style or marker style. By combining multiple trace objects within the data array, you can create complex and layered visualizations with different data series and plot types.
using PlotlyBase
trace1 = scatter(
x=[1, 2, 3, 4],
y=[10, 15, 13, 17],
mode="markers",
name="Trace 1"
)
trace2 = scatter(
x=[1, 2, 3, 4],
y=[5, 9, 11, 12],
mode="lines+markers",
name="Trace 2",
line=attr(color="red")
)
- Layout: defines the plot's overall appearance and formatting, including settings for the plot title, axis labels, legends, margin settings, background colors, grid lines, and more.
layout = Layout(
title="A Scatter Plot with Multiple Traces",
xaxis=attr(
title="X Axis Label",
showgrid=false
),
yaxis=attr(
title="Y Axis Label",
showgrid=true,
range=[0, 20]
)
)
To include a plot in a Genie app, first declare its data and layout as reactive variables
@out plotdata = [trace1, trace2]
@out plotlayout = layout
and add the plot to the UI with the low-code API
ui() = plot(:plotdata, layout=:plotlayout)
or with the plotly
tag in the HTML code
<plotly :data="plotdata" :layout="plotlayout"> </plotly>
Defining plots from Plots.jl
It is also possible to obtain the plotdata
from a plot defined with
Plots.jl using the Plotly backend and the
Plots.plotly_traces
/Plots.plotly_layout
functions as follows:
module App
using GenieFramework
import PlotlyBase, PlotlyKaleido, Plots
Plots.plotly()
p = Plots.plot([1, 2, 3], show=false)
@app begin
@out traces = Plots.plotly_traces(p)
@out layout = Plots.plotly_layout(p)
end
function ui()
plot(:traces, layout=:layout)
end
@page("/", ui)
end
The plotly_traces
function is loaded by Plots.plotly()
in the integration with PlotlyBase
and PlotlyKaleido
. Therefore, PlotlyBase
and PlotlyKaleido
should be available installed and in the environment when Plots.plotly()
is called for plotly_traces
to be defined.
Defining plots in HTML
You can also define the plot entirely in the HTML code and just bind the data to Julia variables,
module App
using GenieFramework
@genietools
@app begin
@out firstX = [1, 2, 3, 4, 5]
@out firstY = [5, 0, 3, -1, 2]
@out secondX = [1, 2, 3, 4, 5]
@out secondY = [1, 4, 6, 4, 4]
end
@page("/", "app.jl.html")
Server.up()
end
<div>
<plotly
:data="[
{
x: firstX,
y: firstY,
mode: 'markers',
type: 'scatter',
marker: { color: 'rgb(201, 90, 218)', size: 12 }
},
{
x: secondX,
y: secondY,
mode: 'lines+markers',
type: 'scatter',
line: { color: 'rgba(61, 185, 100, 0.8)', width: 1.5 },
marker: { color: 'rgb(61, 185, 100)', size: 8 }
}
]"
:layout="{
title: 'Complex StippleJS Plot Example',
xaxis: { title: 'X Axis', range: [0, 5] },
yaxis: { title: 'Y Axis', range: [-1, 6] },
legend: { x: 0.5, y: -0.3, orientation: 'h' }
}"
/>
</div>
Note that this example uses the Javascript PlotlyJS, so the Julia PlotlyBase
package is not needed.
Detecting mouse events on a plot
PlotlyJS allows the detection of various mouse events on a plot, such as clicks, hovers, selections, and relayouts. You can capture these events and execute especific code.
To enable event detection, you'll need to use a named ReactiveModel
defined with @app ModelName
. Note that, as of today, named models are shared across all sessions and users and so any user interaction will trigger a response across all browser windows.
Moreover, event handling code is attached to the ReactiveModel
with the @mixin
macro, and the watchplots
function.
The example below shows how to work with the available mouse events.
module App
using GenieFramework, PlotlyBase, StipplePlotly
@genietools
trace1 = scatter(; x=1:4, y=[0, 2, 3, 5], fill="tozeroy")
trace2 = scatter(; x=1:4, y=[3, 5, 1, 7], fill="tonexty")
@app EventsModel begin
@out traces = [trace1, trace2]
@out plotlayout = PlotlyBase.Layout(title="Filled line chart")
@mixin traces::PlotlyEvents
@onchange traces_click begin
@show traces_click
end
@onchange traces_hover begin
@show traces_hover
end
@onchange traces_selected begin
@show traces_selected
end
@onchange traces_relayout begin
@show traces_relayout
end
end
# start watchning plots when page is loaded
@mounted EventsModel watchplots()
ui() = [plot(:traces, layout=:plotlayout, syncevents = true)]
route("/") do
global model
model = EventsModel |> init |> handlers
page(model, ui()) |> html
end
Server.isrunning() || Server.up()
end