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.