Skip to content

Commit 3c417f6

Browse files
authored
Merge pull request #247 from JuliaDynamics/hw/fixinspector
Hw/fixinspector
2 parents 0fb2758 + ab2c8ad commit 3c417f6

File tree

10 files changed

+135
-85
lines changed

10 files changed

+135
-85
lines changed

NetworkDynamicsInspector/Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
1313
NetworkDynamics = "22e9dc34-2a0d-11e9-0de0-8588d035468b"
1414
Observables = "510215fc-4207-5dde-b226-833fc4488ee2"
1515
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
16+
PrecompileTools = "aea7be01-6a6a-4083-8856-8a6e6704d82a"
1617
SciMLBase = "0bca4576-84f4-4d90-8ffe-ffa030f20462"
1718
WGLMakie = "276b4fcb-3e11-5398-bf8b-a0c2d153d008"
1819

@@ -36,6 +37,7 @@ Graphs = "1.12.0"
3637
NetworkDynamics = "0.9.13"
3738
Observables = "0.5.5"
3839
OrderedCollections = "1.8.0"
40+
PrecompileTools = "1.2.1"
3941
SciMLBase = "2.75.1"
4042
WGLMakie = "0.11"
4143
julia = "1.10"

NetworkDynamicsInspector/src/NetworkDynamicsInspector.jl

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ using Colors: Colors, @colorant_str, RGB, color
2424
using ColorSchemes: ColorSchemes, ColorScheme
2525
using SciMLBase: SciMLBase
2626
using Downloads: Downloads
27+
using PrecompileTools: @compile_workload
2728

2829
# defined based on the julia version
2930
using NetworkDynamics: AnnotatedIOBuffer, AnnotatedString, @styled_str
@@ -65,9 +66,9 @@ function get_webapp(app)
6566
@info "GUI Session updated"
6667
end
6768

68-
clear_obs!(app)
69+
clear_obs_and_close!(app)
6970

70-
resize_gp = js"""
71+
resize_with_gp = js"""
7172
const graphplotCard = document.querySelector(".graphplot-card");
7273
7374
// Function to update the width dynamically
@@ -100,7 +101,6 @@ function get_webapp(app)
100101
// Initial update
101102
updateResizeWithGpWidth();
102103
"""
103-
Bonito.evaljs(session, resize_gp)
104104

105105
DOM.div(
106106
APP_CSS,
@@ -110,6 +110,7 @@ function get_webapp(app)
110110
gpstate_control_card(app, :vertex),
111111
gpstate_control_card(app, :edge),
112112
element_info_card(app, session),
113+
resize_with_gp;
113114
class="graphplot-col"
114115
),
115116
timeseries_col(app, session),
@@ -139,6 +140,7 @@ function inspect(sol; restart=false, reset=false, display=CURRENT_DISPLAY[])
139140
CURRENT_DISPLAY[] = display
140141

141142
appstate = if reset || isnothing(APPSTATE[])
143+
isnothing(APPSTATE[]) || clear_obs_and_close!(APPSTATE[])
142144
AppState(sol)
143145
else
144146
APPSTATE[].sol[] = sol
@@ -242,7 +244,6 @@ function element_info_card(app, session)
242244
$(eltext).on(replaceDivContentWithHTML);
243245
replaceDivContentWithHTML($(eltext).value);
244246
"""
245-
Bonito.evaljs(session, js)
246247

247248
help = HoverHelp(html"""
248249
Show details on last clicked element.
@@ -251,11 +252,41 @@ function element_info_card(app, session)
251252
""")
252253

253254
Card(
254-
[DOM.div(;id="element-info-box"), help];
255+
[DOM.div(;id="element-info-box"), help, js];
255256
class="bonito-card element-info-card resize-with-gp"
256257
)
257258
end
258259

