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().
Related
It's my first post here, I made this account because I'm kind of stumped.
I'm trying to practise passing structures as arguments to callbacks, and for that purpose I created a simple program with a button in a window. In the first iteration, pressing the button with label "Button 1" will change it into a different button with label "Button 2", while in the second the change is done by hovering over the buttons.
Below is the code for the first iteration
#include <gtk/gtk.h>
typedef struct {
GtkWidget *button1;
GtkWidget *button2;
GtkWidget *window;
} example;
void callback_func (GtkWidget *ignored, example *test) {
GtkWidget *window=test->window;
GtkWidget *changebutton1=test->button1;
GtkWidget *changebutton2=test->button2;
gtk_container_remove(GTK_CONTAINER(window),changebutton1);
gtk_container_add(GTK_CONTAINER(window),changebutton2);
gtk_widget_show_all(window);
}
void callback_func2 (GtkWidget *ignored, example *test) {
GtkWidget *window=test->window;
GtkWidget *changebutton1=test->button1;
GtkWidget *changebutton2=test->button2;
gtk_container_remove(GTK_CONTAINER(window),changebutton2);
gtk_container_add(GTK_CONTAINER(window),changebutton1);
gtk_widget_show_all(window);
}
int main(int argc, char *argv[]) {
example test;
gtk_init(&argc,&argv);
GtkWidget *window=gtk_window_new(GTK_WINDOW_TOPLEVEL);
GtkWidget *changebutton1=gtk_button_new_with_label("Button 1");
GtkWidget *changebutton2=gtk_button_new_with_label("Button 2");
test.window=window;
test.button1=changebutton1;
test.button2=changebutton2;
g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
gtk_container_add(GTK_CONTAINER(window),changebutton1);
g_signal_connect (G_OBJECT (changebutton1), "clicked", G_CALLBACK (callback_func), (gpointer*)&test);
g_signal_connect (G_OBJECT (changebutton2),"clicked",G_CALLBACK(callback_func2),(gpointer*)&test);
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
When running the above code, by clicking the button once, the buttons switch properly, but when I click it again (now calling callback_func2 instead of callback_func, which are essentially identical save for the buttons switching place) I get this:
(gtktesting.exe:92024): Gtk-CRITICAL **: gtk_container_add: assertion `GTK_IS_WIDGET (widget)' failed
If I replace the "clicked" events with "enter_notify_event" and "leave_notify_event", the first change fails as well, now yielding more errors.
Anybody know what's going on?
Also, a bonus question. I pretty much copied and pasted the base of this code from another post, since I had been trying to write it by myself unsuccessfully and I wanted to test if it works at all. I noticed that the second argument in the callbacks is "example *test". Can anybody explain the "example" type? It's only the name of the stucture, and I've no idea what it's doing there.
tl;dr
Try:
test.window = g_object_ref(window);
test.button1 = g_object_ref(changebutton1);
test.button2 = g_object_ref(changebutton2);
Full explaination
Objects (i.e. subclasses of GObject) in GTK+ are reference counted. That means, every object (e.g. Widget) has "reference count" - number of pointers that point to it. When number reaches 0 - object is deallocated. Objects are created with reference count of 1. As C does not have smart pointers C++ does nor anything similar, reference counting has to be done manually. User has to call g_object_ref in order to retain a reference (I use words "pointer" and "reference" interchangeably), and g_object_unref when user is done with a reference. That ensures that no object is destroyed while being in use.
GtkWidget is special, as it begins life with "floating" reference. That means that first time widget is referenced its reference count does not increase - its "floating" reference is "sunk". After that it behaves like any other GObject.
When you create your buttons, they are created with reference count 1 ("floating"). When they are added to their container, their references are still 1 (but "sunk"). This means, buttons are owned by the container that they're added to.
Now, when you remove button from container:
gtk_container_remove(GTK_CONTAINER(window),changebutton1);
reference count for changebutton1 is decreased, drops to 0, which forces object destruction, and test.button1 is now dangling pointer.
To overcome this, use g_object_ref anytime you want to store pointer to GObject. That way you express "test participates in ownership of changebutton1" (or, "test is interested in keeping changebutton1 alive).
When you are done with window, button1 and button2, call g_object_unref on them.
Bonus question
Also, a bonus question. I pretty much copied and pasted the base of
this code from another post, since I had been trying to write it by
myself unsuccessfully and I wanted to test if it works at all. I
noticed that the second argument in the callbacks is "example *test".
Can anybody explain the "example" type? It's only the name of the
stucture, and I've no idea what it's doing there.
example is defined here:
typedef struct {
GtkWidget *button1;
GtkWidget *button2;
GtkWidget *window;
} example; // declares type "example"
GObject signal system is designed in a sucha way that it allows passing arbitrary pointer as a last argument to callback, so a programmer can pass extra information from place where signal connection is created (g_signal_connect), to a callback.
I have a main.c file where a progress bar is created and then it will be updated by other.c file.
Inside the main.c file i have this:
static void
a_func ( GtkWidget *dialog,
struct widget_t *Widget,
gint mode)
{
....
....
Widget->pBar = gtk_progress_bar_new ();
....
....
}
which create and run a dialog. When OK is pushed my_func (Widget); will be called.
Inside the other.c file i have this:
static gboolean
fill (gpointer data)
{
GtkWidget *bar = data;
gtk_progress_bar_pulse (GTK_PROGRESS_BAR (bar));
return TRUE;
}
gint my_func (struct mystruct_t *Widget){
....
....
gtk_progress_bar_pulse (GTK_PROGRESS_BAR (Widget->pBar));
g_timeout_add (100, fill, GTK_PROGRESS_BAR (Widget->pBar));
while (gtk_events_pending ())
gtk_main_iteration ();
....
....
}
The problem is that i'm getting this error:
GLib-GObject-WARNING **: invalid unclassed pointer in cast to 'GtkProgressBar'
Gtk-CRITICAL **: gtk_progress_bar_pulse: assertion 'GTK_IS_PROGRESS_BAR (pbar)' failed
EDIT 1:
This is a compilable example: https://gist.github.com/polslinux/96e7b18176ac66e50ee1
EDIT 2:
This is a simple workflow graph: http://it.tinypic.com/r/316us0y/8
This can not work:
crypt_file (Widget);
gtk_widget_destroy (dialog);
You add a GSource to your mainloop which modifies Widget->pBar but you never remove it. On the other hand you destroy the dialog (and thus also pBar itself as it is part of the widget sub-tree) which in turn renders Widget->pBar useless/makes it a dangling pointer.
Things you did not ask for (excuse the direct approach)
Your variable names are crappy at best, upper lower case, calling things Widget which are no widgets but mere structs.
Make sure you do not split into callbacks and other C functions. That will end up totally messy as your project grows.
Try to go for object-orientation. Create a GObject derived klass which handles the mess internally instead of passing translucent structs around.
I have a table that is filled with entry boxes, labels, and buttons.
Currently, if I compile the code, I can get input from a text box but only if the users presses the enter key, and the text only comes from the box they are currently typing in.
I would like to be able to get input from both text boxes when the "Login" button is pushed. I've tried using the same callback function that's used for enter key on the entry box, but GTK gives me an error.
If anyone could show me some code that would allow for me to get text from my entry boxes that are within tables (I know the method for retrieving data from tables and v/boxes is different) it would be greatly appreciated, as I can't seem to find it in any tutorials.
Will update w/working code.
Error when trying to attach status bar to table:
(Entry:5526): Gtk-CRITICAL **: gtk_table_attach: assertion `child->parent == NULL' failed
(Entry:5526): GLib-GObject-WARNING **: invalid cast from GtkTable' toGtkStatusbar'
Your callback function (named callback) needs to access both GtkEntry widgets in order to obtain their values. There are several ways this can be accomplished. Many GTK C programs use global variables, or global variables with file scope (ie a variable declared as static outside of any function within a file).
Remove your entry1 and entry2 variables near the top of the file before any functions:
static GtkWidget *entry1 = 0;
static GtkWidget *entry2 = 0;
And then modify the callback like so:
/* Our callback.
* The data passed to this function is printed to stdout */
static void callback( GtkWidget *widget, gpointer data)
{
const gchar *entry_text1;
const gchar *entry_text2;
g_print ("Hello again - %s was pressed\n", (char *) data);
entry_text1 = gtk_entry_get_text (GTK_ENTRY (entry1));
entry_text2 = gtk_entry_get_text (GTK_ENTRY (entry2));
g_print ("Contents of entries:\n%s\n%s\n", entry_text1, entry_text2);
}
You should additionally make similar modifications to the enter_callback function, and don't forget to remove the GtkWidget pointers to both GtkEntry from main.
As an alternative to using (static) global variables, create a data structure to hold the entries:
typedef struct login_data
{
GtkWidget *entry1;
GtkWidget *entry2;
} login_data;
This then gets passed to the callback (rather than text string as before), and the callback changes like so:
static void callback( GtkWidget *widget, gpointer data)
{
login_data* ld = (login_data*)data;
const gchar *entry_text1;
const gchar *entry_text2;
entry_text1 = gtk_entry_get_text (GTK_ENTRY (ld->entry1));
entry_text2 = gtk_entry_get_text (GTK_ENTRY (ld->entry2));
g_print ("Contents of entries:\n%s\n%s\n", entry_text1, entry_text2);
}
The data structure is dynamically allocated to prevent it going out of scope (not strictly necessary in simple applications) and this is done before using g_signal_connect to connect the callback to the entries:
login_data* ld = g_malloc(sizeof(*ld));
// callback function to execute when login is clicked
g_signal_connect (LoginButton, "clicked", G_CALLBACK (callback), (gpointer) ld);
Using this method, you must change all references to entry1 and entry2 to ld->entry1 and ld->entry2. Lastly, before the program exits, you should call g_free on the dynamically allocated struct ie g_free(ld).
BTW, for this program you don't need two separate callbacks, remove enter_callback and just use callback for both.
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.
I'm trying to find a way to find out which key is pressed down in C. This will be in a graphical environment, written in GTK2, but I don't think the answer lies there. I think I might be able to do this using Xlib, but I haven't been able to find anything conclusive on this.
Does anyone have any suggestions on how to do this?
I've managed to catch a keypress using the follow code:
GtkWidget *window;
void gtk_widget_set_events(window,GDK_KEY_RELEASE_MASK);
g_signal_connect(window,"key_release_event",G_CALLBACK(hello),NULL);
However, I would like to identify which key is pressed. From the link posted by Aditya Kumar, I know the answer lies with using GdkEventKey, since it is a structure which has a keyval field, but I cannot seem to get the syntax right. What is the correct way of getting this number?
This is a method I've tried:
static void hello( GtkWidget *widget,
guint data ){
g_print ("Hello World, %d was pressed\n",data);}
I tried supplying "data" by doing this when I catch the key_release_event:
g_signal_connect(window,"key_release_event",G_CALLBACK(hello),GdkEventKey.keyval);
However, I get a compiler error like so:
hello.c:85:5: error: expected ‘)’ before ‘.’ token
hello.c:85:5: error: expected expression before ‘,’ token
You are correct with your original syntax.
g_signal_connect(window, "key-release-event", G_CALLBACK(key_event), NULL);
Where the key_event function looks something like (note I am using the gdk_keyval_name to convert the keyval int value to a string for printing):
static gboolean
key_event(GtkWidget *widget,
GdkEventKey *event)
{
g_printerr("%s\n",
gdk_keyval_name (event->keyval));
return FALSE;
}
Here's a complete example program:
#include <gtk/gtk.h>
static gboolean
key_event(GtkWidget *widget,
GdkEventKey *event)
{
g_printerr("%s\n",
gdk_keyval_name (event->keyval));
return FALSE;
}
int main( int argc,
char *argv[] )
{
GtkWidget *window;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect(window, "key-release-event", G_CALLBACK(key_event), NULL);
gtk_widget_show (window);
gtk_main ();
return 0;
}
while looking at the gdk reference manual i think you can capture the keyboard events using this unless you specifically want to have a 'C' program.
Here is the link to help you out.
http://www.gtk.org/api/2.6/gdk/gdk-Keyboard-Handling.html
event->keyval is a pointer to a struct, where keyval contains a integer value for the key pressed, this has been used above in a function gdk_keyval_name (event->keyval) which gets a actual name for the key.