Hello, After investigating what Gtk::Builder does with callbacks I figured out there are issues. The first issue is mismatch between GTK C callbacks and GTK Ruby callbacks. In C the GTK functions have a required first argument which is pointer to self. This is because C has no notion of object and self has to be passed explicitly. Then in C to get rid of a window you call gtk_widget_destroy(window) but in ruby you call window.destroy meaning that methods take one fewer argument than gtk C functions. This is not accounted for by signal_connect in Ruby. In C when you connect a signal the first argument passed to the handler is the object that triggered the signal and it is passed as pointer to that kind of object. Obviously, this is meant to be the self pointer. If the signal passes additional data or the user supplied additional data when connecting the signal this is passed in additional arguments. When the signal is connected as swapped the user data and self is swapped when calling the callback. Now in Ruby you don't connect a function, you connect a method. This method is already attached to an object - 'self'. The first argument passed in by GTK is totally redundant when the receiver is the object which triggers the signal. Worse, when connecting existing GTK function as a signal handler and it takes any arguments in Ruby (unlike destroy which takes none because it takes only one in C) you have to wrap it to discard this first argument. Now I don't expect this to be very common use but the possibility exists in C but not in Ruby. Another issue is argument count. While in C extra arguments are ignored in Ruby they are ignored so long as you connect a block. When you connect a method and GTK passes extra arguments your callback fails and your application crashes. In C extra arguments are just ignored which is often used. Again, you may need to wrap a method in Ruby to make it a useful callback if the argument count does not match. Either of this could be resolved by inserting a wrapper automatically at gobj_sig_connect_impl to discard any extra args and/or discard the first arg when it is the same as the receiver. I am not sure if this is useful and wanted feature, though. It would change the API but if anybody used the cases that would change then they would see these issues for sure. Still it's easy enough to rearrange arguments and redispatch the callbacks in user Ruby code, too. The other issue is with Gtk::Builder. Given the issues above it does a lousy job of connecting signals, it can't do any better. The results are nowhere near what the C connections would be. What works properly are signals with no user data (object in glade files). Builder calls the user supplied block to get a handler and connects that. The swapped option does not make any sense in this case and is rightfully ignored. What somewhat works are swapped signals with user data. These are often used to connect a "close" button to call a function which disposes of a window when the button is clicked. As these functions take only the window in C and no arguments in Ruby this works OK - all arguments are ignored. Connecting a GTK function that takes arguments would not work this way because of the extra argument generated when going between C and Ruby. What is broken are signals with user data which are *not* swapped. They are simply handled as swapped, the swapped flag is ignored completely. I guess these should be connected just like signals without user data but pass in the extra argument. It's not quite consistent with C gtk and never will be so long as the extra argument is generated but at least it would be an useful option. Thanks Michal
on 2011-09-02 11:59
on 2011-09-06 09:20
I just wanted to say I'm interested on this topic, but don't quite have time right now to give a thoughtfull answer. I'll try to get back to this topic before the week-end. regards Simon
on 2011-09-21 15:01
Michal Suchanek wrote in post #1019765: > Hello, > > After investigating what Gtk::Builder does with callbacks I figured > [...] > data and self is swapped when calling the callback. Agreed. > Now in Ruby you don't connect a function, you connect a method. This > method is already attached to an object - 'self'. The first argument > passed in by GTK is totally redundant when the receiver is the object > which triggers the signal. I agree with this : The fist argument looks like self. But it is not. For example : require 'gtk2' def on_click(object) puts object.label end my_button = Gtk::Button.new("My label") my_button.signal_connect('clicked') do |button| on_click(button) # should always work on_click(my_button) # works, but bad practice on_click(self) # does not work (on 1.9.2 at least) end my_button.signal_emit('clicked') It fails with "undefined method `label' for main:Object", because self is not anymore the button when the callback is called. That's because 'self' is not the object the method is called from, but the current object. So, while you might think 'self' is 'button', it is not when the callback is called. > Worse, when connecting existing GTK > function as a signal handler and it takes any arguments in Ruby > (unlike destroy which takes none because it takes only one in C) you > have to wrap it to discard this first argument. Now I don't expect > this to be very common use but the possibility exists in C but not in > Ruby. It seems you are confusing the 'destroy' method, and the 'destroy' signal. The 'destroy' method takes no argument, but the 'destroy' signal does. method : http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%... signal : http://ruby-gnome2.sourceforge.jp/hiki.cgi?Gtk%3A%... Also, I'm not sure I get your point. Are you talking about auto calling a method on the widget based on event-type ? Or are you talking about glade, and you want to directly connect the handler you create as a method call to the object which received the event ? So, instead of : builder.connect_signals {|handler| method(handler) } you would like the default to be something like : def my_connect(handler, *args) args[0].send(handler) end Am I right ? > Another issue is argument count. While in C extra arguments are > ignored in Ruby they are ignored so long as you connect a block. When > you connect a method and GTK passes extra arguments your callback > fails and your application crashes. In C extra arguments are just > ignored which is often used. Again, you may need to wrap a method in > Ruby to make it a useful callback if the argument count does not > match. Reading this, I really think you are trying to connect signal to methods directly, and that you should write a custom method to use in 'connect_signals'. I'm not a C programmer, but, are you saying that I can call something like this, and it will work ? gtk_window_move(p_win, 100, 200, 345, "foo", NULL); I really doubt it. Or is it gtk_builder in C which discards the extra args ? > Either of this could be resolved by inserting a wrapper automatically > at gobj_sig_connect_impl to discard any extra args and/or discard the > first arg when it is the same as the receiver. I think it should be done by the programmer, not by Ruby-gnome. > I am not sure if this is useful and wanted feature, though. It would] > change the API but if anybody used the cases that would change then > they would see these issues for sure. Still it's easy enough to > rearrange arguments and redispatch the callbacks in user Ruby code, > too. My code would not work with what you are proposing. I almost never use the handler name to call a method of the same name. > The other issue is with Gtk::Builder. Given the issues above it does a > lousy job of connecting signals, it can't do any better. The results > are nowhere near what the C connections would be. I disagree, it does its job good, lacking only one feature : user_data. The results are just the same as in C, afaik. > What works properly are signals with no user data (object in glade > files). Builder calls the user supplied block to get a handler and > connects that. The swapped option does not make any sense in this case > and is rightfully ignored. Agreed. > What somewhat works are swapped signals with user data. These are > often used to connect a "close" button to call a function which > disposes of a window when the button is clicked. As these functions > take only the window in C and no arguments in Ruby this works OK - all > arguments are ignored. Connecting a GTK function that takes arguments > would not work this way because of the extra argument generated when > going between C and Ruby. Just to be sure, you are talking about having a swapped handler with name 'gtk_window_destroy' ? So, C code calls gtk_window_destroy(user_data) where user_data is the window, without writing any code ? If that's it, I think it's a corner case of C, where you use functions, and not methods. I don't think it is ruby to mimic this behaviour. > What is broken are signals with user data which are *not* swapped. > They are simply handled as swapped, the swapped flag is ignored > completely. I guess these should be connected just like signals > without user data but pass in the extra argument. It's not quite > consistent with C gtk and never will be so long as the extra argument > is generated but at least it would be an useful option. I agree there is something strange going on with user_data. However I disagree that swapped signals function properly. the connect_signals from Gtk::Builder should pass user_data, as last argument if not swapped, or as first if swapped. And it should be up to the programmer to connect as he wants to. regards Simon
on 2011-09-21 16:06
On 21 September 2011 15:01, Simon Arnaud <ruby-forum-incoming@andreas-s.net> wrote: >> method is already attached to an object - 'self'. The first argument > end > It fails with "undefined method `label' for main:Object", because self > is not anymore the button when the callback is called. It depends on the method invoked as the callback. It is perfectly possible to detect if the callback is attached to the triggering object or another object at attach time and it is also possible to detect that a callback is triggered on the triggering object at trigger time. > That's because 'self' is not the object the method is called from, but > the current object. The 'current' object is the object whose method was attached as the signal handler. You can pick it so that the 'self' matches or not, and you can also know in advance if it will (such as in the builder connect_signals). > It seems you are confusing the 'destroy' method, and the 'destroy' > signal. > The 'destroy' method takes no argument, but the 'destroy' signal does. Indeed, that's the difference between Ruby and C. In C the destroy method takes 'self', and the signal callback gets 'self' and 'data', possibly swapped. In Ruby the destroy method takes no arguments, and the callback still gets 'self' and 'data', and the method is no longer compatible with the signal. > > Also, I'm not sure I get your point. Are you talking about auto calling > a method on the widget based on event-type ? I am talking about using existing methods as signal handlers. > > Or are you talking about glade, and you want to directly connect the > handler you create as a method call to the object which received the > event ? > So, instead of : > builder.connect_signals {|handler| method(handler) } > > you would like the default to be something like : > def my_connect(handler, *args) > args[0].send(handler) args[0].send(handler, *(args[1..-1])) more precisely to make it look like what C does Plus extra arguments should be discarded. > end > > Am I right ? That's one way of using the signal connections in Glade, and one that works quite well for quite a few signals in C. For the most common which take only 'self' in C it is hacked in connect_signals in Ruby Builder so that it works but it's not exactly consistent. The moment you try to call a method that takes arguments it will fail. > directly, and that you should write a custom method to use in > 'connect_signals'. Yes, I can write wrappers for any methods I want to connect as callbacks, sure. The wrapper can handle extra arguments or whatnot without any issues. > > I'm not a C programmer, but, are you saying that I can call something > like this, and it will work ? > > gtk_window_move(p_win, 100, 200, 345, "foo", NULL); > > I really doubt it. I am not sure what you are trying to demonstrate here. You can definitely call gtk_widget_destroy(p_win, 100, 200, 345, "foo", NULL) and it will work and it is what the callbacks do all the time. They call gtk_widget_destroy(p_win, p_button). > > Or is it gtk_builder in C which discards the extra args ? Don't think so. C calling convention discards extra arguments automagically. > >> Either of this could be resolved by inserting a wrapper automatically >> at gobj_sig_connect_impl to discard any extra args and/or discard the >> first arg when it is the same as the receiver. > > I think it should be done by the programmer, not by Ruby-gnome. At the very least it should be well documented what the builder does because it is very inconsistent for various cases and very different from the C builder. > >> I am not sure if this is useful and wanted feature, though. It would] >> change the API but if anybody used the cases that would change then >> they would see these issues for sure. Still it's easy enough to >> rearrange arguments and redispatch the callbacks in user Ruby code, >> too. > > My code would not work with what you are proposing. I almost never use > the handler name to call a method of the same name. Then your code *would* work. > >> The other issue is with Gtk::Builder. Given the issues above it does a >> lousy job of connecting signals, it can't do any better. The results >> are nowhere near what the C connections would be. > > I disagree, it does its job good, lacking only one feature : user_data. > The results are just the same as in C, afaik. Well, when you remove half of the features and then say that the other half works .. even that is not true. It handles user_data as the swapped flag, and ignores the swapped flag. That's definitely bogus. >> disposes of a window when the button is clicked. As these functions >> take only the window in C and no arguments in Ruby this works OK - all >> arguments are ignored. Connecting a GTK function that takes arguments >> would not work this way because of the extra argument generated when >> going between C and Ruby. > > Just to be sure, you are talking about having a swapped handler with > name 'gtk_window_destroy' ? So, C code calls > gtk_window_destroy(user_data) where user_data is the window, without > writing any code ? Yes, it does that. > > If that's it, I think it's a corner case of C, where you use functions, > and not methods. I don't think it is ruby to mimic this behaviour. But it is glade to do such, and ruby-gnome2 claims to support glade. > >> What is broken are signals with user data which are *not* swapped. >> They are simply handled as swapped, the swapped flag is ignored >> completely. I guess these should be connected just like signals >> without user data but pass in the extra argument. It's not quite >> consistent with C gtk and never will be so long as the extra argument >> is generated but at least it would be an useful option. > > I agree there is something strange going on with user_data. However I > disagree that swapped signals function properly. yes, they won't if multiple arguments were passed in to the callback but there are quite few such signals, if any. > > the connect_signals from Gtk::Builder should pass user_data, as last > argument if not swapped, or as first if swapped. And it should be up to > the programmer to connect as he wants to. That would definitely suck more, especially since you do *not* get anything but the method name to decide what to do when connecting the handler. For the user to be able to decide how to connect the signal they would have to know all of - signal name - triggering object - user data Then the connection function would be just something like signals_each{ signal_connect *(connector signal, trigger, data, swapped) } that is the user would supply all the arguments based on all the signal data read from the glade layout. Thanks Michal
on 2011-09-21 17:43
On Wed, Sep 21, 2011 at 15:01, Simon Arnaud
<ruby-forum-incoming@andreas-s.net> wrote:
> on_click(my_button) # works, but bad practice
Why is this a bad practice?
on 2011-09-21 18:08
Nikolai Weibull wrote in post #1023132: > On Wed, Sep 21, 2011 at 15:01, Simon Arnaud > <ruby-forum-incoming@andreas-s.net> wrote: > >> on_click(my_button) # works, but bad practice > > Why is this a bad practice? It seems to me that when you have both the same object as external and local scope, local should be used. It will ease refactor also. This example might not be the best to demonstrate the problem though. I understand it is a subjective topic.
on 2011-09-21 18:31
Michal Suchanek wrote in post #1023105: >> That's because 'self' is not the object the method is called from, but >> the current object. > > The 'current' object is the object whose method was attached as the > signal handler. You can pick it so that the 'self' matches or not, and > you can also know in advance if it will (such as in the builder > connect_signals). What I wanted to say was not clear. When you define a method, 'self' is the object the method will be called onto. When you call a method which takes a block, inside the block, 'self' is not the object you call the method onto, it is the object you are in. It's obvious with an array : a = [1, 2, 3] a.each do |item| # self is not a end However, I understand you would like self to be set so it is the object which triggered the signal. >> I'm not a C programmer, but, are you saying that I can call something >> like this, and it will work ? >> >> gtk_window_move(p_win, 100, 200, 345, "foo", NULL); >> >> I really doubt it. > > I am not sure what you are trying to demonstrate here. Just that I'm not a C programmer, at all. I didn't think C would discard any extras by itself. Anyway, I feel you have a strong C background, and you would like ruby to behave like C. However, I don't think it can. In one languauge, you have global functions with arguments, and in another, you haves messages sent to objects. While ruby can mimic C a great deal, I don't think you can imitate every corner case. I understand the usefullness to call gtk_window_destroy(p_win, p_button), and it will work without any effort in C, but I still think it is a C corner case impossible to mimic in ruby. In most (all?) cases, you will need a dispatcher. I fully agree though that the documentation is not very good, and the current behaviour is bogus and confusing.
on 2011-09-22 10:45
On 21 September 2011 18:31, Simon Arnaud <ruby-forum-incoming@andreas-s.net> wrote: > When you define a method, 'self' is the object the method will be called > onto. > When you call a method which takes a block, inside the block, 'self' is > not the object you call the method onto, it is the object you are in. This is true but does not apply to builder. Builder connects methods. > > It's obvious with an array : > > a = [1, 2, 3] > a.each do |item| > # self is not a > end > > However, I understand you would like self to be set so it is the object > which triggered the signal. Not necessarily. What I am saying is that when you attach a method on the object which triggers the signal as signal handler you get redundant reference to self passed as the first argument which makes the signal interface incompatible with the ruby-gnome2 method interface. > Just that I'm not a C programmer, at all. I didn't think C would discard > any extras by itself. > > Anyway, I feel you have a strong C background, and you would like ruby > to behave like C. However, I don't think it can. In one languauge, you No, I would like the builder to behave logically. If the current behaviour were at least documented it would be obvious it's bogus. > have global functions with arguments, and in another, you haves messages > sent to objects. While ruby can mimic C a great deal, I don't think you > can imitate every corner case. > > I understand the usefullness to call gtk_window_destroy(p_win, > p_button), and it will work without any effort in C, but I still think > it is a C corner case impossible to mimic in ruby. It is already emulated partially. Builder already mimics this particular case by attaching a block discarding the extra arguments instead of the method when the provided method does not take arguments. > > In most (all?) cases, you will need a dispatcher. Why should you need a dispatcher? The glib signals system is done so that you shall not need any additional dispatcher. It can trigger multiple handlers for a single signal, you can register the handlers in a particular order, you can pass the handler additional user data, and you can even swap the order in which the triggering object and extra data are passed. Thanks Michal
on 2011-09-22 13:33
On 22 September 2011 10:44, Michal Suchanek <hramrach@centrum.cz> wrote: >> >> > redundant reference to self passed as the first argument which makes >>> > > instead of the method when the provided method does not take > arguments. It's also explained in the g_signal "hello world" : http://developer.gnome.org/gtk-tutorial/2.90/x159.html 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.