259-
260+
@compile_workload begin
261+
using NetworkDynamics: EdgeModel, VertexModel, AntiSymmetric
262+
using Graphs: complete_graph
263+
Base.@propagate_inbounds function diffusionedge!(e, v_s, v_d, (p,), _)
264+
e .= p * (v_s[1] .- v_d[1])
265+
end
266+
e = EdgeModel(g=AntiSymmetric(diffusionedge!), outdim=1, pdim=1, pdef=[1], name=:diff_edge, indim=1)
267+
Base.@propagate_inbounds function diffusionvertex!(dv, _, acc, _, _)
268+
dv[1] = acc[1]
269+
nothing
270+
end
271+
v = VertexModel(f=diffusionvertex!, dim=1, g=1:1, indim=1)
272+
273+
g = complete_graph(3)
274+
nw = Network(g, v, e)
275+
prob = SciMLBase.ODEProblem(nw,
276+
zeros(NetworkDynamics.dim(nw)), (0, 0.1),
277+
zeros(NetworkDynamics.pdim(nw)))
278+
279+
t = [0, 0.1]
280+
u = [zeros(NetworkDynamics.dim(nw)) for _ in t]
281+
sol = SciMLBase.build_solution(prob, nothing, t, u)
282+
283+
app = NetworkDynamicsInspector.AppState(sol);
284+
session = Bonito.Session()
285+
NetworkDynamicsInspector.graphplot_card(app, session);
286+
NetworkDynamicsInspector.gpstate_control_card(app, :vertex);
287+
NetworkDynamicsInspector.element_info_card(app, session);
288+
NetworkDynamicsInspector.timeseries_col(app, session);
289+
NetworkDynamicsInspector.clear_obs_and_close!(app);
290+
end
260291

261292
end # module NetworkDynamicsInspector

NetworkDynamicsInspector/src/graphplot.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ function graphplot_card(app, session)
130130
####
131131
tooltip_text = Observable{String}("")
132132

133-
js = js"""
133+
gp_tooltip_js = js"""
134134
const gpcard = document.querySelector(".graphplot-card");
135135
136136
// Create tooltip element
@@ -177,7 +177,6 @@ function graphplot_card(app, session)
177177
}
178178
});
179179
"""
180-
Bonito.evaljs(session, js)
181180

182181
nhh = NodeHoverHandler() do hstate, idx, event, axis
183182
tooltip_text[] = hstate ? sidx_to_str(VIndex(idx), app) : ""
@@ -220,7 +219,8 @@ function graphplot_card(app, session)
220219
<li><strong>Ctrl + Click</strong> resets axis after zoom</li>
221220
</ul>
222221
""")
223-
Card([WithConfig(fig; resize_to=:parent), help]; class="bonito-card graphplot-card")
222+
Card([WithConfig(fig; resize_to=:parent), help, gp_tooltip_js];
223+
class="bonito-card graphplot-card")
224224
end
225225
function _gracefully_extract_states!(vec, sol, t, idxs, rel)
226226
isvalid(s) = SII.is_variable(sol, s) || SII.is_parameter(sol, s) || SII.is_observed(sol, s)

NetworkDynamicsInspector/src/serving.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,11 +85,13 @@ function serve_app(::BrowserDisp, app)
8585
nothing
8686
end
8787
function close_display(::BrowserDisp; kwargs...)
88+
if haskey(BROWSER_STATE, :handler) && BROWSER_STATE[:handler] isa Bonito.HTTPServer.BrowserDisplay
89+
close(BROWSER_STATE[:handler])
90+
end
8891
BROWSER_STATE[:handler] = nothing
8992
end
9093

9194

92-
9395
function close_session(session)
9496
if !isnothing(session) && Base.isopen(session)
9597
@info "Close existing session"

