In GTK+, all user events are interpreted by signals that are generated by the widgets what are manipulated. This means obvious stuff like button clicks and typing text into a GtkEntry, but it also includes less obvious events like the initial rendering of the widget and moving the mouse over a widget. To a signal, we can attach a function that is called when it is triggered, and this function is usually called a signal handler.
Many widgets have default handlers attached to their signals (these are coded in the actual GTK+ source code); a default handler, for example, is what makes the text that you type into an Entry actually display inside its white box, and what makes the checkbutton depress and change state when you click on it. However, there are many cases when you want to do something special based on a certain signal occurring. A pushbutton (GtkButton) is a good example: it doesn't do anything by default when clicked beyond depressing, so you practically always will want to connect to its "clicked" signal. Developing a graphical application involves selecting which signals you think are important in the interface widgets, and attaching functions to them.
In Kiwi, we suggest grouping the relevant signal handlers for an interface into a class. This means that instead of using a number of independent functions, the signal handlers are really methods of a class. This class is called the Controller. The Controller is conceptually the part of the framework that handles events that are generated in the UI; click a button, a controller method is called.
Since the View holds the interface, it makes sense to attach a controller to each view5, and vice-versa; the Controller constructor takes a View instance and ties itself to it. Since the controller needs to define special methods, it should be subclassed in your code, and the methods implemented.
The Kiwi Controller has a special feature: if you write your method
names using a certain syntax, it "discovers" what widget and signal you
want, and attaches the handler automatically for you. This works for any
type of View, even GladeViews, which means no more messing around with
signal_autoconnect()
and that pesky signal dialog in Glade. The
handler's method name should be written as follows:
on_
or after_
. Use on_
to
connect before the default handler for that widget's signal; use
after_
to connect after it. It is more common to use
on_
6.
quitbutton
, you would use "quitbutton".
__
).
clicked
signal, you would use "clicked". The final method name would be
on_quitbutton__clicked
.
Note that the widget must be attached directly to the controller's
corresponding View; it can't be attached using this syntax to a slave of
that view, for instance (you'll have to call connect()
directly
in that case). Note also that signal handler's parameters will vary from
signal to signal, and the only thing that can be trusted is that the
first parameter will be the widget which generated the signal. You can
use a method signature like the following to avoid problems:
def on_mybutton__clicked(self, button, *args):
This way, args will hold as many arguments as the signal defines for that function (and you can connect this method explicitly to more than one kind of signal, if you need to). Let's see a simple example to make these concepts more concrete (included in the Kiwi tarball at Kiwi/examples/Faren/faren.py):
Let's have a look at the code. I define a Controller
FarenControl that inherits from BaseController, and
which defines two methods that are signal handlers - one for the
"clicked" event for the widget quitbutton
, and another for the
"insert_text" signal for temperature
. I attach the view to the
controller, and tell the view to show itself and run the event loop. Not
much else is worth noting, apart from the fact that the signal handlers
receive the widget as the first parameter, and that the GTK+ text
widgets (GtkEntry, GtkLabel, GtkText) usually take and return strings,
which makes us do conversion here and there.
Thus, the event loop now has two signal handlers that will be triggered
according to the user's interaction: one called when clicking the
quitbutton and one when inserting text into the entry. The user can type
numbers into the entry, and through
after_temperature__insert_text()
, the celsius and farenheit
labels are changed automatically. Clicking quit calls a special method
in the view, hide_and_quit()
, that hides the window and quits
the event loop. Note that the widgets "celsius" and "farenheit" are
empty labels that appear right next to the labels that are written
"Celsius" and "Farenheit"; if you are confused look at the glade file
faren.glade.
insert_text
signal and you want to use
entry.get_text()
, use after_entrywidget__insert_text
-- the default signal handler is responsible for inserting the text
into the entry and you will need it to run before your handler.