User data in signal callbacks backwards?

Hello,

I wrote a demo with some treeview to prototype list behaviour but
there is one surprising feature of ruby-gtk2 which I found:

In C you are supposed to get this callback:

The “changed” signal

void user_function
(GtkTreeSelection *treeselection,
gpointer
user_data) : Run First

meaning that the object which triggered the signal is passed in as the
first pointer which is usually used for the ‘self’ pointer in glib
fake C object interface.

When I create a glade layout where I connect a tree view selection
“changed” signal and pass another object as the user data I get the
user data as self and the object that triggered the signal as
argument. Effectively the arguments are reversed. Without user data I
get the object that loaded the layout as self and the object that
triggered the signal as argument.

Is it intentional that the callback interface is so much different from
C?

I would expect to just get two arguments in the case with user data.
It is the same order as C then and I get access to the layout that way
and just adding user data does not change the call completely.

Thanks

Michal

Hi,

In [email protected]
“[ruby-gnome2-devel-en] User data in signal callbacks backwards?” on
Fri, 26 Aug 2011 23:22:43 +0200,
Michal S. [email protected] wrote:

user_data) : Run First
triggered the signal as argument.
Could you give us a sample Ruby code and a glade file for
this problem?
If you provide them to us, we may fix it.

Thanks,

kou

On 27 August 2011 08:18, Kouhei S. [email protected] wrote:

I am not sure it is actually an error. It just looks backwards if you
look both at Ruby sample and C sample.

Attaching code for both.

Thanks

Michal

On 27 August 2011 08:18, Kouhei S. [email protected] wrote:

OK, perhaps the sample I sent is too long so I will put only the
relevant excerpts here.

In C I write:

void selection_changed (GtkTreeSelection *self, gpointer data)

g_signal_connect (lsel, “changed”,
G_CALLBACK(selection_changed), rsel);

Now when lsel changes selection_changed is called with arguments lsel,
rsel - selection_changed (lsel, rsel).

In glade I have

                  <object class="GtkTreeView" id="treeview1">
                    <property name="visible">True</property>
                    <property name="can_focus">True</property>
                    <property name="model">liststore</property>
                    <property name="enable_search">False</property>
                    <child internal-child="selection">
                      <object class="GtkTreeSelection"

id=“treeview-selection1”>

when treewiew-selection1 changes treewiew-selection2.selection_changed
treeview_selection1 is called.

In my demo it is useful that it’s called that way but since I did not
opt for swapped call I see this as backwards.

The other call is performed as .selection_changed selection1.

The glade loading incantation is taken from some sample:

  @builder = Gtk::Builder.new
  @builder << path_or_data
  @builder.connect_signals {|name| method(name)}

In C there is special call to connect the signals swapped which I
would expect to result in calling the method on the argument:

g_signal_connect_swapped (button, “clicked”,
G_CALLBACK (gtk_widget_destroy),
window);

Thanks

Michal

Michal S. wrote in post #1018884:

On 27 August 2011 08:18, Kouhei S. [email protected] wrote:

I am not sure it is actually an error. It just looks backwards if you
look both at Ruby sample and C sample.

Attaching code for both.

Thanks

Michal

Looking at attached files, I don’t quite get where you try to use “user
data”. In fact, I don’t think you can retrieve “user data” with
Gtk::Builder in ruby.

Looking at treeselection1, you have 2 handlers, one with user data, and
one without. However, if you just comment out the one without user data,
you will notice that the one with is just discarded, nothing is
connected.

Just add puts “Adding callback #{name}” to the builder.connect_signal,
and you will see it is called only once for each selection.

You might think it is backward because, when you change something in
ts1, if it changes something in ts2, the ts2 ‘selection_changed’ is then
called, and it is called before you finish what you were doing in ts1.
At least, that’s the only way I can think of.

The main problem, afaik, is that ruby gtk::builder does not handle “user
data”. I took a look at rbgtkbuilder.c, but I’m really too bad at C to
understand what is going on.

regards

Simon

On 31 August 2011 11:32, Simon A.
[email protected] wrote:

Thanks
connected.
No, that’s not how things work.

The user data in glade can be set to one of the other objects in the
glade layout.

If the callback was just discarded the demo would not work which is
not what I see.

The callback selection_changed is set on treeview-selection1 with user
data treeview-selection2 and when triggered on treeview-selection1
treeview-selection2.selection_changed treeview_selection1 is called.

