Skip to content

Commit ee18264

Browse files
committed
add: comment amd more user-friendly varibales to explore.jl
1 parent b5b3f08 commit ee18264

File tree

6 files changed

+96
-89
lines changed

6 files changed

+96
-89
lines changed

src/ERPExplorer.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ include("explore.jl")
2121
include("functions_preprocessing.jl")
2222
include("functions_formular.jl")
2323
include("functions_plotting.jl")
24-
include("define_scatter_linestyle.jl")
24+
include("functions_scatter_linestyle.jl")
2525
include("widgets_short.jl")
2626
include("widgets_long.jl")
2727

src/explore.jl

Lines changed: 61 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4,89 +4,97 @@ Run the dashboard for explorative ERP analysis.
44
55
Arguments:\\
66
- `model::UnfoldLinearModel{Float64}` - Unfold linear model with categorical and continuous terms.\\
7-
- `positions::Vector{Point{2, Float32}}` - x an y coordinates of the channels.\\
7+
- `positions::Vector{Point{2, Float32}}` - x an y coordinates of the channels on topoplot.\\
88
- `size::Tuple{Float64, Float64}` - size of the topoplot panel.\\
99
10-
Actions:\\
11-
- Extract formula terms and itheir features.\\
12-
- Create interactive formula with checkboxes.\\
13-
- Arrange and map dropdown menus.\\
14-
- Create interactive topoplot.\\
15-
- Create Observable DataFrame with predicted values (yhats) and more.\\
16-
- Create `GridLayout`.\\
17-
- Use `Base.ReentrantLock`, a synchronization primitive_ to manage concurrent access to shared resources in multi-threaded programs\\
18-
- Create Figure.\\
19-
- Translate into into HTML code using DOMs.
20-
2110
**Return Value:** `Hyperscript.Node{Hyperscript.HTMLSVG}` - final HTML code of the dashboard.
2211
"""
2312
function explore(model::UnfoldModel; positions = nothing, size = (700, 600))
13+
# Initialize the App from Bonito. App allows to wrap all interactive elements and to deploy them
2414
myapp = App() do
15+
# Extract formula terms and their features from the model.
2516
variables = extract_variables(model)
26-
widget_checkbox, widget_signal, widget_dom, value_ranges =
17+
# Create formula widgets for each term.
18+
formula_defaults, formula_toggle, formula_DOM, formula_values =
2719
formular_widgets(variables)
2820

21+
# Extract variable names and types from the model.
2922
var_types = map(x -> x[2][3], variables)
30-
varnames = first.(variables)
23+
var_names = first.(variables)
3124

32-
mapping, mapping_dom = mapping_dropdowns(varnames, var_types)
25+
# Create dropdown menues on the left panel of the dashboard.
26+
mapping, mapping_dom = mapping_dropdowns(var_names, var_types)
3327

34-
if isnothing(positions)
35-
channel = Observable(1)
28+
# Create interactive topoplot widget on the lower left panel of the dashboard.
29+
channel_chosen = Observable(1)
30+
if isnothing(positions)
3631
topo_widget = nothing
3732
else
38-
topo_widget, channel = topoplot_widget(positions; size = size .* 0.5)
33+
topo_widget = topoplot_widget(positions, channel_chosen; size = size .* 0.5)
34+
# Problem: does it update?
3935
end
4036

41-
yhats_sig = yhats_signal(model, widget_signal, channel)
42-
on(mapping) do m
43-
ws = widget_signal.val
37+
# Create Observable DataFrame with predicted values (yhats) of the model.
38+
ERP_data = get_ERP_data(model, formula_toggle, channel_chosen)
39+
40+
# when m changes update formula_defaults
41+
on(mapping) do m
42+
ft = formula_toggle.val
4443
ks_m = values(m)
45-
ks_ws = [w.first for w in ws]
46-
for k in ks_ws
47-
widget_checkbox[k][] = k ks_m
44+
ks_ft = [t.first for t in ft]
45+
for k in ks_ft
46+
formula_defaults[k][] = k ks_m
47+
@debug "x" formula_defaults[k][]
4848
end
4949
end
5050

51-
obs = Observable(S.GridLayout())
51+
# Create a new empty grid layout
52+
plot_layout = Observable(S.GridLayout())
53+
54+
# Create a new reentrant mutex (mutual exclusion) lock for safe thread synchronization during plot updates
55+
# mutex - allows only one thread to access protected code at a time
56+
# reentrant - allows the same thread to acquire the lock multiple times without causing a deadlock
57+
# When multiple events occur nearly simultaneously, the lock ensures that:
58+
# Only one plot update happens at a time and Plot data calculations complete fully before starting new ones
59+
lk = Base.ReentrantLock()
5260

53-
l = Base.ReentrantLock()
54-
55-
Makie.onany_latest(yhats_sig, mapping; update = true) do eff, mapping # update = true means only, that it is run once immediately
56-
lock(l) do
57-
_tmp = plot_data(
58-
eff,
59-
value_ranges,
60-
varnames[var_types.==:CategoricalTerm],
61-
varnames[var_types.==:ContinuousTerm],
61+
# Update the the grid layout
62+
Makie.onany_latest(ERP_data, mapping; update = true) do ERP_data, mapping # `update = true` means that it will run once immediately
63+
lock(lk) do
64+
_tmp = update_grid(
65+
ERP_data,
66+
formula_values,
67+
var_names[var_types.==:CategoricalTerm],
68+
var_names[var_types.==:ContinuousTerm],
6269
mapping,
6370
)
64-
65-
#try
66-
obs[] = _tmp
67-
#catch
68-
#end
71+
plot_layout[] = _tmp
6972
end
7073
return
7174
end
75+
7276
css = Asset(joinpath(@__DIR__, "..", "style.css"))
73-
fig = plot(obs; figure = (size = size,))
77+
fig = plot(plot_layout; figure = (size = size,))
78+
79+
# Create header, sidebar, topo and content (figure) panels
80+
cards = Grid(
81+
Card(formula_DOM, style = Styles("grid-area" => "header")),
82+
Card(mapping_dom, style = Styles("grid-area" => "sidebar")),
83+
Card(topo_widget, style = Styles("grid-area" => "topo")),
84+
Card(fig, style = Styles("grid-area" => "content"));
85+
columns = "5fr 1fr",
86+
rows = "1fr 6fr 4fr",
87+
areas = """
88+
'header header'
89+
'content sidebar'
90+
'content topo'
91+
""",
92+
)
93+
# Translate the cards and css into HTML code using DOMs
7494
res = DOM.div(
7595
css,
7696
Bonito.TailwindCSS,
77-
Grid(
78-
Card(widget_dom, style = Styles("grid-area" => "header")),
79-
Card(mapping_dom, style = Styles("grid-area" => "sidebar")),
80-
Card(topo_widget, style = Styles("grid-area" => "topo")),
81-
Card(fig, style = Styles("grid-area" => "content"));
82-
columns = "5fr 1fr",
83-
rows = "1fr 6fr 4fr",
84-
areas = """
85-
'header header'
86-
'content sidebar'
87-
'content topo'
88-
""",
89-
);
97+
cards;
9098
style = Styles(
9199
"height" => "$(1.2*size[2])px",
92100
"width" => "$(size[1])px",

src/functions_formular.jl

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
2-
formular_widgets(variables)
3-
Creates widgets to control each variable of a model.\\
2+
formula_DOMs(variables)
3+
Create widgets to control all model variables.\\
44
55
Arguments:\\
66
- `variables::Vector{Pair{Symbol}}` - vector of key-value pairs with information about the model formula terms.
@@ -12,15 +12,15 @@ Actions:
1212
- Convert checkboxes to Observables.\\
1313
1414
**Return Values:**\\
15-
- `widget_checkbox`: Dictionary with the current values of the widgets (term => values).\\
16-
- `widget_signal`: widget_checkbox but as Observable, a signal that emits a dictionary with the current values of the widgets.\\
17-
- `formular_widget`: The HTML element that can be displayed to interact with the the widgets.\\
18-
- `value_ranges`: A dictionary containing the value ranges of each formula term.\\
15+
- `formula_defaults::Dict{Symbol, Observable{Bool}}` - formula widgets with default values.\\
16+
- `formula_toggle::Observable{Vector{Any}}` - formula widgets with all values and toggle value.\\
17+
- `formula_DOM::Hyperscript.Node{Hyperscript.HTMLSVG}` - HTML element that can be displayed to interact with the the formula.\\
18+
- `formula_values::Vector{Pair{Symbol}}` - formula widgets with all values.\\
1919
"""
2020
function formular_widgets(variables)
21-
value_ranges = [k => value_range(v) for (k, v) in variables]
22-
widgets = [k => widget(v) for (k, v) in value_ranges]
23-
checkboxes = [Bonito.Checkbox(false) for k in value_ranges]
21+
formula_values = [k => value_range(v) for (k, v) in variables]
22+
widgets = [k => widget(v) for (k, v) in formula_values]
23+
checkboxes = [Bonito.Checkbox(false) for k in formula_values]
2424
widget_names = [formular_text("0 ~ 1")]
2525

