Home > database >  How to pass multiple widgets in a GTK single button click event in C
How to pass multiple widgets in a GTK single button click event in C

Time:11-23

So, I have many entries in a 2D array in my GTK application, like this:

GtkWidget *entries[10][10];
for(int i = 0; i < 10; i  ){
        for(int j = 0; j < 10; j  ){
                entries[i][j] = gtk_entry_new();
        }
}

And a button:

GtkWidget *btn;
btn = gtk_button_new_with_label("Calculate");

I want that, when the user clicks on the button, all the values in the entries be passed to the callback function in order to do some operations with the data.

I have:

g_signal_connect(btn, "clicked", G_CALLBACK(calculate), entries);

And a function called calculate():

void calculate(GtkWidget *btn, gpointer data){
        double **values = (double **) data;
    
        for(int i = 0; i < 10; i  ){
                for(int j = 0; j < 10; j  ){
                    printf("%f ", gtk_entry_get_text(GTK_ENTRY(values[i][j])));
                }
        
                printf("\n");
        }
}

Its name suggest that a calculation is going to be done. Indeed, I intend to develop the code to do so, but prior to it I need to get the data in the entries.

Why I am not successful in getting the data?

[UPDATE]

I changed g_signal_connect(btn, "clicked", G_CALLBACK(calculate), entries); to g_signal_connect(btn, "clicked", G_CALLBACK(calculate), &entries[0][0]);

The function calculate now is:

void calculate(GtkWidget* btn, gpointer *data){ 
    GtkWidget **values = (GtkWidget **) data;
    
    for(int i = 0; i < 10; i  ){
        for(int j = 0; j < 10; j  ){
            printf("%s\n", gtk_entry_get_text(GTK_ENTRY(*(values i)   j)));
        }
    }
}

I was successul in getting the text of the first entry, using gtk_entry_get_text(GTK_ENTRY(*values)), but how can I get the texts of the other entries?

CodePudding user response:

You are messing up the type of parameter you pass to your callback function:

GtkWidget *entries[10][10];
for(int i = 0; i < 10; i  ){
        for(int j = 0; j < 10; j  ){
                entries[i][j] = gtk_entry_new();
        }
}
...

g_signal_connect(btn, "clicked", G_CALLBACK(calculate), entries);

This defines an array of 10*10 pointers.

But then you tell your compiler to treat is as a pointer to a pointer:

void calculate(GtkWidget *btn, gpointer data){
        double **values = (double **) data;

This indicates a very different memory layout. A 2D array is not the same as a pointer to pointer. Besides that your array contains pointers itself.

What you need is this:

void calculate(GtkWidget *btn, gpointer data){
       GtkWidget*(*values)[10] = (GtkWidget*(*)[10])data;
...

This should fix your issue with addressing your entries properly.

Besides that you may run into another problem. If the array entries is no longer valid at the time your callback function is called, you have an illegal memory access causing undefined behaviour.

You must make that array static or global. Or you must ensure that the function that did the initialization, never returns before the application is termianted.

CodePudding user response:

Solved in C

I am not a but C Gtk user, but I was able to make it work under Gtkmm 3.24 (which I am more familiar with). Here is my program, which should be translatable to the C language:

#include <array>
#include <iostream>
#include <string>

#include <gtkmm.h>

class MainWindow : public Gtk::ApplicationWindow
{

public:

    MainWindow();

private:

    Gtk::Grid m_layout;

    Gtk::Button m_button;
    std::array<Gtk::Entry, 4> m_entries;

};

MainWindow::MainWindow()
: m_button{"Print entries content"}
{
    // Configuring entries:
    size_t count = 0u;
    for(auto& entry : m_entries)
    {
        entry.set_text("Entry #"   std::to_string(count));
          count;
    }

    // Configuring the button handler:
    m_button.signal_clicked().connect(
        [this]()
        {
            for(const auto& entry : m_entries)
            {
                std::cout << "Entry text : " << entry.get_text() << std::endl;
            }
        }
    );

    // Configuring window layout:
    m_layout.attach(m_entries[0], 0, 0, 1, 1);
    m_layout.attach(m_entries[1], 1, 0, 1, 1);
    m_layout.attach(m_entries[2], 0, 1, 1, 1);
    m_layout.attach(m_entries[3], 1, 1, 1, 1);
    m_layout.attach(m_button, 0, 2, 2, 1);

    // Adding the layout to the window:
    add(m_layout);
}

int main(int argc, char *argv[])
{
    std::cout << "Gtk(mm) version : " << gtk_get_major_version() << "."
                                      << gtk_get_minor_version() << "."
                                      << gtk_get_micro_version() << std::endl;

    auto app = Gtk::Application::create(argc, argv, "org.gtkmm.examples.base");
  
    MainWindow window;
    window.show_all();
  
    return app->run(window);
}

By the way, note this is a (minimal) simplified version of what I have understood your problem to be.

Other notes on your code snippets

Looking at your code, I see this line:

double **values = (double **) data;

however, when you connect, you have:

g_signal_connect(btn, "clicked", G_CALLBACK(calculate), entries);

From what I understand from the GtkButton clicked signal handler, the type of data should match entries, which is a table of GtkEntry. Why, then cast it to (double **)?

  • Related