The examples we have seen so far use a simple mapping: one View per window. While simple applications will use this and nothing else, wouldn't it be interesting to reuse parts of your interface just like you reuse code when programming?
UI Composition allows you to create components of your application UI, which in Kiwi are named slaves, and attach them to other Views. This allows you to embed widgets in any way you like. A slave is really just another View (there is a class called SlaveView, which is for interfaces without a toplevel window that do not use Glade, but any view can be used as a slave) that happens to be attached to another view.
To implement this, Kiwi uses GTK+'s really cool ``reparenting'', a property which allows a widget to be removed from one place in the widget hierarchy and placed in the other (in Kiwi terms, a View's widget is replaced by another View).
A view that will embed one or more slaves is called a Parent view (though there is no class with that name; read on). There are two important requirements for a Parent view:
I'll demonstrate by showing how a parent view is built in glade. Look at examples/Browser/news_shell.glade:
In news.glade I put together a simple dialog, and in the place where
I want the slave to be embedded, I have added an EventBox (with any
name) and a label (named my_placeholder
). We now write a
small SlaveView called NewsView:
from kiwi.ui.views import SlaveView from kiwi.ui.objectlist import ObjectList, Column class Item: def __init__(self, news, author): self.news, self.author = news, author class NewsView(SlaveView): def __init__(self): news = ObjectList(Column("news", "News",), Column("author", "Author")) for item in newsitems: news.append(item) SlaveView.__init__(self, toplevel=news) # Some news articles borrowed from Pigdog Journal for our example newsitems = [ NewsItem("Smallpox Vaccinations for EVERYONE", "JRoyale"), NewsItem("Is that uranium in your pocket or are you just happy to see me?", "Baron Earl"), ]
Now of course, this view can't display itself; it's a SlaveView
,
and it lacks a toplevel window. But that doesn't matter; we want to
attach it as a slave to a main View. We create news
, an instance
of NewsView, and a BaseView instance called
shell
, which will be our parent View. We then call the method
attach_slave()
, which does the actual reparenting; it takes as
arguments the name of the widget to be replaced and the view to be
attached. At this point, we run the parent view as we would normally:
from kiwi.ui.views import BaseView news = NewsView() shell = BaseView("news_shell", delete_handler=mainquit) shell.attach_slave("my_placeholder", news) news.show_all() news.focus_toplevel() # explained next section, don't worry shell.show_and_loop()
In summary, I gather some random news, create a NewsView
instance, a BaseView instance (that will be the parent), call
the special function attach_slave()
, and that's it. (There is a
minor detail with widget focus that is discussed in the next section;
basically, the slave needs to be attached to the parent before trying to
focus its widgets). The final result is shown below:
Note that all the examples up to now have been very simple and static: there is no user interaction going on beyond closing the window (for instance, the buttons in the previous example do nothing!). To be able to handle events, we need to understand how the Controller classes (and their derivates, the Delegates) work. The next section explains them in detail.