Refreshing Gtk widget from inside a loop


#1

I placed within the main GtkWindow a GtkEntry widget to show the step position (what is already done) within a running function. However it shows only the fist and the last step position. And I could not figure out how to write the concrete code.
The program structure is the following :

function main()
ui = Builder(filename =“D:\WORK\main.glade](//WORK//main.glade)”)
some code lines
signal_connect(x ->; run_job(), ui[“run”], “clicked”) # function where the GtkEntry widget is
some more code line
showall(ui[“win_main”]);
end

The run_job function looks like:

function run_job()
do step 1
set_gtk_property!(ui[“verbose_info”], :text, “run_job(): step 1 done”);

    do step 2

set_gtk_property!(ui[“verbose_info”], :text, “run_job(): step 2 done”);

   do step n

set_gtk_property!(ui[“verbose_info”], :text, “run_job(): step n done”);
end

Whenever the function run_job() starts I get the message within the verboase_info
GtkEntry: “run_job(): step 1 done” and when the function comes to the end “run_job(): step n done”.
No steps in between are displayed.
Therefore my question is how to manage that all steps are shown during the run of run_job().
At which place in my code I have to insert the necessary lines?

Thank you for any advice


#2

ui = Builder(filename =“D:\WORK\main.glade](//WORK//main.glade)”)

should read:

ui = Builder(filename =“D:\WORK\main.glade")


#3

Hi Carsten,

this is a really tricky one. The issue is that Julia runs on a single thread and the the UI is frozen when you are in a callback. I have recently developed a workaround for this but it requires quite some code. Since I have not so much experience with Tasks it would be great if someone else could shime in here and tell us if we have some major limitation in Julia (or its Task machinery) or if the issue is with Gtk.jl or if we just have not found the right user code to solve it.

Cheers, Tobi


#4

Here is more or less my workaround. The callback is what would be called in signal_connect:

mutable struct MyState
  task::Union{Task,Nothing}
  someCounter::Int
end

function asyncDo()
  state = MyState(nothing, 0)
  state.task = Task(()->asyncDoInner(state))
  schedule(state.task)
  return state
end

function asyncDoInner(state::MyState)
    for i=1:100
       # do something
      state.someCounter = i
      sleep(0.01) # I think the yield suffices
      yield()
    end
end

# the following function is the callback that is invoked called by signal_connect
function callback()
  state = asyncDo()
  timer = nothing
  function update(::Timer)
    if Base.istaskfailed(state.task)
      close(timer)
      error("something is wrong")
      return
    end
    # Here the UI should be update. Access state.someCounter to get the progress
    if istaskdone(state.task)
      close(timer)
    end
    return
  end
  timer = Timer(update, 0.0, interval=0.1)
  return
end

#5

Thank you for your prompt reply!
I’ll give it a try beginning of January. All I’m left with is:
A Happy New Year and best wishes!

Carsten