NetworkDynamicsInspector/src/timeseries.jl

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,7 @@
11
function timeseries_col(app, session)
2-
col = DOM.div(
3-
timeslider_card(app),
4-
timeseries_cards(app, session),
5-
add_timeseries_button(app),
6-
class="timeseries-col"
7-
)
8-
92
# add js to update the active ts plot
103
onload_js = js"""
11-
(tscol) => {
4+
const tscol = document.querySelector(".timeseries-col");
125
tscol.addEventListener("click", function(event) {
136
// Find the closest .timeseries-card ancestor of the clicked element
147
const clickedCard = event.target.closest('.timeseries-card');
@@ -36,11 +29,15 @@ function timeseries_col(app, session)
3629
const triggerWindowResize_throttled = Bonito.throttle_function(triggerWindowResize, 100);
3730
const resizeObserver = new ResizeObserver(triggerWindowResize_throttled);
3831
resizeObserver.observe(tscol);
39-
}
4032
"""
41-
Bonito.onload(session, col, onload_js)
4233

43-
return col
34+
DOM.div(
35+
timeslider_card(app),
36+
timeseries_cards(app, session),
37+
add_timeseries_button(app),
38+
onload_js;
39+
class="timeseries-col"
40+
)
4441
end
4542

4643
function timeseries_cards(app, session)
@@ -55,38 +52,42 @@ function timeseries_cards(app, session)
5552
known_tsplots = collect(keys(cache))
5653
current_tsplots = collect(values(_tsplots))
5754

58-
# do nothing if the TSPlots objects themselve did not change
59-
Set(known_tsplots) == Set(current_tsplots) && return
60-
61-
# del_tsplots = setdiff(known_tsplots, current_tsplots)
62-
# new_tsplots = setdiff(current_tsplots, known_tsplots)
63-
# FIXME: it is not possible to redisplay the same tsplot, recreate all
64-
del_tsplots = known_tsplots
65-
new_tsplots = current_tsplots
55+
# do nothing if the TSPlots objects themselve did not change unless on update!
56+
if !isdefined(container, 0) || Set(known_tsplots) != Set(current_tsplots)
57+
# del_tsplots = setdiff(known_tsplots, current_tsplots)
58+
# new_tsplots = setdiff(current_tsplots, known_tsplots)
59+
# FIXME: it is not possible to redisplay the same tsplot, recreate all
60+
del_tsplots = known_tsplots
61+
new_tsplots = current_tsplots
62+
63+
for del in del_tsplots
64+
(; card, observerfunctions, plotqueue) = cache[del]
65+
close(plotqueue) # close plotqueue
66+
Observables.off.(observerfunctions) # disable all observer functions
67+
delete!(cache, del) # delete from cache
68+
end
69+
for new in new_tsplots
70+
key = only([key for (key, value) in _tsplots if value == new])
71+
ntup = timeseries_card(app, key, session)
72+
cache[new] = ntup
73+
end
6674

67-
for del in del_tsplots
68-
(; card, observerfunctions, plotqueue) = cache[del]
69-
close(plotqueue) # close plotqueue
70-
Observables.off.(observerfunctions) # disable all observer functions
71-
delete!(cache, del) # delete from cache
75+
cards = [cache[ts].card for ts in values(_tsplots)]
76+
# update the display by notifying the content
77+
container[] = DOM.div(cards; class="timeseries-stack")
7278
end
73-
for new in new_tsplots
74-
key = only([key for (key, value) in _tsplots if value == new])
75-
ntup = timeseries_card(app, key, session)
76-
cache[new] = ntup
77-
end
78-
79-
cards = [cache[ts].card for ts in values(_tsplots)]
80-
# update the display by notifying the content
81-
container[] = DOM.div(cards; class="timeseries-stack")
8279

8380
nothing
8481
end
8582

8683
# update the selected components in the graphplot when the active tsplot changes
8784
on(app.active_tsplot; update=true) do active
88-
activesel = app.tsplots[][active].selcomp[]
89-
app.graphplot._selcomp[] = activesel
85+
if haskey(app.tsplots[], active)
86+
activesel = app.tsplots[][active].selcomp[]
87+
app.graphplot._selcomp[] = activesel
88+
else
89+
app.active_tsplot[] = first(keys(app.tsplots[]))
90+
end
9091
end
9192

