.. _shell-tutorial:

Getting Started with :mod:`dip.shell`
=====================================

A :term:`shell` is an abstraction of an application and its user interface.  In
simple terms it is a :term:`view`.  An instance of a shell implements the
:class:`~dip.ui.IView` interface and can be adapted to the
:class:`~dip.shell.IShell` interface.

A shell visualises :term:`actions<action>` and provides :term:`areas<area>`
where :term:`tools<tool>` can display their own views.  A shell provides a
*main* view that is the focus of the user's normal interaction with the
application.

Different shells may visualise actions and views in different ways.  For
example a shell specifically designed for mobile devices may only allow one
view to be visible at a time and to provide device specific methods for the
user to switch between views.

A tool is a sub-set of an application's functionality that implements a set of
logically related operations.  Breaking an application down to a number of
tools encourages the use of components and the ability to re-use them in other
applications.  A tool is independent of any particular shell implementation.

dip includes some standard tools that implement functionality common to many
applications.  The most important of these is the
:class:`~dip.shell.tools.model_manager.ModelManagerTool` which handles the
lifecycle of :term:`application models<managed model>`.  It provides actions
that implement the standard ``New``, ``Open``, ``Save``, ``Save As`` and
``Close`` operations.  When interacting with the user these will automatically
take account of the managed models and tools that are available, and the
storage where managed models can be read from or written to.

In the following sections we describe the different aspects of using a shell.


Creating and Displaying a Shell
-------------------------------

Like other views, shells are normally defined declaratively, i.e. a shell
factory is created which, when called, will then create a toolkit specific
instance of the shell.

The following is a complete example (which you can be downloaded from
:download:`here</examples/shell/trivial_shell.py>`) which uses the
:class:`~dip.shell.shells.main_window.MainWindowShell` included with dip.

.. literalinclude:: /examples/shell/trivial_shell.py

The code should be self-explanatory.


Adding a Tool to a Shell
------------------------

There are several ways to add a tool to a shell.  This example (which you can
download from :download:`here</examples/shell/quit_shell.py>`) is identical to
the previous example except that it passes the
:class:`~dip.shell.tools.quit.QuitTool` class in a list as the
:attr:`~dip.shell.BaseShellFactory.tool_factories` attribute.

.. literalinclude:: /examples/shell/quit_shell.py

Every instance of the shell will automatically have an instance of
:class:`~dip.shell.tools.quit.QuitTool` as a tool.  Alternative we could have
added it later, as follows::

    view_factory.tool_factories.append(QuitTool)

Finally we could have added an instance of the tool to a specific instance of
the shell, as follows::

    IShell(view).tools.append(QuitTool())


More on the Quit Tool
---------------------

The main purpose of the Quit Tool is to provide an action that, when triggered,
quits the application.  However the application may not be in a state where it
can quit without losing data.  Therefore the Quit Tool looks at all the other
tools in the shell to see which implement the :class:`~dip.shell.IQuitVeto`
interface.  It then looks at the interface's
:attr:`~dip.shell.IQuitVeto.reasons` attribute for a list of the reasons why
the attempt to quit should be vetoed.  Any reasons are displayed to the user
and the quit is abandoned.  So long as no tool provides a reason the quit goes
ahead.


Writing a Tool
--------------

We now work through the implementation of a new tool.  The code that implements
the tool is shown below.  (The complete example can be downloaded from
:download:`here</examples/shell/zoom_shell.py>`.)   The tool is added to the
shell in the same way as the Quit Tool.

.. literalinclude:: /examples/shell/zoom_shell.py
    :start-after: from dip.ui import
    :end-before: # Every application

The tool allows its view to be zoomed in and out of.  This does this simply by
changing the font size of the view.  It provides three actions: one to zoom in;
one to zoom out; and one to toggle between the normal size and the previous
non-normal size.

We will now walk through this code.  Note that we use a mixture of toolkit
independent and toolkit specific API calls and that, as a consequence, the tool
requires a PyQt4 based toolkit.  We have done this because the toolkit
independent API has no support for fonts.  Should font support be added in the
future it would be easy to rewrite this tool to be toolkit independent and be
reusable in any dip application.

The following code is the start of the class definition.  A tool is a model
that implements the :class:`~dip.shell.ITool` interface.

.. literalinclude:: /examples/shell/zoom_shell.py
    :start-after: from dip.ui import
    :end-before: # The increment

