2
votes

GTK's "toggled" signal fires when a radio button is selected, but before that, it also fires upon deselection of the previously selected radio button.

I am working with a GUI that uses radio buttons, each representing a group of entities. The first of the pair of "toggled" signals is triggering some unwanted updates to other fields in the GUI -- updates that I only want to happen when the newly selected button triggers the callback. How do I work around this signal and limit the callback function to only operate on selection instead of deselection? I've considered a flag variable within the class I'm coding, but perhaps there is a more GTK-approved technique.

3

3 Answers

1
votes

I don't think you can only get selection signals, but you can do something else. You can write your signal handler so it gets the button that was toggled (assuming you are reusing the same handler for several buttons). Then you can check its state to see if it was selected or deselected.

The way you do this is to connect with an adapter:

// do this for each button (this one is for "buttona"):
buttona.signal_toggled().connect(
   sigc::bind(
      sigc::mem_fun(*this, &myclass::handle_button_toggled),
      buttona
   )
);

In your class, handle_button_toggled includes the button parameter:

myclass {
  Gtk::ToggleButton buttona, buttonb, ...
  ....
  void handle_button_toggled(Gtk::ToggleButton &b) {
   ...check state of b ...
  }
}

In C++11, you can alternatively use a lambda expression:

buttona.signal_toggled().connect([this,&buttona]{
  handled_button_toggled(buttona); 
});
0
votes
// C++11
#include <gtkmm.h>
#include <iostream>
#include <vector>

using namespace std;

class RadioBox
{
public:
    Gtk::Box                     {Gtk::ORIENTATION_HORIZONTAL};
    vector<Gtk::RadioButton*>    rb_list;
    string                       selected_text;
    int                          selected_pos;

    RadioBox(int defpos,
             std::initializer_list<string> rb_name)
    {
        add(box);
        int i = 0;
        for (auto& rb_name_i : rb_name) {
          Gtk::RadioButton* rb  = new Gtk::RadioButton{rb_name_i};
          rb_list.push_back(rb);
          if (i==defpos) {
            rb->set_active();
          }
          box.pack_start(*rb, 0, 0);
          if (i != 0) {
            rb->join_group(*rb_list[0]);
          }
          rb->signal_toggled().connect(
            sigc::bind(
              sigc::mem_fun(*this, &LabRadio::clicked),
              rb,
              i
            ));
          i++;
        }
    }

    void clicked(Gtk::RadioButton* rb, int pos) {
       if (!rb) return;
       if (rb->get_active()) {
         selected_pos  = pos;
         selected_text = rb->get_label().c_str();
         cout << "RadioButton:selected"
              << " pos:" << selected_pos
              << " text:" << selected_text
              << "\n";
       }
    }

    ~RadioBox() {
        rb_count = rb_list.size();
        for(int i=0; i < rb_count; ++i) {
          if (rb_list[i]) delete rb_list[i];
        }
    }

};


//USAGE:

class mywindow : public Gtk::Window {
  public:
    RadioBox myradiobox {2, {"Apples", "Pears", "Oranges", "Peaches"}};

  // ... your code here

  mywindow() {
    // ...
    <somewidget>.pack_start(myradiobox.box);
    // ...
  }

};
0
votes
This is a demo code using Radio Buttons, where you can find how I find which radio button is selected:
#include <gtkmm/window.h>
#include <gtkmm/box.h>
#include <gtkmm/radiobutton.h>
#include <gtkmm/separator.h>
#include <gtkmm/application.h>
#include <iostream>

class ButtonWindow : public Gtk::Window
{
private:
   //Child widgets:
   Gtk::Box m_Box_Top, m_Box1, m_Box2;
   Gtk::RadioButton m_RadioButton1, m_RadioButton2, m_RadioButton3;
   Gtk::Separator m_Separator;
   Gtk::Button m_Button_Close;
   Gtk::RadioButton *m_SelectedButton{nullptr};

public:
   ButtonWindow()
      : m_Box_Top(Gtk::ORIENTATION_VERTICAL),
        m_Box1(Gtk::ORIENTATION_VERTICAL, 15),
        m_Box2(Gtk::ORIENTATION_VERTICAL, 0),
        m_RadioButton1("button 1"),
        m_RadioButton2("button 2"),
        m_RadioButton3("button 3"),
        m_Button_Close("close")
   {
      // Set title and border of the window
      set_title("radio buttons");
      set_border_width(0);

      // Put radio buttons 2 and 3 in the same group as 1:
      m_RadioButton2.join_group(m_RadioButton1);
      m_RadioButton3.join_group(m_RadioButton1);

      // Add outer box to the window (because the window
      // can only contain a single widget)
      add(m_Box_Top);

      //Put the inner boxes and the separator in the outer box:
      m_Box_Top.pack_start(m_Box1);
      m_Box_Top.pack_start(m_Separator);
      m_Box_Top.pack_start(m_Box2);

      // Set the inner boxes' borders
      m_Box1.set_border_width(20);
      m_Box2.set_border_width(10);

      // Put the radio buttons in Box1:
      m_Box1.pack_start(m_RadioButton1);
      m_Box1.pack_start(m_RadioButton2);
      m_Box1.pack_start(m_RadioButton3);

      // Put Close button in Box2:
      m_Box2.pack_start(m_Button_Close);

      // Connect the button signals:
#if 1 // C++11: (change this to #if 0 to use the traditional way)
      m_RadioButton1.signal_clicked().connect([&]{on_radio_button_clicked(m_RadioButton1);});
      m_RadioButton2.signal_clicked().connect([&]{on_radio_button_clicked(m_RadioButton2);});
      m_RadioButton3.signal_clicked().connect([&]{on_radio_button_clicked(m_RadioButton3);});

      m_Button_Close.signal_clicked().connect([&]{on_close_button_clicked();});
#else // Traditional:
  m_RadioButton1.signal_clicked() // Full sigc
     .connect(sigc::bind(sigc::mem_fun(*this, &ButtonWindow::on_radio_button_clicked),
                         sigc::ref(m_RadioButton1)));

  m_RadioButton2.signal_clicked() // sigc && C++98
     .connect(std::bind(sigc::mem_fun(*this, &ButtonWindow::on_radio_button_clicked),
                        std::ref(m_RadioButton2)));

  m_RadioButton3.signal_clicked() // Full C++98
     .connect(std::bind(&ButtonWindow::on_radio_button_clicked, this,
                        std::ref(m_RadioButton3)));

      m_Button_Close.signal_clicked()
         .connect(sigc::mem_fun(*this, &ButtonWindow::on_close_button_clicked));
#endif

      // Set the second button active:
      m_RadioButton2.set_active();

      // Make the close button the default widget:
      m_Button_Close.set_can_default();
      m_Button_Close.grab_default();

      // Show all children of the window:
      show_all_children();
   }
  
protected:
   //Signal handlers:
   void on_radio_button_clicked(Gtk::RadioButton& button)
   {
      if(m_SelectedButton != &button && button.get_active())
      {
         m_SelectedButton = &button;
         std::cout << "Radio "<< m_SelectedButton->get_label() << " selected.\n";
      }
   }

   void on_close_button_clicked()
   {
      hide(); // Close the application
   }
};

int main(int argc, char *argv[])
{
   auto app = Gtk::Application::create(argc, argv, "org.gtkmm.example");

   ButtonWindow button;

   //Shows the window and returns when it is closed.
   return app->run(button);
}