Search code examples
cglobal-variablesgtkorganizationcode-organization

How do i organize a GTK program?


I have a function called create_interface. In that function is a signal handler for the Open menu button (the menu is created in create_interface). I want to pass the window widget and also pass along the tree view widget, because when you open a file an entry in the tree view is supposed to show up. I tried passing them as a struct, but, although its works, GTK generates some error messages.

// This is global.
struct everything
        {
                GtkWidget *window;
                GtkWidget *tree_view;
        }

GtkWidget *create_interface (void)
{
        struct everything instance;
        struct everything *widgets;
        widgets = &instance;
        
        ...code here...

        g_signal_connect(file_mi_open_file_dialog, "clicked", G_CALLBACK(open_file_dialog), widgets);

The function open_file_dialog looks like this:

void open_file_dialog (GtkWidget *wid, gpointer data)
{
        struct everything *sample_name = data;
        ...rest of code here...

I was wondering if there was another way of organizing a program so you do not have to have global variables.


Solution

  • I tried passing them as a struct, but, although its works, GTK generates some error messages.

    The problem is that you're trying to use a stack-allocated struct, which becomes invalid after the create_interface() function is done, while you would normally expect these values to still be valid at a later moment in time (for example, when open_file_dialog() is called.

    I was wondering if there was another way of organizing a program so you do not have to have global variables.

    One possible solution is indeed to use a global variable: this will be valid throughout the lifetime of the program, but it has major drawbacks: it doesn't scale if you have to do that for each callback, and it's architecturally not really clean.

    Another solution is to allocate your "closure" (i.e. the variables you want to capture at the moment you create your callback) on the heap, and to free it when you're done with it. GLib even helps you with this by a call g_signal_connect_data() (or g_signal_connect_object() if you save your fields in a GObject)

    // Technically you don't need the typedef
    typedef struct _ClickedClosure {
        GtkWidget *window;
        GtkWidget *treeview;
    } ClickedClosure;
    
    GtkWidget *create_interface (void)
    {
        // Note that the g_new0 allocates on the heap instead of using the stack
        ClickedClosure *closure = g_new0 (ClickedClosure, 1);
    
        // code here to initialize your window/treeview ...
    
        // put the widgets in the closure
        closure->window = my_window;
        closure->treeview = treeview;
    
        // Connect to the signal
        g_signal_connect_data(file_mi_open_file_dialog, "clicked",
                              G_CALLBACK(open_file_dialog), closure, g_free, 0);
    }
    
    void open_file_dialog (GtkWidget *wid, gpointer data)
    {
        // Since this was allocated on the heap, this is still valid
        ClickedClosure *sample_name = data;
    
        // code handling here ...
    }
    

    Very often, what developers using GTK do, is that they're using their own GObject classes/objects already, which they can then use with g_signal_connect_object(), or by manually freeing it with g_object_unref() later. Using GObject then also allows you to to a runtime-typecheck cast, which makes sure your closure has the right type.