Signals and Callbacks

A button is not much use if it doesn't do anything. Gtk+ uses signals as a method for communicating that something of interest has happened. Most signals will be emitted as a consequence of user interaction: clicking on a button, closing a window, or just moving the mouse. You connect your signals to particular functions to make something happen.

Let's try a simple example:

b = GtkButton("Press me")
win = GtkWindow(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

b = GtkButton("Press me")
win = GtkWindow(b, "Callbacks")
id = signal_connect(b, "clicked") do widget
     println(widget, " was clicked!")
end

If you try this, and click on the button, you should see something like the following:

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!

That's quite a lot of output; let's just print the label of the button:

id2 = signal_connect(b, "clicked") do widget
    println("\"", get_gtk_property(widget,:label,String), "\" was clicked!")
end

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! Gtk+ allows you to define multiple signal handlers for a given object; even the execution order can be specified. Callbacks for some signals 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 callback should return nothing (void in C parlance), so you can't prevent other callbacks from running. However, we can disconnect the first signal handler:

signal_handler_disconnect(b, id)

Now clicking on the button just yields

julia> "Press me" was clicked!

Alternatively, you can temporarily enable or disable individual handlers with signal_handler_block and signal_handler_unblock.

The arguments of the callback depend on the signal type. For example, instead of using the "clicked" signal–-for which the Julia handler should be defined with just a single argument–-we could have used "button-press-event":

b = GtkButton("Pick a mouse button")
win = GtkWindow(b, "Callbacks")
id = signal_connect(b, "button-press-event") do widget, event
    println("You pressed button ", event.button)
end

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. 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")

In some situations you may want or need to use an approach that is more analogous to julia's @cfunction callback syntax. One advantage of this alternative approach is that, in cases of error, the backtraces are much more informative.