Gtk4 C - which radio button from group was selected

Hello,
please could anybody helps me how could I get which radiobutton was in group selected?

And in function What print to stdout its label?

my code:

#include <gtk/gtk.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string>


using namespace std;

void What (GtkWidget* wid, GtkWidget* data) {
    cout << "what"<< endl;

}

static void appActivate (GApplication *app, gpointer user_data)
{
    GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));

    GtkWidget  *vBox = gtk_box_new (GTK_ORIENTATION_VERTICAL,10);
    gtk_window_set_child (GTK_WINDOW (win), vBox);

    GtkWidget *radioBtn;
    string text;
    GtkWidget* skupina = NULL;
    for (int i = 0; i < 10; ++i)
    {
        text = "btn " + to_string(i);
        radioBtn = gtk_check_button_new_with_label(text.c_str());
        if (skupina == NULL)
        {
            skupina = radioBtn;
        }
        else
        {
            gtk_check_button_set_group (GTK_CHECK_BUTTON (radioBtn), GTK_CHECK_BUTTON (skupina));
        }
        gtk_box_append (GTK_BOX (vBox), radioBtn);
    }

    GtkWidget *tlac = gtk_button_new_with_mnemonic("what is selected");
    gtk_box_append (GTK_BOX (vBox), tlac);
    g_signal_connect_swapped(GTK_BUTTON(tlac), "clicked", G_CALLBACK(What), skupina);


    GtkWidget *btnBack = gtk_button_new_with_label("Close");
    gtk_box_append  (GTK_BOX (vBox), btnBack);
    g_signal_connect_swapped(GTK_BUTTON(btnBack), "clicked", G_CALLBACK(gtk_window_destroy), win);

    gtk_widget_show (win);
}


int main(int argc, char **argv)
{
    GtkApplication *app;
    app = gtk_application_new ("testing.app", G_APPLICATION_FLAGS_NONE);
    g_signal_connect (app, "activate", G_CALLBACK (appActivate), NULL);
    return g_application_run (G_APPLICATION (app), NULL, NULL);
    g_object_unref (app);

    return 0;
}

Is getActive() and getLabel() not available for your C++? And if I remember correctly, in GTK4 you just create a group for the check buttons, to get the radio version. That ensures, that always only one button is active. See GTK4 for Graphical User Interfaces.

Yes indeed, see Gtk.CheckButton (I think you told us some weeks ago that you are using GTK4?)

The UI components tutorial for radio buttons has an example of how to use the “toggled” signal.

Thank you for reply!
I have seen this example, but there is something litle different that I need. In example is emmited signal when is some radiobutton selected and in signal is also with text defined which radiobutton is selected.

But I need go trough whole group and check which radio button from group is active.
for testing if radio button is active I thing I can use gtk_check_button_get_active, but I can not find how to go trough group.
thanks

I think GTK itself does not support iterating over the group. But it should work when you put all the widgets in a C++ container like an array, iterate over that container and call gtk_check_button_get_active() on the items? (When you really have dozens of them). But note, for radio ones, always one of them is active, so I see no need for iterating. and for non-radio, when you know the initial state, and use the toggled signal when state changes, you have all you need? But I have still to read the link of Mr. Bassi, have never seen that one before.

[EDIT] Or, if you do not want to use a C or C++ container, you may put all the radio buttons in its own GTK container, like a Box. GTK allows to iterate over such containers, I think for GTK it is to get the first child, and then somehow getting the next. I never tried – in GTK 3 it was different. Also see checkbox - How to access dynamically created GTK Checkbutton in plain C - Stack Overflow

Thank you for reply!
So I have tried pass vector of strings (to try it how to do that).

I have added few rows of code to previous example

But when I call function “What” it crashs. See stdout:

vektor content:
0-> btn 0
1-> btn 1
2-> btn 2
3-> btn 3
4-> btn 4
5-> btn 5
6-> btn 6
7-> btn 7
8-> btn 8
9-> btn 9
what
size = 23270260629
0:
Process returned -1073741819 (0xC0000005)   execution time : 246.108 s
Press any key to continue.

and ccode changes (rest remains the same):

