Gtk4 get text from entry

,

I’m learning gtk4 in C. I’m trying to do a simple number base conversion program. The actual number conversion logic is just a placeholder, right now I just want to get the gtk stuff to work.

#include <gtk/gtk.h>
#include <stdlib.h>

void convert_number(GtkWidget *widget, gpointer user_data) {
    GtkWidget **widgets = (GtkWidget **)user_data;
    GtkWidget *entry = widgets[0];
    GtkWidget *combo_box_from = widgets[1];
    GtkWidget *combo_box_to = widgets[2];
    GtkWidget *label = widgets[3];

    const gchar *number_str = gtk_editable_get_text(GTK_EDITABLE(entry));
    int base_from = gtk_drop_down_get_selected(GTK_DROP_DOWN(combo_box_from));
    int base_to = gtk_drop_down_get_selected(GTK_DROP_DOWN(combo_box_to));

    char *endptr;
    long number = strtol(number_str, &endptr, base_from);
    if (*endptr != '\0') {
        gtk_label_set_text(GTK_LABEL(label), "Invalid number");
        return;
    }

    char result[33];
    snprintf(result, sizeof(result), "%ld", number);
    gtk_label_set_text(GTK_LABEL(label), result);
}

void activate(GtkApplication *app, gpointer user_data) {
    GtkWidget *window = gtk_application_window_new(app);
    gtk_window_set_title(GTK_WINDOW(window), "Conversor de bases");
    gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);

    GtkWidget *grid = gtk_grid_new();
    GtkWidget *entry = gtk_entry_new();
    gtk_entry_set_placeholder_text(GTK_ENTRY(entry), "Digite um número");
    gtk_editable_set_editable(GTK_EDITABLE(entry), TRUE);

    GtkStringList *list_from = gtk_string_list_new(NULL);
    GtkStringList *list_to = gtk_string_list_new(NULL);

    GtkWidget *button = gtk_button_new_with_label("Converter");

    const gchar *bases[] = {"Binario", "Octal", "Decimal", "Hexadecimal"};
    for (int i = 0; i < 4; i++) {
        gtk_string_list_append(list_from, bases[i]);
        gtk_string_list_append(list_to, bases[i]);
    }

    GtkWidget *combo_box_from = gtk_drop_down_new(G_LIST_MODEL(list_from), NULL);
    GtkWidget *combo_box_to = gtk_drop_down_new(G_LIST_MODEL(list_to), NULL);

    GtkWidget *label = gtk_label_new("");

    GtkWidget *widgets[] = {entry, combo_box_from, combo_box_to, label};
    g_signal_connect(button, "clicked", G_CALLBACK(convert_number), widgets);

    gtk_grid_attach(GTK_GRID(grid), entry, 0, 0, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), combo_box_from, 0, 1, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), combo_box_to, 0, 2, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), button, 0, 3, 1, 1);
    gtk_grid_attach(GTK_GRID(grid), label, 0, 4, 1, 1);

    gtk_window_set_child(GTK_WINDOW(window), grid);
    gtk_window_present(GTK_WINDOW(window));
}

int main(int argc, char *argv[]) {
    GtkApplication *app = gtk_application_new("gtk.example.app", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
    int status = g_application_run(G_APPLICATION(app), argc, argv);
    g_object_unref(app);
    return status;
}

Everything renders fine, but after I type a number and select the base I want to convert from and the base I want to convert to and click the convert button I get a segfault.

It seems to happen on line 11

const gchar *number_str = gtk_editable_get_text(GTK_EDITABLE(entry));

If I add

if (!GTK_IS_EDITABLE(entry)) {
    return;
}

It segfaults there

What am I doing wrong?

The user_data, widgets, is allocated on the stack, so its lifetime is the duration of the call to activate(). You could just declare it static.

EDIT: and populate the elements separately, because the initializer would then need to consist of constants.

Separately, using a struct would be better than an array of GtkWidget.

1 Like

I thought something like that was happening so I also tried allocating widgets with malloc but it had the same error.

In the end I did end up using a struct that I made static and it worked just fine.

struct widget_list {
    GtkWidget *entry;
    GtkWidget *combo_box_from;
    GtkWidget *combo_box_to;
    GtkWidget *label;
};
...
    static struct widget_list widgets;

I had never used the static keyword before. The way I thought I understood it was that if I used malloc the stuff would still be valid until I called free, I guess I gotta study more about what C is doing memory wise.

Thanks for the help!

EDIT: I think I misunderstood the flow of the program when using gtk and callbacks and stuff. If I malloc my struct in the main function and free it after the application has run then it works like I’d expect and I don’t get segfaults.

int main(int argc, char *argv[]) {
    struct widget_list *widgets = malloc(sizeof(struct widget_list));

    GtkApplication *app = gtk_application_new("gtk.example.app", G_APPLICATION_DEFAULT_FLAGS);
    g_signal_connect(app, "activate", G_CALLBACK(activate), widgets);
    int status = g_application_run(G_APPLICATION(app), argc, argv);
    g_object_unref(app);

    free(widgets);

    return status;
}

I hope this is somewhat equivalent to just using the static keyword on the activate function. Or I guess it doesn’t even need to be done with malloc at that point.

In a more complex C program, the recommended way is to use g_new or g_new0 along with g_signal_connect_data:

    typedef struct { /* ... */ } widget_list;
    widget_list *list = g_new(widget_list, 1);
    /* ... */
    g_signal_connect_data(button, "clicked", G_CALLBACK(convert_number), widgets, g_free, 0);

This will ensure g_free(widgets) is called automatically when the button is destroyed. So in a sense this lets you have structs that are “owned” by a widget and attached to a specific closure.

2 Likes

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.