Concepts ======== The program is setup in a classic Model-View-Controller (MVC) design. The roles of the three parts are as follows: * **Model** Data storage and application logic. * **View** Presentation to the user, based on the model. * **Controller** Process user input. Update model and view when demanded. Here's a scheme giving essentially the same information: .. image:: _static/mvc.png :width: 400 Model ----- Handling pictures gives the following basic data: * **Image**: The image files. * **Thumbnails**: Basically an additional image, scaled down and related to the original image file. * **Tags**: A list of tags for each image. * **Metadata**: Metadata for internal use, such as timestamps, access statistics, counts, flags, references, etc. The *Model* defines how and where this data is stored. Due to the non-intrusiveness of *TagIt*, tags and metadata can be stored in an *SQLite* database. Thumbnails can be stored within the database or as extra files. If desired, tags and thumbnails can also be written into the *Image* file directly, via EXIF and IPTC. The specific behaviour of the *Model* is controlled via the configuration. Controller ---------- The controller processes user input and modifies the model on the user's behalf. If necessary, it can demand the view to update. Most of the application logic is therefore implemented in the *controller*. Another reason for upholding this concept strictly is that the *view* is typically strongly dependent on the UI framework chosen. The controller is not. Logic being static across frameworks is therefore moved to the controller. The controller comes in a hierarchy, along but not identitcal to the typical widget hierarchy. Since the application is controlled by the UI framework, the view classes have to take care of the controller instantiation. How this is done exactly is defined in the *view*. However, you start with a *root Controller*. Using its *add_child* method, new controllers can be created within the hierarchy. Note that the hierarchy is only towards the top, via the *parent* member, not downwards. If done right in the view, any widget can easily create a new controller which is attached to a parent. While the controller hierarchy normally follows the widget hierarchy, this is not at all necessary. See the image below to get a grasp of how it's done in *TagIt*. .. image:: _static/hierarchy.png :width: 400 Having a tree of controllers allows passing events. The event infrastructure is kept rather simple, yet effective. Use *bind* to bind a callback and then *dispatch* the event to run all callbacks. Similar to how *kivy* handles this. Consider the following example. .. sourcecode:: python class MyListener(Controller): def __init__(self, widget, settings, parent=None): super(MyListener, self).__init__(widget, settings, parent) self.parent.bind(on_change=self.change_callback) def change_callback(self, *args): print args # 1, 2, 3, 4 return True # Stops event processing class MyDispatcher(Controller): def on_action(self): self.parent.dispatch('on_change', 1, 2, 3, 4) These events allow distributed keybindings. Each controller can attach itself to the keyboard events of *CMainWindow*. Then it can define its own keybindings in its own space while not interfering with other components. View ---- There's two parts here, the UI logic and the design. The design covers how structures are displayed in terms of layout, colors and graphics. The UI logic describes how elements interact with each other. Logic affecting data or application behaviour is implemented in the controller, though. The *view* is heavily dependent on a UI framework. A framework change will likely result in re-writing the whole *view*. Currently, *kivy* was chosen as the only UI framework and *view* implementation. The *kivy* sourced view implements its logic in python classes, the layout is described in the *kv language*. These two are somewhat seperate but fitted to each other and the boundary is flexible. This decoupling allows the design to be less independent from the UI logic and behaviour. For example, UI elements (let's say a button) can be referred to by name or throw generic events, so the specific layout is irrelevant for the actions of that element. This way makes it easier to modify the UI in minor ways. Since *kivy* takes care if input - keyboard as well as clicks and touches - they have to be handed over to the *Controller*. This happens through the *VMainWindow* class, which controlls key events on the *kivy*-side. Key presses are passed to *CMainWindow* and distributed further with the controller events system. Mouse events are directly passed from the widgets to the respective controller. Controller creation is achieved by extending the *Widget* class during runtime. The widget tree is traversed upwards until a controller is found. From this controller, a child of the specified type is generated and stored within the widget. With this method, a controller is always guaranteed to have a parent (except for the root of course). .. EOF ..