void What (GtkWidget* wid, GtkWidget* data) {
    cout << "what"<< endl;
     vector<string> *vektorTextuPtr = (vector<string>*) g_object_get_data(G_OBJECT(data),"vektorTextu");

     cout << "size = " << to_string(vektorTextuPtr->size()) << endl;


    for (unsigned int i = 0; i < vektorTextuPtr->size(); ++i) {
        cout << to_string(i) << ": " << vektorTextuPtr->at(i) << endl;

    }
}

static void appActivate (GApplication *app, gpointer user_data)
{
    GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));

    GtkWidget  *vBox = gtk_box_new (GTK_ORIENTATION_VERTICAL,10);
    gtk_window_set_child (GTK_WINDOW (win), vBox);

    vector<string> vektorTextu;

    GtkWidget *radioBtn;
    string text;
    GtkWidget* skupina = NULL;
    for (int i = 0; i < 10; ++i)
    {
        text = "btn " + to_string(i);
        radioBtn = gtk_check_button_new_with_label(text.c_str());
        if (skupina == NULL)
        {
            skupina = radioBtn;
        }
        else
        {
            gtk_check_button_set_group (GTK_CHECK_BUTTON (radioBtn), GTK_CHECK_BUTTON (skupina));
        }
        gtk_box_append (GTK_BOX (vBox), radioBtn);
        vektorTextu.push_back(text);
    }

    GtkWidget *tlac = gtk_button_new_with_mnemonic("what is selected");
    gtk_box_append (GTK_BOX (vBox), tlac);
    g_object_set_data(G_OBJECT(tlac),"vektorTextu",&vektorTextu);
    g_signal_connect_swapped(GTK_BUTTON(tlac), "clicked", G_CALLBACK(What), tlac);


    GtkWidget *btnBack = gtk_button_new_with_label("Close");
    gtk_box_append  (GTK_BOX (vBox), btnBack);
    g_signal_connect_swapped(GTK_BUTTON(btnBack), "clicked", G_CALLBACK(gtk_window_destroy), win);

    gtk_widget_show (win);

    cout << "vektor content:" << endl;
    for (unsigned int i = 0; i< vektorTextu.size();++i) {
        cout << to_string(i) << "-> " << vektorTextu.at(i) << endl;
    }
}

please what I am doing bad with passing vector?
Thanks for any advice

I hope someone with more C++ knowledge can help you, I am not really smart enough for C++ (Read a book about it maybe ten years ago, but forgot most of it.) My guess is, that “vector vektorTextu” is allocated on the stack, so referencing it with “&” and using it later in the “What” callback may fail, as the vector is already freed. I would avoid g_object_get_data(). I think C++ has other ways to subclass widgets, or you may use just a global vector. C++ experts can tell you.

thanks!
so I have found soulution (if anybody need it):
insted of creating

vector<string> vektorTextuPtr

I have created:

vector<string> *vektorTextuPtr = new std::vector<string>() ;

pushed data in it with:

vektorTextuPtr->push_back(text);

and set dat to G_Object with:

g_object_set_data(G_OBJECT(tlac),"vektorTextu",vektorTextuPtr);

function what is the same and during closing app I have also delete: vektorTextuPtr

1 Like

and here is solution for vector<GtkWidget*>
I have two solutions and do not know which is better
1:

#include <gtk/gtk.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>

using namespace std;

void CloseApp (GtkWidget* wid, GtkWidget* data) {
    vector<GtkWidget*> *vektorRadioButtonuPtr = (vector<GtkWidget*>*) g_object_get_data(G_OBJECT(data),"vektorRadioButtonu");
    delete vektorRadioButtonuPtr;
    g_object_unref(G_OBJECT(wid));
}

void What (GtkWidget* wid, GtkWidget* data) {
    cout << "what"<< endl;

     vector<GtkWidget*> *vektorRadioButtonuPtr = (vector<GtkWidget*>*) g_object_get_data(G_OBJECT(data),"vektorRadioButtonu");

    GtkWidget *testovanyRadioBtn = nullptr;
    for (unsigned int i = 0; i < vektorRadioButtonuPtr->size(); ++i) {
            testovanyRadioBtn = vektorRadioButtonuPtr->at(i);
            if (gtk_check_button_get_active(GTK_CHECK_BUTTON(testovanyRadioBtn))) {
                cout << "activated button found: " << gtk_check_button_get_label (GTK_CHECK_BUTTON(testovanyRadioBtn)) << endl;
                return;
            }
    }
    cout << "no button selected" << endl;
}

