Here a short MCV to illustrate the problem I meet :
I call a dialog box from my main window.
I click on one of the buttons from the dialog (usual way I think)
If I click again on one button, dialog will not display again (I got a bunch of errors instead).
This doesn't happen of I use gtk_widget_hide instead.
(interfaces are designed with Glade3)
typedef struct {
GtkBuilder *builder;
gchar *stuff;
} Context;
void conDisplay(GtkWidget *g, gpointer userdata) {
GtkWidget *dlg, *parent;
Context *ctx=(Context *)userdata;
int ret=0;
g_printerr("clicked\n");
parent=GTK_WIDGET(gtk_builder_get_object(ctx->builder,(gchar *)"MCV"));
if (ctx->stuff) {
g_printerr("Already connected\n");
dlg=GTK_WIDGET(gtk_builder_get_object(ctx->builder,(gchar *)"question"));
gtk_window_set_transient_for(GTK_WINDOW(dlg),GTK_WINDOW(parent));
ret=gtk_dialog_run(dlg);
if (ret==-3) { // OK clicked
g_printerr("OK from Already connected\n");
}
else { g_printerr("Unknown\n"); }
gtk_window_set_transient_for(GTK_WINDOW(dlg),NULL);
gtk_widget_destroy(GTK_WIDGET(dlg));
} else {
dlg=GTK_WIDGET(gtk_builder_get_object(ctx->builder,(gchar *)"connect"));
gtk_window_set_transient_for(GTK_WINDOW(dlg),GTK_WINDOW(parent));
ret=gtk_dialog_run(dlg);
if (ret==-1) { // GO clicked
g_printerr("GO\n");
ctx->stuff="Hello";
}
else { g_printerr("Cancel\n"); }
gtk_window_set_transient_for(GTK_WINDOW(dlg),NULL);
gtk_widget_destroy(GTK_WIDGET(dlg));
}
}
int main(int argc, char **argv)
{
Context ctx;
GtkWidget *mainwin;
GtkWidget *btnCon;
GError *error=NULL;
/* Init GTK+ */
gtk_init(&argc,&argv);
ctx.builder=gtk_builder_new();
ctx.stuff=NULL;
// Load UI from file.
gtk_builder_add_from_file(ctx.builder,"mcv.glade",&error);
mainwin=GTK_WIDGET(gtk_builder_get_object(ctx.builder,(gchar *)"MCV"));
btnCon=GTK_WIDGET(gtk_builder_get_object(ctx.builder,(gchar *)"con"));
g_signal_connect(btnCon, "clicked", (GCallback)conDisplay, &ctx);
gtk_widget_show_all(mainwin);
gtk_main();
return 0;
}
Thanks for your help !
Best regards.
V.
From GtkBuilder:
A GtkBuilder holds a reference to all objects that it has constructed and drops these references when it is finalized. This finalization can cause the destruction of non-widget objects or widgets which are not contained in a toplevel window. For toplevel windows constructed by a builder, it is the responsibility of the user to call gtk_widget_destroy() to get rid of them and all the widgets they contain.
Since GtkDialog inherits from GtkWindow I would suggest separating mainwin and dlg GtkBuilders and gladefiles and use temporary GtkBuilder in conDisplay:
void conDisplay(GtkWidget *g, gpointer userdata) {
GtkWidget *dlg, *parent;
Context *ctx=(Context *)userdata;
GtkBuilder *builder = gtk_builder_new_from_file("dlg.glade"); //or even pass glade filename in Context
/* rest of the code */
gtk_widget_destroy(GTK_WIDGET(dlg));
g_object_unref(G_OBJECT(builder));
}
Related
I have seen people pass a widget like this
static void on_button_click(GtkWidget *button, gpointer data) {
gtk_label_set_label(GTK_LABEL(data), "Hello, World!");
}
...
g_signal_connect(button, "clicked", G_CALLBACK(on_button_click), label);
but how do you pass 2 widgets like a label and an entry?
I want to write a calculator so I need to read from the entry, parse and write the results to the label when the user click (on) the button.
Thanks in advance for helping this noob.
The last argument (gpointer data) can point to whatever you want, so options are basically limitless.
I think in order from "quick hack for a demo app" up to "scalable/maintainable for large, complex applications", some of these options might be:
For a simple program with only a handful of widgets, I might be inclined to just store the GtkWidget pointers in global memory:
GtkWidget *window;
GtkWidget *label;
GtkWidget *btnEnter;
GtkWidget *btnClear;
GtkWidget *entry;
// copy entry to label for simple demo purposes
static void on_enter_button_click(GtkWidget *button, gpointer data) {
// data unused, just get global references to label + entry
gtk_label_set_label(GTK_LABEL(label), gtk_entry_get_text(entry));
}
// clear the label. maybe this is another function your calculator has
static void on_clear_button_click(GtkWidget *button, gpointer data) {
// data unused, just get global references...
gtk_label_set_label(GTK_LABEL(label), "");
}
int main(int argc, char**argv){
// Initialize all the widgets here
// register unique callbacks
g_signal_connect(btnEnter, "clicked", G_CALLBACK(on_enter_button_click), NULL);
g_signal_connect(btnClear, "clicked", G_CALLBACK(on_clear_button_click), NULL);
}
You can group these into a struct if uncomfortable using global memory or want slightly more ability to scale/refactor/reuse your code:
struct MyWidgets {
GtkWidget *window;
GtkWidget *label;
GtkWidget *btnEnter;
GtkWidget *btnClear;
GtkWidget *entry;
};
// copy entry to label for simple demo purposes
static void on_enter_button_click(GtkWidget *button, gpointer data) {
struct MyWidgets *widgets = (struct MyWidgets*) data;
gtk_label_set_label(GTK_LABEL(widgets->label), gtk_entry_get_text(widgets->entry));
}
// clear the label... maybe this is another function your calculator has?
static void on_clear_button_click(GtkWidget *button, gpointer data) {
struct MyWidgets *widgets = (struct MyWidgets*) data;
// data unused, just get global references...
gtk_label_set_label(GTK_LABEL(widgets->label), "");
}
int main(int argc, char**argv){
struct MyWidgets widgets;
// Initialize all the widgets here
// register unique callbacks
g_signal_connect(btnEnter, "clicked", G_CALLBACK(on_enter_button_click), &widgets);
g_signal_connect(btnClear, "clicked", G_CALLBACK(on_clear_button_click), &widgets);
}
But for something really simple, above is basically same as first example, but with extra steps :).
Another alternative could be to group widgets within any GtkContainer "class",which may fall out naturally in grouping objects into a particular layout, and then use something like gtk_widget_get_parent() to get parent container, then any one of the several methods on GtkContainer that allows iterating through children.
ANOTHER alternative is composite widgets. This tutorial demonstrates some of the concepts there and implements a single callback function for handling multiple widgets: https://www.cc.gatech.edu/data_files/public/doc/gtk/tutorial/gtk_tut-20.html
I need to get text from a bunch of GtkEntries when a button click. I create a custom struct and pass it to the button's click callback. I don't want to use gtk_entry_get_text(*entry) since I need to pass the struct of GtkEntries.
typedef struct{
const gchar* id1;
const gchar* id2;
} EntryData;
static void on_click(GtkWidget *widget,
gpointer data) {
EntryData* d= (EntryData*)data;
printf ("Entry contents: %s\n", d->id1);
printf ("Entry contents: %s\n", d->id2);
}
int main(int argc,char *argv[]) {
// ....
GtkButton *button_create_hp;
GtkEntry *entry_id1;
GtkEntry *entry_id2;
gtk_init(&argc, &argv);
//...... widget and object initialization
gtk_entry_set_text(entry_ssd,"");
gchar *strval1="sl";
gchar *strval2="sl";
g_object_get(G_OBJECT (entry_id1), "text", &strval1,NULL);
g_object_get(G_OBJECT (entry_id2), "text", &strval2,NULL);
EntryData entryData={
.id1= strval1,
.id2= strval2
};
g_signal_connect (button_create_hp, "clicked", G_CALLBACK(on_click),&entryData);
gtk_main();
return 0;
}
I also tried with g_object_get_property (G_OBJECT (entry_id), "text", &val);
In both cases changed values not printed when the button clicked.
Can you suggest a proper way to get values and pass it from GtkEntries
It's not very obvious from the docs, but when you somehow modify text it may be moved to another location, making previous pointer invalid.
If you don't want to pass GtkEntries to your structures, you can update pointers when EntryBuffer emits "deleted-text" or "inserted-text" (or connect to GtkEntry's "notify::text")
On on GTK window there is a red close icon rendered in the title bar. Normally when you click on this, the window is closed and it's resources released.
Is there a way of intercepting the normal flow to prevent the window from being destroyed so that I can show it again later? i.e. I want to hide the window not close/destroy it.
This is what I have so far.
void destroy_window_callback(GtkWidget* widget, WebWindow_Linux* source)
{
printf("Don't destroy the window, just hide it.\n");
}
g_signal_connect(web_window, "destroy", G_CALLBACK(destroy_window_callback), this);
This is probably what you need
#include <gtk/gtk.h>
void
on_button_clicked(GtkButton *button, gpointer data)
{
GtkWidget *widget;
widget = (GtkWidget *) data;
if (widget == NULL)
return;
gtk_widget_show(widget);
return;
}
gboolean
on_widget_deleted(GtkWidget *widget, GdkEvent *event, gpointer data)
{
gtk_widget_hide(widget);
return TRUE;
}
int
main(int argc, char **argv)
{
GtkWidget *window1;
GtkWidget *window2;
GtkWidget *button;
gtk_init(&argc, &argv);
window1 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
window2 = gtk_window_new(GTK_WINDOW_TOPLEVEL);
button = gtk_button_new_with_label("Show again...");
g_signal_connect(G_OBJECT(window1),
"destroy", gtk_main_quit, NULL);
g_signal_connect(G_OBJECT(window2),
"delete-event", G_CALLBACK(on_widget_deleted), NULL);
g_signal_connect(G_OBJECT(button),
"clicked", G_CALLBACK(on_button_clicked), window2);
gtk_container_add(GTK_CONTAINER(window1), button);
gtk_widget_set_size_request(window1, 300, 100);
gtk_widget_set_size_request(window2, 300, 100);
gtk_widget_show_all(window1);
gtk_widget_show(window2);
gtk_main();
return 0;
}
We basically have three widgets, two top level windows and a button. The first window has it's "destroy" event connected to gtk_main_quit() quitting the application when the window's close button is pressed. The second window has it's "delete-event" connected to a custom function. This is the important one. As you see it returns TRUE indicating that the signal was handled and thus preventing to call the default handler and hence preventing the call to gtk_widget_destroy(). Also in it we can hide the widget if we want.
I have following declarations:
typedef struct window_and_search_entry
{
GtkWidget *window;
GtkWidget *search_entry;
} WINDOW_AND_SEARCH_ENTRY;
and then in main:
GtkWidget *window;
GtkWidget *search_entry;
...
WINDOW_AND_SEARCH_ENTRY window_and_search_entry;
window_and_search_entry.window = window;
window_and_search_entry.search_entry = search_entry;
and that two:
g_signal_connect_swapped(G_OBJECT(search_entry), "activate", G_CALLBACK(analyse), (gpointer) &window_and_search_entry);
g_signal_connect_swapped(G_OBJECT(do_it_button), "clicked", G_CALLBACK(analyse), (gpointer) &window_and_search_entry);
And I want to create function which takes text, and window, makes some operations on it and if an error occurs print it out with other function which takes window as parameter
ELEMENT *analyse(GtkWidget *widget, gpointer user_data)
{
//((WINDOW_AND_SEARCH_ENTRY *) user_data)->search_entry;
GtkWidget *a = ((WINDOW_AND_SEARCH_ENTRY*)(user_data))->search_entry;
const char *text = gtk_entry_get_text(GTK_ENTRY(a));
g_print("%s\n", text);
ELEMENT *heap[100];
int index = 0;
return heap[1];
}
I tried many variants but I get "Segmentation fault" after having something typed in entry_box followed by enter or button. I want to print the text to console. Please help me, thanks.
Your analyse callback requires g_signal_connect, not g_signal_connect_swapped. Also there is no need to cast pointers: the G_CALLBACK() macro already cast the whole function, so this would suffice:
ELEMENT *analyse(GtkWidget *widget, WINDOW_AND_SEARCH_ENTRY *data)
With g_signal_connect_swapped it would be:
ELEMENT *analyse(WINDOW_AND_SEARCH_ENTRY *data, GtkWidget *widget);
This swapped version is only a C convenience for already existing functions, e.g. you can do something like this:
g_signal_connect_swapped(G_OBJECT(search_entry), "activate",
G_CALLBACK(g_print), "Activate signal called");
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).