GtkSignals

Ubuntu Opportunistic Developers Week March 2010 - Using GTK+ signals in Python - Sense Hofstede - Mar 6 2010

(02:01:39 PM) qense: Hello everyone!
(02:01:53 PM) qense: This session I will be talking about GTK+ signals.
(02:03:13 PM) qense: Signals aren't specific to GTK+, but opportunistic programmers will deal mostly with GTK+ signals.
(02:03:24 PM) qense: What are signals?
(02:03:54 PM) qense: Signals are a nifty way of allowing an application or library to respond to certain events.
(02:04:17 PM) qense: Signals are emitted at all sorts of events, so you can listen to almost anything if you know where to look.
(02:04:41 PM) qense: I assume that most of you are using Python, so I'll be mainly using Python examples.
(02:05:01 PM) qense: However, since the PyGTK library is generated from the C GTK+ library it is not that different.
(02:05:58 PM) qense: Object.connect("signal-name", callback_function) is equal to g_signal_connect(signal_owner, "signal-name", G_CALLBACK (callback_function))
(02:06:22 PM) qense: A really good source of information is http://www.pygtk.org/docs/pygtk/
(02:06:49 PM) qense: This is the online documentation of PyGTK and you can find, amongst other things, all signals documented here.
(02:07:24 PM) qense: It is important to keep in mind that most types in the GTK+ and GObject libraries are derived from other GTK+ and/or GObject types.
(02:07:33 PM) qense: All types are derived from gobject.GObject
(02:08:23 PM) qense: A type has got all signals it's derived from.
(02:08:32 PM) qense: gobject.GObject has got only one signal 'notify'
(02:08:49 PM) qense: However, that means that all GTK+ objects have got that signal as well.
(02:09:26 PM) qense: The 'notify' signal is emitted whenever an property of the object is changed.
(02:10:27 PM) qense: However, this doesn't have to mean the value of those properties has changed, it could have been that someone or something assigned the an identical value to a property
(02:11:00 PM) qense: All GTK+ types are derived from gtk.Object (gtk.Object itself is derived from gobject.GObject)
(02:11:15 PM) qense: gtk.Object has one signal as well: 'destroy'
(02:11:30 PM) qense: This is emitted when the object is destroyed
(02:11:38 PM) qense: You use this e.g. when constructing a window.
(02:11:50 PM) qense: window = gtk.Window();
(02:11:54 PM) qense: window.show();
(02:12:04 PM) qense: window.connect("destroy", gtk.main_quit());
(02:12:08 PM) qense: gtk_main();
(02:12:33 PM) qense: Now, what did I write above?
(02:12:51 PM) qense: First I created a new window, then I showed it and then I connected a callback function.
(02:12:52 PM) qense: Whoops
(02:12:56 PM) qense: I see I connected it wrongly.
(02:13:00 PM) qense: This sure would have generated a bug.
(02:13:31 PM) qense: It should have been
(02:13:34 PM) qense:  window.connect("destroy", gtk.main_quit);
(02:13:42 PM) qense: Without the parentheses.
(02:14:13 PM) qense: What you do when connecting is first specifying the signal name, then you give the reference to the callback function.
(02:14:22 PM) qense: However, if you add the parentheses you invoke the method.
(02:14:49 PM) qense: As you can see I connected a method from a library: gtk.main_quit()
(02:14:58 PM) qense: That method ends the GTK+ loop.
(02:15:45 PM) qense: If you wouldn't connect this to a destroy signal you would have to forcefully quit the application in order for the GTK+ main loop to end.
(02:16:01 PM) qense: You can also specify locally defined methods as callback functions.
(02:16:24 PM) qense: Any questions about the working of GTK+ signals, or about what I've told so far?
(02:19:28 PM) ClassBot: ryzrecreel asked: So the callback function can be the method of another object?
(02:20:17 PM) qense: ryzrecreel: Yes, the callback can be any valid method. When connecting signals from inside a class you could e.g. also specify "self.my_wonderful_callback" method as the callback function.
(02:20:48 PM) qense: However, you can't assign MyClass.my_callback as the callback function, you need to pass the method of an instance.
(02:21:27 PM) qense: OK, lets talk a bit more about the callback function.
(02:22:21 PM) qense: Because when a signal is emitted and your well-crafted callback function is called you want to know something more about what happened.
(02:22:43 PM) qense: For that a series of arguments are sent to the callback function.
(02:22:54 PM) qense: You need to make sure that it can accept those arguments.
(02:23:06 PM) qense: Most of those callback functions look the same.
(02:23:28 PM) qense: This is what the PyGTK documentation says the callback function for the 'notify' signal should look like:
(02:23:30 PM) qense: def callback(gobject, property_spec, user_param1, ...)
(02:24:06 PM) qense: If you're defining the callback as a member of a class make sure you add an extra 'self' argument to the beginning
(02:24:22 PM) qense: the first argument of the callback function is 'gobject', but of course you can change the names in your own code.
(02:24:35 PM) qense: 'gobject' is the GObject that changed and therefore emitted the notify signal
(02:25:08 PM) qense: Most of the times you know what object you're getting since you specifically connected your callback to it, but this can be useful when you're connected to multiple objects.
(02:25:24 PM) qense: But mostly it's very useful to have a reference to the object by hand
(02:25:36 PM) qense: the second argument is 'property_spec'
(02:26:05 PM) qense: It provides information about the property that changed so you can access it if you would want  to.
(02:26:37 PM) qense: The last is user data; when connecting to a signal you can provide extra userdata as extra arguments at the end of the function.
(02:26:44 PM) qense: You'd do this:
(02:27:27 PM) qense: window.connect("destroy", gtk.main_quit, "test-window", 123);
(02:27:48 PM) qense: and the last two arguments would be the last two arguments for the callback function
(02:29:17 PM) ClassBot: lsteeger asked: What happens if the object used for the callback function in 'connect' call is destroyed before the event occurs?
(02:30:00 PM) qense: lsteeger: If you didn't disconnect the callback function first you will get an error.
(02:31:02 PM) qense: When you connect to a signal using the .connect() function the connect() function returns a handler id.
(02:31:20 PM) qense: You can pass that handler id as the sole argument for .disconnect() for disconnecting from the signal.
(02:32:08 PM) qense: Naturally, when you remove the object you connected to the callbacks are disconnected automatically.
(02:32:36 PM) qense: There are a few very interesting signals.
(02:32:51 PM) qense: gtk.Widget has the most, which means all GtkWidgets have got them.
(02:33:48 PM) qense: To give you an idea of what you can do with GTK+: all you see happening on screen is mostly done with publicly accessible signals and methods.
(02:34:48 PM) qense: There is a 'show' signal that is emitted when the show() method is called.
(02:35:12 PM) qense: There is a 'show-help' signal that is emitted when the user presses Ctrl+F1 on his or her keyboard.
(02:37:20 PM) qense: An interesting signal is "enter-notify-event", which is emitted whenever a mousebutton enters the widget.
(02:38:15 PM) qense: It is used by many widgets like buttons.
(02:38:50 PM) ClassBot: lsteeger asked: If I extend a widget, can I define 'new' signals of my own?
(02:38:57 PM) qense: lsteeger: Excellent question!
(02:39:10 PM) qense: Yes you can, it is exactly what all widgets are doing.
(02:40:22 PM) qense: You do that with the "gobject.signal_new()" function since actually signals are provided by GObject.
(02:41:03 PM) qense: Lets paste the function from the documentation:
(02:41:04 PM) qense: def gobject.signal_new(signal_name, type, flags, return_type, param_types)
(02:41:13 PM) qense: The first argument is the name you give to the signal.
(02:41:17 PM) qense: This has to be a unique name.
(02:41:38 PM) qense: The second is the object (type) you're adding the signal to.
(02:42:11 PM) qense: If you want to add a signal to myproject.MyWidget you write myproject.MyWidget there, without quotation marks.
(02:42:23 PM) qense: The third argument is an interesting one: there you provide flags.
(02:43:14 PM) qense: it goes a bit too far to explain them all, but you can make the signals do more than what I showed with the 'destroyed' signal.
(02:43:35 PM) qense: most of the times you provide something like gobject.SIGNAL_RUN_LAST as the flag.
(02:43:53 PM) qense: It has got something to do with when you emit the signal.
(02:44:25 PM) qense: If you want to provide multiple flags you use |  to separate them
(02:45:17 PM) qense: The next argument is the return type of your callback function, which could be something like gobject.TYPE_STRING or gobject.TYPE_NONE if your callback doesn't return anything at all.
(02:45:46 PM) qense: The last argument is a tuple of types of the arguments passed to the callback.
(02:46:10 PM) qense: e.g. (myproject.MyWidget, gobject.TYPE_BOOL)
(02:46:32 PM) qense: You're not finished with just defining a signal, you'll also want to emit it something.
(02:46:35 PM) qense: somehwere
(02:47:09 PM) qense: You do that with gobject.emit() method.
(02:47:43 PM) qense: The first argument is the signal name
(02:47:51 PM) qense: The others are the arguments that are sent to the callback
(02:48:54 PM) qense: If you want to emit a signal inside your MyWidget class you call self.emit()
(02:49:09 PM) ClassBot: lsteeger asked: With multiple subscribers to an object's events, does the emit() infrastructure apply a hierarchy to their invocation?
(02:50:35 PM) qense: lsteeger: Yes it does, the first callback that is connected to a signal is the first to be called.
(02:51:00 PM) qense: you can use the . stop_emission(signal_name) method to stop the emission, if you wanted to.
(02:51:46 PM) qense: lsteeger: yes, First In, First Out indeed
(02:52:42 PM) qense: However, the default signal handler is always at the end of the list.
(02:52:58 PM) qense: You'll have to use the .connect_after() method to be added after that callback.
(02:53:06 PM) qense: Default signal handlere? What is that?
(02:53:29 PM) qense: It is the default signal handler, and it's always called.
(02:53:34 PM) qense: If it's specified, of course.
(02:54:19 PM) qense: You can do that by creating a function in a class with the name "do_signal_name"
(02:55:29 PM) qense: If you want to override default signal handlers, make sure you add __gtype_name__ = "MyWidget" as a class property
(02:55:36 PM) qense: class MyWidget (gtk.Widget):
(02:55:42 PM) qense:     __gtype_name__ = "MyWidget"
(02:56:14 PM) qense: Any questions about what I've told this session?
(02:56:19 PM) qense: Or other questions?
(02:58:36 PM) qense: Remember, if you want to learn what signals there are, go to http://www.pygtk.org/docs/pygtk/ and take a look at the page of the widget you want to know more about.
(02:59:07 PM) ClassBot: ryzrecreel asked: when using quickly with glade the connects are made for you. Is there any way to over ride that?
(02:59:46 PM) qense: ryzrecreel: In the generated code you can see that the signals are connected using .connect_object()
(03:00:08 PM) qense: That means it searches for the callback functions in the object you provide.
(03:00:15 PM) qense: The callback functions are provided in Galde
(03:00:16 PM) qense: Glade
(03:00:20 PM) qense: you can change those there.
(03:00:41 PM) qense: <lsteeger> QUESTION: In a multi-core/processor environment, do multiple event handlers get invoked asynchrounously?
(03:01:26 PM) qense: lsteeger: That doesn't depend on multi-threading, signal handlers don't get handled asynchronously, but one by one.
(03:01:33 PM) qense: OK, thank you for listening, this was all!
(03:01:53 PM) qense: This also was the last session of the Ubuntu Opportunistic Developer Week
(03:01:57 PM) qense: I hope you enjoyed it.

MeetingLogs/OpWeek1003/GtkSignals (last edited 2010-03-07 19:15:30 by pool-71-182-100-128)