===================
Pyface Applications
===================

.. py:currentmodule:: pyface.application

Pyface provides a collection of classes suitable for representing an
application.  Although the classes are principally designed to work with
GUI-based applications, the base class :py:class:`Application` class does not
and so can be used as the basis for command-line or server applications with
a shared codebase with a GUI application.

Additionally, the application classes are designed to be forward-compatible
with Envisage application classes so applications developed using this
framework can easily be refactored to take advantage of the Envisage's plug-in
extensibility if that should be required.

Application Interface
=====================

The core of the :py:class:`Application` class' API are three methods:
:py:meth:`Application.run`, :py:meth:`Application.start`, and
:py:meth:`Application.stop`.  The :py:meth:`Application.run` method is the
entry-point of the application, and performs the following steps:

1. calls the :py:meth:`Application.start` method to initialize the
   application's state.

2. if the start is successful, calls an internal :py:meth:`Application._run`
   method which does the actual work (such as running the GUI event loop).

3. when the :py:meth:`Application._run` method returns, calls
   :py:meth:`Application.stop` to release any resources used by the application
   and de-initialize as needed.

Subclasses of the base :py:class:`Application` class will likely override all
three of these methods, but :py:class:`pyface.gui_application.GUIApplication`
subclasses may be able to avoid having to touch them, depending on what
addition resources they need to access.

In addition to these three methods, there is an :py:meth:`Application.exit`
method that can be called by code that wants to halt the application at that
point.  For the base class this simply raises an :py:class:``ApplicationExit``
exception, but for event loop based classes it may instead call appropriate
toolkit code to close open windows and stop the application event loop.

During this life-cycle, the application will emit various Traits events
indicating the state that it is currently in.  The sequence of events is
normally :py:attr:`Application.starting`, :py:attr:`Application.started`
:py:attr:`Application.application_initialized`,
:py:attr:`Application.stopping`, and :py:attr:`Application.stopped`

In addition to the core methods and events, the :py:class:`Application` class
has some traits for metadata about the application, primarily the
human-readable application name, a description of the application, and a
globally unique application id.  The application also has traits holding
platform-dependent "home" and "user" directory paths, which by default are
provided by :py:mod:`traits.etsconfig` which give reasonable platform-dependent
results.

Application Example
-------------------

A simple command-line application might look something like this::

    import argparse
    from pyface.application import Application
    from traits.api import Str

    class HelloApplication(Application):
        """ Simple application subclass that greets a location. """

        #: The location being greeted.
        location = Str("world")

        def _run(self):
            super()._run()
            print("Hello "+self.location)

    def main():
        app = HelloApplication()

        parser = argparse.ArgumentParser(description=app.description)
        parser.add_argument('location', nargs='?', default=app.location,
                            help="the location to greet")
        parser.parse_args(namespace=app)

        app.run()

    if __name__ == '__main__':
        main()


GUIApplication Interface
========================

.. py:currentmodule:: pyface.gui_application

The :py:class:`GUIApplication` subclass is the base class to use for
Pyface-based GUI applications. This class invokes the Pyface toolkit's event
loop in the :py:meth:`GUIApplication._run` method, and stops it in the
:py:meth:`GUIApplication._exit` method.

The class has code that tracks application windows through their lifecycle.
The application has a list :py:attr:`GUIApplication.windows` of all known
windows.  Windows can be added to the list with the
:py:meth:`GUIApplication.add_window` method which also handles opening the
window.  Windows are automatically removed from the list when they are closed.
In addition the class has a :py:meth:`GUIApplication.create_window` method
which by default calls the :py:attr:`GUIApplication.window_factory` attribute
and handles setting titles, icons and sizing of the created windows.  Windows
created by :py:meth:`GUIApplication.create_window` still need to be added to
the application's list of windows via :py:meth:`GUIApplication.add_window`.

The :py:meth:`GUIApplication.start` method of the class must ensure that there
is at least one open window by the point that the event loop starts,
particularly with the WxPython backend.  The default
:py:meth:`GUIApplication.start` method calls
:py:meth:`GUIApplication._create_windows` to perform the initial set-up of open
windows.  The default behaviour is to call the
:py:meth:`GUIApplication.create_windows` method and then add that window.

In most cases to use the base :py:class:`GUIApplication` class, you will want
to:

