.. Tutorial documentation master file, created by sphinx-quickstart on Wed Apr 28 12:57:44 2010. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. .. |Swig| replace:: *Swig* .. |glade| replace:: *Glade* .. |pygtk| replace:: *PyGTK* .. |python| replace:: *Python* .. |mvco| replace:: *MVC--O* .. |mvc| replace:: *MVC* pattern .. |obs| replace:: *Observer* pattern .. |gui| replace:: *GUI* .. |pygtkmvc| replace:: *pygtkmvc* .. |vc| replace:: *V&C* ################################################# A short tutorial for |pygtkmvc| Version |version| ################################################# .. toctree:: What is this? ============= |pygtkmvc| is a practical implementation of |mvc| and |obs|. The goal of this tutorial is to provide readers with sufficient awareness about what |pygtkmvc| is and what it can do, in order to allow them to quickly decide whether it might be useful or not for their needs. The tutorial is thought to be minimal, meaning that many of provided features are not mentioned. For any further information the user should refer to the manual pages. How do I install it? ==================== Please refer to the User Manual for all details about the installation. The example in few words ======================== The sample application must provide a single window, with a label showing the value of a numeric counter. The window contains also a button, which increments the counter by one every time it is pressed. .. figure:: images/appl.png :width: 4cm How the mini-application looks like Since this tutorial must fit into few pages, the example is extremely simple. Moreover, a more convoluted example would not help to better understand what |pygtkmvc| can be used for. The framework ============= The implementation of this example is split into three distinct parts. First, the |gui| is constructed from a |glade| file. Second, the |gui| is constructed *by hand*, by creating and connecting manually all widgets. Third, adapters are applied to make the code much easier. Hand-made view is mainly presented to make this tutorial more complete, but the readers should keep in mind that they are going to adopt the former most of the times, or even more often a mixture of them, where many parts come from one or more glade files, and some others are built manually. The use of glade files is also the reason why the |pygtkmvc| framework is split into three distinct parts. It is a matter of fact that in practice the Model-View-Controller pattern will almost always collapse to a two-level framework, where the View and the Controller are represented by a unique monolithic entity (let's call it |vc|), and the Model is still separated by the rest. The |pygtkmvc| framework provides three well-distinguishable levels, to allow the pure-glade parts to go into the View side (in a direct and very natural way), and all the remaining parts that would be put in the |vc| part, to go either in the Controller part, or in the View part, depending on how much close to the |gui| stuff are. For example, all the widgets signal handlers must go in the Controller side, whereas the code that sets some attributes of a specific widget might live either in the Controller or in the View, depending on how much those attributes are bounded to the application logic. The more some code depends on the logic of the application, the farther it lives from the View side. If some code depends only on the logic without any relation with the |gui| stuff, it must live in the Model. The implementation glade-based ============================== The model --------- The model is represented by class ``MyModel``, derived from class ``Model``, that in turn is provided by the framework. The class ``MyModel`` contains a field called ``counter`` to hold the value of a numeric counter. Since we are interested in monitoring and show any change of this counter, we declare it as an *observable property*. :: from gtkmvc import Model class MyModel (Model): # observable properties: counter = 0 __observables__ = ('counter',) pass # end of class All that it is required to do, is calling class ``Model``'s constructor from within the derived class' constructor, and defining a class variable ``__observables__`` containing the name of the observable properties. The base class ``Model`` will do all the boring work automatically. Names in ``__observables__`` can contain wilcards, see the user manual for further information. The glade-based view -------------------- |glade| while editing the example is depicted in figure :ref:`glade-figure`. The names for the main window, the label and the button are significant, and signal clicked of the button has been associated with a function called ``on_button_clicked``. .. note:: Since version 1.99.1 `gtk.Builder` glade format is supported by view along with old `glade` format. .. _glade-figure: .. figure:: images/glade.png :width: 18cm Glade at work The result is saved in file ``pygtkmvc-example.glade``. The view is represented by class ``MyView``, that derives from class ``View`` provided by |pygtkmvc|. The class ``View`` can be thought as a container that holds a set of widgets, and may associate each widget with a string name. When a glade file is used to build the view, each widget will be associated automatically inside the view with the corresponding name occurring in the glade file. Later, each ``View`` instance is connected to a corresponding ``Controller``, and when built from a glade file, methods inside the ``Controller`` will be scanned to try to connect automatically all signals declared in the glade file. :: from gtkmvc import View class MyView (View): def __init__(self): View.__init__(self, builder="pygtkmvc-example.glade") return def set_text(self, text): self['label'].set_text(text) return pass # end of class Class ``MyView`` calls simply ``View``'s class constructor from within its constructor, by passing the glade file name, either with the keyword argument `builder` for :class:`gtk.Builder` format, or keyword argument `glade` for the old |glade| format. All the hard work is carried out by class ``View``. Most of the time user's views are like class ``MyView`` here: they derive from ``View`` and simply call View's constructor by passing glade filename and possibly name of top-level widget. In this situation, it is possible to use a much compact declaration for views:: from gtkmvc import View class MyView2 (View): builder = "pygtkmvc-example.glade" def set_text(self, text): self['label'].set_text(text) return pass # end of class Attributes `builder`, `glade` and `top` can be overridden by using the corresponding keyword arguments when calling the constructor:: ... v = MyView2(builder='a-different-pygtkmvc-example.glade') The controller -------------- The controller - so to speak - is the most complicated part of this example. It is the only part of the |mvc| which knows the model and the view instances which it is linked to. These are accessible via members ``self.model`` and ``self.view`` respectively. :: # This is file ctrl_glade.py from gtkmvc import Controller import gtk class MyController (Controller): def __init__(self, model, view): Controller.__init__(self, model, view) # The controller is an observer for properties contained in # the model: return def register_view(self, view): """This method is called by the view, that calls it when it is ready to register itself. Here we connect the 'pressed' signal of the button with a controller's method. Signal 'destroy' for the main window is handled as well.""" # connects some signals manually: self.view['main_window'].connect('destroy', gtk.main_quit) # initializes the text of label: self.view.set_text("%d" % self.model.counter) return # signals: def on_button_clicked(self, button): self.model.counter += 1 # changes the model return # observable properties: @Controller.observe("counter", assign=True) def counter_change(self, model, prop_name, info): self.view.set_text("%d" % info.new) print "Property '%s' changed value from %d to %d" \ % (prop_name, info.old, info.new) return pass # end of class In the class constructor, at first base class constructor is called, passing the ``Model`` instance this ``Controller`` instance belongs to. From that moment on, class member self.model will be accessible. ``Controller``'s base class ``Observer`` calls method ``Model.register_observer`` in order to make the controller an observer for the observable property ``counter`` in the model. After this, every change applied to ``MyModel`` class' member ``counter`` will make method ``counter_change`` of class ``MyController`` be called automatically. In fact, the method has been registered as a notification method for assignments with the decorator `@Controller.observe`. At construction time also the ``View`` instance is passed, to be registered to be the view of the controller. Method ``register_view`` is called when a class ``View`` instance requires to be registered to the controller it belongs to. This method is mainly used to connect signals and initialize the |gui| side that depends on the application logic. In the example, signal ``"destroy"`` of the main window is connected to ``gtk.main_quit`` to close the application when the user closes the window. Notice here the use of member ``self.view`` and how a class ``View`` can be used as a map to retrieve widgets from their names. Also, the text label is initialized to the initial value of the counter. Method ``on_button_clicked`` is called as a callback every time the user clicks the button. The corresponding signal is automatically connected to this method when class ``MyView`` registers itself within the controller. Finally, method ``counter_change`` is called when the property counter in class ``MyModel`` changes (gets assigned to a different value). The model containing the property, the name of the property, and a structure carrying other information (e.g. the old and new value) are passed to this method. Notice that the model is passed since the controller might be an observer for more than one model, even different from the model it is directly connected to in the |mvc| chain. The main code ------------- Main code is pretty trivial: :: # This is file main_glade.py import gtk from model import MyModel from ctrl_glade import MyController from view_glade import MyView m = MyModel() v = MyView() c = MyController(m, c) gtk.main() A triple MVC is created, and main loop is started. The implementation without glade ================================ The model --------- The model does not depend on the controller+view sides, so it is exactly the same as for the implementation glade-based. The view -------- Using manually constructed views is slightly less intuitive that using glade-based views, since the architecture of the view-side |pygtkmvc| is mainly designed to be used with glade files. :: # This is file view_no_glade.py from gtkmvc.view import View import gtk class MyViewNoGlade (View): def __init__(self): # The view here is not constructed from a glade file. View.__init__(self) # The set of widgets: w = gtk.Window() h = gtk.VBox() l = gtk.Label() b = gtk.Button("Press") h.pack_start(l) h.pack_end(b) w.add(h) w.show_all() # We add all widgets we are interested in retrieving later in # the view, by giving them a name. Suppose you need access # only to the main window, label and button. Widgets are # added like in a map: self['main_window'] = w self['label'] = l self['button'] = b return def set_text(self, text): self['label'].set_text(text) return pass # end of class The entire work is carried out by the View's constructor which can be exploited to manually construct all the widgets set. Following lines are used to build the widgets set, and to associate a few of them with string names (only those that will have to accessed later). Notice that here |glade| file has not been used at all. Nevertheless, a mixed solution where |glade| file(s) and manually constructed widgets sets is fully supported. The controller -------------- The controller is the same that has been used for the glade-based version, a part from a further signal connection that is performed to connect the button "``clicked``" event to class method ``self.on_button_clicked``. For this reason, class ``MyControllerNoGLade`` is derived from class ``MyController`` to reduce typing. :: # This is file ctrl_no_glade.py from ctrl_glade import MyController class MyControllerNoGlade (MyController): def register_view(self, view): # connects the signals: self.view['button'].connect('clicked', self.on_button_clicked) return pass # end of class The main code ------------- Like previous version, main code for manually built view is very short: :: # This is file main_no_glade.py import gtk from model import MyModel from ctrl_no_glade import MyControllerNoGlade from view_no_glade import MyViewNoGlade m = MyModel() v = MyViewNoGlade() c = MyControllerNoGlade(m,v) gtk.main() Multiple views, one model ========================= This example shows the powerful of the |obs|. Here both the glade-based and manually built versions are being run at the same time, with a single instance of class ``MyModel`` shared between those two versions. The execution of this example results in two windows being displayed; by clicking the button of one of them, the counter is incremented, and the labels in both of them are updated. :: # This is file main_mixed.py import gtk from model import MyModel from ctrl_no_glade import MyControllerNoGlade from ctrl_glade import MyController from view_no_glade import MyViewNoGlade from view_glade import MyView m = MyModel() v1 = MyViewNoGlade() c1 = MyControllerNoGlade(m,v1) v2 = MyView() c2 = MyController(m,v2) gtk.main() Using Adapters ============== Since version 1.2 adapters largely contribute to make the code simpler and so to reduce development costs and efforts. Adapters *adapt* some part of the model to some part of the view. In a simple version, one adapter makes one property (possibly observable) into the model communicate autonomously with a single widget into the view, and viceversa. Readers can find all information about adapters in the user manual. We want to have an adapter to handle coordination between property ``counter`` and the label. Model and View remain unchanged. It is the Controller that can be simplified as follows: :: # This is file ctrl_glade_adap.py from gtkmvc import Controller import gtk class MyControllerAdap (Controller): def register_view(self, view): # connects the signals: self.view['main_window'].connect('destroy', gtk.main_quit) return def register_adapters(self): self.adapt("counter", "label") return # signals: def on_button_clicked(self, button): self.model.counter += 1 # changes the model return pass # end of class Controller method ``register_adapters`` is called by the framework when adapters can be instantiated. The controller is no longer interested in observing property ``counter`` and to initialize the value shown in the label, as these activities are now transparently carried out by the adapter. Notice that if editable widget like a text entry were used instead of a label, the adapter would also have taken care about changes of the text entry reporting them to the property. Now suppose you want to apply some customization to the way the label shows the property's value. Method ``register_adapters`` might be: :: from gtkmvc import adapters def register_adapters(self): a = adapters.Adapter(self.model, "counter") a.connect_widget(self.view['label'], setter=lambda w,v: w.set_markup("Counter=%02d" % v)) self.adapt(a) return .. figure:: images/adap.png :width: 6cm Adapter at work Here an adapter is created explicitly, and parameter ``setter`` is used to custom the functional block that is in charge of writing to the widget. There are several types of adapters that can be used, depending on the property kind and the widget type they adapt. Adapter offer a very straight and simple default support, but they can be largely customized when needs get more advanced. See the user manual for further information. Generating a standard project from scratch ========================================== Since version 1.2.0 a little application called *gtkmvc-progen* is available to help setting up from scratch a new application based on |pygtkmvc|. *gtkmvc-progen* is available both as a command-line program and as a GUI application. .. figure:: images/progen.png :width: 12cm *gtkmvc-progen* GUI at work By the way, the source code of *gtkmvc-progen* is simple and can be exploted to learn more about gtkmvc-based applications. A model carries out all the work, and a controller/view pair provides the GUI if needed. *gtkmvc-progen* can be executed either locally from the script directory, or can be executed as any other program if |pygtkmvc| has been officially installed on the hosting system. From the local script directory: :: $> python gtkmvc-progen name=hello author="Roberto Cavada" gui=no If |pygtkmvc| was installed: :: $> gtkmvc-progen name=hello author="Roberto Cavada" gui=no "name=hello" is an example of setting of a property that customizes the way *gtkmvc-progen* works. See the user manual for a full list. A new directory called ``hello`` will be created in the current directory (as property *destdir* is "." by default. Let us run now the resulting application skeleton. :: $> cd hello $> ls hello.py resources src $> python hello.py .. figure:: images/hello.png :width: 8cm The skeletal hello application Conclusions =========== The author does hope that this tutorial will be useful to help those who are unsettled about whether |pygtkmvc| can fit their needs or not. Even if very simple, from this tutorial should result clear to the reader that both the MVC and Observer patterns can strongly improve the quality of middle and big size |gui| applications, especially if combined with the use of glade-based views. |pygtkmvc| has been extensively used to produce a few large |obs| applications based on |python| and |pygtk|. In this scenario, many design choices that led to |pygtkmvc| had been determined from practical needs, and this made easiness and transparency the most appreciated quality of the framework. For any further information, and a detailed list of supported features, please refer to the user manual. For implementation details, see the API manual.