[Gtk-rs] How to connect signals from glade

Hello, I have a button gtk::Button called fcb and a gtk::FileChooserNative called fcn. I have created a signal from glade called on_fcb_clicked and mapped it to fcb's clicked. When clicked, I want fcn to invoke it's method called run().

Excerpt from my glade:

<object class="GtkButton" id="fcb">
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="image">refresh_image</property>
                <signal name="clicked" handler="on_fcb_clicked" swapped="no"/>
</object>
<!--- --->
<object class="GtkFileChooserNative" id="fcn">
    <property name="create_folders">False</property>
    <property name="filter">ff</property>
    <property name="title">Select iso file</property>
    <property name="transient_for">aw</property>
    <property name="accept_label">Select</property>
    <property name="cancel_label">Cancel</property>
    <signal name="file-activated" handler="fcn_activated" object="start" swapped="no"/>
</object>

In my code I have:

pub struct UI {
    builder: gtk::Builder,
    fcn: Option<gtk::FileChooserNative>,
    fcb: Option<gtk::Button>,
}

// ...

    fn set_filechooser(&mut self) {
        self.fcn = Some(gtk::FileChooserNative::new(
            Some("Select iso image"),
            Some(&self.get_element_by_id::<gtk::ApplicationWindow>(element_ids::APP_WINDOW)),
            gtk::FileChooserAction::Open,
            Some("Select"),
            Some("Cancel"),
        ));

        self.fcb = Some(self.get_element_by_id(element_ids::FILE_CHOOSER_BUTTON));
        self.fcb.unwrap().connect("on_fcb_clicked", false, |_| {
            self.fcn.unwrap().run();
            None
        });
    }

However building this yeilds:

$ cargo run
   Compiling nixwriter v0.1.0 (/home/adnan338/proj/nixwriter)
error[E0277]: `std::ptr::NonNull<gobject_sys::GObject>` cannot be sent between threads safely
  --> src/frontend/mod.rs:49:27
   |
49 |         self.fcb.unwrap().connect("on_fcb_clicked", false, |_| {
   |                           ^^^^^^^ `std::ptr::NonNull<gobject_sys::GObject>` cannot be sent between threads safely
   |
   = help: within `frontend::UI`, the trait `std::marker::Send` is not implemented for `std::ptr::NonNull<gobject_sys::GObject>`
   = note: required because it appears within the type `glib::object::ObjectRef`
   = note: required because it appears within the type `gtk::auto::builder::Builder`
   = note: required because it appears within the type `frontend::UI`
   = note: required because of the requirements on the impl of `std::marker::Send` for `&mut frontend::UI`
   = note: required because it appears within the type `[closure@src/frontend/mod.rs:49:60: 52:10 self:&mut frontend::UI]`

error[E0277]: `std::ptr::NonNull<gobject_sys::GObject>` cannot be shared between threads safely
  --> src/frontend/mod.rs:49:27
   |
49 |           self.fcb.unwrap().connect("on_fcb_clicked", false, |_| {
   |  ___________________________^^^^^^^__________________________-
   | |                           |
   | |                           `std::ptr::NonNull<gobject_sys::GObject>` cannot be shared between threads safely
50 | |             self.fcn.unwrap().run();
51 | |             None
52 | |         });
   | |_________- within this `[closure@src/frontend/mod.rs:49:60: 52:10 self:&mut frontend::UI]`
   |
   = help: within `[closure@src/frontend/mod.rs:49:60: 52:10 self:&mut frontend::UI]`, the trait `std::marker::Sync` is not implemented for `std::ptr::NonNull<gobject_sys::GObject>`
   = note: required because it appears within the type `glib::object::ObjectRef`
   = note: required because it appears within the type `gtk::auto::builder::Builder`
   = note: required because it appears within the type `frontend::UI`
   = note: required because it appears within the type `&mut frontend::UI`
   = note: required because it appears within the type `[closure@src/frontend/mod.rs:49:60: 52:10 self:&mut frontend::UI]`

I cannot find any information to help me figure out how to send None in Sync. Any pointers?

The closure isn’t Send because it contains a reference to self, which can’t leave the current thread because of the GTK elements it contains; as far as Rust is concerned, the callback might be run on any thread.

It looks like there’s a connect_local method that doesn’t require the closure to be Send + Sync; it may be appropriate here— I’m not particularly familiar with GTK-rs.

Beyond fixing the Send issue by using connect_local, you will also need to somehow share the object with the closure in a way that does not involve borrows (probably Rc and Weak). Additionally, you will likely need an .as_ref() or .as_mut() before that unwrap() to avoid errors about destroying the self.fcb field.

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.