2626
for k = 1:length(widgets)
@@ -35,11 +35,11 @@ function formular_widgets(variables)
3535
)
3636
end
3737

38-
formular_widget = Row(widget_names...)
38+
formula_DOM = Row(widget_names...)
3939
widget_values = map(nw -> nw[2].value, widgets)
4040
checkbox_values = map(c -> c.value, checkboxes)
4141

42-
widget_signal =
42+
formula_toggle =
4343
lift(widget_values..., checkbox_values...; ignore_equal_values = true) do args...
4444
result = []
4545
for i = 1:length(args[1:end/2])
@@ -49,13 +49,13 @@ function formular_widgets(variables)
4949
end
5050
return result
5151
end
52-
widget_checkbox = Dict(k => c for (c, (k, v)) in zip(checkbox_values, variables))
52+
formula_defaults = Dict(k => c for (c, (k, v)) in zip(checkbox_values, variables))
5353

54-
return widget_checkbox, widget_signal, formular_widget, value_ranges
54+
return formula_defaults, formula_toggle, formula_DOM, formula_values
5555
end
5656

5757
"""
58-
yhats_signal(model, widget_signal, channel)
58+
get_ERP_data(model, widget_signal, channel)
5959
Creates a dictionary with yhat values and more.\\
6060
6161
Arguments:\\
@@ -70,7 +70,7 @@ Actions:\\
7070
7171
**Return Value:** `yhats_signal::Observable{Any}` containing DataFrame with yhats.
7272
"""
73-
function yhats_signal(model, widget_signal, channel)
73+
function get_ERP_data(model, widget_signal, channel)
7474