static void appActivate (GApplication *app, gpointer user_data)
{
    GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));

    GtkWidget  *vBox = gtk_box_new (GTK_ORIENTATION_VERTICAL,10);
    gtk_window_set_child (GTK_WINDOW (win), vBox);

    vector<GtkWidget *> *vektorRadioButtonuPtr = new std::vector<GtkWidget *>() ;

    GtkWidget *radioBtn;
    string text;
    GtkWidget* skupina = NULL;
    for (int i = 0; i < 10; ++i)
    {
        text = "btn " + to_string(i);
        radioBtn = gtk_check_button_new_with_label(text.c_str());
        if (skupina == NULL)
        {
            skupina = radioBtn;
        }
        else
        {
            gtk_check_button_set_group (GTK_CHECK_BUTTON (radioBtn), GTK_CHECK_BUTTON (skupina));
        }
        gtk_box_append (GTK_BOX (vBox), radioBtn);
        vektorRadioButtonuPtr->push_back(radioBtn);
    }

    GtkWidget *tlac = gtk_button_new_with_mnemonic("what is selected");
    gtk_box_append (GTK_BOX (vBox), tlac);
    g_object_set_data(G_OBJECT(tlac),"vektorRadioButtonu",vektorRadioButtonuPtr);
    g_signal_connect_swapped(GTK_BUTTON(tlac), "clicked", G_CALLBACK(What), tlac);

    GtkWidget *btnBack = gtk_button_new_with_label("Close");
    gtk_box_append  (GTK_BOX (vBox), btnBack);

    g_object_set_data(G_OBJECT(win),"vektorRadioButtonu",vektorRadioButtonuPtr);
    g_signal_connect_swapped(GTK_BUTTON(btnBack), "clicked", G_CALLBACK(CloseApp), win);

    gtk_widget_show (win);
}


int main(int argc, char **argv)
{
    GtkApplication *app;
    app = gtk_application_new ("testing.app", G_APPLICATION_FLAGS_NONE);
    g_signal_connect (app, "activate", G_CALLBACK (appActivate), NULL);
    return g_application_run (G_APPLICATION (app), NULL, NULL);
    g_object_unref (app);

    return 0;
}

2:

#include <gtk/gtk.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <vector>

using namespace std;

class RadioButtonGroup
{
    private:
     vector<GtkWidget*> vektorRadioButtonu;

     public:
     void appendRadioBtn(GtkWidget* radio)
     {
        vektorRadioButtonu.push_back(radio);
     }

     string getSelectedText()
     {
         GtkWidget* testovanyRadioBtn;
         for (unsigned int cnt = 0; cnt < vektorRadioButtonu.size(); ++cnt )
         {
             testovanyRadioBtn = vektorRadioButtonu.at(cnt);
             if (gtk_check_button_get_active(GTK_CHECK_BUTTON(testovanyRadioBtn)))
             {
                return gtk_check_button_get_label(GTK_CHECK_BUTTON(testovanyRadioBtn));
            }
         }
         return "";
     }
};

RadioButtonGroup mojeSkupinaRBtns;

void CloseApp (GtkWidget* wid, GtkWidget* data) {

    g_object_unref(G_OBJECT(wid));
}

void What (GtkWidget* wid, GtkWidget* data) {
    cout << "what"<< endl;

    cout << "selected: " << mojeSkupinaRBtns.getSelectedText() << endl;

}

static void appActivate (GApplication *app, gpointer user_data)
{
    GtkWidget *win = gtk_application_window_new (GTK_APPLICATION (app));

    GtkWidget  *vBox = gtk_box_new (GTK_ORIENTATION_VERTICAL,10);
    gtk_window_set_child (GTK_WINDOW (win), vBox);

    GtkWidget *radioBtn;
    string text;
    GtkWidget* skupina = NULL;
    for (int i = 0; i < 10; ++i)
    {
        text = "btn " + to_string(i);
        radioBtn = gtk_check_button_new_with_label(text.c_str());
        if (skupina == NULL)
        {
            skupina = radioBtn;
        }
        else
        {
            gtk_check_button_set_group (GTK_CHECK_BUTTON (radioBtn), GTK_CHECK_BUTTON (skupina));
        }
        gtk_box_append (GTK_BOX (vBox), radioBtn);
        mojeSkupinaRBtns.appendRadioBtn(radioBtn);
    }

    GtkWidget *tlac = gtk_button_new_with_mnemonic("what is selected");
    gtk_box_append (GTK_BOX (vBox), tlac);
    g_signal_connect_swapped(GTK_BUTTON(tlac), "clicked", G_CALLBACK(What), tlac);

    GtkWidget *btnBack = gtk_button_new_with_label("Close");
    gtk_box_append  (GTK_BOX (vBox), btnBack);

    g_signal_connect_swapped(GTK_BUTTON(btnBack), "clicked", G_CALLBACK(CloseApp), win);

    gtk_widget_show (win);
}

