diff --git a/README.md b/README.md index 7c3b1ae1..d3566e1d 100644 --- a/README.md +++ b/README.md @@ -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. @@ -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. @@ -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. diff --git a/docs/src/howto/async.md b/docs/src/howto/async.md index d55a3859..195c9d97 100644 --- a/docs/src/howto/async.md +++ b/docs/src/howto/async.md @@ -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 @@ -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 diff --git a/docs/src/index.md b/docs/src/index.md index 3641c1fd..8cb8dc38 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -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. diff --git a/examples/README.md b/examples/README.md index 402c561f..e1aa9887 100644 --- a/examples/README.md +++ b/examples/README.md @@ -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. diff --git a/examples/thread.jl b/examples/thread.jl new file mode 100644 index 00000000..3465d39a --- /dev/null +++ b/examples/thread.jl @@ -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) diff --git a/examples/thread_timeout.jl b/examples/thread_timeout.jl new file mode 100644 index 00000000..3590daef --- /dev/null +++ b/examples/thread_timeout.jl @@ -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) diff --git a/src/GLib/loop.jl b/src/GLib/loop.jl index 19ba3fa8..42ae14f5 100644 --- a/src/GLib/loop.jl +++ b/src/GLib/loop.jl @@ -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"))) """