Failed to push! errors with 'checkboxes'
tomerarnon opened this issue · 4 comments
This is an error I have so far only encountered with checkboxes when they are somehow associated with draw methods.
Upon pressing the checkbox
for the first time, I receive this error message and all of the signals stop updating. i.e. sliders no longer update, other buttons no longer update their values, etc.
Failed to push!
false
to node
input-2: Signal{Bool}(false, nactions=3)
error at node: map(input-2)-2: Signal{Void}(nothing, nactions=0)
MethodError: Cannot `convert` an object of type Gtk.GtkCanvas to an object of type Void
This may have arisen from a call to the constructor Void(...),
since type constructors fall back to convert methods.
in send_value!(::Reactive.Signal{Void}, ::Gtk.GtkCanvas, ::Int64) at C:\Users\Tomer\.julia\v0.5\Reactive\src\core.jl:154
in (::Reactive.##25#26{##5#6{GtkReactive.Checkbox,GtkReactive.Canvas{GtkReactive.UserUnit},Reactive.Signal{Array{ColorTypes.RGB{FixedPointNumbers.Normed{UInt8,8}},2}},Reactive.Signal{GtkReactive.ZoomRegion{RoundingIntegers.RInt64}}},Tuple{Reactive.Signal{Bool}}})(::Reactive.Signal{Void}, ::Int64) at C:\Users\Tomer\.julia\v0.5\Reactive\src\operators.jl:41
in do_action(::Reactive.Action, ::Int64) at C:\Users\Tomer\.julia\v0.5\Reactive\src\core.jl:162
in run(::Int64) at C:\Users\Tomer\.julia\v0.5\Reactive\src\core.jl:244
in (::Reactive.##17#19)() at .\task.jl:360
This code I am trying to run is basically:
imagesignal = map(slider, button) do s, b
get_image(s, b)
end
show_image = map(button) do b
if b
display_image(canvas, imagesignal, zoom_region)
setproperty... #visible
else
setproperty... # not visible
end
end
(i.e. only draw to the canvas if the button is pressed, but always check the slider value to update which image should be drawn.)
Where display_image
creates a map(zoom_region, imagesignal)
block that returns a signal of the zoomed image and a draw(canvas, imgsig)
block that copy!()
s the image into the canvas.
I can't say for sure whether I first ran into this before or after updating GtkReactive (it was yesterday), but I've since tried downgrading to older versions of GtkReactive/Reactive with no luck. Is the issue with the checkbox
, with Reactive, or with neither?
Looks like a mapped function is sometimes returning a Bool
and sometimes returning nothing
(of type Void
). Maybe check to make sure that show_image = map(button) do b ...
always returns a consistent type? Or you could use map(button; typ=Any)
.
Unfortunately, I don't think that this is the problem. I have written a brief example to help illustrate what is going on in my code. It functions just like the problem discussed, i.e., one function selects an image based on a slider value, and another two functions deal with the drawing based on the other signals. Pushing the checkbox
should make the image disappear and reappear. Instead, the image disappears the first time, and signals stop working subsequently.
One possibility is that the if clicked
statement isn't the correct way of handling signals which should be idle?
In any case, as you can see, the function select_img
always returns a consistent type.
using Gtk.ShortNames, GtkReactive, TestImages
function select_img(n ::Int)
if n < 50
return testimage("lighthouse");
else n >= 50
return testimage("mountainstream.png")
end
end
function display_image(cnvs ::GtkReactive.Canvas{GtkReactive.UserUnit},
img ::Reactive.Signal{Array{ColorTypes.RGB{FixedPointNumbers.Normed{UInt8,8}},2}},
zoom_region ::Reactive.Signal{GtkReactive.ZoomRegion{RoundingIntegers.RInt64}},)
imgsig = map(zoom_region, img) do r, i
cv = r.currentview
view(i, UnitRange{Int}(cv.y), UnitRange{Int}(cv.x))
end
redraw = draw(cnvs, imgsig) do c, image
copy!(c, image)
set_coords(c, value(zoom_region))
end
end
function make_image_visible(btn ::GtkReactive.Checkbox,
cnvs ::GtkReactive.Canvas{GtkReactive.UserUnit},
img ::Reactive.Signal{Array{ColorTypes.RGB{FixedPointNumbers.Normed{UInt8,8}},2}},
zoom_region ::Reactive.Signal{GtkReactive.ZoomRegion{RoundingIntegers.RInt64}})
show = map(btn) do clicked
println(clicked);
if clicked
display_image(cnvs, img, zoom_region)
setproperty!(widget(cnvs), :visible, true)
else
setproperty!(widget(cnvs), :visible, false)
end
end
end
###setup###
win = Window("Image");
c = canvas(UserUnit, 450,300);
setproperty!(widget(c), :expand, true)
g = Grid();
cb = checkbox(true, label="lighthouse");
s = slider(1:100)
box = Box(:v, visible=true,
border_width = 10, spacing = 10);
push!(box, cb);
push!(box, s);
g[1,2] = widget(c)
g[1,1] = box
push!(win, g)
zr = Signal(ZoomRegion(Array{Any,2}(512,768)));
zoomsigs1 = init_zoom_rubberband(c, zr);
########
image = map(signal(s)) do n
select_img(n);
end
draw_c = make_image_visible(cb, c, image, zr)
showall(win)
There are a couple of issues here:
- Remember that Reactive signals, if garbage-collected (GCed), stop functioning. The
redraw
signal ofdisplay_image
gets dropped and therefore is vulnerable to GC. - You probably don't want to create new Signals every time the user clicks on the checkbox---the "graph" should largely be static in your GUI, and any changes are made by
push!
ing new values to "old" signals. You can create the signals once and make drawing conditional on the state of the checkbox viafilterwhen
.
Since I don't think this has anything specific to do with GtkReactive (it's more of a Reactive issue), I'm going to close this (but feel free to complain if you disagree or find other related problems).
BTW, if you don't know about it you might be interested in JuliaImages/ImageView.jl#115 (testers wanted 😉 )
Thanks for the reply; it was very helpful and enlightening. I hesitate to reopen this, but I want to make sure I get this right. The further along I progress the harder it will be to correct trivial but dire errors like this. I've rewritten the example, below. In this case I am returning the various signals so that they are recorded globally. Is this what you meant?
Also, thank you for the tip about ImageView.jl
. I'll check it out!
function image_to_canvas(imgsig::Reactive.Signal{Array{ColorTypes.RGB{FixedPointNumbers.Normed{UInt8,8}},2}},
zr ::Reactive.Signal{GtkReactive.ZoomRegion{RoundingIntegers.RInt64}},
btn::GtkReactive.Checkbox,
c ::GtkReactive.Canvas)
btnsig = map(btn) do b
setproperty!(c, :visible, b)
end
imgzoom = map(zr, imgsig) do r, i
cv = r.currentview
view(i, UnitRange{Int}(cv.y), UnitRange{Int}(cv.x))
end
drawsig = draw(c, imgzoom) do canvas, i
copy!(canvas, i)
set_coords(c, value(zr))
end
drawsig = filterwhen(signal(btn), value(drawsig), drawsig);
return btnsig, imgzoom, drawsig
end
function select_img(n ::Int)
if n < 50
return testimage("lighthouse");
else n >= 50
return testimage("mountainstream.png")
end
end
win = Window("Image");
c = canvas(UserUnit, 450,300);
setproperty!(widget(c), :expand, false)
g = Grid();
cb = checkbox(true, label="lighthouse");
s = slider(1:100)
box = Box(:v, visible=true,
border_width = 10, spacing = 10);
push!(box, cb);
push!(box, s);
g[1,2] = widget(c)
g[1,1] = box
push!(win, g)
zr = Signal(ZoomRegion(Array{Any,2}(512,768)));
zoomsigs = init_zoom_rubberband(c, zr);
image = map(signal(s)) do n
select_img(n);
end
imgsig = filterwhen(signal(cb), image, image)
btnsig, imgzoom, drawsig = image_to_canvas(imgsig, zr, cb, c)
showall(win)
I greatly appreciate the help, even though I know this isn't really an issue with GtkReactive.jl
but entirely "user error". I'm surprised any of this ever worked at all, honestly, but I guess that's a common problem when you're programming as a rookie 😜.