0
votes

Is it possible to block the "clicked" signal from getting to gtk's built-in handler which toggles the "up/down" state of a pair of gtk radio buttons or gtk toggle buttons?

Background:

I am working on a GTK application (in C, for linux & OS X) which needs to turn a hardware GPIO pin on and off over a network connection. The pin is turned on or off by sending a message (i.e. it is event-driven.) The hardware periodically sends a message back which gives the actual state of the hardware pin and it is important that the true state of the pin is reflected to the user (it might be different from the user's select state for a number of reasons which I won't go into.) I am currently using a pair of gtk radio buttons to turn the pin on and off, I set them up like this:

void create_control_buttons(control_t *item)
{
    assert(item != NULL);

    // Create grouped Gtk radio buttons.
    item->button0 = gtk_radio_button_new_with_label(NULL, "Off");
    item->button1 = gtk_radio_button_new_with_label_from_widget(GTK_RADIO_BUTTON(item->button0), "On");

    // Make the radio buttons look like normal buttons.
    gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(item->button0), FALSE);
    gtk_toggle_button_set_mode(GTK_TOGGLE_BUTTON(item->button1), FALSE);

    g_signal_connect(G_OBJECT(item->button0), "clicked", G_CALLBACK(on_control_clicked), (gpointer)item);
    g_signal_connect(G_OBJECT(item->button1), "clicked", G_CALLBACK(on_control_clicked), (gpointer)item);

}


void on_control_clicked(GtkWidget *widget, gpointer user_data)
{
    control_t *item = user_data;
    assert(item != NULL);

    if (widget == item->button0) {
        // Send 'off' signal

    } else if (widget == item->button1) {
        // send 'on' signal

    }
}

Another function is called when a periodic status message is received back from the hardware. It updates the button state like this:

if (state == ON) {
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item->button1), 1);
} else {
    gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(item->button0), 1);
}

The code works but there are two problems: 1. It sends two messages if the radio buttons change state. 2. The buttons will change state immediately on "click" but will change back again if the hardware pin state is different, resulting in a rather "glitchy" appearance.

I could do this with ordinary gtk buttons and some other type of widget to reflect the remote state but I would really like the on/off buttons to stay pressed "up" or "down" to show the state for aesthetic / ergonomic reasons.

Is there a way of stopping gtk updating the button state internally, so it only gets updated with gtk_toggle_button_set_active() ?

Please forgive my ignorance, I'm quite new to GTK and there's probably a really obvious solution to this but I've spent a load of time Googling and trying different things out to no avail and I really want to avoid having to write a custom widget.

1
Are your radiobuttons only for indicating state, not allowing the user to change them? If so you might be using the wrong tool for the job; look into using a GtkDrawingArea, drawing the state, and using gtk_widget_queue_draw(). - andlabs
Hi andlabs, no they are to do both: change & display the state, it's just that the two need to be 'decoupled' slightly. - Halzephron

1 Answers

0
votes

I had my troubles using radio button, I found it is easier to use a couple of toggle buttons (my buttons switch between MANual state and AUTOmatic state) with a callback that defines them as a radio button. I am using GTK with Python so it could be a bit different (self. is the handler), but I think the idea is the same

  #Defines the MAN/AUTO button as radio button on CLICKING
  def on_MAN_button_clicked(self, togglebutton, data=None):  
    if self.MAN_button.get_active() == True:
      self.AUTO_button.set_active(False)

  def on_AUTO_button_clicked(self, togglebutton, data=None):  
    if self.AUTO_button.get_active() == True:
      self.MAN_button.set_active(False)

So maybe you can change your first IF structure with the condition

#Pseudocode
IF thisbutton.get_active() == 1: send 'on' signal
ELSE IF otherbutton.get_active() == 1: send 'off' signal

I think this is the cause of double sending: by clicking one button of the radiobutton group, you are "unclicking" the other. (as I remember before giving up with radio button, it can be wrong)

I am not sure to understand what is "internally updating". As I know, the button change state only if

  1. you click on it
  2. execute in some way the "clicking" or the "toggling" callbacks
  3. you use the set_active() instruction

So my first suggestion is: search if there are unwanted "toggling" callbacks in the rest of your code. I don't think the set_active instruction counts as a click (but I think it counts as a toggle)

The "glitchy" problem is to much unclear for me to try a solution without seeing all the code. Sorry.