Skip to content

Commit 226fb4c

Browse files
authored
Merge pull request #12 from JuliaImageRecon/nh/observables
Move from custom listeners logic to observables
2 parents 1dcab5a + 35646b6 commit 226fb4c

File tree

9 files changed

+151
-175
lines changed

9 files changed

+151
-175
lines changed

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,17 @@ version = "0.3.6"
55

66
[deps]
77
LRUCache = "8ac3fa9e-de4c-5943-b1dc-09c6b5f20637"
8+
Observables = "510215fc-4207-5dde-b226-833fc4488ee2"
89
Scratch = "6c6a2e73-6563-6170-7368-637461726353"
910
TOML = "fa267f1f-6049-4f14-aa54-33bafae1ed76"
1011
ThreadPools = "b189fb0b-2eb5-4ed4-bc0c-d34c51242431"
1112

1213
[compat]
14+
LRUCache = "1.6"
15+
Observables = "0.5.5"
1316
Scratch = "1.2"
1417
TOML = "1"
1518
ThreadPools = "2.1"
16-
LRUCache = "1.6"
1719
julia = "1.9"
1820

1921
[extras]

src/AbstractImageReconstruction.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ module AbstractImageReconstruction
22

33
using TOML
44
using ThreadPools
5+
using Observables
56
using Scratch
67
using LRUCache
78

src/RecoPlans/Cache.jl

Lines changed: 6 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,14 +18,13 @@ end
1818

1919
function clear!(plan::RecoPlan{<:ProcessResultCache}, preserve::Bool = true)
2020
dict = getfield(plan, :values)
21-
set = getfield(plan, :setProperties)
2221
for key in keys(dict)
23-
value = dict[key]
22+
value = dict[key][]
23+
# Dont remove cache when clearing and preserving structure
2424
if typeof(value) <: RecoPlan && preserve
2525
clear!(value, preserve)
2626
else
27-
dict[key] = missing
28-
set[key] = false
27+
dict[key] = Observable{Any}(missing)
2928
end
3029
end
3130
return plan
@@ -34,23 +33,15 @@ end
3433
# Make cache transparent for property getter/setter
3534
function Base.setproperty!(plan::RecoPlan{<:ProcessResultCache}, name::Symbol, value)
3635
if in(name, [:param, :cache, :maxsize])
37-
old = getproperty(plan, name)
38-
setvalue!(plan, name, value)
39-
getfield(plan, :setProperties)[name] = true
40-
for listener in getlisteners(plan, name)
41-
try
42-
propertyupdate!(listener, plan, name, old, x)
43-
catch e
44-
@error "Exception in listener $listener " e
45-
end
46-
end
36+
t = type(plan, name)
37+
getfield(plan, :values)[name][] = validvalue(plan, t, value) ? value : convert(t, x)
4738
else
4839
setproperty!(plan.param, name, value)
4940
end
5041
end
5142
function Base.getproperty(plan::RecoPlan{<:ProcessResultCache}, name::Symbol)
5243
if in(name, [:param, :cache, :maxsize])
53-
return getfield(plan, :values)[name]
44+
return getfield(plan, :values)[name][]
5445
else
5546
return getproperty(plan.param, name)
5647
end

src/RecoPlans/LinkedFieldListener.jl

Lines changed: 0 additions & 28 deletions
This file was deleted.
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
export LinkedPropertyListener
2+
mutable struct LinkedPropertyListener{T<:AbstractImageReconstructionParameters} <: AbstractPlanListener
3+
plan::RecoPlan{T}
4+
field::Symbol
5+
fn::Function
6+
active::Bool
7+
end
8+
9+
"""
10+
LinkedPropertyListener(fn, target::RecoPlan, targetProp, source::RecoPlan, sourceProp)
11+
12+
Connect two properties of `RecoPlans`. Set `target.targetProp` to `fn(source.sourceProp)` whenever `source.sourceProp` changes and `target.targetProp` was not changed outside of the listener.
13+
"""
14+
function LinkedPropertyListener(fn::Function, target::RecoPlan, targetProp::Symbol, source::RecoPlan, sourceProp::Symbol)
15+
listener = LinkedPropertyListener(source, sourceProp, fn, true)
16+
17+
# Attach the listener to the target property
18+
# We can serialize the listener from the target property and store the source property
19+
on(listener, target, targetProp)
20+
21+
# Attach callback to the source property
22+
on(source, sourceProp) do x
23+
if listener.active
24+
setproperty!(target, targetProp, fn(x))
25+
# Set active to true again, because the flag is changed by the target listener
26+
listener.active = true
27+
end
28+
end
29+
30+
return listener
31+
end
32+
(listener::LinkedPropertyListener)(val) = listener.active = false
33+
34+
35+
function toDictValue!(dict, value::LinkedPropertyListener)
36+
dict["plan"] = string.(parentproperties(value.plan))
37+
dict["field"] = string(value.field)
38+
dict["fn"] = toDict(value.fn)
39+
end
40+
41+
function loadListener!(::Type{LinkedPropertyListener}, target::RecoPlan, targetProp, dict, args...)
42+
# Find the root plan
43+
root = parent(target)
44+
while !isnothing(parent(root))
45+
root = parent(root)
46+
end
47+
48+
# From the root plan, find the source plan
49+
source = root
50+
for param in dict["plan"][1:end]
51+
source = getproperty(plan, Symbol(param))
52+
end
53+
sourceProp = Symbol(dict["field"])
54+
55+
# Retrieve the function
56+
fn = tomlType(dict["fn"], modDict)
57+
return LinkedPropertyListener(fn, target, targetProp, source, sourceProp)
58+
end

src/RecoPlans/Listeners.jl

Lines changed: 5 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,9 @@
1-
export TransientListener, SerializableListener
2-
abstract type TransientListener <: AbstractPlanListener end
3-
abstract type SerializableListener <: AbstractPlanListener end
1+
export AbstractPlanListener
2+
abstract type AbstractPlanListener end
43

54
const LISTENER_TAG = "_listener"
65

7-
export propertyupdate!, valueupdate
8-
function propertyupdate!(listener::AbstractPlanListener, origin, field, old, new)
9-
# NOP
10-
end
11-
function valueupdate(listener::AbstractPlanListener, origin, field, old, new)
12-
# NOP
13-
end
6+
Observables.on(f, plan::RecoPlan, field::Symbol; kwargs...) = on(f, getfield(plan, :values)[field]; kwargs...)
7+
Observables.off(plan::RecoPlan, field::Symbol, f) = off(f, getfield(plan, :values)[field])
148

15-
export getlisteners, addListener!, removeListener!
16-
getlisteners(plan::RecoPlan, field::Symbol) = getfield(plan, :listeners)[field]
17-
function addListener!(plan::RecoPlan, field::Symbol, listener::AbstractPlanListener)
18-
listeners = getlisteners(plan, field)
19-
push!(listeners, listener)
20-
end
21-
function removeListener!(plan::RecoPlan, field::Symbol, listener::AbstractPlanListener)
22-
listeners = getlisteners(plan, field)
23-
idx = findall(x->isequal(x, listener), listeners)
24-
isnothing(idx) && deleteat!(listeners, idx)
25-
end
26-
27-
include("LinkedFieldListener.jl")
9+
include("LinkedPropertyListener.jl")

0 commit comments

Comments
 (0)