9293
return container
@@ -224,7 +225,7 @@ function timeseries_card(app, key, session)
224225
comp_ms_id = Bonito.JSString(comp_sel.id)
225226
state_ms_id = Bonito.JSString(state_sel.id)
226227

227-
js = js"""
228+
legendlike_style_js = js"""
228229
function colorListItems(items) {
229230
let styleTag = document.getElementById("dynamic-style-$(comp_ms_id)");
230231
@@ -294,7 +295,6 @@ function timeseries_card(app, key, session)
294295
linestyleListItems(c);
295296
});
296297
"""
297-
Bonito.evaljs(session, js)
298298

299299
fig, ax = with_theme(apptheme()) do
300300
fig = Figure(size=(100, 400))
@@ -401,7 +401,9 @@ function timeseries_card(app, key, session)
401401
@error "$key: Plotting failed for idx $(valid_idxs[])" e
402402
end
403403
end
404-
put!(plotqueue, task)
404+
if !is_precompiling()
405+
put!(plotqueue, task)
406+
end
405407
nothing
406408
end |> track_obsf
407409

@@ -444,6 +446,7 @@ function timeseries_card(app, key, session)
444446
card = Card(
445447
[DOM.div(
446448
comp_state_sel_dom,
449+
legendlike_style_js,
447450
DOM.div(WithConfig(fig; resize_to=:parent); class="timeseries-axis-container"),
448451
closebutton(app, key);
449452
class="timeseries-card-container"
@@ -473,7 +476,7 @@ function _plot_queue(key)
473476
rmt = take!(ch) # remove the executed task
474477
@assert istaskdone(rmt)
475478
end
476-
@info "Plot queue for $key closed"
479+
@debug "Plot queue for $key closed"
477480
end
478481
end
479482

NetworkDynamicsInspector/src/utils.jl

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,28 +27,34 @@ function gen_state_options(nw::Network, sidxs)
2727
options
2828
end
2929

30-
function clear_obs!(x::Union{NamedTuple, AbstractDict})
30+
function clear_obs_and_close!(x::Union{NamedTuple, AbstractDict})
3131
for v in values(x)
32-
clear_obs!(v)
32+
clear_obs_and_close!(v)
3333
end
3434
end
35-
function clear_obs!(obs::Observable)
35+
function clear_obs_and_close!(obs::Observable)
3636
empty!(obs.listeners)
37-
clear_obs!(obs.val)
37+
clear_obs_and_close!(obs.val)
3838
end
39-
function clear_obs!(v::AbstractVector)
39+
function clear_obs_and_close!(v::AbstractVector)
4040
for el in v
41-
clear_obs!(el)
41+
clear_obs_and_close!(el)
4242
end
4343
end
44-
function clear_obs!(x::T) where {T}
44+
function clear_obs_and_close!(x::T) where {T}
4545
if T <: Union{GraphPlot, TimeseriesPlot, AppState}
4646
for f in fieldnames(T)
47-
clear_obs!(getfield(x, f))
47+
clear_obs_and_close!(getfield(x, f))
4848
end
4949
end
5050
x
5151
end
52+
function clear_obs_and_close!(of::Observables.ObserverFunction)
53+
Observables.off(of)
54+
end
55+
function clear_obs_and_close!(ch::Channel)
56+
close(ch)
57+
end
5258

5359
NetworkDynamics.extract_nw(o::Observable) = extract_nw(o.val)
5460
function NetworkDynamics.extract_nw(o::NamedTuple)
@@ -155,3 +161,5 @@ function wait_for(queue)
155161
sleep(0.01)
156162
end
157163
end
164+
165+
is_precompiling() = ccall(:jl_generating_output, Cint, ()) == 1

0 commit comments

Comments
 (0)