7575
yhats_signal = Observable{Any}(nothing; ignore_equal_values = true)
7676

src/functions_plotting.jl

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11

22
"""
3-
plot_data(data, value_ranges, categorical_vars, continuous_vars, mapping_obs)
4-
Plotting an interactive dashboard.
3+
update_grid(data, value_ranges, categorical_vars, continuous_vars, mapping_obs)
4+
Plotting and updating an interactive dashboard.
55
66
Arguments:\\
77
- `data::DataFrame` - the result of `effects(Dict(...), model) ` with columns: yhat, channel, dummy, time, eventname and unique columns for each formula term.\\
@@ -19,14 +19,14 @@ Action:\\
1919
2020
**Return Value:** `Makie.GridLayoutSpec`.
2121
"""
22-
function plot_data(data, value_ranges, cat_terms, continuous_vars, mapping_obs)
22+
function update_grid(data, value_ranges, cat_terms, continuous_vars, mapping_obs)
2323
# Convert observable mapping to values
2424
mapping = to_value(mapping_obs)
2525

2626
# Identify activated categorical and continuous variables
2727
cat_active = Dict(cat => data[1, cat] != "typical_value" for cat in cat_terms)
2828
cont_active = Dict(cont => data[1, cont] != "typical_value" for cont in continuous_vars)
29-
29+
#@debug cat_active
3030
# Retrieve unique categorical levels
3131
cat_levels = [unique(data[!, cat]) for cat in cat_terms]
3232

