@@ -4,89 +4,97 @@ Run the dashboard for explorative ERP analysis.
44
55Arguments:\\
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"""
2312function 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" ,
0 commit comments