The following code defines the value used as a multiplier used when zooming in
and used as a divisor when zooming out.  As it is an attribute its value can
easily be overridden when the tool is created.

.. literalinclude:: /examples/shell/zoom_shell.py
    :start-after: # The increment
    :end-before: # This action shows if

The following code defines the checkable action used to toggle the view between
its normal size and its previous non-normal size.  By default the action will
be checked.  Because we haven't specified either the action's text or
identifier they will both be derived from the name of the attribute.

.. literalinclude:: /examples/shell/zoom_shell.py
    :start-after: and previous size
    :end-before: # The collection of actions

The following code defines the action collection that contains the actions
provided by the tool.

.. literalinclude:: /examples/shell/zoom_shell.py
    :start-after: in a sub-menu
    :end-before: # The Zoom In

The first three arguments are the identifiers of the actions in the collection.
They will be visualised in this order.

The :attr:`~dip.ui.ActionCollection.text` attribute is provided as a hint to
the toolkit that we would like the collection to be visualised as part of a
hierachy (typically a sub-menu).  The toolkit is free to ignore the hint.

The :attr:`~dip.ui.ActionCollection.within` attribute specifies where the
collection will be visualised.  Its value will either be the identifier of
another action collection or (as in this case) the idenfifier of a *well known*
collection.

The following code defines the remaining actions.

.. literalinclude:: /examples/shell/zoom_shell.py
    :start-after: within=
    :end-before: # The current font

The following code defines various attributes used internally by the tool.

.. literalinclude:: /examples/shell/zoom_shell.py
    :start-after: zoom_out =
    :end-before: @normal_size.triggered

The following code defines the method that is called when the ``normal_size``
action is triggered.  It simply sets the size to the default size or the
previous non-default size depending on the action's checked state.

.. literalinclude:: /examples/shell/zoom_shell.py
    :start-after: _uses_pixels
    :end-before: @ITool.views.default

Alternatively the :func:`~dip.model.observe` decorator could have been used as
follows::

    @observe('normal_size.checked')
    def __normal_size_checked_changed(self, change):
        """ Invoked when the Normal Size action's checked attribute changes. """

The following code defines the method that is called when the
:attr:`~dip.shell.ITool.views` attribute is first referenced.  It returns a
single element list containing the default view.  Note that we are returning a
toolkit specific widget that has not been adapted to the :class:`~dip.ui.IView`
interface.  Because dip knows what interface is needed, and that the PyQt4
toolkit provides an appropriate adapter, then the widget will be adapted
automatically.

.. literalinclude:: /examples/shell/zoom_shell.py
    :start-after: else self._previous_size)
    :end-before: @zoom_in.triggered

The following code defines the methods that are called when the zoom in and
zoom out actions are triggered.  They simply update the current size.