@@ -93,7 +93,6 @@ function plot_data(data, value_ranges, cat_terms, continuous_vars, mapping_obs)
9393
axes[r_ix, c_ix] = S.Axis(; plots = plots)
9494
end
9595
end
96-
9796
palettes = merge(line_styles, scatter_styles)
9897

9998
legends = Union{Nothing,Makie.BlockSpec}[]

src/define_scatter_linestyle.jl renamed to src/functions_scatter_linestyle.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,13 @@ function define_scatter_line_style!(
4444
line_cmap = []
4545
line_crange = []
4646
line_color = args
47-
if isempty(args) || !any(x -> x[1] .== :color, args)
47+
if isempty(args) || !any(x -> x[1] == :color, args)
4848
push!(args, :color => :black) # default color for lines
4949
end
5050

5151
else
52-
line_cmap = [kw => cmap for (name, (kw, (lims, cmap))) in line_styles]
53-
line_crange = [:colorrange => lims for (name, (kw, (lims, cmap))) in line_styles]
52+
line_cmap = [kw => cmap for (_, (kw, (_, cmap))) in line_styles]
53+
line_crange = [:colorrange => lims for (_, (_, (lims, _))) in line_styles]
5454
line_color = [:color => sub[!, name] for name in continuous_vars]
5555

5656
end

src/widgets_long.jl

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ Actions:\\
1616
- Arrange containers on the panel using Col() and Row(). Specify their styling.\\
1717
1818
**Return Values:**\\
19-
- `mapping::Observable{Dict{Symbol, Symbol}}` - interactive dictionary with menus and their default value.\\
19+
- `mapping::Observable{Dict{Symbol, Symbol}}` - interactive dictionary with menues and their default value.\\
2020
- `mapping_dom::Hyperscript.Node{Hyperscript.HTMLSVG}` - dropdown menus in HTML code with styling and layout.\\
2121
"""
2222
function mapping_dropdowns(varnames, var_types)
@@ -79,20 +79,20 @@ Actions:\\
7979
- Hide decorations and spines.\\
8080
8181
**Return Values:**\\
82-
- `h_topo::Makie.FigureAxisPlot` - topoplot widget.\\
83-
- `interactive_scatter::Observable{Int64}` - number of the selected channel.\\
82+
- `topo_widget::Makie.FigureAxisPlot` - topoplot widget.\\
83+
- `channel_chosen::Observable{Int64}` - number of the selected channel.\\
8484
"""
85-
function topoplot_widget(positions; size = ())
85+
function topoplot_widget(positions, channel_chosen; size = ())
8686
strokecolor = Observable(repeat([:red], length(to_value(positions)))) # crashing
87-
interactive_scatter = Observable(1)
87+
#channel_chosen = Observable(1)
8888

8989
colorrange = vcat(0, 1)
9090
colormap = vcat(Gray(0.5), Gray(1))
9191

9292
data_obs = Observable(zeros(length(to_value(positions))))
9393
data_obs.val[1] = 1
9494

95-
h_topo = eeg_topoplot(
95+
topo_widget = eeg_topoplot(
9696
data_obs,
9797
nothing;
9898
positions = positions,
@@ -104,20 +104,20 @@ function topoplot_widget(positions; size = ())
104104
label_scatter = (; strokecolor = :black, strokewidth = 1.0, markersize = 20.0),
105105
)
106106

107-
on(events(h_topo).mousebutton) do event
107+
on(events(topo_widget).mousebutton) do event
108108
if event.button == Mouse.left && event.action == Mouse.press
109-
plt, p = pick(h_topo)
109+
plt, p = pick(topo_widget)
110110
if isa(plt, Makie.Scatter)
111111
data_obs[] .= 0
112112
data_obs[][p] = 1
113113
notify(data_obs)
114-
interactive_scatter[] = p
114+
channel_chosen[] = p
115115
end
116116

117117
end
118118
end
119-
hidedecorations!(h_topo.axis)
120-
hidespines!(h_topo.axis)
121-
return h_topo, interactive_scatter
119+
hidedecorations!(topo_widget.axis)
120+
hidespines!(topo_widget.axis)
121+
return topo_widget
122122

123123
end

0 commit comments

Comments
 (0)