##
2.6 Controllers

In GTK+, all user events are interpreted by signals that are generated by the widgets that are manipulated. This means obvious stuff like button clicks and typing text into a `gtk.Entry`, 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 (`gtk.Button`) 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 view4, 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 BaseViews, 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:

1. Start the name with `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_` 5.

2. Add the widget name: this is the name of the View's instance variable for that widget. If your view has a widget called `quitbutton`, you would use "quitbutton".

3. Follow the widget name by two underscores (`__`).

4. Finish the name by appending the signal name you want to capture. For instance, if you wanted to handle that quitbutton's `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).

Let's see a simple example to make these concepts more concrete (included in the Kiwi tarball at Kiwi/examples/framework/faren/faren.py):

    #!/usr/bin/env python
    import gtk

    from kiwi.controllers import BaseController
    from kiwi.ui.views import BaseView
    from kiwi.ui.gadgets import quit_if_last

    class FarenControl(BaseController):

        def on_quitbutton__clicked(self, *args):
            self.view.hide_and_quit()

        def after_temperature__insert_text(self, entry, *args):
            try:
                temp = float(entry.get_text())
            except ValueError:
                temp = 0
            celsius = (temp - 32) * 5/9.0
            farenheit = (temp * 9/5.0) + 32
            self.view.celsius.set_text("%.2f" % celsius)
            self.view.farenheit.set_text("%.2f" % farenheit)

    widgets = ["quitbutton", "temperature", "celsius", "farenheit"]
    view = BaseView(gladefile="faren", delete_handler=quit_if_last,
                    widgets=widgets)
    ctl = FarenControl(view)
    view.show()
    gtk.main()

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 (gtk.Entry, gtk.Label, gtk.Text) 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.

#### Footnotes

... view4
  Ancient versions of Kiwi had a MultiView class, that held various subviews for portions of it, and required one controller for each subview. The new SlaveView approach is nicer, because it allows reusing the actual interface in a much more flexible manner.
...on_5
  Hint: if you are connecting to a `gtk.Entry`'s `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.