* create a subclass of :py:class:`~pyface.application_window.ApplicationWindow`
  that at least overrides the
  :py:meth:`~pyface.application_window.ApplicationWindow._create_contents`
  method to create the desired user interface.  Ideally this window should be
  able to work without needing an application, as this helps with reusability.
* create a window factory function that taskes the application and arbitrary
  other keyword arguments and creates an instance of the 
  :py:class:`~pyface.application_window.ApplicationWindow` subclass along with
  any additional application-dependent state that is required. This can
  include application-dependent menus, toolbars, etc. as well as other state
  injected by from application.

While the :py:class:`GUIApplication` class can be used without subclassing, for
complex applications it is likely that the a subclass will need to be used,
and will likely override the :py:meth:`~GUIApplication.start`,
:py:meth:`~GUIApplication.stop` and :py:meth:`~GUIApplication.create_window`
methods to perform additional application-specific customization.

GUIApplication Example
----------------------

The following example shows how to build a simple but functional
:py:class:`GUIApplication` which provides an interactive Python shell.  The
application has two parts: a
:py:class:`pyface.application_window.ApplicationWindow` subclass which is
responsible for creating the Python shell widget, and a window factory which
is responsible for creating the windows and populating the menu bar.

The application window subclass looks like this::

    from pyface.api import ApplicationWindow, PythonShell
    from traits.api import Instance

    class PythonShellWindow(ApplicationWindow):
        """ An application window that displays a simple Python shell. """

        #: The title of the window.
        title = "Python Shell"

        #: The Python shell widget to use.
        shell = Instance('pyface.i_python_shell.IPythonShell')

        def _create_contents(self, parent):
            """ Create the shell widget. """
            self.shell = PythonShell(parent)
            return self.shell.control

Note that we don't (and shouldn't) need the application to be available for
this class to work - it is a perfectly good stand-alone class that can
potentially be re-used in many different contexts.

While the GUI application window subclass looks like this::

    from pyface.api import GUIApplication
    from pyface.action.api import (
        AboutAction, CloseWindowAction, CreateWindowAction, ExitAction, Group,
        MenuBarManager, MenuManager
    )
    from python_shell_window import PythonShellWindow

    def create_python_shell_window(application, **kwargs):
        window = PythonShellWindow()
        window.menu_bar_manager = MenuBarManager(
            MenuManager(
                Group(CreateWindowAction(application=application)),
                Group(
                    CloseWindowAction(window=window),
                    ExitAction(application=application),
                ),
                name='&File',
            ),
            MenuManager(
                AboutAction(application=application),
                name='&Help',
            )
        )
        return window

    def main():
        app = GUIApplication(
            name="Python Shell",
            description="An example application that provides a Python shell.",
            icon='python_icon',
            logo='python_logo',
            window_factory=create_python_shell_window,
        )
        app.run()

    if __name__ == '__main__':
        main()

A more complete version of this can be found in the Pyface examples.

TasksApplication Interface
==========================

.. py:currentmodule:: pyface.tasks.tasks_application

The :py:class:`TasksApplication` class works in a similar way to the
:py:class:`~pyface.gui_application.GUIApplication` class, but instead of the
supplying a window factory, instead you provide one or more
:py:class:`TaskFactory` instances which provide information about the
different Tasks that are available for each window.

In addition the :py:class:`TasksApplication` has traits that hold extra
application-specific :py:class:`~pyface.actions.action.Action`s via the
:py:class:`~pyface.tasks.actions.schema_addition.SchemaAddition` mechanism,
and :py:class:`~pyface.tasks.dock_pane.DockPane` via factories for creating
the dock panes.

A complete :py:class:`TasksApplication` can be as simple as::

    from pyface.tasks.api import TaskFactory, TasksApplication
    from python_editor_task import PythonEditorTask

    def main():
        app = TasksApplication(
            id="example_python_editor_application",
            name="Python Editor",
            description=(
                "An example Tasks application that provides a Python editor."
            ),
            icon='python_icon',
            logo='python_logo',
            task_factories=[
                TaskFactory(
                    id='example.python_editor_task',
                    name="Python Editor",
                    factory=PythonEditorTask
                )
            ],
        )

        # invoke the mainloop
        app.run()

    if __name__ == '__main__':
        main()

A more complete version of this can be found in the Pyface examples.