Just add puts “Adding callback #{name}” to the builder.connect_signal,
and you will see it is called only once for each selection.

It is called twice with selection_changed.

When I comment out the two with user data it is also called twice but
the demo does not work then so the signals with user data are not
connected in the block but are still connected.

You might think it is backward because, when you change something in
ts1, if it changes something in ts2, the ts2 ‘selection_changed’ is then
called, and it is called before you finish what you were doing in ts1.
At least, that’s the only way I can think of.

No, when I have Item 4 selected in one view and select item 4 in the
other I get:
sel “Item 4” “Item 4”
sel “Item 4” nil
sav “Item 4” nil
sav “Item 5” “Item 4”

So the other callback fires before the first finishes due to the
unselect call - the method producing the sav output is called from the
callback.
There is no problem with that, though. The sel output is produced at
the very start of the callback and is in order.

The main problem, afaik, is that ruby gtk::builder does not handle “user
data”. I took a look at rbgtkbuilder.c, but I’m really too bad at C to
understand what is going on.

Obviously it does.

Thanks

Michal

Michal S. wrote in post #1019345:

The callback selection_changed is set on treeview-selection1 with user
data treeview-selection2 and when triggered on treeview-selection1
treeview-selection2.selection_changed treeview_selection1 is called.

Indeed. I tested this, see attachment, and this is really bad.

Basically, ruby Gtk::Builder sends a message to the “user data” object,
with the name of the callback, passing expected arguments for the
signal. And it does so even if you do not connect_signals. Just comment
out ‘method(handler)’ line 20 to be sure.

Just add puts “Adding callback #{name}” to the builder.connect_signal,
and you will see it is called only once for each selection.

It is called twice with selection_changed.

And it should be called 4 times. It’s only called for the one without
“user data”.

When I comment out the two with user data it is also called twice but
the demo does not work then so the signals with user data are not
connected in the block but are still connected.

That’s because of the buggy behaviour of gtk::builder I outline above.

You might think it is backward because, when you change something in
ts1, if it changes something in ts2, the ts2 ‘selection_changed’ is then
called, and it is called before you finish what you were doing in ts1.
At least, that’s the only way I can think of.

No, when I have Item 4 selected in one view and select item 4 in the
other I get:
sel “Item 4” “Item 4”
sel “Item 4” nil
sav “Item 4” nil
sav “Item 5” “Item 4”

So the other callback fires before the first finishes due to the
unselect call - the method producing the sav output is called from the
callback.
There is no problem with that, though. The sel output is produced at
the very start of the callback and is in order.

After more thorough testing, it calls 3 callbacks.
The first two are the backward callback, which I would call buggy,
and should not exist.
The third one is the expected callback, which just sets the title, and
nothing else.

The main problem, afaik, is that ruby gtk::builder does not handle “user
data”. I took a look at rbgtkbuilder.c, but I’m really too bad at C to
understand what is going on.

Obviously it does.

I still think it does not. It just has a buggy behaviour which makes it
work somewhat, but is not what I expect.

for example, if I have :

I want to write somewhere in my code :

def my_callback(button, user_data)

and not :

my_entry = builder[‘y_entry’]
def my_entry.my_callback(button)

or even worse, reopening Gtk::Entry, like you do on TreeSelection :
class Gtk::Entry
def my_callback(button)

I think it should be fixed. And signals should handle “user data”, and
passing them as the last argument of the signal, if they are present.

Simon

On 27 August 2011 08:18, Kouhei S. [email protected] wrote:

When I create a glade layout where I connect a tree view selection
“changed” signal and pass another object as the user data I get the
user data as self and the object that triggered the signal as
argument. Effectively the arguments are reversed. Without user data I
get the object that loaded the layout as self and the object that
triggered the signal as argument.

Could you give us a sample Ruby code and a glade file for
this problem?
If you provide them to us, we may fix it.

I think it may be fixed with something like this:

— …/…/lib/gtk2/base.rb~ 1970-01-01 01:00:00.000000000 +0100
+++ …/…/lib/gtk2/base.rb 2011-09-01 18:04:16.000000000 +0200
@@ -76,6 +76,11 @@
handler_name, connect_object, flags)
handler_name = canonical_handler_name(handler_name)
if connect_object

  •       if ! flags.swapped? then
    
  •          tmp = object
    
  •          object = connect_object
    
  •          connect_object = tmp
    
  •       end
         handler = connect_object.method(handler_name)
       else
         handler = connector.call(handler_name)
    

