I'm trying to use threads because I want to write messages in textArea and these messages are generated by a proc called by click a button:
import nigui
import std/strformat
import os
var ch: Channel[string]
proc messaggiInTextArea(primoMsg: string) {.thread.} =
ch.send(primoMsg)
for x in 1..10:
ch.send(&"Messaggio n°: {x}")
sleep(1000)
ch.send("Dopo il ciclo for")
ch.send("")
app.init()
var window = newWindow("Test Thread")
var container = newLayoutContainer(Layout_Vertical)
window.add(container)
var buttonSave = newButton("Esegui test")
container.add(buttonSave)
var areaMessaggi = newTextArea()
areaMessaggi.editable=false
container.add(areaMessaggi)
buttonSave.onClick = proc(event: ClickEvent) =
var th: Thread[string]
areaMessaggi.text=""
areaMessaggi.addLine("Inizia il test: prima della chiamata alla procedura")
ch.open()
createThread(th, messaggiInTextArea, "Prima chiamata dalla proc")
while true:
let msg = ch.recv()
if msg == "":
break
areaMessaggi.addLine(msg)
joinThreads(th)
ch.close()
areaMessaggi.addLine("Fine del test: dopo la chiamata alla procedura")
window.show()
app.run()
the result it is that all messages are written in the same time to the end the proc.
Since I'm not very expert in threads, it will be clear to most of you why what I've done doesn't work.
Could you explain why and give me an alternative solution?
Thank you all
Hi.
When I run your test program (under Linux), the messages don't appear all at once.
One small suggestion for improvement: The application is not very response, because recv() waits until something is received. You can use tryRecv() in combination with app.sleep() instead.
import nigui import std/strformat import os var ch: Channel[string] proc messaggiInTextArea(primoMsg: string) {.thread.} = ch.send(primoMsg) for x in 1..10: ch.send(&"Messaggio n°: {x}") sleep(1000) ch.send("Dopo il ciclo for") ch.send("") app.init() var window = newWindow("Test Thread") var container = newLayoutContainer(Layout_Vertical) window.add(container) var buttonSave = newButton("Esegui test") container.add(buttonSave) var areaMessaggi = newTextArea() areaMessaggi.editable=false container.add(areaMessaggi) buttonSave.onClick = proc(event: ClickEvent) = var th: Thread[string] areaMessaggi.text="" areaMessaggi.addLine("Inizia il test: prima della chiamata alla procedura") ch.open() createThread(th, messaggiInTextArea, "Prima chiamata dalla proc") while true: let (received, msg) = ch.tryRecv() if received: if msg == "": break areaMessaggi.addLine(msg) else: app.sleep(50) joinThreads(th) ch.close() areaMessaggi.addLine("Fine del test: dopo la chiamata alla procedura") window.show() app.run()
Hi!
That's the general issue of retained GUI's that's powered by some kind of event loop (Qt, VCL, Tcl/Tk and etc.).
Also you shouldn't change (call methods of, or change member values) of GUI elements from other threads than main, that's the fast way to get race condition or/and segfault in retained mode GUI, because main GUI thread can render/change that elements in parallel (concurently) from the main thread. That's definately an error. There's some way to schedule some callback on the main GUI thread in general or signal/slots (Qt) or global mutex acquire (FLTK).
Your callback for buttonSave.onClick occupies exclusively event loop till callback finishes. In the same time GUI update is taking place in the same event loop, so your GUI is not updated until event loop gets out of button callback.
Hope I explain the issue understandable in some degree. I have no expirience with Nim or NiGui so I might be wrong, take my answer with the grain of salt.
In each retained GUI framework there is some way to call event loop scheduling from callback to process pending events (e.g. update GUI). Looks like it's app.processEvents() in NiGui (https://github.com/simonkrauter/NiGui/blob/master/src/nigui.nim#L472C1-L472C30)
Do note that this design considered as antipattern because if you click again on the button, another instance of callback would be executed concurrently and things gets pretty messy and may lead to segfault if there's some callback-local data that starts changing from all pending callbacks, that schedules in some kind of app.processEvents().
Please take a look at example in NiGui repo: https://github.com/simonkrauter/NiGui/blob/master/examples/example_15_threading.nim#L31C1-L31C26 IMHO it's better to schedule callback on event loop (GUI main thread) from you worker thread with app.queueMain.
Thanks for your reply and your suggestion.
I modified my code and so it works perfectly
Thanks for your precise explanation.
Now I have to understand how to adapt example 15 to what I have to do.