.. _architecture:

Architecture
============

In this section we attempt to describe the overall architecture of dip and the
philosophy behind it.  This will include a summary of the purpose of each of
the main modules that make up dip.  Later sections will take the form of
tutorials in using each of these modules.

Before we outline the philosophy of dip we first consider two issues: the
realities of software development; and the myth of toolkit independence.


The Realities of Software Development
-------------------------------------

Software development is a messy business.  Perhaps the most desired quality of
a developer is hindsight - *"I wish I'd done it differently at the start"*.

Most successful applications start out small with limited scope.  It's only
later, after more development turns it into something more widely used and with
much more functionality than originally anticipated, that the hindsight kicks
in and the need for a framework becomes apparent to help sort out the somewhat
ad-hoc implementation.

Most frameworks require the developer to use the framework from the very start
of development because they impose a particular way of doing things that don't
integrate particularly well with more ad-hoc approaches.  Applying such a
framework to an existing application often means rewriting that application,
something that is both expensive and risky.

Clearly a framework that than be adopted a bit at a time provides a means by
which an application can be re-designed and re-implemented incrementally almost
as a side effect of normal maintenance and development.


The Myth of Toolkit Independence
--------------------------------

Many application frameworks support the concept of GUI toolkit independence.
In other words they allow you to develop your application so that it can be run
without changes using any of the supported toolkits.  This is typically
implemented as a toolkit independent API and the framework defines wrappers
that translate that API to the toolkit specific API.

The toolkit independent API is typically more abstract and requires fewer lines
of code to create simple GUIs.  However it is also less flexible and less
appropriate for the more complex, application specific parts of the GUI.
Because of this the framework will usually provide access to the underlying
toolkit specific objects.

Most organizations and individual developers will choose a particular GUI
toolkit and use it for all their applications.  A typical application will use
the toolkit independent, more abstract, API for the simple parts of the GUI and
the toolkit dependent API, *for their chosen toolkit*, for the more complex
parts of the GUI.  In other words, for the vast majority of applications, the
toolkit independence offered by frameworks is irrelevant.  What does matter is
how good the abstract API is in creating simple GUIs in few lines of code, and
how easy it is to mix GUIs created by the abstract API and GUIs created with
the toolkit specific API.

Of course toolkit independence is important in code that is likely to be shared
between organizations and individuals that have made different toolkit choices.
The most obvious example of such code is dip itself.  Internally dip always
uses the toolkit independent API.

dip ensures that it is easy to mix GUIs it creates with GUIs created by the
toolkit specific API by inverting the usual approach of implementing toolkit
independence.  Instead of the independent API creating and returning wrappers
around the toolkit specific GUI objects, dip always returns the toolkit
specific GUI objects themselves.  Equally, when a GUI object is passed to dip
to use it makes no difference whether that object was originally created by dip
or by another part of the application that knows nothing about dip.

For example, some other framework may provide a ``Dialog`` class whose methods
implement the toolkit independent API for a dialog.  It may have a instance
attribute called ``control`` which references the toolkit specific object, e.g.
a :class:`~PyQt4.QtGui.QDialog` instance.  Whenever you refer to the dialog in
your application it will always be to the ``Dialog`` instance.  This makes the
dialog difficult to share with other parts of your application that are not
using the framework.

When dip creates a dialog it returns the :class:`~PyQt4.QtGui.QDialog` instance
itself (assuming the default PyQt4 toolkit is being used).  Obviously this can
be shared with all other parts of your application whether or not they are
using dip.  If dip itself wants to use the dialog it will only do so using the
toolkit independent API.  Exactly how this is done is explained in a later
section.


Philosophy
----------

Like any framework the main purpose of dip is to provide functionality common
to all applications and so allowing developers to concentrate their efforts on
the functionality that is specific to their particular application.

Central to dip's philosophy is that it acknowledges how software is actually
developed, rather than how the text books would like it to be.  It recognises
that there is never a right way to do something - just that some approaches are
more appropriate than others in any particular situation, and that situations
change over time.

In particular:

- you choose to use as much or as little as you want
- it will keep out of the way when it's not wanted
- you choose the degree of abstraction you want to use
- if you don't like any of it, replace it.

The single most important feature of dip is that it is easy to adopt it a bit
at a time.  It can be used in part of an application while allowing other parts
to remain unchanged.  An application that has grown "organically" over time can
be easily migrated to one that is well designed, component based, without
needing to take time out to do a major rewrite.


Module Overview
---------------

