6

I'm trying to learn how to make GUIs using gtk+ 3.0. I want to pass a simple argument, an integer, to a callback function, so that when I press the button the value of the argument changes. Here's my code:

#include <gtk/gtk.h>

void buttonFunction(GtkWidget * widget, gpointer data, int & n){
    n = 1;
}

int main(int argc, char ** argv){
    int n = 0;
    GtkWidget * window;
    GtkWidget * button; 

    gtk_init(&argc,&argv);

    window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    button = gtk_button_new_with_label("Osss");

    gtk_container_add(GTK_CONTAINER(window),button);
    gtk_widget_show_all(window);

    g_signal_connect(G_OBJECT(window), "destroy",G_CALLBACK(gtk_main_quit), NULL);
    g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(buttonFunction), n);

    gtk_main();

    return 0;
}

The only way I found to pass the argument was as a pointer:

void buttonFunction(GtkWidget * widget, gpointer * data);
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(buttonFunction), &n);

How do I pass multiple arguments this way tho?

1
  • 1
    Your callbck arguments are wrong: should be gpointer data not gpointer * data. gpointer is already a pointer, it's a typedef to void *. Commented Mar 14, 2014 at 10:30

3 Answers 3

10

To pass multiple arguments, you define a structure, fill it, and pass a pointer to the structure as the gpointer user_data parameter of the g_signal_connect, which is the last parameter. Then in your callback, you just cast the user_data parameter to a pointer to your structure.

int main (int argc, char **argv)
{
    int n = 0;
    GtkWidget *window;
    GtkWidget *button; 

    gtk_init (&argc,&argv);

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    button = gtk_button_new_with_label ("Osss");

    gtk_container_add (GTK_CONTAINER(window), button);
    gtk_widget_show_all (window);

    g_signal_connect(G_OBJECT(window), "destroy", G_CALLBACK(gtk_main_quit), NULL);

    /* Here's the magic: you pass a pointer to the variable you'd like to modify
     * in the callback, be it a simple variable or a struct */
    g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(buttonFunction), &n);

    gtk_main();

    return 0;
}

void on_button_clicked (GtkButton *button, gpointer user_data) /* No extra parameter here ! */
{
    /* you cast to the type of what you passed as last argument of g_signal_connect */ 
    int *pn = user_data; 
    *pn = 1;
}

You MUST use the signature of the callback defined in the documentation (look at the "signals" section of the documentation for GtkButton), you can't make things up. BTW, you can't pass n as a reference instead of a pointer. If you want to use GTK in C++, give a look at GTKmm.

Sign up to request clarification or add additional context in comments.

Comments

5

There are a bunch of useful macros which store an integer (32bit, no longer!) into a pointer.

int a = 42;
gpointer ptr = GINT_TO_POINTER (a)
//GUINT_TO_POINTER (a), GBOOLEAN_TO_POINTER (a), GSIZE_TO_POINTER (a)

reverse:

int a2 = GPOINTER_TO_INT (ptr);
//GPOINTER_TO_UINT (ptr), GPOINTER_TO_...

4 Comments

Why would I need to use GINT_TO_POINTER when I can simply do gpointer ptr = &a; and later in the callback int* ptr = user_data?
Because the execution is deferred via the glib event loop, and as such this a very common misconception and use after free case since the common a lives on the stack or cause a memory leak if it lives on the heap. GINT_TO_POINTER does not take the address, it places the variable into the 4 bytes / 8bytes size of the pointer itself, and restores it from the pointer itself, not by dereferencing it. Note that the pointer itself points to an arbitrary memory location, so it reall should under no circumstances be dereferenced.
Where is GBOOLEAN_TO_POINTER defined?
It used to exist in gedit-utils.h, but was removed in early 2020.
1
int a = 42;
`gpointer ptr = GINT_TO_POINTER (a)`
//GUINT_TO_POINTER (a), GBOOLEAN_TO_POINTER (a), GSIZE_TO_POINTER (a)
reverse:

int a2 = GPOINTER_TO_INT (ptr);
//GPOINTER_TO_UINT (ptr), GPOINTER_TO_...

The above works perfectly fine to pass integer data thru g_signal_connect. Used as follows.

GtkWidget *Parison_ON_Buttons[10];

for (i=0; i < 8; i++)
{
    gpointer ONptr = GINT_TO_POINTER (i);
    g_signal_connect(GTK_SPIN_BUTTON(Parison_ON_Buttons[i]), "activate", G_CALLBACK 
    (Get_Parison_ON_Values),ONptr);
}

static gboolean Get_Parison_ON_Values (GtkSpinButton *button, gpointer data)
{
 int ONButtonNUmPressed = GPOINTER_TO_INT (data);
....
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.