int main(int argc, char **argv)
{
    GtkApplication *app;
    app = gtk_application_new ("testing.app", G_APPLICATION_FLAGS_NONE);
    g_signal_connect (app, "activate", G_CALLBACK (appActivate), NULL);
    return g_application_run (G_APPLICATION (app), NULL, NULL);
    g_object_unref (app);

    return 0;
}

Neither.

You want to keep a state tracker with the active state of the radio buttons, and then print out the state at the end, instead of traversing a list of widgets and querying them.

For instance, in C:

#include <gtk/gtk.h>

#define N_BUTTONS 4

// Our state tracker array; every cell in the array maps to the *value*
// of the corresponding check button's active property
static GArray *radio_state;

static void
on_what_clicked (GtkButton *button)
{
  g_print ("* Current button state *\n");
  for (guint i = 0; i < radio_state->len; i++)
    {
      gboolean state = g_array_index (radio_state, gboolean, i);

      g_print ("** Radio button %d state is: %s\n", i,
               state ? "> active <" : "inactive");
    }
}

static void
on_radio_notify_active (GtkCheckButton *button,
                        GParamSpec *pspec,
                        gpointer data)
{
  // Unpack the index from the pointer data argument
  guint idx = GPOINTER_TO_UINT (data);

  // Update the state for the given index
  gboolean *val = &g_array_index (radio_state, gboolean, idx);

  *val = gtk_check_button_get_active (button);
}

static void
on_activate (GtkApplication *app)
{
  // State tracker for the radio buttons
  if (radio_state == NULL)
    radio_state = g_array_sized_new (FALSE, TRUE,
                                     sizeof (gboolean),
                                     N_BUTTONS);

  GtkWidget *win = gtk_application_window_new (app);

  GtkWidget *content_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 18);
  gtk_window_set_child (GTK_WINDOW (win), content_box);

  GtkWidget *radio_box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 6);
  gtk_box_append (GTK_BOX (content_box), radio_box);

  GtkWidget *radio = NULL;
  for (guint i = 0; i < N_BUTTONS; i++)
    {
      g_autofree char *label = g_strdup_printf ("Radio button %d", i);

      GtkWidget *button = gtk_check_button_new_with_label (label);

      if (radio == NULL)
        radio = button;
      else
        gtk_check_button_set_group (GTK_CHECK_BUTTON (button),
                                    GTK_CHECK_BUTTON (radio));

      // Insert the current state of the button inside the tracker array
      gboolean val = gtk_check_button_get_active (GTK_CHECK_BUTTON (button));
      g_array_append_val (radio_state, val);
      g_assert (radio_state->len - 1 == i);

      // Update the state tracker every time a button changes its active
      // state. We pass the index in the radio_state array to the callback
      // so we can directly access it without having to walk the container
      // or store the actual widgets. The GUINT_TO_POINTER macro
      // safely stores a plain unsigned integer into a pointer
      g_signal_connect (button, "notify::active",
                        G_CALLBACK (on_radio_notify_active),
                        GUINT_TO_POINTER (i));

      gtk_box_append (GTK_BOX (radio_box), button);
    }

  GtkWidget *button = gtk_button_new_with_label ("What is selected");
  g_signal_connect (button, "clicked", G_CALLBACK (on_what_clicked), NULL);
  gtk_box_append (GTK_BOX (content_box), button);

  gtk_window_present (GTK_WINDOW (win));
}

int
main (void)
{
  g_autoptr (GtkApplication) app = gtk_application_new ("com.example.App", 0);

  g_signal_connect (app, "activate", G_CALLBACK (on_activate), NULL);

  return g_application_run (G_APPLICATION (app), 0, NULL);
}

This is a very, very rough example. In general, for your own application, you really want to subclass GtkApplication and GtkApplicationWindow, and store widget pointers and state trackers in your classes, so you don’t have to keep around global variables.

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