khchen / wNim

Nim's Windows GUI Framework
MIT License
327 stars 17 forks source link

How can I expect to finish running a child thread without freezing the main thread window? #47

Closed rockcavera closed 4 years ago

rockcavera commented 4 years ago

I looked for it in the documents, but I didn't find it. I need to know how to work with more than one thread and keep the interface window usable, that is, without freezing.

Currently I use NiGui and as I ended up not compiling my application for other platforms, but only for Windows, I found the wNim more complete and I intend to change it.

With NiGui I practically use this code to wait for the thread to finish and keep the window without freezing and working the other events, buttons, boxes, etc.:

button_check.onClick = proc(event: ClickEvent) =
  button_other.enabled = false
  button_abort.enabled = true

  var thr: Thread[void]

  createThread(thr, check)

  while running(thr):
    app.sleep(1000)
khchen commented 4 years ago

Take a look at examples/printpreview.nim. The print() proc can run in another thread if you compile it with --threads:on .

rockcavera commented 4 years ago

Take a look at examples/printpreview.nim. The print() proc can run in another thread if you compile it with --threads:on .

That's not what I'm looking for.

rockcavera commented 4 years ago

https://docs.microsoft.com/en-us/windows/win32/winmsg/using-messages-and-message-queues#examining-a-message-queue

bunkford commented 4 years ago

I use channels to communicate between threads.

I then use wEvent_Timer to check if there are any messages to process from the thread.

rockcavera commented 4 years ago

I use channels to communicate between threads.

I then use wEvent_Timer to check if there are any messages to process from the thread.

The timer idea is very good. I believe it is better than creating a secondary event loop with PeekMessage.

Thanks. I will test.

khchen commented 4 years ago

If you are sure what you want to do is GC safe, you can write this:

import threadpool, os
import wNim/[wApp, wFrame, wButton, wStatusBar]

let app = App()
let frame = Frame(title="wNim with Threads", size=(400, 300))
let button = Button(frame, label="Press Me to Wait a Second")
let statusBar = StatusBar(frame)

frame.center()
frame.show()

proc thread() =
  {.gcsafe.}:
    for i in 1..10:
      frame.statusBar.label = $i
      os.sleep(100)

    button.enable()

button.wEvent_Button do ():
  button.disable()
  spawn thread()

app.mainLoop()

Or you can pass message by Win32 API SendMessage(). For example:

import threadpool, os
import wNim/[wApp, wFrame, wButton, wStatusBar]
import winim/lean

const wEvent_SetStatus = wEvent_App + 1
const wEvent_ThreadEnd = wEvent_App + 2

let app = App()
let frame = Frame(title="wNim with Threads", size=(400, 300))
let button = Button(frame, label="Press Me to Wait a Second")
let statusBar = StatusBar(frame)

frame.center()
frame.show()

frame.wEvent_SetStatus do (event: wEvent):
  statusBar.label = $cast[cstring](event.wParam)

frame.wEvent_ThreadEnd do ():
  button.enable()

proc thread(hFrame: HWND) =
  for i in 1..10:
    SendMessage(hFrame, wEvent_SetStatus, cast[WPARAM](cstring $i), 0)
    os.sleep(100)

  SendMessage(hFrame, wEvent_ThreadEnd, 0, 0)

button.wEvent_Button do ():
  button.disable()
  spawn thread(frame.handle)

app.mainLoop()
khchen commented 4 years ago

I posted more examples in wNim's wiki page. https://github.com/khchen/wNim/wiki/Create-and-Communicate-Window-in-Child-Thread