how to get the state of a grouped gtk.CheckButton using Action

117 views Asked by At

I'm trying to get the state of a gtk.CheckButton using Action since gtk docs says:

When the code must keep track of the state of a group of radio buttons, it is recommended to keep track of such state through a stateful GAction with a target for each button. Using the toggled signals to keep track of the group changes and state is discouraged.

use gtk::{Application, ApplicationWindow, Button, CheckButton};
use gtk::gio::SimpleAction;
use gtk::Orientation::Vertical;
use gtk::prelude::*;

fn main() {
    let application = Application::builder().application_id("a.b").build();
    application.connect_activate(|application| {
        let main_box = gtk::Box::builder().orientation(Vertical).build();
        let variant_a = "a".to_variant();
        let variant_b = "b".to_variant();
        let window = ApplicationWindow::builder().application(application).child(&main_box).build();
        let action_name = "win.me";
        let action = SimpleAction::new_stateful(action_name, Some(variant_a.type_()), &variant_a);
        window.add_action(&action);
        let button_a = CheckButton::builder().label("a").action_name(action_name)
            .action_target(&variant_a).build();
        main_box.append(&button_a);
        let button_b = CheckButton::builder().label("b").action_name(action_name)
            .action_target(&variant_b).group(&button_a).build();
        main_box.append(&button_b);
        let button = Button::builder().label("click").build();
        action.connect_change_state(|_, state| {
            println!("{:?}", state);
        });
        button.connect_clicked(move |_| {
            println!("{} {:?}", action.name(), action.state());
        });
        main_box.append(&button);
        window.present();
    });
    application.run();
}

However the CheckButtons are not sensitive. Setting sensitive to true makes them sensitive but clicking them won't make them checked.

In another try I used SimpleActionGroup and action-target-value but with the same results:

fn main() {
    let application = Application::builder().application_id("a.b").build();
    application.connect_activate(|application| {
        let main_box = gtk::Box::builder().orientation(Vertical).build();
        let variant_a = "a".to_variant();
        let variant_b = "b".to_variant();
        let window = ApplicationWindow::builder().application(application).child(&main_box).build();
        let action_name = "my.action";
        let action_group = SimpleActionGroup::new();
        window.insert_action_group("my.group", Some(&action_group));
        let action = SimpleAction::new_stateful(action_name, Some(variant_a.type_()), &variant_a);
        action_group.add_action(&action);
        let button_a = CheckButton::builder().label("a").action_name(action_name).build();
        button_a.set_action_target_value(Some(&variant_a));
        main_box.append(&button_a);
        let button_b = CheckButton::builder().label("b").action_name(action_name).group(&button_a).build();
        button_b.set_action_target_value(Some(&variant_b));
        main_box.append(&button_b);
        let button = Button::builder().label("click").build();
        action.connect_activate(|_, state| {
            println!("{:?}", state);
        });
        action.connect_change_state(|_, state| {
            println!("{:?}", state);
        });
        button.connect_clicked(move |_| {
            println!("{} {:?}", action.name(), action.state());
        });
        main_box.append(&button);
        window.present();
    });
    application.run();
}

How do I use Action to get the state of the grouped CheckButtons?

3

There are 3 answers

4
Kripto On

Okay, I'm not a RUST programmer, but your code is missing a few things:

  1. Besides creating a SimplesAction, you also have to create a SimplesActionGroup to put the SimpleAction inside. You cannot add a SimpleAction directly to the window.

  2. You should not use .action_target(&variant...) , but .action_target_value(&variant...)

  3. In the Action's callback function, you have to manually call set_state() to set the "state" of the Action to the value received as a parameter in the callback when the "change_state" signal is triggered, which is the target_value of the clicked button .

The initial state defined in the Action will cause the checkbutton that has this target_value to be rendered as active. When you click on another button, the callback will receive the state of the button and set_state() will set this new state to the Action, which will automatically activate the clicked button, whose target_value corresponds to this new state.

0
raven On

you could either use an action group:

let action_group = SimpleActionGroup::new();
window.insert_action_group("gg", Some(&action_group));
action_group.add_action(&action);

and then set the action name: action_name("gg.aa") or directly add the action to the window:

    let application = Application::builder().application_id("a.b").build();
    application.connect_activate(|application| {
        let main_box = gtk::Box::builder().orientation(Vertical).build();
        let variant_a = "a".to_variant();
        let window = ApplicationWindow::builder().application(application).child(&main_box).build();
        let action = SimpleAction::new_stateful("aa", Some(variant_a.type_()), &variant_a);
        window.add_action(&action);
        let button_a = CheckButton::builder().label("a").action_name("win.aa")
            .action_target(&variant_a).build();
        main_box.append(&button_a);
        let button_b = CheckButton::builder().label("b").action_name("win.aa").group(&button_a)
            .action_target(&"b".to_variant()).build();
        main_box.append(&button_b);
        action.connect_activate(|action, state| {
            println!("{:?}", state);
            if let Some(state) = state {
                action.set_state(state);
            }
        });
        window.present();
    });
    application.run();

for more info see gtk4-rs book.

1
gorignak On

You need to set the action name on the buttons.

use gtk::gio::{SimpleAction, SimpleActionGroup};
use gtk::prelude::*;
use gtk::Orientation::Vertical;
use gtk::{Application, ApplicationWindow, CheckButton};
use gtk4 as gtk;

fn main() {
    let application = Application::builder().application_id("a.b").build();
    application.connect_activate(|application| {
        let main_box = gtk::Box::builder().orientation(Vertical).build();
        let variant_a = "a".to_variant();
        let variant_b = "b".to_variant();
        let window = ApplicationWindow::builder()
            .application(application)
            .child(&main_box)
            .build();
        let action_name = "myaction";
        let action_group = SimpleActionGroup::new();
        window.insert_action_group("mygroup", Some(&action_group));
        let action = SimpleAction::new_stateful(action_name, Some(variant_a.type_()), &variant_a);
        action_group.add_action(&action);
        let button_a = CheckButton::builder()
            .label("a")
            .action_name(action_name)
            .build();
        button_a.set_sensitive(true);
        button_a.set_action_target_value(Some(&variant_a));
        button_a.set_action_name(Some("mygroup.myaction"));
        main_box.append(&button_a);
        let button_b = CheckButton::builder()
            .label("b")
            .action_name(action_name)
            .group(&button_a)
            .build();
        button_b.set_sensitive(true);
        button_b.set_action_target_value(Some(&variant_b));
        button_b.set_action_name(Some("mygroup.myaction"));
        main_box.append(&button_b);
        action.connect_activate(|a, state| {
            println!("Activate: {:?}", state);
            a.set_state(state.expect("New state"));
        });
        window.present();
    });
    application.run();
}