:mod:`dip.model`
................

The :mod:`dip.model` module implements a declarative type system for Python.
It is used throughout the rest of dip.

The core of the module is the :class:`~dip.model.Model` class.  Sub-classes
define attributes as class attributes using :term:`attribute type` classes such
as :class:`~dip.model.Bool` and :class:`~dip.model.Str`.  When such a sub-class
is instantiated corresponding instance attributes are automatically created.

A model will enforce an attribute's type when rebinding it to a different
value.  It will also apply any additional constraints imposed by a particular
type.  All types can provide an default value (that may be overridden) for an
attribute.

The initial values of attributes can be delayed until they are actually needed.
This can be used to implement the lazy loading of Python modules.

An attribute can be observed and arbitrary Python code invoked when its value
changes.

The :class:`~dip.model.MappingProxy` class allows the use of any Python mapping
object, with some limitations, to be used as a model.

The module also provides support for :term:`interfaces<interface>` and
:term:`adapters<adapter>`.

The section :ref:`model-tutorial` contains a full tutorial for this module.


:mod:`dip.ui`
.............

The :mod:`dip.ui` module provides a set of classes that allow a user
interface, or just a part of a user interface, to be defined declaratively.

dip follows the conventional :term:`model`-:term:`view`-:term:`controller`
pattern.  The user interacts with the view and the controller makes appropriate
updates to the model.  More specifically, the view contains a number of
:term:`editors<editor>` each of which is bound to an attribute of the model.  A
view may contain sub-views.

For each type of view dip provides a view factory.  When an instance of the
factory is called it is passed the model and an optional controller.  A default
controller will be created automatically if required.  The factory creates a
:term:`toolkit` specific implementation of the view.  For example, assuming the
default PyQt4 toolkit, the :class:`~dip.ui.Dialog` factory will create a
:class:`~PyQt4.QtGui.QDialog` instance.  Other commonly used view factories
include :class:`~dip.ui.Form` and :class:`~dip.ui.LineEditor`.

A user interface that is defined declaratively is often done so as a class
attribute::

    from dip.ui import Dialog, Form, LineEditor

    class MyUIFactory:

        # Declaratively define the dialog.
        ui = Dialog(
                Form(
                    LineEditor('name'),
                    LineEditor('address')
                )
             )

        def __call__(self, model):
            """ Create and return an instance of the dialog. """

            return self.ui(model)

The ``'name'`` and ``'address'`` are the names of attributes in the model that
the :class:`~PyQt4.QtGui.QLineEdit` instances that are created are bound to.

Actually the above example is unnecessarily long as dip has sensible defaults
and will inspect the model to obtain any missing information.  The definition
of the dialog in this case only needs to be::

        ui = Dialog('name', 'address')

Each view has a toolkit independent API.  These are defined as
:term:`interfaces<interface>` contained in the :mod:`dip.ui` module.  Access
to the toolkit independent API is gained by adapting the toolkit specific
object to the interface.  Extending the previous example::

    from dip.ui import IDialog

    ui_factory = MyUiFactory()

    dialog_1 = ui_factory(model)
    dialog_1.exec()

    dialog_2 = ui_factory(model)
    IDialog(dialog_2).execute()

In the case of ``dialog_1`` we are calling the
:meth:`~PyQt4.QtGui.QDialog.exec` method of :class:`~PyQt4.QtGui.QDialog` to
enter the dialog's event loop.  This will only work if we know the toolkit uses
:class:`~PyQt4.QtGui.QDialog` (or a sub-class) to implement dialogs.

