Get text from GtkEntry with property "text" - c

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")

Related

How to pass 2 or more GTK widget to callback function

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

GTK3 : gtk_widget_destroy versus gtk_widget_hide

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));
}

Changing GTK label in C using signal_connect

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]);

GTK+ 2.0 C weird structure and g_signal_connect_swapped

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");

return filename from G_CALLBACK in GTK

Hey guys so I would like to return value of filename from function (or pointer) but Im to sure how to do it. My filename stores path to file name /home/username/file. So here is my main
gchar *filename = NULL;
button = GTK_WIDGET( gtk_builder_get_object( builder, "button1" ) );
g_signal_connect (button, "clicked", G_CALLBACK (show_dialog), &filename);
And show dialog look like this
static void
show_dialog ( GtkWidget *button,
gint response_id,
gpointer user_data )
/* Init filechoosedialog, builder etc */
{
gchar *filename = gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (filechoosedialog));
}
Do I need to change static void show_dialog to gchar show_dialog and at end do I need to do return filename; and what else do I need to declare because Im getting compile error
warning: return makes integer from pointer without a cast
So I would like to add code like this to my main and to get value of filename
if filename != NULL
g_print ("Selected filename: %s\n", filename);
This sounds like a misunderstanding of event driven programming. You don't do things like this in main(), you do it in the event handler. In your example you would call g_print() from show_dialog().
If you want to do something more complex you'll need to know some application state: that's what the g_signal_connect() userdata pointer is for. As an example, if you want to set the filename as the text of a label, you would set a pointer to the label as the user data pointer and can then set the label text in the event handler.
More generically, the userdata pointer can be anything: e.g. a data structure that you can use to store the filename string. Typical usage is an application data struct that is allocated in main: The struct contains all "application global" variables (like filename) and a pointer to the struct is given to all GTK+ callbacks so all functions can access and modify the struct contents.
If the problem was using the userdata pointer, here's an example using just a pointer to the filename as userdata:
g_signal_connect (button, "clicked", G_CALLBACK (button_clicked_cb), &filename);
static void
button_clicked_cb (GtkWidget *button,
gint response_id,
gpointer user_data)
{
char **filename_ptr = (char**)user_data;
*filename_ptr = g_strdup ("newly allocated string that the filename pointer in main() stack will now point to");
}

Resources