I'm having the hardest time getting an integer passed to a callback function, since the last argument of g_signal_connect is required to be a pointer. Here is where I connect the signal to the callback:
for (i=0;i<10;i++)
{
...
gtk_widget_set_events(tab_ebs[i],GDK_BUTTON_PRESS_MASK);
g_signal_connect (G_OBJECT (tab_ebs[i]), "button_press_event", G_CALLBACK (tab_clicked_cb), GINT_TO_POINTER(i));
}
and here is the callback:
void tab_clicked_cb (gpointer p)
{
printf("tab #%d clicked\n", GPOINTER_TO_INT(p));
}
What I get in stdout, are statements such as:
tab #6578976 clicked
tab #6579264 clicked
tab #6579552 clicked
tab #6579840 clicked
When I only have ten tabs. How can I pass an integer to a callback fcn on a 64 bit system? Thanks.
nos got me half way there. Turns out I was also missing an argument for the event in my callback function. Here is the form that worked:
void tab_clicked (GtkWidget *widget, GdkEventButton *ev, gpointer p)
{
printf("tab #%d clicked\n", GPOINTER_TO_INT(p));
}
Your callback function is likely wrong, most Gtk callback handlers passes the widget that originated the event as the first parameter to the callback function. So it should be e.g.
void tab_clicked_cb (GtkWidget *widget, gpointer p)
{
printf("tab #%d clicked\n", GPOINTER_TO_INT(p));
}
Edit, The Gtk docs are not at all clear on what it's callback handler is for the button_press_event, the docs read as the callback handler for button_press_event doesn't receive any arguments.
Related
I have GTK GUI where it has a control button that opens a new window called c_window in my struct. c_window has a quit button that I need to connect to the c_quit function. When I tried to do so the window is not terminated and a warning keeps appearing each time I pressed on quit button:
(main_menu:2682): Gtk-CRITICAL **: 06:30:38.469: gtk_widget_destroy: assertion 'GTK_IS_WIDGET (widget)' failed
my c_quit function is:
static gboolean c_quit (gpointer userData)
{
message_details *details = (message_details *)userData;
gtk_widget_destroy (details->c_window);
return FALSE;
}
how do I connect it from the main
g_signal_connect(G_OBJECT(details->c_quit), "clicked", G_CALLBACK (c_quit), details->c_window);
Please note that details is my struct where I define my GtkWidget elements.
First of all, your signal connection function is:
g_signal_connect(G_OBJECT(details->c_quit), "clicked", G_CALLBACK (c_quit), details->c_window);
Which contains an unnecessary cast for details->c_quit. Additionally, you're passing the c_window field of the details pointer.
Next, your callback is:
static gboolean c_quit (gpointer userData)
{
message_details *details = (message_details *)userData;
gtk_widget_destroy (details->c_window);
return FALSE;
}
which has the wrong signature for a callback to the GtkButton::clicked signal; the correct signature would be:
static void
c_quit (GtkButton *button, gpointer data)
Additionally, I'd recommend using a proper function name for the callback, so you can easily understand what it does, and where it's referenced. For instance, something quit_button__clicked.
Inside your callback you're accessing the wrong parameter, with the wrong type:
message_details *details = (message_details *)userData;
Because you're not passing the details data, you're passing a pointer to the window itself. Additionally, gpointer is an alias to void*, and C allows implicit casting to and from a void*; this means you don't need an explicit cast there.
So, to recap:
connect the signal properly, using the correct arguments to send to your callback:
g_signal_connect (details->c_quit, "clicked",
G_CALLBACK (quit_button__clicked),
details);
use the right prototype for the signal you're connecting to, and access the right data:
static void
quit_button__clicked (GtkButton *button G_GNUC_UNUSED,
gpointer user_data)
{
message_details *details = user_data;
gtk_widget_destroy (details->c_window);
}
If you just want to call gtk_widget_destroy() on the window widget in response to a clicked signal on a GtkButton, you can also condense everything into a single call:
g_signal_connect_swapped (details->c_quit, "clicked",
G_CALLBACK (gtk_widget_destroy),
details->c_window);
The g_signal_connect_swapped() function will swap the first and last arguments of the signal when invoking the callback—the first argument being the instance that emitted the signal, and the last argument being the user data passed to connection function. This means that gtk_widget_destroy() will be called with the user data argument of g_signal_connect_swapped().
Hello I am making GUI in GTK I have some menu items, and I am trying to change main label after clicking a mouse on specific menu element.
widgets[i][0] = gtk_menu_item_new_with_label(arrayOfStrings[i]);
//arrayOfStrings is : char** arrayOfStrings
gtk_menu_shell_append(GTK_MENU_SHELL(indicator_menu), widgets[i][0]);
I was trying this:
void set_main_label(GtkWidget *widget)
{
app_indicator_set_label(indicator, arrayOfString[2],arrayOfString[2]);
}
and after this I call it like:
g_signal_connect(widgets[i][0], "activate",G_CALLBACK(set_main_label), widgets[i][0]);
But my problem is that void set_main_label(void) must have void argument. And I need to pass there string (char*) which is stored in arrayOfStrings. What do you suggest? Now I can change label only to one specific string set in set_main_label function, but I cannot pass it as an argument into function, what do you suggest? .
This is what the user_data parameter is for. set_main_label() does not have a void argument list - check the documentation:
void
user_function (GtkMenuItem *widget,
gpointer user_data)
You can pass any argument you like into the callback via the user_data parameter. But it must be known at the time you connect the signal.
So you could do something like this:
void
set_main_label(GtkMenuItem *widget, gpointer user_data)
{
const char *label = (const char *)user_data;
app_indicator_set_label(indicator, label, label);
}
g_signal_connect(widgets[i][0], "activate",
G_CALLBACK(set_main_label), arrayOfString[2]);
What I wanted to do is to connect the mouse click "clicked" signal and the keypress "key_press_event" signal to the same callback function. The code started off like this:
static void s_up(GtkWidget *btn,
gpointer data)
{
code ...
}
...
int main(int argc, char **argv)
{
...
g_signal_connect(button, "clicked", G_CALLBACK(s_up), NULL);
}
It handled mouse clicks just fine. Then I wanted to add keyboard presses to it. So I changed the code to
static void s_up(GtkWidget *btn,
GdkEventKey *event
gpointer data)
{
if(event->keyval == 's')
more code ...
}
...
int main(int argc, char **argv)
{
...
g_signal_connect(button, "key_press_event", G_CALLBACK(s_up), NULL);
}
It worked when I press "s" on my keyboard.
My question is how to connect both signals to the same s_up callback function? Other than the obvious problem of if(event->keyval == 's'), I tried calling both gtk_signal_connect, and when I clicked on the button, I got SegFault pointing to s_up. Pressing "s" didn't do anything. Any help is appreciated.
You can't connect both signals directly to the same signal handler because they require different arguments.
The reason why you get a segmentation fault is because event->keyval only makes sense when you receive a key-press-event. When you receive a clicked event the second argument will be NULL, since it maps to the gpointer argument supplied with clicked events and you've set that to NULL when you installed the signal handler, so event will be NULL.
If the reason why you want to connect both signals to the same handler is that you have common code that should run no matter which signal triggered it, then the solution is to have one handler for each signal and a third function with common code that both of them can call.
Use a wrapper function to indirectly call the same callback:
static void
callback(GtkWidget *button, gpointer data)
{
/* TODO */
}
static gboolean
wrapper(GtkWidget *button, GdkEventKey *event, gpointer data)
{
if (event->keyval == 's')
callback(button, data);
return FALSE;
}
int
main(int argc, char **argv)
{
/* TODO */
g_signal_connect(button, "clicked", G_CALLBACK(callback), NULL);
g_signal_connect(button, "key-press-event", G_CALLBACK(wrapper), NULL);
/* TODO */
}
If you do not use data you can also use a single function, but in my opinion the complexity introduced is not worth the effort:
static gboolean
callback(GtkWidget *button, GdkEventKey *event)
{
if (event != NULL && event->keyval == 's')
callback(button, data);
return FALSE;
}
int
main(int argc, char **argv)
{
/* TODO */
g_signal_connect(button, "clicked", G_CALLBACK(callback), NULL);
g_signal_connect(button, "key-press-event", G_CALLBACK(callback), NULL);
/* TODO */
}
To be able to understand the latter example you must know that:
the return value is discarded if not used (you must return FALSE from a key press event if you don't want to stop the event handling);
the extra arguments in callbacks are silently discarded.
This means when callback() is called by a click you have the data value (NULL) in event while when it is called from a key press the event is properly set and data is silently discarded.
This in turn means if you add a check for event != NULL in your second example things start working.
I am learning GTK+ and this simple application crashes every time I run it.
It creates a label in the main window, and every time a button is clicked (the key_press_event) the label and the title should swap.
If I comment out the gtk_label_set_text in the change_title function the title alternates correctly and the app doesn't crash. Why does gtk_label_set_text crash my app?
#include <gtk/gtk.h>
#include <string.h>
const gchar first[]="FIRST";
const gchar last[]="LAST";
static void destroy(GtkWidget *window,gpointer data)
{
gtk_main_quit();
}
static gboolean change_title(GtkWidget *widget,GtkLabel *data)
{
if(strcmp(last,gtk_window_get_title(GTK_WINDOW(widget)))){
gtk_window_set_title(GTK_WINDOW(widget),last);
gtk_label_set_text(data,first);
} else {
gtk_window_set_title(GTK_WINDOW(widget),first);
gtk_label_set_text(data,last);
}
return FALSE;
}
int main(int argc,char **argv)
{
GtkWidget *window, *label;
gtk_init(&argc,&argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window),last);
gtk_widget_set_size_request(window,300,100);
g_signal_connect(window,"destroy_event",G_CALLBACK(destroy),NULL);
label = gtk_label_new("caasdasdjadnjadjahadjad");
gtk_container_add(GTK_CONTAINER(window),label);
g_signal_connect(window,"key_press_event",G_CALLBACK(change_title),GTK_LABEL(label));
gtk_widget_show_all(window);
gtk_main();
return 0;
}
EDIT: I found the problem using GDB, the label pointer isn't passed correctly to the change_title function. I don't know why. (Ex: in main() label = 0xb6406608 , in change_title() label = 0x807bda8)
After doing a simple Google search on key_press_event I saw that the callback to that event have another argument between the widget and the user-data pointer. The prototype is this:
gboolean key_event_handler(GtkWidget *widget,GdkEventKey *event, gpointer data);
So simple change your function to this:
static gboolean change_title(GtkWidget *widget, GdkEventKey *event, GtkLabel *data)
and it should work.
Your change_title function has the wrong prototype.
See the documentation for the proper prototype. Most *-event signals pass the actual event as an argument in the handler function, since the handler typically needs to inspect the event in order to execute. For instance, here the GdkEventKey event will contain information about which key was pressed (or released).
I am having some trouble placing my popup menu where I want in GTK(along the same y axis as my button so it seems to dropdown from the button).
I got it working when I was coding a similar thing in win32 but for the life of me I cannot get this thing to draw where I want!
I've done my research and I know what (I think) should work and that is when I make the call to
gtk_menu_popup()
I should pass it in a function pointer to a method for placing the popup (link to the method specification - http://developer.gnome.org/gtk/2.24/GtkMenu.html#GtkMenuPositionFunc )
But i'm a bit rubbish with function pointers ( I have the Kernighan and ritchie book beside as I speak) but I'm pretty sure I'm doing it right.
Heres my attempt at the method :
void set_position(GtkMenu *menu,gint *x,gint *y,gboolean push_in,gpointer user_data)
{
printf("Help!\n";
GtkWidget *originButton = (GtkWidget *) gtk_object_get_data(GTK_OBJECT(menu),"button");
gdk_window_get_position(originButton->window,x,y);
//now I realise this will place it at the top of the widget but thats acceptable for now, I just want the method to be called!
}
I call it from the button handler by doing this :
static gboolean handler (GtkWidget *widget,GdkEvent *event)
{
GdkEventButton *bevent = (GdkEventButton *) event;
gtk_menu_popup(GTK_MENU(widget),NULL,NULL,set_position,bevent->button,bevent->button,bevent->time);
}
But when I compile this it says its not a GtkMenuPositionFunc, so I just cast it (dont know if thats right though).
The problem is that I dont think my method is getting called because it never prints out help :( plus it still spawns the menu wherever I click (probably due to it not calling the method or whatever it should be doing).
Any help/ideas would be greatly received :) thanks :)
I had the same problem earlier. I used the following code.
static void
set_position(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data)
{
GtkWidget *button = GTK_BUTTON(user_data);
gdk_window_get_origin(button->window, x, y);
*x += button->allocation.x;
*y += (button->allocation.y + (button->allocation.height));
}
static void
handler(GtkWidget *menu, GtkWidget *button)
{
GtkRequisition requisition;
gtk_widget_size_request(menu, &requisition);
if (button->allocation.width > requisition.width)
gtk_widget_set_size_request(menu, button->allocation.width, -1);
gtk_menu_popup(GTK_MENU (menu),
NULL, NULL,
(GtkMenuPositionFunc) set_position, button,
0, gtk_get_current_event_time());
}
Okay, thanks very much to Pfeiffer for his sample code. I had to make some changes to it because I was getting compiler errors (i have Werror on though).
In the set poisition method I ended up casting to a GTK_WIDGET instead of a GTK_BUTTON becauses thats what it was expecting
So my new set position method looked like :
static void set_position(GtkMenu *menu, gint *x, gint *y, gboolean *push_in, gpointer user_data) {
GtkWidget *button = GTK_WIDGET(user_data);
gdk_window_get_origin(button->window, x, y);
*x += button->allocation.x;
*y += (button->allocation.y + (button->allocation.height));
}
And as I couldn't figure out how to connect and pass the two widgets that Pfeiffer was passing I have this currently :
static gboolean handler(GtkWidget *widget, gpointer data) {
gtk_menu_popup(GTK_MENU(data), NULL, NULL,(GtkMenuPositionFunc)set_position,widget,(guint)widget, gtk_get_current_event_time());
return TRUE;
}
Also had to cast widget to a (guint) otherwise I got a compiler error about the 6th parameter :)
I will probably end up putting more in this method but this is the bare minimum I needed to get it to work.
That is connected using the normal gtk_signal_connect method
gtk_signal_connect(GTK_OBJECT(button), "clicked", GTK_SIGNAL_FUNC(handler), GTK_OBJECT(menu));
where button and menu are GtkButton and GtkMenu instances I was using previously :)
Full credit goes to Pfeiffer for the answer, i'm just showing what worked for me :)
Thanks and hope this helps someone :)