added threading examples
This commit is contained in:
parent
a0b5d10f6e
commit
06dd5e6ed9
5 changed files with 243 additions and 0 deletions
156
README.adoc
156
README.adoc
|
|
@ -2711,6 +2711,37 @@ proc main =
|
||||||
main()
|
main()
|
||||||
----
|
----
|
||||||
|
|
||||||
|
== A VTE example
|
||||||
|
|
||||||
|
The following code shows how we can use the vte library to create a plain shell window:
|
||||||
|
|
||||||
|
[[vte.nim]]
|
||||||
|
[source,nim]
|
||||||
|
.vte.nim
|
||||||
|
----
|
||||||
|
# https://vincent.bernat.im/en/blog/2017-write-own-terminal
|
||||||
|
import gintro/[gtk, glib, gobject, gio, vte]
|
||||||
|
|
||||||
|
proc appActivate(app: Application) =
|
||||||
|
let window = newApplicationWindow(app)
|
||||||
|
window.title = "GTK3 & Nim"
|
||||||
|
window.defaultSize = (600, 200)
|
||||||
|
let terminal = newTerminal()
|
||||||
|
let environ = getEnviron()
|
||||||
|
let command = environ.environGetenv("SHELL")
|
||||||
|
var pid = 0
|
||||||
|
echo terminal.spawnSync({}, nil, [command], [], {SpawnFlag.leaveDescriptorsOpen}, nil, nil, pid, nil)
|
||||||
|
window.add(terminal)
|
||||||
|
showAll(window)
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
let app = newApplication("org.gtk.example")
|
||||||
|
connect(app, "activate", appActivate)
|
||||||
|
discard run(app)
|
||||||
|
|
||||||
|
main()
|
||||||
|
----
|
||||||
|
|
||||||
== A GStreamer example
|
== A GStreamer example
|
||||||
|
|
||||||
Recently someone asked about gstreamer support for gintro, see https://github.com/StefanSalewski/gintro/issues/59 .
|
Recently someone asked about gstreamer support for gintro, see https://github.com/StefanSalewski/gintro/issues/59 .
|
||||||
|
|
@ -2744,6 +2775,131 @@ proc main =
|
||||||
main()
|
main()
|
||||||
----
|
----
|
||||||
|
|
||||||
|
== Threading Examples
|
||||||
|
|
||||||
|
A common problem with GTK GUI's is the fact that functions that run for a longer time period in the
|
||||||
|
GTK main thread can block the GUI for display updates or user input. We can solve this problem
|
||||||
|
by creating a separate thread which runs these functions in the background.
|
||||||
|
|
||||||
|
The GTK GUI is not really thread safe, so we can not update the GUI from other threads directly. An
|
||||||
|
easy way to solve this problem is to use the glib functions g-idle-add() or g-timeout-add().
|
||||||
|
|
||||||
|
https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-idle-add
|
||||||
|
|
||||||
|
https://developer.gnome.org/glib/stable/glib-The-Main-Event-Loop.html#g-timeout-add
|
||||||
|
|
||||||
|
The glib function g-timeout-add() adds a user defined function to the GTK main loop that is called
|
||||||
|
periodically, and the function g-idle-add() adds a user defined function to the GTK main loop
|
||||||
|
that is called when GTK is not busy with other stuff. Both functions are executed in the GTK main
|
||||||
|
thread, so both can update GUI widgets or call other GTK related functions.
|
||||||
|
|
||||||
|
In our first example we create a Nim worker thread that sends data over a Nim channel to the main
|
||||||
|
thread. We use timeoutAdd() to call periodically a function that reads data from the channel and
|
||||||
|
updates the GUI. In this example the worker thread only does a plain countdown -- in a real
|
||||||
|
work example it would do the hard work in the background, i.e. running a chess engine to find the
|
||||||
|
next best move or sorting a large data base. The function updateGUI() receives the current
|
||||||
|
counter value and updates the label of a button.
|
||||||
|
|
||||||
|
[[thread1.nim]]
|
||||||
|
[source,nim]
|
||||||
|
.thread1.nim
|
||||||
|
----
|
||||||
|
# https://nim-lang.org/docs/channels.html
|
||||||
|
# nim c --threads:on --gc:arc -r thread1.nim
|
||||||
|
|
||||||
|
import gintro/[gtk, glib, gobject, gio]
|
||||||
|
from os import sleep
|
||||||
|
|
||||||
|
var channel: Channel[int]
|
||||||
|
var workThread: system.Thread[void]
|
||||||
|
|
||||||
|
proc workProc =
|
||||||
|
var countdown {.global.} = 25
|
||||||
|
while countdown > 0:
|
||||||
|
sleep(1000)
|
||||||
|
dec(countdown)
|
||||||
|
channel.send(countdown)
|
||||||
|
|
||||||
|
proc updateGUI(b: Button): bool =
|
||||||
|
let msg = channel.tryRecv()
|
||||||
|
if msg.dataAvailable:
|
||||||
|
b.label = $msg.msg
|
||||||
|
if msg.msg == 0:
|
||||||
|
workThread.joinThread
|
||||||
|
channel.close
|
||||||
|
return SOURCE_REMOVE
|
||||||
|
return SOURCE_CONTINUE
|
||||||
|
|
||||||
|
proc buttonClicked (b: Button) =
|
||||||
|
b.label = utf8Strreverse(b.label, -1)
|
||||||
|
|
||||||
|
proc activate (app: Application) =
|
||||||
|
let window = newApplicationWindow(app)
|
||||||
|
window.title = "Countdown"
|
||||||
|
window.defaultSize = (250, 50)
|
||||||
|
let button = newButton("Click Me")
|
||||||
|
window.add(button)
|
||||||
|
button.connect("clicked", buttonClicked)
|
||||||
|
window.showAll
|
||||||
|
channel.open
|
||||||
|
createThread(workThread, workProc)
|
||||||
|
discard timeoutAdd(1000 div 60, updateGUI, button)
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
let app = newApplication("org.gtk.example")
|
||||||
|
connect(app, "activate", activate)
|
||||||
|
discard app.run
|
||||||
|
|
||||||
|
main()
|
||||||
|
----
|
||||||
|
|
||||||
|
The next example uses the function g-idle-add() from inside the worker thread to call a user defined function
|
||||||
|
that then updates a GUI widget:
|
||||||
|
|
||||||
|
[[thread2.nim]]
|
||||||
|
[source,nim]
|
||||||
|
.thread2.nim
|
||||||
|
----
|
||||||
|
# nim c --threads:on --gc:arc -r thread2.nim
|
||||||
|
|
||||||
|
import gintro/[gtk, glib, gobject, gio]
|
||||||
|
from os import sleep
|
||||||
|
|
||||||
|
var workThread: system.Thread[void]
|
||||||
|
var button: Button
|
||||||
|
|
||||||
|
proc idleFunc(i: int): bool =
|
||||||
|
button.label = $i
|
||||||
|
return SOURCE_REMOVE
|
||||||
|
|
||||||
|
proc workProc =
|
||||||
|
var countdown {.global.} = 25
|
||||||
|
while countdown > 0:
|
||||||
|
sleep(1000)
|
||||||
|
dec(countdown)
|
||||||
|
idleAdd(idleFunc, countdown)
|
||||||
|
|
||||||
|
proc buttonClicked(button: Button) =
|
||||||
|
button.label = utf8Strreverse(button.label, -1)
|
||||||
|
|
||||||
|
proc activate(app: Application) =
|
||||||
|
let window = newApplicationWindow(app)
|
||||||
|
window.title = "Countdown"
|
||||||
|
window.defaultSize = (250, 50)
|
||||||
|
button = newButton("Click Me")
|
||||||
|
window.add(button)
|
||||||
|
button.connect("clicked", buttonClicked)
|
||||||
|
window.showAll
|
||||||
|
createThread(workThread, workProc)
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
let app = newApplication("org.gtk.example")
|
||||||
|
connect(app, "activate", activate)
|
||||||
|
discard app.run
|
||||||
|
|
||||||
|
main()
|
||||||
|
----
|
||||||
|
|
||||||
== A HeaderBar example for GTK4
|
== A HeaderBar example for GTK4
|
||||||
|
|
||||||
The following code is the Nim version of a GTK4 example in C. It will work in its unmodified form
|
The following code is the Nim version of a GTK4 example in C. It will work in its unmodified form
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ nim c listview.nim
|
||||||
nim c menubar.nim
|
nim c menubar.nim
|
||||||
nim c overlay_tree1.nim
|
nim c overlay_tree1.nim
|
||||||
nim c simpledrawingarea.nim
|
nim c simpledrawingarea.nim
|
||||||
|
nim c spinbutton.nim
|
||||||
nim c sourceview_test.nim
|
nim c sourceview_test.nim
|
||||||
nim c t0.nim
|
nim c t0.nim
|
||||||
nim c vte.nim
|
nim c vte.nim
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ nim c --gc:arc listview.nim
|
||||||
nim c --gc:arc menubar.nim
|
nim c --gc:arc menubar.nim
|
||||||
nim c --gc:arc overlay_tree1.nim
|
nim c --gc:arc overlay_tree1.nim
|
||||||
nim c --gc:arc simpledrawingarea.nim
|
nim c --gc:arc simpledrawingarea.nim
|
||||||
|
nim c --gc:arc spinbutton.nim
|
||||||
nim c --gc:arc sourceview_test.nim
|
nim c --gc:arc sourceview_test.nim
|
||||||
nim c --gc:arc t0.nim
|
nim c --gc:arc t0.nim
|
||||||
nim c --gc:arc vte.nim
|
nim c --gc:arc vte.nim
|
||||||
|
|
|
||||||
47
examples/gtk3/thread1.nim
Normal file
47
examples/gtk3/thread1.nim
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
# https://nim-lang.org/docs/channels.html
|
||||||
|
# nim c --threads:on --gc:arc -r thread1.nim
|
||||||
|
|
||||||
|
import gintro/[gtk, glib, gobject, gio]
|
||||||
|
from os import sleep
|
||||||
|
|
||||||
|
var channel: Channel[int]
|
||||||
|
var workThread: system.Thread[void]
|
||||||
|
|
||||||
|
proc workProc =
|
||||||
|
var countdown {.global.} = 25
|
||||||
|
while countdown > 0:
|
||||||
|
sleep(1000)
|
||||||
|
dec(countdown)
|
||||||
|
channel.send(countdown)
|
||||||
|
|
||||||
|
proc updateGUI(b: Button): bool =
|
||||||
|
let msg = channel.tryRecv()
|
||||||
|
if msg.dataAvailable:
|
||||||
|
b.label = $msg.msg
|
||||||
|
if msg.msg == 0:
|
||||||
|
workThread.joinThread
|
||||||
|
channel.close
|
||||||
|
return SOURCE_REMOVE
|
||||||
|
return SOURCE_CONTINUE
|
||||||
|
|
||||||
|
proc buttonClicked (b: Button) =
|
||||||
|
b.label = utf8Strreverse(b.label, -1)
|
||||||
|
|
||||||
|
proc activate (app: Application) =
|
||||||
|
let window = newApplicationWindow(app)
|
||||||
|
window.title = "Countdown"
|
||||||
|
window.defaultSize = (250, 50)
|
||||||
|
let button = newButton("Click Me")
|
||||||
|
window.add(button)
|
||||||
|
button.connect("clicked", buttonClicked)
|
||||||
|
window.showAll
|
||||||
|
channel.open
|
||||||
|
createThread(workThread, workProc)
|
||||||
|
discard timeoutAdd(1000 div 60, updateGUI, button)
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
let app = newApplication("org.gtk.example")
|
||||||
|
connect(app, "activate", activate)
|
||||||
|
discard app.run
|
||||||
|
|
||||||
|
main()
|
||||||
38
examples/gtk3/thread2.nim
Normal file
38
examples/gtk3/thread2.nim
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
# nim c --threads:on --gc:arc -r thread2.nim
|
||||||
|
|
||||||
|
import gintro/[gtk, glib, gobject, gio]
|
||||||
|
from os import sleep
|
||||||
|
|
||||||
|
var workThread: system.Thread[void]
|
||||||
|
var button: Button
|
||||||
|
|
||||||
|
proc idleFunc(i: int): bool =
|
||||||
|
button.label = $i
|
||||||
|
return SOURCE_REMOVE
|
||||||
|
|
||||||
|
proc workProc =
|
||||||
|
var countdown {.global.} = 25
|
||||||
|
while countdown > 0:
|
||||||
|
sleep(1000)
|
||||||
|
dec(countdown)
|
||||||
|
idleAdd(idleFunc, countdown)
|
||||||
|
|
||||||
|
proc buttonClicked(button: Button) =
|
||||||
|
button.label = utf8Strreverse(button.label, -1)
|
||||||
|
|
||||||
|
proc activate(app: Application) =
|
||||||
|
let window = newApplicationWindow(app)
|
||||||
|
window.title = "Countdown"
|
||||||
|
window.defaultSize = (250, 50)
|
||||||
|
button = newButton("Click Me")
|
||||||
|
window.add(button)
|
||||||
|
button.connect("clicked", buttonClicked)
|
||||||
|
window.showAll
|
||||||
|
createThread(workThread, workProc)
|
||||||
|
|
||||||
|
proc main =
|
||||||
|
let app = newApplication("org.gtk.example")
|
||||||
|
connect(app, "activate", activate)
|
||||||
|
discard app.run
|
||||||
|
|
||||||
|
main()
|
||||||
Loading…
Add table
Add a link
Reference in a new issue