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
on 2011-08-26 23:23
on 2011-08-27 08:20
Hi, In <CAOMqctTB_5p=aL=pfDixPQYdARG8+9MpELET9QEEAyeq3ffxxg@mail.gmail.com> "[ruby-gnome2-devel-en] User data in signal callbacks backwards?" on Fri, 26 Aug 2011 23:22:43 +0200, Michal Suchanek <hramrach@centrum.cz> 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 2011-08-28 12:23
On 27 August 2011 08:18, Kouhei Sutou <kou@cozmixng.org> 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 2011-08-30 16:05
On 27 August 2011 08:18, Kouhei Sutou <kou@cozmixng.org> 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"> <signal name="changed" handler="selection_changed" object="treeview-selection2" swapped="no"/> <signal name="changed" handler="selection_changed" swapped="no"/> 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 <the class that loaded the glade data>.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
on 2011-08-31 11:32
Michal Suchanek wrote in post #1018884: > On 27 August 2011 08:18, Kouhei Sutou <kou@cozmixng.org> 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 2011-08-31 12:26
On 31 August 2011 11:32, Simon Arnaud <ruby-forum-incoming@andreas-s.net> 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
on 2011-08-31 14:51
Michal Suchanek 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 : <object class="GtkButton" id="test_button"> <signal name="clicked" handler="my_callback" object="my_entry" swapped="no"/> </object> 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 2011-08-31 16:08
On 31 August 2011 14:51, Simon Arnaud <ruby-forum-incoming@andreas-s.net> wrote: > Michal Suchanek 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 <signal name="clicked" handler="gtk_widget_destroy" object="window1" swapped="yes"/> to produce a button that closes the application without any C code. Indeed, adding <signal name="row-activated" handler="destroy" object="window1" swapped="yes"/> to the treeviews and replacing the app_quit handlers on the button with <signal name="clicked" handler="destroy" object="window1" swapped="yes"/> allows closing the application window without running any code of my own. Thanks Michal
on 2011-09-01 18:07
On 27 August 2011 08:18, Kouhei Sutou <kou@cozmixng.org> 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 2011-09-02 00:52
On 1 September 2011 18:06, Michal Suchanek <hramrach@centrum.cz> 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
Please log in before posting. Registration is free and takes only a minute.
Existing account
(Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
Log in with Google account | Log in with Yahoo account
No account? Register here.