Hi. I'm trying to make a simple gui app that performs functions at the press of a button. Of course, func is called in the main thread, so gui is freezing. I tried use the threadpool module, but I do something wrong. From what I understood, to read the result from thread, I have to use ^, but this causes freezing gui. When I calls without ^ works well, but how then read return? This is my simple code, I used nigui.
import strutils
import threadpool
import nigui
import os
app.init()
var window = newWindow("Win")
window.width = 600.scaleToDpi
window.height = 400.scaleToDpi
var container = newLayoutContainer(Layout_Vertical)
window.add(container)
var button = newButton("Start")
var textArea = newTextArea()
container.add(button)
container.add(textArea)
proc doSomthing(time: int): string {.thread.} =
sleep(time)
return "end"
button.onClick = proc(event: ClickEvent) =
var t = spawn doSomthing(5000)
textArea.addLine(^t)
window.show()
app.run()
If you do ^t just after spawn, you are effectively not using the worker thread as the UI thread is blocked waiting the worker thread to finish.
What you should do is to spawn a thread and inside the worker thread notify the UI thread that "t" is ready, so UI thread can then update "textArea".
This is easier said than done depending on UI library though. Most UI libraries have a queue where other threads can push closures to be executed by the main/UI thread.
Another possibility is to avoid the creation of the worker thread. If the library provides a way to mix async-eventlopp and UI mainloop, you can try to use async/await to avoid locking the main thread too.
I have not used nigui but libui (ui in nimble) provides both ways with functions:
If your threads are long-living and you are okay to talk in json between threads. You can make use of threadproxy library.
import strutils
import nigui
import os
import threadproxy
proc workerMain(proxy: ThreadProxy) {.thread.} =
proxy.onData "task1":
let n = data.getInt()
echo "block for ", n
sleep n # compute work
# await sleepAsync n # IO work
echo "wake"
result = %"some text"
waitFor proxy.poll()
proc main() =
# setup thread
let proxy = newMainThreadProxy("main")
proxy.createThread("worker", workerMain)
app.init()
var window = newWindow("Win")
window.width = 600.scaleToDpi
window.height = 400.scaleToDpi
var container = newLayoutContainer(Layout_Vertical)
window.add(container)
var button = newButton("Start")
var textArea = newTextArea()
container.add(button)
container.add(textArea)
button.onClick = proc(event: ClickEvent) =
let fut = proxy.ask("worker", "task1", %5000)
fut.addCallback:
if fut.failed:
# handler error here
echo fut.readError.msg
else:
let json = fut.read
textArea.addLine(json.getStr())
window.show()
while true:
app.processEvents()
while proxy.process():
discard
sleep 16
when isMainModule:
main()
@jackhftang looks promising :) I haven't seen this library before (unfortunately awesome nim is not updated). I'm sure I'll try to use it
@aguspiza2 ah, now it's all clear ;) I saw libui, but it looked 'unfriendly' compared to nigui (at least for me) but, of course, i also try. Thanks for explaining ;)