I am able to produce a window which has a widget (button) and a canvas which is ‘drawing’ a GR plot. The problem is that on display, calling the ‘draw’ function again, does not result in an updated plot. The button can listen for signals of “clicked” and that function handler can respond by calling the draw function, but that does not update the plot in the canvas. Now, if the window is resized the plot then updates and redraws/refreshes.
This code here displays the effect:
using Gtk.ShortNames, Gtk.GConstants
using Printf
using GR
#counter to track the draw calls
global drawInt = 0
function draw(widget)
ctx = Gtk.getgc(widget)
ENV["GKS_WSTYPE"] = "142"
ENV["GKSconid"] = @sprintf("%lu", UInt64(ctx.ptr))
#random data to plot that gets shifted with every draw call
scatter( randn(100) .+ drawInt , rand(100) .+ drawInt )
global drawInt
drawInt += 1
println("in draw fn drawInt=$(drawInt)")
end
#put a canvas for the plot and a button
win = Window("Gtk") |> (bx = Box(:v))
canvas = Canvas(600, 450)
push!(bx, canvas)
bt = Button("ok")
push!(bx, bt)
function on_button_clicked(w)
ctx = Gtk.getgc(canvas)
draw(ctx)
println("in button fn drawInt=$(drawInt)")
end
signal_connect(on_button_clicked, bt, "clicked")
canvas.draw = draw
Gtk.showall(win)
There are some println statement so that the calls to the draw and button functions can be tracked as the canvas plot fails to update. Press the button multiple times to see the calls and updates to the global drawInt counter. Now, resize the window with the mouse, or click on another application window pane (eg browser) and you will see plot space update (the counter is a shift for the dummy data).
How can the update to the plot be performed without requiring a window pane event?
(from the Gtk3 developer guide something similar is stated-> Gtk – 3.0, The contents of a widget often need to be partially or fully redrawn, e.g. when another window is moved and uncovers part of the widget, or when the window containing it is resized. It is also possible to explicitly cause part or all of the widget to be redrawn, by calling gtk_widget_queue_draw() or its variants. GTK+ takes care of most of the details by providing a ready-to-use cairo context to the ::draw signal handler)
I have run the code, and see the same effect as I have described. I don’t know if it runs differently on your system (I’m on Debian9), but the button registers the clicks, the function scopes appear to be running their inner statements, but the update to that red rectangle you put, does not appear until a window pane event/signal is received
that is with Cairo, and the goal is to have the ability to include figures from GR or Plots in general so that numerical investigations in a gui do not require ground up work from Cairo
I don’t know if it is the same on your system, but running your code multiple times shows that the drawing of the rescalled rectangle only occurs after a window pane event signal and not due to a widget event signal
As you imply, the canvas draw function is invoked by events like resizing the parent window,
so probably that is not where you should update the plot contents. Consider maintaining global buffers for data to be plotted, and have the draw function use that data, but otherwise as you have it above. The on_button_clicked method would then update the global data buffers, then force a redraw.
I am unsure how to do that within the Gtk.jl package. The Gtk3 developer guides at something along the lines of what you say with gtk_widget_queue_draw() but I don’t see how I can do that within Gtk.jl. Or can I access that global buffer with a direct call?
I just meant some global Julia variables (I suspect there are ways to attach data to Gtk widgets, but have no experience there). This is what I had in mind:
canvas = nothing
#counter to track the draw calls
drawInt = 0
const xvals = Ref(zeros(0))
const yvals = Ref(zeros(0))
function newplot(widget)
global drawInt
drawInt += 1
#random data to plot that gets shifted with every call
xvals[] = randn(100) .+ drawInt
yvals[] = rand(100) .+ drawInt
end
function mydraw(widget)
ctx = Gtk.getgc(widget)
ENV["GKS_WSTYPE"] = "142"
ENV["GKSconid"] = @sprintf("%lu", UInt64(ctx.ptr))
scatter( xvals[], yvals[] )
println("in draw fn drawInt=$(drawInt)")
end
function on_button_clicked(w)
newplot(w)
ctx = Gtk.getgc(canvas)
draw(canvas)
println("in button fn drawInt=$(drawInt)")
end
function runme()
#put a canvas for the plot and a button
win = Window("Gtk") |> (bx = Box(:v))
global canvas
canvas = Canvas(600, 450)
push!(bx, canvas)
bt = Button("ok")
push!(bx, bt)
canvas.draw = mydraw
newplot(canvas)
signal_connect(on_button_clicked, bt, "clicked")
Gtk.showall(win)
end
Edit: this builds on the suggestions from @lobingera
Did you run the example in an new julia session (include)? As there are already a list of functions with the name draw defined (in your code and in Gtk) this might lead to some ambiguity.
I remember having a similar problem and it was back then subject of ‘wrong’ binding of draw.
Gtk.draw(widget) calls reveal on the widget and that ccalls gtk_widget_queue_draw so it puts a signal into Gtk’s queue for redrawing. That’s actually the same way how the resize/uncover works.
Technically GR/Plots also renders with Cairo (as actually you cannot do anything else in GtkCanvas) - so you use the same interface.
After doing some more exploration I see the meaning of your sentence
“Technically GR/Plots also renders with Cairo (as actually you cannot do anything else in GtkCanvas) - so you use the same interface.”
Question, would it maybe be a more flexible approach to consider the canvas widgets as regions for Cairo drawings? And somehow convert plots into Cairo drawings? Where maybe we go from a plot to imshow? Would that maybe be too slow? but it would avoid problems correct?
Someone else ran your code and it… did solve the problem as well; thanks alot*! I don’t know why it exhibited different behavior at first. The only thing is that the resizing still affects the size of the rectangle
I’m not fully sure if i understand you question(s)
but there are alternative plotting packages like Gadfly and Winston that allow you to plot scatter and line plots using Cairo in the process.
if you are interested in seeing the sequence of path construction and filling/drawing in Cairo you can use a CairoScriptSurface to write coordinates and operators to a file.
I just used globals to make things visible to all methods. It would be much better to use a composite widget which includes all the needed data as properties; that would take more effort to design.
The const variables here are Ref types, and act like pointers so the vector content can be replaced. This pattern can help for efficiency and prevents some programmer error. This usage is not well documented, and should perhaps be discouraged since it’s not naturally thread-safe.
Although one could probably consolidate the functions, the separation simplifies the logic.