In the case of ``dialog_2`` we are adapting the :class:`~PyQt4.QtGui.QDialog`
(although we don't actually care what type of object it is) to the
:class:`~dip.ui.IDialog` interface and calling its
:meth:`~dip.ui.IDialog.execute` method to do the same thing.  This will always
work no matter what toolkit is being used.

The section :ref:`ui-tutorial` contains a full tutorial for this module.


:mod:`dip.pui`
..............

The :mod:`dip.pui` module provides a set of classes that allow a user
interface, or just a part of a user interface, to be defined procedurally.

For every :term:`view` factory implemented by the :mod:`dip.ui` module this
module implements a callable of the same name that will create the toolkit
specific object.  For example calling :func:`dip.pui.Dialog` will create a
toolkit specific object that can be adapted to the :class:`~dip.ui.IDialog`
interface.  Calling an *instance* of the :class:`dip.ui.Dialog` class will
create a similar object.

While it is recommended that user interfaces are created declaratively using
the :mod:`dip.ui` module it is sometimes necessary to create new elements, in a
toolkit independent way, and add them to an existing user interface.


:mod:`dip.toolkits`
...................

The :mod:`dip.toolkits` module implements the :term:`toolkits<toolkit>` that
are included with dip.  The default toolkit uses :mod:`PyQt4`.

A toolkit is an implementation of the :class:`~dip.toolkits.IToolkit`.  The
name of the default toolkit can be set using the :envvar:`DIP_TOOLKIT`
environment variable.  If the name contains a ``.`` then it is assumed to be
the name of a module that contains a callable called ``Toolkit`` that will
create the toolkit.  Otherwise it is assumed that the name refers to a toolkit
included with dip.

Normally an application doesn't need to worry about toolkits, however it may
want to change the standard behaviour of a toolkit.  For example it may want to
use a custom version of a particular user interface element, say a file
selector dialog.  All that needs to be done is to sub-class an existing
toolkit, reimplement the appropriate methods
(:meth:`~dip.toolkits.IToolkit.get_open_file` in this case).


:mod:`dip.shell`
................

The :mod:`dip.shell` module contains classes that implement
:term:`shells<shell>`.  A shell is an abstraction of an application and its
user interface.  The application's functionality is implemented as a set of
:term:`tools<tool>`.  A tool will define and manage a number of
:term:`actions<action>` and :term:`views<view>`.  A shell will visualise any
tool actions and provide a number of :term:`areas<area>` in which views can be
placed.

The use of the shell abstraction by an application is entirely optional.

dip provides a shell implementation that uses :class:`~dip.ui.MainWindow`.  The
actions are visualised as menu items and tool buttons.  The areas are
visualised as docks and tabbed widgets.  The default PyQt4 toolkit uses a
:class:`~PyQt4.QtGui.QMainWindow` to implement a main window.

dip includes the following tools:

- :class:`~dip.shell.tools.dirty.DirtyTool` is a tool that supports the feature
  common to many toolkits where some indication is given (usually in the main
  window's title bar) that the application has unsaved data.

- :class:`~dip.shell.tools.form.FormTool` is the base class of tools that
  create form based views for editing models.

- :class:`~dip.shell.tools.model_manager.ModelManagerTool` is a tool that
  manages the lifecycle of models on behalf of an application.  It implements
  (with the help of the :mod:`dip.io` module) the standard ``New``, ``Open``,
  ``Save``, ``Save As`` and ``Close`` actions.

- :class:`~dip.shell.tools.quit.QuitTool` is a tool that manages the user's
  ability to quit the application.  It implements the standard ``Quit`` action
  and will allow other tools to veto an attempt to quit if, for example, there
  is unsaved data.

- :class:`~dip.shell.tools.whats_this.WhatsThisTool` is a tool that implements
  the standard ``What's This?`` action.

The section :ref:`shell-tutorial` contains a full tutorial for this module.


:mod:`dip.io`
.............

The :mod:`dip.io` module provides a set of classes that enables models to be
completely decoupled from where they are stored and the data formats used to
store them.  This allows support for new types of storage to be added without
requiring any application changes.  It also allows models to be imported from
and exported to new data formats without requiring changes to existing code.

The section :ref:`io-tutorial` contains a full tutorial for this module.


:mod:`dip.plugins`
..................

The :mod:`dip.plugins` module provides facilities for structuring an
application as a set of :term:`plugins<plugin>`.  Plugins are completely
decoupled from each other which makes it easier to add new functionality in the
form of new plugins without requiring changes to existing code.

The section :ref:`plugins-tutorial` contains a full tutorial for this module.


:mod:`dip.automate`
...................

The :mod:`dip.automate` module provides facilities for the automation of
applications.  Automated applications do not need to be dip applications.
Applications do not need to be modified in order to be automated.  (Although
there are steps that can be taken when writing applications that make
automation easier.)

The module defines an automation API that is used in automation scripts.  A
toolkit will implement that API, typically by providing appropriate adapters,
using toolkit specific features.  In the case of the default PyQt4 toolkit the
:mod:`~PyQt4.QtTest` module is used.

dip also includes the ``dip-automate`` tool which runs an application under the
control of an automation script.

The section :ref:`automate-tutorial` contains a full tutorial for this module.


:mod:`dip.developer`
....................

The :mod:`dip.developer` module implements a number of tools that can be
included in an application to help in the debugging of that application.
