Skip to content

Commit

Permalink
add some multithreading examples and improve docs
Browse files Browse the repository at this point in the history
  • Loading branch information
jwahlstrand committed Dec 10, 2023
1 parent 54b187e commit 2b1a52b
Show file tree
Hide file tree
Showing 7 changed files with 132 additions and 17 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
[![](https://img.shields.io/badge/docs-stable-blue.svg)](https://juliahub.com/docs/Gtk4)


GUI building using the [GTK](https://www.gtk.org) library, version 4. For a mature Julia package that supports GTK version 3, see [Gtk.jl](https://github.com/JuliaGraphics/Gtk.jl). **Note that Gtk.jl and Gtk4.jl cannot be imported in the same Julia session.**
GUI building using [GTK](https://www.gtk.org) version 4. For a mature Julia package that supports GTK version 3, see [Gtk.jl](https://github.com/JuliaGraphics/Gtk.jl). **Note that Gtk.jl and Gtk4.jl cannot be imported in the same Julia session.**

This package builds on Gtk.jl but uses GObject introspection to support more of the functionality of the GTK library and its dependencies. GObject introspection for Julia is implemented using [GI.jl](https://github.com/JuliaGtk/Gtk4.jl/tree/main/GI), which is also hosted in this repository.

Expand All @@ -23,8 +23,8 @@ Those seeking a more user friendly, well documented, curated GUI building experi
## Related packages

Other registered packages extend the functionality of Gtk4.jl:
- [GtkObservables.jl](https://github.com/JuliaGizmos/GtkObservables.jl): provides integration with [Observables.jl](https://github.com/JuliaGizmos/Observables.jl). This can simplify making interactive GUI's with Gtk4.jl.
- [Gtk4Makie.jl](https://github.com/JuliaGtk/Gtk4Makie.jl): provides integration with the popular plotting package [Makie.jl](https://github.com/MakieOrg/Makie.jl), specifically its interactive, high performance GLMakie backend. This allows you to include interactive plots in your GUI.
- [GtkObservables.jl](https://github.com/JuliaGizmos/GtkObservables.jl): provides integration with [Observables.jl](https://github.com/JuliaGizmos/Observables.jl). This package can simplify making interactive GUI's with Gtk4.jl.
- [Gtk4Makie.jl](https://github.com/JuliaGtk/Gtk4Makie.jl): provides integration with the popular plotting package [Makie.jl](https://github.com/MakieOrg/Makie.jl), specifically its interactive, high performance GLMakie backend.

## Current status
For auto-generated code, Gtk4.jl relies on GObject introspection data generated on a Linux x86_64 machine, which may result in code that crashes on 32 bit computers. This seems to affect mostly obscure parts of GLib that are unlikely to be useful to Julia users, but 32 bit users should be aware of this.
Expand All @@ -33,7 +33,7 @@ Note that this package uses binaries for the GTK library and its dependencies th

### Known incompatibilities

Gtk4.jl interferes with some other packages, including [PyPlot.jl](https://github.com/JuliaPy/PyPlot.jl) and [GLMakie.jl](https://github.com/MakieOrg/Makie.jl). To use Gtk4 based packages with Makie, you can use [Gtk4Makie.jl](https://github.com/JuliaGtk/Gtk4Makie.jl).
Gtk4.jl is known to interfere with some other packages, including [PyPlot.jl](https://github.com/JuliaPy/PyPlot.jl) and [GLMakie.jl](https://github.com/MakieOrg/Makie.jl). To use Gtk4 based packages with Makie, you can use [Gtk4Makie.jl](https://github.com/JuliaGtk/Gtk4Makie.jl).

## Enabling GTK4's EGL backend (Linux)
On Wayland, a Cairo-based fallback backend will be used unless you tell `libglvnd_jll` where to find libEGL. This can be done by setting the environment variable __EGL_VENDOR_LIBRARY_DIRS. See [here](https://gitlab.freedesktop.org/glvnd/libglvnd/-/blob/master/src/EGL/icd_enumeration.md) for details.
Expand Down
17 changes: 13 additions & 4 deletions docs/src/howto/async.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,22 @@ responsiveness either using multithreading or using separate processes. Use of a
process includes slightly more overhead but also ensures user interface responsiveness more
robustly.

## Multithreading

!!! note "Example"
The code below can be found in "thread.jl" in the ["examples" subdirectory](https://github.com/JuliaGtk/Gtk4.jl/tree/main/examples).

Here is an example using [threads](https://docs.julialang.org/en/v1/manual/multi-threading/).
Notice that this example will freeze the UI during computation unless Julia is run with two
or more threads (`julia -t2` on the command line).
or more threads, for example by calling `julia -t2` or `julia -t1,1` to use the interactive
threadpool in recent versions of Julia.

```julia
using Gtk4

btn = GtkButton("Start")
sp = GtkSpinner()
ent = GtkEntry()
ent = GtkEntry(;hexpand=true)

grid = GtkGrid()
grid[1,1] = btn
Expand All @@ -37,14 +43,17 @@ signal_connect(btn, "clicked") do widget
Gtk4.GLib.g_idle_add() do
stop(sp)
ent.text = "I counted to $counter in a thread!"
Cint(false)
false
end
end
end

win = GtkWindow(grid, "Threads", 200, 200)
win = GtkWindow(grid, "Threads", 300, 200)
```

A modified version of this code that includes an updating counter can be found in "thread_timeout.jl" in the ["examples" subdirectory](https://github.com/JuliaGtk/Gtk4.jl/tree/main/examples).

## Separate processes

Here is an example using a separate process to offload the work. This toy example is
fairly straightforward, but things can get more complex if the offloaded task is more
Expand Down
7 changes: 4 additions & 3 deletions docs/src/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,11 @@ Complete Gtk documentation is available at [https://www.gtk.org/docs/](https://w

## Usage

* See [Getting Started](@ref) for an introduction to using the package, adapted from the [Gtk.jl manual](https://juliagraphics.github.io/Gtk.jl/latest/).
* See [Gtk4 Reference](@ref) for an API reference automatically generated from docstrings.
* Manual/tutorial: see [Getting Started](@ref) and following pages for an introduction to using the package, adapted from the [Gtk.jl manual](https://juliagraphics.github.io/Gtk.jl/latest/).
* How-to guides: see [Using Gtk4 outside the REPL](@ref) and following pages for practical discussions of various sticky issues when using Gtk4.jl.
* Reference: see [Gtk4 Reference](@ref) for an API reference automatically generated from docstrings.
* See [Differences between Gtk.jl and Gtk4.jl](@ref) for a summary of the differences between this package and Gtk.jl.

## History

This package was adapted from Gtk.jl, which was written by [Jameson Nash and others](https://github.com/JuliaGraphics/Gtk.jl/contributors) and supported GTK versions 2 and 3. With version 4 there were so many changes to the GTK API that it would have been messy to try to support it and previous versions in the same package. Note that much of the GLib/GObject functionality that underlies GTK is largely the same code as in Gtk.jl. Some changes were made to try to take better advantage of GObject introspection or to remove old code that was no longer necessary in recent versions of Julia.
This package and its documentation were adapted from Gtk.jl, which was written by [Jameson Nash and others](https://github.com/JuliaGraphics/Gtk.jl/contributors) and supported GTK versions 2 and 3. With version 4 there were so many changes to the GTK API that it would have been messy to try to support it and previous versions in the same package. Note that much of the GLib/GObject functionality that underlies GTK is largely the same code as in Gtk.jl. Some changes were made to try to take better advantage of GObject introspection or to remove old code that was no longer necessary in recent versions of Julia.
5 changes: 5 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,11 @@
- `filteredlistview_tree.jl` demonstrates using `GtkListView` to show a tree. Uses `GtkCustomFilter` to filter what's shown.
- `listbox.jl` demonstrates `GtkListBox` to show a huge list of strings. This widget is a little easier to use than `GtkListView` but may be less performant.

## Multithreading

- `thread.jl` is a basic example of doing work in a separate thread while maintaining a responsive UI.
- `thread_timeout.jl` adds a label that updates during the task in the example above.

## Applications

- `application.jl` is a simple example of using `GtkApplication` and `GAction`s.
Expand Down
44 changes: 44 additions & 0 deletions examples/thread.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Demonstrates keeping the UI responsive while doing tasks in a thread

if Threads.nthreads() == 1 && Threads.nthreads(:interactive) < 1
@warn("This example is intended to be run with multiple threads enabled.")
end

using Gtk4

btn = GtkButton("Start")
sp = GtkSpinner()
ent = GtkEntry(;hexpand=true)

ltp = Threads.threadpool()

grid = GtkGrid()
grid[1:2,1] = GtkLabel("The GTK loop is running in thread $(Threads.threadid()) ($ltp threadpool)")
grid[1,2] = btn
grid[2,2] = sp
grid[1:2,3] = ent

signal_connect(btn, "clicked") do widget
start(sp)
Threads.@spawn begin
# Do work
stop_time = time() + 3
counter = 0
while time() < stop_time
counter += 1
end

tid = Threads.threadid()
tp = Threads.threadpool()

# Interacting with GTK from a thread other than the main thread is
# generally not allowed, so we register an idle callback instead.
Gtk4.GLib.g_idle_add() do
stop(sp)
ent.text = "I counted to $counter in thread $tid in the $tp threadpool!"
false
end
end
end

win = GtkWindow(grid, "Threads", 420, 200)
56 changes: 56 additions & 0 deletions examples/thread_timeout.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Demonstrates keeping the UI responsive while doing tasks in a thread

if Threads.nthreads() == 1 && Threads.nthreads(:interactive) < 1
@warn("This example is intended to be run with multiple threads enabled.")
end

using Gtk4

btn = GtkButton("Start")
sp = GtkSpinner()
ent = GtkEntry(;hexpand=true)
label = GtkLabel("")

ltp = Threads.threadpool()

grid = GtkGrid()
grid[1:2,1] = GtkLabel("The GTK loop is running in thread $(Threads.threadid()) ($ltp threadpool)")
grid[1,2] = btn
grid[2,2] = sp
grid[1:2,3] = ent
grid[1:2,4] = label

counter = Ref(0)

signal_connect(btn, "clicked") do widget
start(sp)
stop_time = time() + 3

# g_timeout_add can be used to periodically call a function from the main loop
Gtk4.GLib.g_timeout_add(50) do # create a function that will be called every 50 milliseconds
label.label = "counter: $(counter[])"
return time() < stop_time # return true to keep calling the function, false to stop
end

Threads.@spawn begin
# Do work

counter[] = 0
while time() < stop_time
counter[] += 1
end

tid = Threads.threadid()
tp = Threads.threadpool()

# Interacting with GTK from a thread other than the main thread is
# generally not allowed, so we register an idle callback instead.
Gtk4.GLib.g_idle_add() do
stop(sp)
ent.text = "I counted to $(counter[]) in thread $tid in the $tp threadpool!"
false
end
end
end

win = GtkWindow(grid, "Threads with updating counter", 420, 200)
12 changes: 6 additions & 6 deletions src/GLib/loop.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@
g_timeout_add(f, interval, priority=PRIORITY_DEFAULT)
Add a function `f` that will be called every `interval` milliseconds by the GTK
main loop. The function is expected to return a `Cint`. If it returns a nonzero
value, the function will be called again after another `interval` milliseconds.
Otherwise it will not be called again. The optional `priority` argument, which
is an integer, sets the priority of the event source (smaller is higher priority).
The GLib main loop uses this priority value to decide what sources to handle next.
main loop. If the function returns `true`, it will be called again after
another `interval` milliseconds. If it returns `false` it will not be called
again. The optional `priority` argument, which is an integer, sets the priority
of the event source (smaller is higher priority). The GLib main loop uses this
priority value to decide what sources to handle next.
This function returns an event source ID that can be used with `g_source_remove`
to stop the timeout.
to stop the timeout externally.
Related GTK function: [`g_timeout_add`()]($(gtkdoc_func_url("glib","timeout_add")))
"""
Expand Down

0 comments on commit 2b1a52b

Please sign in to comment.