Skip to content
This repository has been archived by the owner on Jul 22, 2024. It is now read-only.

Commit

Permalink
Extend the documentation on signals to cover more advanced usages
Browse files Browse the repository at this point in the history
Motivated by #161
  • Loading branch information
timholy committed Aug 29, 2015
1 parent b9e459b commit 0463ab0
Show file tree
Hide file tree
Showing 2 changed files with 99 additions and 8 deletions.
34 changes: 26 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,17 +254,34 @@ Most signals will be _emitted_ as a consequence of user interaction: clicking on
closing a window, or just moving the mouse. You _connect_ your signals to particular functions
to make something happen.
Let's do a simple example:
Let's try a simple example:
```jl
b = @Button("Press me")
win = @Window(b, "Callbacks")
showall(win)

function button_clicked_callback(widget)
println(widget, " was clicked!")
end

id = signal_connect(button_clicked_callback, b, "clicked")
```
Here, `button_clicked_callback` is a *callback function*, something
designed to be called by GTK+ to implement the response to user
action. You use the `signal_connect` function to specify when it
should be called: in this case, when widget `b` (your button) emits
the `"clicked"` signal.
Using Julia's `do` syntax, the exact same code could alternatively be
written as
```jl
b = @Button("Press me")
win = @Window(b, "Callbacks")
id = signal_connect(b, "clicked") do widget
println(widget, " was clicked!")
end
```
`signal_connect` specifies that a callback function should be run when the `"clicked"`
signal is received. In this case, we used the `do` syntax to define the function, but
we could alternatively have passed the function as `id = signal_connect(func, b, "clicked")`.
If you try this, and click on the button, you should see something like the following:
```
Expand All @@ -281,11 +298,11 @@ Now you get something like this:
julia> GtkButton(action-name=NULL, action-target, related-action, use-action-appearance=TRUE, name="", parent, width-request=-1, height-request=-1, visible=TRUE, sensitive=TRUE, app-paintable=FALSE, can-focus=TRUE, has-focus=TRUE, is-focus=TRUE, can-default=FALSE, has-default=FALSE, receives-default=TRUE, composite-child=FALSE, style, events=0, no-show-all=FALSE, has-tooltip=FALSE, tooltip-markup=NULL, tooltip-text=NULL, window, double-buffered=TRUE, halign=GTK_ALIGN_FILL, valign=GTK_ALIGN_FILL, margin-left=0, margin-right=0, margin-top=0, margin-bottom=0, margin=0, hexpand=FALSE, vexpand=FALSE, hexpand-set=FALSE, vexpand-set=FALSE, expand=FALSE, border-width=0, resize-mode=GTK_RESIZE_PARENT, child, label="Press me", image, relief=GTK_RELIEF_NORMAL, use-underline=TRUE, use-stock=FALSE, focus-on-click=TRUE, xalign=0.500000, yalign=0.500000, image-position=GTK_POS_LEFT, ) was clicked!
"Press me" was clicked!
```
Notice that _both_ of the callback functions executed.
Notice that _both_ of the callback functions executed!
Gtk+ allows you to define multiple signal handlers for a given object; even the execution order can be [specified](https://developer.gnome.org/gobject/stable/gobject-Signals.html#gobject-Signals.description).
Callbacks for some [signals](https://developer.gnome.org/gtk3/stable/GtkWidget.html#GtkWidget-accel-closures-changed) require that you return an `Int32`, with value 0 if you want the next handler to run or 1 if you want to prevent any other handlers from running on this event.
The `"clicked"` signal doesn't provide a return value, so other callbacks will always be run.
The [`"clicked"` signal callback](https://developer.gnome.org/gtk3/stable/GtkButton.html#GtkButton-clicked) should return `nothing` (`void` in C parlance), so you can't prevent other callbacks from running.
However, we can disconnect the first signal handler:
```jl
signal_handler_disconnect(b, id)
Expand All @@ -307,12 +324,14 @@ For example, instead of using the `"clicked"` signal---for which the Julia handl
```
Note that this signal requires two arguments, here `widget` and `event`, and that `event` contained useful information.
Arguments and their meaning are described along with their corresponding [signals](https://developer.gnome.org/gtk3/stable/GtkWidget.html#GtkWidget-accel-closures-changed).
Note that you should omit the final `user_data` argument described in the Gtk documentation;
**You should omit the final `user_data` argument described in the Gtk documentation**;
keep in mind that you can always address other variables from inside your function block, or define the callback in terms of an anonymous function:
```
id = signal_connect((widget, event) -> cb_buttonpressed(widget, event, guistate, drawfunction, ...), b, "button-press-event")
```
Finally, in some situations you may want or need to use an [approach that is more analagous to julia's `cfunction` callback syntax](doc/more_signals.md).
### Usage without the REPL
If you're using Gtk from command-line scripts, one problem you may encounter is that Julia quits before you have a chance to see or interact with your windows. In such cases, the following design pattern can be helpful:
Expand Down Expand Up @@ -486,4 +505,3 @@ info_dialog("Julia rocks!")
ask_dialog('Do you like chocolate ice cream?, "I like it", "Not at all") && println("That's my favorite too.")
warn_dialog("Oops!... I did it again", win)
```
73 changes: 73 additions & 0 deletions doc/more_signals.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
### More about signals and signal-handlers

In addition to the ["simple"
interface](../README.md#callbacks-and-signals), `signal_connect`
supports an approach that allows your callback function to be directly
compiled to machine code. Not only is this more efficient, but it can
occasionally be useful in avoiding problems from callback interrupts.

This alternative syntax is as follows:
```
signal_connect(cb, widget, signalname, return_type, parameter_type_tuple, after, user_data=widget)
```
where:

- `cb` is your callback function. You should use a generic function
(i.e., one defined as `function foo(x,y,z) ... end`), and the
arguments and return type should match the GTK+ documentation for
the widget and signal ([see
examples](https://developer.gnome.org/gtk3/stable/GtkWidget.html#GtkWidget-accel-closures-changed)).
**In contrast with the [simpler
interface](../README.md#callbacks-and-signals), when writing these
callbacks you must include the `user_data` argument**. See examples below.
- `widget` is the widget that will send the signal
- `signalname` is a string or symbol identifying the signal, e.g.,
`"clicked"` or `"button-press-event"`
- `return_type` is the type of the value returned by your
callback. Usually `Void` (for `void`) or `Cint` (for `gboolean`)
- `parameter_type_tuple` specifies the types of the *middle* arguments
to the callback function, omitting the first (the widget) and last
(`user_data`). For example, for [`"clicked"`](https://developer.gnome.org/gtk3/stable/GtkButton.html#GtkButton-clicked) we have
`parameter_type_tuple = ()` (because there are no middle arguments)
and for [`"button-press-event"`](https://developer.gnome.org/gtk3/stable/GtkWidget.html#GtkWidget-button-press-event) we have `parameter_type_tuple =
(Ptr{GdkEventButton},)`.
- `after` is a boolean, `true` if you want your callback to run after
the default handler for your signal. When in doubt, specify `false`.
- `user_data` contains any additional information your callback needs
to operate. For example, you can pass other widgets, tuples of
values, etc. If omitted, it defaults to `widget`.

The callback's argument need to match the GTK documentation, with the
exception of the `user_data` argument: rather than a pointer, this
will automatically be converted back to a Julia type for you.

As an example, consider this GUI for which pressing a button updates
a text counter:

```jl
box = @Box(:h)
btn = @Button("click me")
lbl = @Label("0")
push!(box, btn)
push!(box, lbl)
win = @Window(box, "Callbacks")
showall(win)

const counter = [0] # Pack counter value inside array so it is a reference

# "clicked" callback declaration is
# void user_function(GtkButton *button, gpointer user_data)
# But user_data gets converted into a Julia object automatically
function button_cb(widgetptr::Ptr, user_data)
widget = convert(Button, widgetptr) # pointer -> object
label, cntr = user_data # unpack the user_data tuple
cntr[] = cntr[]+1 # increment counter[1]
setproperty!(label, :label, string(cntr[]))
nothing # return type is void
end

signal_connect(button_cb, btn, "clicked", Void, (), false, (lbl, counter))
```

You should note that the value of `counter[]` matches the display in
the GUI.

0 comments on commit 0463ab0

Please sign in to comment.