buttoncomboboxjuliagtksignals

How to remove ComboBox selection using button?


I am trying to do a simple GUI using Gtk with the Julia programming language, however, when I try to get the button to remove the active selection in the combobox programmatically, I get:

ERROR: AssertionError: xor(prev, current_task() !== g_stack)

How to get this simple example to work?

Here is my non-functional code:

using Gtk

# Create widgets------------------------------------
cb = GtkComboBoxText()
button = GtkButton("Remove Active")

# Create and Add choices to ComboBox ---------------
choices = ["zero", "one", "two", "three", "four"]
for choice in choices
  push!(cb,choice)                           
end

# Function to get the selected choice (text) from the ComboBox
function getChoice()
    i = get_gtk_property(cb, "active", Int)
    return choices[i+1]
end

# Function that handles the ComboBox selection change---
function selection_changed(widget)
    sel = getChoice()
    println("We selected: $sel")
end

# Function to handle the button press------------------
function removeChoice(widget)
    set_gtk_property!(cb,:active,-1)
end

# Connect the signals to the widgets -------------------
signal_connect(selection_changed, cb, "changed")
signal_connect(removeChoice, button, "clicked") 


# Create window, and add widgets to it using Box Layout
win = GtkWindow("ComboBoxText Example",200,50)

vbox = GtkBox(:v)                          
push!(win, vbox)                           
push!(vbox, cb)                        
push!(vbox, button)

showall(win)

Solution

  • Note the warning at the end of this Gtk.jl manual page:

    Warning: it is essential to avoid task switching inside Gtk callbacks, as this corrupts the Gtk C-stack. For example, use @async print or queue a message for yourself. ... if you are still seeing segfaults in some random method due to there existing a callback that recursively calls the glib main loop (such as making a dialog box) or otherwise causes g_yield to be called, wrap the faulting code in GLib.@sigatom. This will postpone execution of that code block until it can be run on the proper stack (but will otherwise acts like normal control flow).

    This is what happens here, when you try to change the selection status of the combo box from with a signal-handler callback - the "callback that recursively calls the glib main loop" as the manual page calls it.

    Using either @async or Gtk.GLib.@sigatom in front of the set_gtk_property! call avoids this problem and allows the code to run.

    In this case, that leads to a different error message because removeChoice itself leads to selection_change being called, and the getChoice call made there does not take into account that get_gtk_property(cb, "active", Int) could return -1. So we get a BoundsError. How you fix that depends on your use case, for demo purposes I just return nothing here in that case:

    # Function to get the selected choice (text) from the ComboBox
    function getChoice()
        i = get_gtk_property(cb, "active", Int)
        return i >= 0 ? choices[i+1] : nothing
    end
    
    # Function that handles the ComboBox selection change---
    function selection_changed(widget)
        sel = getChoice()
        println("We selected: $sel")
    end
    
    # Function to handle the button press------------------
    function removeChoice(widget)
        @async set_gtk_property!(cb,:active,-1)
    end
    

    Running this, the output as I select two, then "Remove Active", then four, then "Remove Active" again in the GUI is:

    julia> We selected: two
    We selected: nothing
    We selected: four
    We selected: nothing