Will test later.

Thanks

Michal

On 1 September 2011 18:06, Michal S. [email protected] wrote:

In C you are supposed to get this callback:
fake C object interface.
If you provide them to us, we may fix it.

I think it may be fixed with something like this:

Hmm. forget that.

The current function generally does not work for any but the simplest
case.

There is a fundamental problem that the ruby-gtk2 callback mechanism
calls a method on an object and hands it the arguments gtk provides,
and first of the arguments is what would be normally considered the
receiver in Glib.

The other fundamental issue is that swapped callbacks should ‘just
work’ so they are called on the data, with arguments ignored.

Given button B and window W you can g_signal_connect( B, ‘clicked’ ,
gtk_widget_destroy, W) to have gtk_widget_destroy(B,W) called whenever
B is clicked. Of course, you will probably want
g_signal_connect_swapped( B, ‘clicked’ , gtk_widget_destroy, W) to
have gtk_widget_destroy(W,B) called. Destroy takes one argument so B
is ignored.

Now in Ruby you would connect a method, not a function. So what would
get called is something like
O.destroy B,W or O.destroy W,B where O is some arbitrary object chosen
when connecting the signal.

To make the swapped signal work as expected so that “glade programs”
work builder connects
W.destroy when W is set as data for the (B - clicked - destroy)
connection in glade. The arguments B,W or W,B passed by GTK are
ignored because destroy takes no argument in Ruby.

Maybe whatever does the callback dispatching in ruby-gtk2 could weed
out the first argument if it is equal to the receiver. That would not
give consistent results when calling callbacks on different objects,
though.

I put together this update to the connect_signals method which
distinguishes three cases:

  1. no user data (connect_object) passed to signal in Glade - ask user
    for method, as before

  2. user data is supplied and signal marked as swapped - connect to
    data, as before

  3. user data is supplied and signal is not swapped - ask user for
    method - previously the signal was handled as swapped

    def connect_signals(connector, object, signal_name,
    handler_name, connect_object, flags)
    handler_name = canonical_handler_name(handler_name)
    trigger = object
    if connect_object and flags.swapped? then
    # Handle swapped signals automagically
    receiver = connect_object
    data = object
    handler = receiver.method(handler_name)
    else
    handler = connector.call(handler_name)
    data = connect_object
    end
    unless handler
    $stderr.puts(“Undefined handler: #{handler_name}”) if $DEBUG
    return
    end
    if flags.after?
    signal_connect_method = :signal_connect_after
    else
    signal_connect_method = :signal_connect
    end

     args = [signal_connect_method, signal_name]
     args << data if data
    
     if handler.arity.zero?
       $stderr.puts("Ignoring user data: #{data} while connecting 
    

signal
#{signal_name.inspect} of #{trigger} to #{handler_name}" + (receiver ?
" on #{receiver}" : “”)) if $DEBUG && data
trigger.send(*args) {handler.call}
else
$stderr.puts(“Insufficient arity: #{handler.arity} of
#{handler} while connecting signal
#{signal_name.inspect} of #{trigger} to #{handler_name}” + (receiver ?
" on #{receiver}" : “”)) if $DEBUG && data && (handler.arity < 2)
trigger.send(*args, &handler)
end
end

Thanks

Michal

On 31 August 2011 14:51, Simon A.
[email protected] wrote:

Michal S. wrote in post #1019345:

sav “Item 5” “Item 4”
The third one is the expected callback, which just sets the title, and
nothing else.

So we can agree what’s called, and that it is not what is expected.

for example, if I have :
and not :

my_entry = builder[‘y_entry’]
def my_entry.my_callback(button)

or even worse, reopening Gtk::Entry, like you do on TreeSelection :
class Gtk::Entry
def my_callback(button)

I would totally reopen the TreeSelection even if I got the callbacks
correctly. It fits my use case nicely. The problem is I have to, even
though I did not opt for this.

My first guess would be that Gtk::Builder somehow interprets the
swapped=“no” as meaning swapped because earlier glades only generated
swapped=“yes” and not swapped=“no”.

The swapped=“yes” callbacks should arguably be handled internally as
they would be in C. The
g_signal_connect_swapped (button, “clicked”,
G_CALLBACK (gtk_widget_destroy),
window);
can be done in Glade by
to produce a button that closes the application without any C code.

Indeed, adding to the treeviews and
replacing the app_quit handlers on the button with
allows closing the application window without running any code of my
own.

Thanks

Michal