.. _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.

Far better is a framework that can be adopted a bit at a time providing a means
by which an application can be re-designed and re-implemented incrementally
almost as a side effect of normal maintenance and development.  dip is such a
framework.


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.  Like other frameworks dip implements the independent API
as a wrapper around the toolkit specific API.  A toolkit independent object can
be converted to a toolkit specific object by calling a simple function
(specifically :func:`~dip.model.unadapted`).  Equally, when a toolkit specific
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.


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 that is adapted to the
factory specific :term:`interface`.

For example, assuming the default PyQt4 toolkit, the :class:`~dip.ui.Dialog`
factory will create a :class:`~PyQt4.QtGui.QDialog` instance that has been
adapted to the :class:`~dip.ui.IDialog` interface.  An application can then
choose to use the dialog using the API defined by the :class:`~dip.ui.IDialog`
interface or the :class:`~PyQt4.QtGui.QDialog` API on the unadapted dialog.

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.model import unadapted
    from dip.ui import IDialog

    ui_factory = MyUiFactory()

    dialog_1 = ui_factory(model)
    dialog_1.execute()

    dialog_2 = ui_factory(model)
    unadapted(dialog_2).exec()

In the case of ``dialog_1`` we are calling the
:meth:`~dip.ui.IDialog.execute` method of the :class:`~dip.ui.IDialog`
interface to enter the dialog's event loop.  This will always work no matter
what toolkit is being used.

In the case of ``dialog_2`` we are using the :func:`~dip.model.unadapted`
function to get a reference to the actual :class:`~PyQt4.QtGui.QDialog` that
was created by the view factory.  We are then calling the toolkit specific
:meth:`~PyQt4.QtGui.QDialog.exec` method 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.

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 is 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`
interface.  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 view, 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.settings`
...................

The :mod:`dip.settings` module provides a set of classes that provide support
for the reading and writing of user-specific settings from and to persistent
storage.

The singleton :class:`~dip.settings.SettingsManager` is used to load all the
user's settings for the application.  The
:meth:`~dip.settings.ISettingsManager.restore` method is called to read and
restore the settings for the models that are passed to it.  The
:meth:`~dip.settings.ISettingsManager.save` method is called to save and write
the settings for the models that are passed to it.

An individual setting has a string identifier.  A model that wants to read and
write any number of settings must implement the
:class:`~dip.settings.ISettings` interface (or provide an appropriate adapter).

A :term:`toolkit` will usually provide adapters between toolkit-specific
widgets and :class:`~dip.settings.ISettings` so that the geometry and
configuration of a user interface can be remembered by passing the list of
views to :meth:`~dip.settings.ISettingsManager.restore` before the
application's event loop is entered and then to
:meth:`~dip.settings.ISettingsManager.save` after the event loop exits.

The section :ref:`ui-tutorial` contains a section describing the use of this
module in more detail.


:mod:`dip.publish`
..................

The :mod:`dip.publish` module provides a set of classes that implement a simple
publish/subscribe framework.  While the :mod:`dip.model` module provides a
low-level notification mechanism for changes to model attributes, the
:mod:`dip.publish` module allows a :term:`tool` to register an interest in
specific types of event rather than a specific model instance.

Published events are managed by a publication manager which is an
implementation of the :class:`~dip.publish.IPublicationManager` interface.  A
:term:`shell` contains such an implementation and tools added to the shell are
automatically registered with the publication manager if they implement either
of the :class:`~dip.publish.IPublisher` or :class:`~dip.publish.ISubscriber`
interfaces.

A published event is simply a string (typically using the dotted notation) and
a model that the event is related to.  A subscriber specifies the type of model
it is interested in and observes the
:attr:`~dip.publish.ISubscriber.subscription` attribute to receive
notifications of events related to that type of model.

dip defines some well known events.  For example the 
:class:`~dip.shell.tools.model_manager.ModelManagerTool` tool publishes the
``dip.events.opened`` and ``dip.events.closed`` events whenever it opens and
closes a :term:`managed model`.


: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.