.. literalinclude:: /examples/shell/zoom_shell.py
    :start-after: QLabel(
    :end-before: @_current_size.default

The remaining methods implement the actual setting of the size of the font and
should be self-explanatory.


More on Actions and Action Collections
--------------------------------------

When a tool is added to a shell the shell will first introspect the tool for
any action collections and for any actions.  Action collections and actions
found in this way are added to the shell automatically.  However the order in
which they are added is random - except that all collections will be added
before any actions.

If a tool provides a non-empty list as its :attr:`~dip.shell.ITool.actions`
attribute then the introspection of the tool is not done and only those action
collections and actions in the list are added.  They are added in the order in
which they appear in the list.

If, in the previous example, we didn't prefer to place the actions in their own
sub-menu then we could have simply omitted the definition of the action
collection.  However we would then have to specify the
:attr:`~dip.ui.Action.within` attribute of each of the actions.  We would also
have had to specify the value of the :attr:`~dip.shell.ITool.actions` attribute
in order to provide an explicit order for the actions.  The corresponding code
would be as follows::

    normal_size = Action(checked=True, within='dip.ui.collections.tools')

    zoom_in = Action(within='dip.ui.collections.tools')

    zoom_out = Action(within='dip.ui.collections.tools')

    @ITool.actions.default
    def actions(self):
        """ Invoked to return the explicitly ordered actions. """

        return [self.zoom_in, self.normal_size, self.zoom_out]


Using the Model Manager
-----------------------

A very common application pattern is to open a file, read the model from it,
use a tool to modify the model, and save the model by writing it back to the
file.  A user can usually also create a new model and save a model under a
different name.  These operations are usually invoked by the user using the
traditional ``New``, ``Open``, ``Save``, ``Save As`` and ``Close`` actions.

dip provides the :mod:`~dip.shell.tools.model_manager.ModelManagerTool` which
implements those traditional actions on behalf of tools and the models they can
handle.  The model manager interacts with the user, using appropriate dialogs
and wizards, guiding them through the selection of models, storage locations
and tools depending on what is available.

In order for a tool to use the model manager it must implement, or be able to
be adapted to, the :class:`~dip.shell.IManagedModelTool` interface.  Likewise
any models must implement, or be able to be adapted to, the
:class:`~dip.shell.IManagedModel` interface.  The final requirement is that
models are read from and written to storage using the :mod:`dip.io` module.

.. note::

    The :mod:`dip.io` module is covered in detail in :ref:`io-tutorial`.  For
    now we will just cover the essentials needed for reading and writing our
    example models.

We will now work through the implementation of a simple, but complete, Python
source code editor based on the :class:`~PyQt4.Qsci.QScintilla` editor widget
shown below.  (The example can also be downloaded from
:download:`here</examples/shell/python_editor.py>`.)

.. literalinclude:: /examples/shell/python_editor.py

First of all we will look at the definition of our model.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: from dip.ui
    :end-before: @adapt(PythonCode, to=IManagedModel

We have chosen to use the :class:`~PyQt4.Qsci.QScintilla` widget to hold the
text of the Python code being edited - in effect we are combining the model and
the view.  This is convenient for this particular example but is a bad idea if
we ever want to re-use the model in a different context.  Another point to make
about the model is that it doesn't make any use of dip.

We have said that, to be managed by the model manager, a model must implement
the :class:`~dip.shell.IManagedModel` interface.  Therefore we need to provide
an adapter between our model and that interface.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: self.editor = editor
    :end-before: @adapt(PythonCode, to=[IFilterHints

A model that uses the :mod:`dip.io` module has one or more
:term:`formats<format>`, i.e. ways in which the model can be encoded when being
written to storage and decoded when being read from storage.  A :term:`codec`
must be available that supports a particular format.  One of these formats is
the model's native format which is used to select an appropriate codec when
opening and saving the model.  Other formats (and therefore codecs) may be used
when importing or exporting the model.

The rest of the adapter implements support for the interface's
:attr:`~dip.shell.IDirty.dirty` attribute.  The ``__init__()`` method ensures
that when the user edits the text then any observers of the 
:attr:`~dip.shell.IDirty.dirty` attribute are invoked.  The remaining methods
implement a getter and setter for the attribute that delegate to the
underlying editor widget.

We now need to make sure that the :mod:`dip.io` module is able to read and
write our model from and to storage.  The format will identify a codec.  (We
will come on to defining new codecs later on.)  A codec will define interfaces
for a decoder and an encoder and a model must implement those interfaces (or be
able to be adapted to them) in order to be read and written.  As we will see
later we have chosen to use the :class:`~dip.io.codecs.unicode.UnicodeCodec`
codec provided by dip that uses a Unicode byte stream (specifically a UTF-8
byte stream).  To use this codec a model must implement, or be able to be
adapted to, the the :class:`~dip.io.codecs.unicode.IUnicodeDecoder` and
:class:`~dip.io.codecs.unicode.IUnicodeEncoder` interfaces.

dip doesn't make any assumptions about where a model may be stored, and an
application should not care.  In fact, new types of storage can be added to the
:mod:`dip.io` module without requiring any change to an application.  For
example if support for some cloud storage is added then an application will
automatically be able to open and save models in the cloud.  The model manager
will automatically take the new type of storage into account when interacting
with the user.

The most common form of storage is, of course, the local filesystem.  When the
user uses a file dialog to specify the name of a file to open they expect that
the dialog has applied a filter to the names of the files displayed so that
only those that are valid at the time can be selected.  Therefore we want our
example to be able to specify appropriate filename filters.  But the concept is
specific to a particular form of storage, and our application isn't supposed to
know about particular forms of storage.  We manage this contradiction by the
use of *hints*.  A hint is an interface that an object may optionally implement
or provide an adapter for.  Some other part of the system (the filesystem
storage support in this case) can choose to test to see if the interface has
been implemented for the object and, if so, extract the hinted at information.
The :mod:`dip.io` module provides the :class:`~dip.io.IFilterHints` interface
for passing a filename filter hint.

So, getting back to our example, we need to provide adapters for the
:class:`~dip.io.IFilterHints`, :class:`~dip.io.codecs.unicode.IUnicodeDecoder`
and :class:`~dip.io.codecs.unicode.IUnicodeEncoder` interfaces.  We choose to
implement these as a single adapter, shown below.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: setModified(value)
    :end-before: @implements(IManagedModelTool)

That completes the work we have to do for our model.  We will now work through
the implementation of the tool that allow us to edit our model.  We will take
this a step at a time.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: return model.editor
    :end-before: # The tool's identifier

As we said earlier a tool that wants to make use of a model manager must
implement the :class:`~dip.shell.IManagedModelTool` interface.  In addition our
tool is sub-classed from :class:`~dip.shell.BaseManagedModelTool`.  This is an
optional class that implements a lot of the housekeeping needed by managed
model tools.

The following line sets the tool's identifier.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: # The tool's identifier
    :end-before: # The action that toggles

The identifier is optional but we make use of it later when handling the
application's command line arguments.  It doesn't matter what the identifier is
so long as it uniquely identifies a tool.

Our tool implements just one action of its own as shown below.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: # The action that toggles
    :end-before: # To keep the application simple

This is a checkable action that toggles the display of line numbers by the
editor.  Obviously in a real application the tool would implement many more
actions.

A managed model tool is normally expected to handle more than one instance of a
model.  In other words when the user creates a new instance of a model it is
added to the exsiting tool - a new tool is not created for it.  This is not
always possible or desireable.  For example an existing tool may be being
reused and doesn't support multiple model instances, or, as in this case, we
want to keep the behaviour of our application as simple as a possible.  This
behaviour is determined by the
:attr:`~dip.shell.IManagedModelTool.model_policy` attribute which we set as
shown below.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: # time.
    :end-before: def create_views

The default value is ``many``.  By setting it to ``one`` the model manager will
ensure that the tool always has exactly one model to handle.  It will
automatically create a new model when the tool is created.  If the user invokes
either the ``New`` or ``Open`` actions then the existing model is closed.  If
the existing model has been modified then the user will be asked if they want
to save it or to discard the changes.

The :attr:`~dip.shell.IManagedModelTool.model_policy` attribute may also be set
to ``zero_or_one``.  This is similar to ``one`` except that the model manager
does not automatically create a new model when the tool is created.

We now come one to the part of the tool that creates the views that visualise a
model.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: model_policy =
    :end-before: def handles

As we have chosen to use the editor widget to store the text of the code then
we simply return a one element list containing the editor.  Before returning it
we configure its use of line numbers according to the current state of the
action.

The :meth:`~dip.shell.IManagedModelTool.handles` method, shown below, is
automatically invoked by the model manager to determine if the tool can handle
a particular model.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: return [view]
    :end-before: @line_nrs.triggered

The following method is invoked when the action is triggered.  It simply
updates the editor widget's use of line numbers according to the new state of
the action.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: return isinstance
    :end-before: def _configure_line_nrs

Finally we have the internal method, shown below, that actually does the work
of configuring the use of line numbers by the editor widget.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: current_view
    :end-before: # Every application

We said earlier that we would be using dip's
:class:`~dip.io.codecs.unicode.UnicodeCodec` to decode and encode our model as
a UTF-8 byte stream.  The line below creates an instance of the codec and adds
it to our application.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: # Add our codec
    :end-before: IoManager.storage_factories.append

The codec's format matches the native format of our model.

We also need to tell our application that we want support for storing our model
in the local filesystem.  This is done by adding the appropriate storage
factory as shown below.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: IoManager.codecs.append
    :end-before: # Define the shell

Now we move on to the declaration of the shell, shown below.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: # Define the shell
    :end-before: # Create the shell

The first thing to note is the setting of the
:attr:`~dip.shell.IShell.main_area_policy` attribute to ``single``.  This
determines how the main area of the shell is visualised and we are specifying
that the main area will only ever contain one view.  We can say this because
we only have one tool and that tool's
:attr:`~dip.shell.IManagedModelTool.model_policy` attribute is set to ``one``.
If we didn't set the main area policy then the default PyQt4 toolkit would
place the view under a tab bar, which we don't want.

The next thing to look at is the list of tool factories.

Most platforms provide some mechanism for indicating to the user that an
application has unsaved data.  In dip this is enabled by specifying the ``[*]``
marker in the shell's :attr:`~dip.ui.IView.window_title` attribute.
:class:`~dip.shell.tools.dirty.DirtyTool` is a tool that automatically monitors
other tools (specifically those that implement the :class:`~dip.shell.IDirty`
interface) and updates the application's overall state appropriately.

Tool factories are just Python callables that are invoked without arguments.
As we want to pass arguments to the
:class:`~dip.shell.tools.model_manager.ModelManagerTool` we are creating then
we wrap it in a lambda function.

The last part of the shell's declaration is the setting of the
:attr:`~dip.shell.IShell.window_title_template` attribute.  The shell's
:attr:`~dip.ui.IView.window_title` attribute is updated from this template
whenever the current view changes.  The ``[*]`` marker is passed to the
window title unchanged.  The ``[view]`` marker is replaced by the name of the
current view.  By default the name of a view created by a managed model tool is
the name of the model being visualised by the view, which (again by default) is
the location where the model is stored.  Therefore, in our example and if the
Python code is stored in the filesystem, the ``[view]`` marker will be
replaced by the name of the Python file.

.. note::

    In many places dip will display the *name* of an object (e.g. the name of a
    tool or a model or a storage location).  Normally dip will see if the
    object implements or can be adapted to the :class:`~dip.ui.IDisplay`
    interface and, if so, uses its :attr:`~dip.ui.IDisplay.name` attribute.  If
    this isn't available then it will usually use the object's string
    representation.  If you find that you are seeing the standard Python string
    representation of an object (e.g. ``<foo.Foo object at 0xdeadbeef>``) then
    try providing an adapter between the type of your object and the
    :class:`~dip.ui.IDisplay` interface.

The final part of this example we will look at is the handling of the command
line arguments shown below.

.. literalinclude:: /examples/shell/python_editor.py
    :start-after: # If a command line
    :end-before: # Make the shell visible

If a command line argument is given then it is assumed to be a storage location
where some Python code can be read from.  The location is passed to the shell
along with the native format of the Python code and the identifier of the tool
that we wish to use.  The result is that the code is read and displayed in an
editor.


More on the Model Manager
-------------------------

In the previous example we took many shortcuts because we knew that we would
only have one tool and one type of model.  While convenient, those assumptions
make it very difficult to reuse the tool and model in a different context.  In
this section we will extend the example to turn it into the basis for an IDE.
(Actually, all we will do is to add a new tool and type of model but it will
invalidate all the assumptions we made previously.)

In the updated example we will add the concept of a project.  A project will
have some metadata (e.g. a name and description) and a list of storage
locations containing the Python code that makes up the project.  A project will
be stored as XML.  We will provide a tool to allow a project to be created and
updated.

We will now work through the significant changes.  The complete example can be
downloaded from :download:`here</examples/shell/python_ide.py>`.

As we will now have more than one type of model the model manager will need to
ask the user to choose between them, for example when the user invokes the
``New`` action.  To do this the model manager will display a list of the model
factories that it knows about.  If a model factory implements the
:class:`~dip.ui.IDisplay` interface then its :attr:`~dip.ui.IDisplay.name`
attribute is used, otherwise the factory's string representation is used which,
in our case, would be ``class '__main__.PythonCode'>``.  So, rather than just
use the ``PythconCode`` class as our model factory, we create the following
factory class.

.. literalinclude:: /examples/shell/python_ide.py
    :start-after: self.editor = editor
    :end-before: @adapt(PythonCode, to=IManagedModel)

An instance of this class will, when called without arguments, create an
instance of the model, i.e. an instance of ``PythonCode``.  The factory
instance also implements the :class:`~dip.ui.IDisplay` interface and so the
model manager will use the string ``Python code`` whenever it displays the
factory to the user.

We also want an instance of ``PythonCodeEditorTool`` to be displayed in a user
friendly way by the model manager so we implement the :class:`~dip.ui.IDisplay`
interface for it as well.

.. literalinclude:: /examples/shell/python_ide.py
    :lines: 107

The next change we make is to the declaration of the action that toggles the
line numbers.  Because we may now have more than one type of view being
displayed we only want the action to be enabled when the current view is an
editor created by our ``PythonCodeEditorTool``.  Therefore we want the action
to be disabled initially.

.. literalinclude:: /examples/shell/python_ide.py
    :start-after: # The action that toggles
    :end-before: # The Python code editor name

Next we provide the user friendly name required by the
:class:`~dip.ui.IDisplay` interface.

.. literalinclude:: /examples/shell/python_ide.py
    :start-after: # The Python code editor name
    :end-before: def create_views(

We have also removed the setting of the
:attr:`~dip.shell.IManagedModelTool.model_policy` attribute so that it reverts
to its default value of ``many``.  This means that the tool will allow the user
to edit several Python source code files at a time.

Of course now that the line number action is initially disable we need to
enable it when a view created by the tool becomes current, and disable it when
the view is no longer current.  As shown below this is done by observing the
:attr:`~dip.shell.ITool.current_view` attribute of the
:class:`~dip.shell.ITool` interface.

.. literalinclude:: /examples/shell/python_ide.py
    :start-after: self._configure_line_nrs(self.current_view)
    :end-before: def _configure_line_nrs(

That completes the changes needed to our existing Python source code model and
editor tool.  We will now go through the new project model and editing tool
which, naturally, follows the same pattern.

First we define our ``Project`` model as follows.

.. literalinclude:: /examples/shell/python_ide.py
    :pyobject: Project

The next step is to create a factory class for our new model, just as we did
before, as follows.

.. literalinclude:: /examples/shell/python_ide.py
    :start-after: contents = List(Str())
    :end-before: @adapt(Project, to=IManagedModel)

Then we provide an adapter for our new model that adapts it to the
:class:`~dip.shell.IManagedModel` interface as follows.

.. literalinclude:: /examples/shell/python_ide.py
    :start-after: return Project()
    :end-before: @adapt(Project, to=[IFilterHints

The final step in adding our new model is to ensure that it can be read from
and written to storage.  As well as dip having built in support for models
stored as Unicode byte streams it also has built in support for models stored
as XML.  This support is implemented by the
:class:`~dip.io.codecs.xml.XmlCodec` codec and the
:class:`~dip.io.codecs.xml.IXmlDecoder` and
:class:`~dip.io.codecs.xml.IXmlEncoder` interfaces as follows.

.. literalinclude:: /examples/shell/python_ide.py
    :start-after: native_format = 'myorganization.formats.project'
    :end-before: @implements(IDisplay)

We now move on to the tool that will provide a view for editing our new
``Project`` model.  The need for tools that edit instances of
:class:`~dip.model.Model` is, of course, a very common requirement and so dip
has specific support in the form of the :class:`~dip.shell.tools.form.FormTool`
class.  Its use is shown below.

.. literalinclude:: /examples/shell/python_ide.py
    :start-after: filter = "Project files
    :end-before: # Every application

First we define the tool's identifier by declaring its
:attr:`~dip.shell.ITool.id` attribute.  We then specify the type of model that
the tool handles using the :attr:`~dip.shell.tools.form.FormTool.model_type`
attribute.  Next is the user friendly name of the tool using the
:attr:`~dip.ui.IDisplay.name` attribute of the :class:`~dip.ui.IDisplay`
interface.  Finally we provide a decorated method that provides the default
value of the :attr:`~dip.shell.tools.form.FormTool.view_factory` attribute.  It
is here that we determine exactly what parts of the model can be edited (all of
it in this case) and how the individual attribute editors are configured.

We next have to create the codec for our XML project and tell our application
about it.

.. literalinclude:: /examples/shell/python_ide.py
    :start-after: IoManager.codecs.append
    :end-before: IoManager.storage_factories.append

The next set of changes needed are to the definition of the shell itself.  The
updated definition is shown below.

.. literalinclude:: /examples/shell/python_ide.py
    :start-after: # Define the shell
    :end-before: # Create the shell

The first thing to note is that we have removed the setting of the
:attr:`~dip.shell.IShell.main_area_policy` attribute so that it reverts to its
default value of ``many``.  This means that, with the default PyQt4 toolkit,
each view will be placed in its own tab irrespective of how many views are
displayed.

The next change is to the list of model factories set to the model manager's
:attr:`~dip.shell.tools.model_manager.ModelManagerTool.model_factories`
attribute.  Similarly we have added the new ``ProjectEditorTool`` to the
shell's :attr:`~dip.shell.IShell.tool_factories` attribute.

The final change to the definition of the shell is the replacement of the use
of the :attr:`~dip.shell.IShell.window_title_template` attribute with the use
of the :attr:`~dip.ui.IView.window_title` attribute.  Because the name of each
view is displayed elsewhere there is now no need to show it in the shell's
window title.

The very last change is the handling of any command line argument, shown below.

.. literalinclude:: /examples/shell/python_ide.py
    :start-after: if len(sys.argv
    :end-before: # Make the shell

In the context of an IDE it is more sensible to assume that the command line
argument refers to a project rather than an individual Python source code file.
