def test_extension_point_changed(self):
        """ extension point changed """

        registry = self.registry

        # Add an extension point.
        registry.add_extension_point(self._create_extension_point('my.ep'))

        # Declare a class that consumes the extension.
        class Foo(TestBase):
            x = ExtensionPoint(id='my.ep')

            def _x_changed(self):
                """ Static trait change handler. """

                self.x_changed_called = True

                return

        f = Foo()

        # Connect the extension points on the object so that it can listen
        # for changes.
        ExtensionPoint.connect_extension_point_traits(f)

        # Set the extensions.
        registry.set_extensions('my.ep', [42, 'a string', True])

        # Make sure that instances of the class pick up the extensions.
        self.assertEqual(3, len(f.x))
        self.assertEqual([42, 'a string', True],  f.x)

        # Make sure the trait change handler was called.
        self.assert_(f.x_changed_called)

        # Reset the change handler flag.
        f.x_changed_called = False

        # Disconnect the extension points on the object.
        ExtensionPoint.disconnect_extension_point_traits(f)

        # Set the extensions.
        registry.set_extensions('my.ep', [98, 99, 100])

        # Make sure the trait change handler was *not* called.
        self.assertEqual(False, f.x_changed_called)

        return
Example #2
0
        class PluginA(Plugin):
            id = "A"
            x = ExtensionPoint(List(Int), id="x")

            def _x_items_changed(self, event):
                self.added = event.added
                self.removed = event.removed
Example #3
0
class HelloWorld(Plugin):
    """ The 'Hello World' plugin.

    This plugin offers a single extension point 'greetings' which is a list of
    greetings, one of which is used to produce the '<greeting> World' message
    when the plugin is started.

    """

    # This tells us that the plugin offers the 'greetings' extension point,
    # and that plugins that want to contribute to it must each provide a list
    # of strings (Str).
    greetings = ExtensionPoint(List(Str),
                               id="greetings",
                               desc='Greetings for "Hello World"')

    # Plugin's have two important lifecyle methods, 'start' and 'stop'. These
    # methods are called automatically by Envisage when the application is
    # started and stopped respectively.
    def start(self):
        """ Start the plugin. """

        # Standard library imports.
        #
        # We put this import here just to emphasize that it is only used in
        # this specific plugin.
        import random

        print("{} World!".format(random.choice(self.greetings)))
Example #4
0
    def test_invalid_extension_point_type(self):
        """ invalid extension point type """

        # Extension points currently have to be 'List's of something! The
        # default is a list of anything.
        with self.assertRaises(TypeError):
            ExtensionPoint(Int, "my.ep")
Example #5
0
        class Foo(TestBase):
            x = ExtensionPoint(id="my.ep")

            def _x_changed(self):
                """ Static trait change handler. """

                self.x_changed_called = True
Example #6
0
class FlowTaskPlugin(Plugin):
    """
    An Envisage plugin wrapping FlowTask
    """

    # Extension point IDs.
    PREFERENCES       = 'envisage.preferences'
    PREFERENCES_PANES = 'envisage.ui.tasks.preferences_panes'
    TASKS             = 'envisage.ui.tasks.tasks'
    
    # these need to be declared in a Plugin instance; we pass them to
    # the task instance thru its factory, below.
    op_plugins = ExtensionPoint(List(IOperationPlugin), OP_PLUGIN_EXT)
    view_plugins = ExtensionPoint(List(IViewPlugin), VIEW_PLUGIN_EXT)    

    #### 'IPlugin' interface ##################################################

    # The plugin's unique identifier.
    id = 'edu.mit.synbio.cytoflow'

    # The plugin's name (suitable for displaying to the user).
    name = 'Cytoflow'

    ###########################################################################
    # Protected interface.
    ###########################################################################

    @contributes_to(PREFERENCES)
    def _get_preferences(self):
        filename = os.path.join(os.path.dirname(__file__), 'preferences.ini')
        return [ 'file://' + filename ]
    
    @contributes_to(PREFERENCES_PANES)
    def _get_preferences_panes(self):
        from .preferences import CytoflowPreferencesPane
        return [CytoflowPreferencesPane]

    @contributes_to(TASKS)
    def _get_tasks(self):
        return [TaskFactory(id = 'edu.mit.synbio.cytoflowgui.flow_task',
                            name = 'Cytometry analysis',
                            factory = lambda **x: FlowTask(application = self.application,
                                                           op_plugins = self.op_plugins,
                                                           view_plugins = self.view_plugins,
                                                           model = self.application.model,
                                                           filename = self.application.filename,
                                                           **x))]
Example #7
0
class FileTypePlugin(Plugin):
    """ Plugin for identifying file types
    """

    # The Ids of the extension points that this plugin offers.
    RECOGNIZER = 'omnivore.file_recognizer'

    # Extension point IDs.
    SERVICE_OFFERS = 'envisage.service_offers'

    #### 'IPlugin' interface ##################################################

    # The plugin's unique identifier.
    id = 'omnivore.file_type.plugin'

    # The plugin's name (suitable for displaying to the user).
    name = 'File Type'

    #### Extension points offered by this plugin ##############################

    recognizers = ExtensionPoint(List(Instance(IFileRecognizer)),
                                 id=RECOGNIZER,
                                 desc="""
    
    This extension point allows you to contribute file scanners that determine
    MIME types from a byte stream.
    
        """)

    #### Contributions to extension points made by this plugin ################

    service_offers = List(contributes_to=SERVICE_OFFERS)

    ###########################################################################
    # Protected interface.
    ###########################################################################

    def _service_offers_default(self):
        """ Trait initializer. """

        log.debug("in _service_offers_default")
        offer1 = ServiceOffer(
            protocol=
            'omnivore.file_type.i_file_recognizer.IFileRecognizerDriver',
            factory=self._create_file_recognizer_driver_service)

        return [offer1]

    def _create_file_recognizer_driver_service(self):
        """ Factory method for the File Recognizer Driver service. """

        log.debug("known recognizers: %s" % str(self.recognizers))

        # Lazy importing, even though this is a fundamental service and
        # therefore doesn't buy us anything.  But as an example it's useful.
        from .driver import FileRecognizerDriver
        return FileRecognizerDriver(recognizers=self.recognizers,
                                    application=self.application)
Example #8
0
    def test_extension_point_changed(self):
        """ extension point changed """

        registry = self.registry

        # Add an extension point.
        registry.add_extension_point(self._create_extension_point("my.ep"))

        # Declare a class that consumes the extension.
        class Foo(TestBase):
            x = ExtensionPoint(id="my.ep")

            def _x_changed(self):
                """ Static trait change handler. """

                self.x_changed_called = True

        f = Foo()

        # Connect the extension points on the object so that it can listen
        # for changes.
        ExtensionPoint.connect_extension_point_traits(f)

        # Set the extensions.
        registry.set_extensions("my.ep", [42, "a string", True])

        # Make sure that instances of the class pick up the extensions.
        self.assertEqual(3, len(f.x))
        self.assertEqual([42, "a string", True], f.x)

        # Make sure the trait change handler was called.
        self.assertTrue(f.x_changed_called)

        # Reset the change handler flag.
        f.x_changed_called = False

        # Disconnect the extension points on the object.
        ExtensionPoint.disconnect_extension_point_traits(f)

        # Set the extensions.
        registry.set_extensions("my.ep", [98, 99, 100])

        # Make sure the trait change handler was *not* called.
        self.assertEqual(False, f.x_changed_called)
    def setUp(self):
        # We do all of the testing via the application to make sure it offers
        # the same interface!
        registry = Application(extension_registry=ExtensionRegistry())
        extension_point = ExtensionPoint(id="my.ep", trait_type=List())
        registry.add_extension_point(extension_point)
        self.registry = registry

        # A place to record events that listeners receive.
        self.events = []
Example #10
0
class CorePyFibrePlugin(CorePlugin):
    """Inherits from the Envisage CorePlugin to include
    extra extension points for classes that fulfil the
    IMultiImageFactory interface"""

    id = 'pyfibre.core.pyfibre_plugin'

    #: Extension points for IMultiImageFactory
    multi_image_factories = ExtensionPoint(List(IMultiImageFactory),
                                           id=MULTI_IMAGE_FACTORIES)
class IPythonKernelPlugin(Plugin):
    """ An IPython kernel plugin. """

    # Extension point IDs.
    SERVICE_OFFERS = 'envisage.service_offers'

    #### 'IPlugin' interface ##################################################

    # The plugin unique identifier.
    id = 'envisage.plugins.ipython_kernel'

    # The plugin name (suitable for displaying to the user).
    name = 'IPython embedded kernel plugin'

    def stop(self):
        logger.info('Shutting down the embedded ipython kernel')
        self.kernel.shutdown()

    #### Extension points offered by this plugin ##############################

    kernel_namespace = ExtensionPoint(List,
                                      id=IPYTHON_NAMESPACE,
                                      desc="""

        Variables to add to the IPython kernel namespace.
        This is a list of tuples (name, value).

        """)

    #### Contributions to extension points made by this plugin ################

    kernel = Instance(IPYTHON_KERNEL_PROTOCOL)

    service_offers = List(contributes_to=SERVICE_OFFERS)

    def _service_offers_default(self):

        ipython_kernel_service_offer = ServiceOffer(
            protocol=IPYTHON_KERNEL_PROTOCOL,
            factory=self._create_kernel,
        )
        return [ipython_kernel_service_offer]

    def _create_kernel(self):
        return self.kernel

    #### Trait initializers ###################################################

    def _kernel_default(self):
        from .internal_ipkernel import InternalIPKernel
        kernel = InternalIPKernel()
        bind_extension_point(kernel, 'initial_namespace', IPYTHON_NAMESPACE,
                             self.application)
        return kernel
class EnvisagePythonShellTask(PythonShellTask):
    """ Subclass of PythonShellTask that gets its bindings and commands from
    an Envisage ExtensionPoint
    """
    id='envisage.plugins.tasks.python_shell_task'
    
    # ExtensionPointUser interface
    extension_registry = Property(Instance(IExtensionRegistry))
    
    # The list of bindings for the shell
    bindings = ExtensionPoint(id=BINDINGS)
    
    # The list of commands to run on shell startup
    commands = ExtensionPoint(id=COMMANDS)
    
    # property getter/setters
    
    def _get_extension_registry(self):
        if self.window is not None:
            return self.window.application
        return None
Example #13
0
class ByteViewersPlugin(FrameworkPlugin):
    """ Plugin containing all the viewers for byte data
    """

    # Extension point IDs.
    VIEWERS = 'omnivore8bit.viewers'

    #### 'IPlugin' interface ##################################################

    # The plugin's unique identifier.
    id = 'omnivore8bit.viewer.plugin'

    # The plugin's name (suitable for displaying to the user).
    name = 'Omnivore Byte Viewers'

    #### Contributions to extension points made by this plugin ################

    viewers = List(contributes_to=VIEWERS)

    segment_viewers = ExtensionPoint(
        List(Instance(SegmentViewer)),
        id="omnivore8bit.viewers",
        desc="A list of SegmentViewers that can display the data in a segment")

    def _viewers_default(self):
        # from omnivore8bit.viewers.bitmap import MemoryMapViewer
        from omnivore8bit.viewers.bitmap2 import BitmapViewer
        from omnivore8bit.viewers.char2 import CharViewer
        from omnivore8bit.viewers.cpu2 import DisassemblyViewer
        from omnivore8bit.viewers.hex2 import HexEditViewer
        from omnivore8bit.viewers.info import CommentsViewer, UndoViewer, SegmentListViewer
        from omnivore8bit.viewers.map2 import MapViewer
        from omnivore8bit.viewers.tile import TileViewer
        from omnivore8bit.viewers.jumpman2 import JumpmanViewer, TriggerPaintingViewer, LevelSummaryViewer
        from omnivore8bit.viewers.emulator import Atari800Viewer, CPU6502Viewer, ANTICViewer, POKEYViewer, GTIAViewer, PIAViewer

        return [
            BitmapViewer, CharViewer, DisassemblyViewer, HexEditViewer,
            CommentsViewer, UndoViewer, SegmentListViewer, MapViewer,
            TileViewer, JumpmanViewer, TriggerPaintingViewer,
            LevelSummaryViewer, Atari800Viewer, CPU6502Viewer, ANTICViewer,
            POKEYViewer, GTIAViewer, PIAViewer
        ]
Example #14
0
class PluginE(Plugin):
    """ Another plugin that expects to be started before contributing to
    extension points. """

    id = "E"
    x = ExtensionPoint(List, id="e.x")

    y = List(Int, contributes_to="d.x")

    started = Bool(False)

    def start(self):
        self.started = True

    def _y_default(self):
        if self.started:
            return [1, 2, 3]
        else:
            return []
class IPythonKernelPlugin(Plugin):
    """ An IPython kernel plugin. """

    #: The plugin unique identifier.
    id = 'envisage.plugins.ipython_kernel'

    #: The plugin name (suitable for displaying to the user).
    name = 'IPython embedded kernel plugin'

    #: Extension point for objects contributed to the IPython kernel namespace.
    kernel_namespace = ExtensionPoint(List,
                                      id=IPYTHON_NAMESPACE,
                                      desc="""

        Variables to add to the IPython kernel namespace.
        This is a list of tuples (name, value).

        """)

    #: Service offers contributed by this plugin.
    service_offers = List(contributes_to=SERVICE_OFFERS)

    #: Whether to initialize the kernel when the service is created.
    #: The default is ``False```, for backwards compatibility. It will change
    #: to ``True`` in a future version of Envisage. External users wanting
    #: to use the future behaviour now should pass ``init_ipkernel=True``
    #: when creating the plugin.
    init_ipkernel = Bool(False)

    def stop(self):
        """ Stop the plugin. """
        self._destroy_kernel()

    # Private traits and methods

    #: The InternalIPKernel instance provided by the service.
    _kernel = Instance(IPYTHON_KERNEL_PROTOCOL)

    def _create_kernel(self):
        from .internal_ipkernel import InternalIPKernel

        # This shouldn't happen with a normal lifecycle, but add a warning
        # just in case.
        if self._kernel is not None:
            warnings.warn(
                "A kernel already exists. "
                "No new kernel will be created.",
                RuntimeWarning,
            )
            return

        logger.debug("Creating the embedded IPython kernel")
        kernel = self._kernel = InternalIPKernel()
        bind_extension_point(kernel, 'initial_namespace', IPYTHON_NAMESPACE,
                             self.application)
        if self.init_ipkernel:
            kernel.init_ipkernel()
        else:
            warnings.warn(
                ("In the future, the IPython kernel will be initialized "
                 "automatically at creation time. To enable this "
                 "future behaviour now, create the plugin using "
                 "IPythonKernelPlugin(init_ipkernel=True)"),
                DeprecationWarning,
            )

        return kernel

    def _destroy_kernel(self):
        """
        Destroy any existing kernel.
        """
        if self._kernel is None:
            return

        logger.debug("Shutting down the embedded IPython kernel")
        self._kernel.shutdown()
        self._kernel = None

    def _service_offers_default(self):
        ipython_kernel_service_offer = ServiceOffer(
            protocol=IPYTHON_KERNEL_PROTOCOL,
            factory=self._create_kernel,
        )
        return [ipython_kernel_service_offer]
Example #16
0
class PythonShellView(View):
    """ A view containing an interactive Python shell. """

    #### 'IView' interface ####################################################

    # The part's globally unique identifier.
    id = 'envisage.plugins.python_shell_view'

    # The part's name (displayed to the user).
    name = 'Python'

    # The default position of the view relative to the item specified in the
    # 'relative_to' trait.
    position = 'bottom'

    #### 'PythonShellView' interface ##########################################

    # The interpreter's namespace.
    namespace = Property(DictStrAny)

    # The names bound in the interpreter's namespace.
    names = Property

    # Original value for 'sys.stdout':
    original_stdout = Any

    # Stdout text is posted to this event
    stdout_text = Event

    #### 'IExtensionPointUser' interface ######################################

    # The extension registry that the object's extension points are stored in.
    extension_registry = Property(Instance(IExtensionRegistry))

    #### Private interface ####################################################

    # Bindings.
    _bindings = ExtensionPoint(id='envisage.plugins.python_shell.bindings')

    # Commands.
    _commands = ExtensionPoint(id='envisage.plugins.python_shell.commands')

    ###########################################################################
    # 'IExtensionPointUser' interface.
    ###########################################################################

    def _get_extension_registry(self):
        """ Trait property getter. """

        return self.window.application

    ###########################################################################
    # 'View' interface.
    ###########################################################################

    def create_control(self, parent):
        """ Creates the toolkit-specific control that represents the view. """

        self.shell = shell = PythonShell(parent)
        shell.on_trait_change(self._on_key_pressed, 'key_pressed')
        shell.on_trait_change(self._on_command_executed, 'command_executed')

        # Write application standard out to this shell instead of to DOS window
        self.on_trait_change(
            self._on_write_stdout, 'stdout_text', dispatch='ui'
        )
        self.original_stdout = sys.stdout
        sys.stdout = PseudoFile(self._write_stdout)

        # Namespace contributions.
        for bindings in self._bindings:
            for name, value in bindings.items():
                self.bind(name, value)

        for command in self._commands:
            self.execute_command(command)

        # We take note of the starting set of names and types bound in the
        # interpreter's namespace so that we can show the user what they have
        # added or removed in the namespace view.
        self._namespace_types = set((name, type(value)) for name, value in \
                                        self.namespace.items())

        # Register the view as a service.
        app = self.window.application
        self._service_id = app.register_service(IPythonShell, self)

        return self.shell.control

    def destroy_control(self):
        """ Destroys the toolkit-specific control that represents the view.

        """

        super(PythonShellView, self).destroy_control()

        # Unregister the view as a service.
        self.window.application.unregister_service(self._service_id)

        # Remove the sys.stdout handlers.
        self.on_trait_change(
            self._on_write_stdout, 'stdout_text', remove=True
        )

        # Restore the original stdout.
        sys.stdout = self.original_stdout

        return

    ###########################################################################
    # 'PythonShellView' interface.
    ###########################################################################

    #### Properties ###########################################################

    def _get_namespace(self):
        """ Property getter. """

        return self.shell.interpreter().locals

    def _get_names(self):
        """ Property getter. """

        return list(self.shell.interpreter().locals.keys())

    #### Methods ##############################################################

    def bind(self, name, value):
        """ Binds a name to a value in the interpreter's namespace. """

        self.shell.bind(name, value)

        return

    def execute_command(self, command, hidden=True):
        """ Execute a command in the interpreter. """

        return self.shell.execute_command(command, hidden)

    def execute_file(self, path, hidden=True):
        """ Execute a command in the interpreter. """

        return self.shell.execute_file(path, hidden)

    def lookup(self, name):
        """ Returns the value bound to a name in the interpreter's namespace.

        """

        return self.shell.interpreter().locals[name]

    ###########################################################################
    # Private interface.
    ###########################################################################

    def _write_stdout(self, text):
        """ Handles text written to stdout. """

        self.stdout_text = text

        return

    #### Trait change handlers ################################################

    def _on_command_executed(self, shell):
        """ Dynamic trait change handler. """

        if self.control is not None:
            # Get the set of tuples of names and types in the current namespace.
            namespace_types = set((name, type(value)) for name, value in \
                                                        self.namespace.items())
            # Figure out the changes in the namespace, if any.
            added = namespace_types.difference(self._namespace_types)
            removed = self._namespace_types.difference(namespace_types)
            # Cache the new list, to use for comparison next time.
            self._namespace_types = namespace_types
            # Fire events if there are change.
            if len(added) > 0 or len(removed) > 0:
                self.trait_property_changed('namespace', {}, self.namespace)
                self.trait_property_changed('names', [], self.names)

        return

    def _on_key_pressed(self, event):
        """ Dynamic trait change handler. """

        if event.alt_down and event.key_code == 317:
            zoom = self.shell.control.GetZoom()
            if zoom != 20:
                self.shell.control.SetZoom(zoom+1)

        elif event.alt_down and event.key_code == 319:
            zoom = self.shell.control.GetZoom()
            if zoom != -10:
                self.shell.control.SetZoom(zoom-1)

        return

    def _on_write_stdout(self, text):
        """ Dynamic trait change handler. """

        self.shell.control.write(text)

        return
Example #17
0
class LoggerPlugin(Plugin):
    """Logger plugin."""

    id = ID
    name = "Logger plugin"

    #### Extension points for this plugin #####################################

    MAIL_FILES = "apptools.logger.plugin.mail_files"

    mail_files = ExtensionPoint(
        List(Callable),
        id=MAIL_FILES,
        desc="""

        This extension point allows you to contribute functions which will be
        called to add project files to the zip file that the user mails back
        with bug reports from the Quality Agent.

        The function will be passed a zipfile.ZipFile object.

        """,
    )

    #### Contributions to extension points made by this plugin ################

    PREFERENCES = "envisage.preferences"
    PREFERENCES_PAGES = "envisage.ui.workbench.preferences_pages"
    VIEWS = "envisage.ui.workbench.views"

    preferences = List(contributes_to=PREFERENCES)
    preferences_pages = List(contributes_to=PREFERENCES_PAGES)
    views = List(contributes_to=VIEWS)

    def _preferences_default(self):
        return ["pkgfile://%s/plugin/preferences.ini" % ID]

    def _preferences_pages_default(self):
        from apptools.logger.plugin.view.logger_preferences_page import (
            LoggerPreferencesPage, )

        return [LoggerPreferencesPage]

    def _views_default(self):
        return [self._logger_view_factory]

    #### Plugin interface #####################################################

    def start(self):
        """Starts the plugin."""
        preferences = LoggerPreferences()
        service = LoggerService(application=self.application,
                                preferences=preferences)
        formatter = logging.Formatter("%(levelname)s|%(asctime)s|%(message)s")
        handler = LogQueueHandler()
        handler.setLevel(preferences.level_)
        handler.setFormatter(formatter)
        root_logger = logging.getLogger()
        root_logger.addHandler(handler)
        root_logger.setLevel(preferences.level_)
        service.handler = handler
        self.application.register_service(ILOGGER, service)

    def stop(self):
        """Stops the plugin."""
        service = self.application.get_service(ILOGGER)
        service.save_preferences()

    #### LoggerPlugin private interface #######################################

    def _logger_view_factory(self, **traits):
        from apptools.logger.plugin.view.logger_view import LoggerView

        service = self.application.get_service(ILOGGER)
        view = LoggerView(service=service, **traits)
        # Record the created view on the service.
        service.plugin_view = view
        return view
Example #18
0
 class Foo(TestBase):
     x = ExtensionPoint(id="my.ep")
Example #19
0
class PythonShellPlugin(Plugin):
    """ A tasks plugin to display a simple Python shell to the user.
    """

    # Extension point IDs.
    BINDINGS = BINDINGS
    COMMANDS = COMMANDS
    TASKS = "envisage.ui.tasks.tasks"

    #### 'IPlugin' interface ##################################################

    # The plugin's unique identifier.
    id = "envisage.plugins.tasks.python_shell_plugin"

    # The plugin's name (suitable for displaying to the user).
    name = "Python Shell"

    #### Extension points exposed by this plugin ##############################

    bindings = ExtensionPoint(
        List(Dict),
        id=BINDINGS,
        desc="""
        This extension point allows you to contribute name/value pairs that
        will be bound when the interactive Python shell is started.

        e.g. Each item in the list is a dictionary of name/value pairs::

            {'x' : 10, 'y' : ['a', 'b', 'c']}
        """,
    )

    commands = ExtensionPoint(
        List(Str),
        id=COMMANDS,
        desc="""
        This extension point allows you to contribute commands that are
        executed when the interactive Python shell is started.

        e.g. Each item in the list is a string of arbitrary Python code::

          'import os, sys'
          'from traits.api import *'

        Yes, I know this is insecure but it follows the usual Python rule of
        'we are all consenting adults'.
        """,
    )

    #### Contributions to extension points made by this plugin ################

    # Bindings.
    contributed_bindings = List(contributes_to=BINDINGS)
    tasks = List(contributes_to=TASKS)

    ###########################################################################
    # Protected interface.
    ###########################################################################

    def start(self):
        logger.debug("started python shell plugin")

    def _contributed_bindings_default(self):
        """ By default, expose the Envisage application object to the namespace
        """
        return [{"application": self.application}]

    def _tasks_default(self):
        return [
            TaskFactory(
                id="envisage.plugins.tasks.python_shell_task",
                name="Python Shell",
                factory=EnvisagePythonShellTask,
            ),
        ]
Example #20
0
 class Foo(HasTraits):
     x = ExtensionPoint(List(Int), id="my.ep")
Example #21
0
class MOTDPlugin(Plugin):
    """ The 'Message of the Day' plugin.

    This plugin simply prints the 'Message of the Day' to stdout.

    """

    # The Ids of the extension points that this plugin offers.
    MESSAGES = "acme.motd.messages"

    # The Ids of the extension points that this plugin contributes to.
    SERVICE_OFFERS = "envisage.service_offers"

    #### 'IPlugin' interface ##################################################

    # The plugin's unique identifier.
    id = "acme.motd"

    # The plugin's name (suitable for displaying to the user).
    name = "MOTD"

    #### Extension points offered by this plugin ##############################

    # The messages extension point.
    #
    # Notice that we use the string name of the 'IMessage' interface rather
    # than actually importing it. This makes sure that the import only happens
    # when somebody actually gets the contributions to the extension point.
    messages = ExtensionPoint(
        List(Instance("acme.motd.api.IMessage")),
        id=MESSAGES,
        desc="""

        This extension point allows you to contribute messages to the 'Message
        Of The Day'.

        """,
    )

    #### Contributions to extension points made by this plugin ################

    service_offers = List(contributes_to=SERVICE_OFFERS)

    def _service_offers_default(self):
        """ Trait initializer. """

        # Register the protocol as a string containing the actual module path
        # and *not* a module path that goes via an 'api.py' file as this does
        # not match what Python thinks the module is! This allows the service
        # to be looked up by passing either the exact same string, or the
        # actual protocol object itself (the latter is the preferred way of
        # doing it!).
        motd_service_offer = ServiceOffer(
            protocol="acme.motd.i_motd.IMOTD",
            factory=self._create_motd_service,
        )

        return [motd_service_offer]

    ###########################################################################
    # Private interface.
    ###########################################################################

    def _create_motd_service(self):
        """ Factory method for the 'MOTD' service. """

        # Only do imports when you need to! This makes sure that the import
        # only happens when somebody needs an 'IMOTD' service.
        from acme.motd.motd import MOTD

        return MOTD(messages=self.messages)

    # This plugin does all of its work in this method which gets called when
    # the application has started all of its plugins.
    @on_trait_change("application:started")
    def _print_motd(self):
        """ Print the 'Message of the Day' to stdout! """

        # Note that we always offer the service via its name, but look it up
        # via the actual protocol.
        from acme.motd.api import IMOTD

        # Lookup the MOTD service.
        motd = self.application.get_service(IMOTD)

        # Get the message of the day...
        message = motd.motd()

        # ... and print it.
        print('\n"%s"\n\n- %s' % (message.text, message.author))
Example #22
0
 class Foo(TestBase):
     x = ExtensionPoint(List(Int), id="my.ep")
Example #23
0
class ConnectomeFile2Plugin(Plugin):

    # Extension points we contribute to.
    VIEWS  = 'envisage.ui.workbench.views'    
    #- for application scope and
    SERVICE_OFFERS = 'envisage.service_offers'

    #### Extension points offered by this plugin ##############################

    CFILE = 'cviewer.plugins.cff2.cfile'

    # The connectome file extension point.
    cfiles = ExtensionPoint(
        List( ), id=CFILE, desc="""

        This extension point allows you to contribute files in the connectome
        file format, e.g. adding template atlases

        """
    )
    
    # The plugin's unique identifier.
    id = 'connectome.file'

    # The plugin's name (suitable for displaying to the user).
    name = 'Connectome File'
    
    # Services we contribute.
    service_offers = List(contributes_to=SERVICE_OFFERS)

    # Views.
    views = List(contributes_to=VIEWS)
       
    # it contributes at the same time one cfile to the extension point!
    cfile = List(contributes_to=CFILE)

    #####################################################################
    # Private methods
    #####################################################################

    def _cfile_default(self):
        """ Creates the dummy cfile contributed """
        return  self._get_cff_service()

    # a service offer looks up a corresponding
    # file from the extension point, and then can be used
    # below in the cfile_view_factory to display it.
    # ...e.g. open a cff just changes its contents.
    # and this makes also window available in the cfile for service registry lookup

    def _service_offers_default(self):
        """ Trait initializer. """
        cfile_service_offer = ServiceOffer(
            protocol = 'cviewer.plugins.cff2.cfile.CFile',
            factory = 'cviewer.plugins.cff2.cfile.CFile'
        )
        
        return [cfile_service_offer]
        
    def _get_cff_service(self):
        """ Looks up a cfile from the application service offers
        to be used by the view factory
        
        """
        return self.application.get_service('cviewer.plugins.cff2.cfile.CFile')
    

    def _views_default(self):
        """ Trait initializer. """
        return [self._cfile_view_factory]


    def _cfile_view_factory(self, window, **traits):
        """ Factory method for connectome file views. """
        from pyface.workbench.traits_ui_view import \
                TraitsUIView
        
        from cviewer.plugins.cff2.ui.cff_view import CFFView
        
        cfile = self._get_cff_service()
        cfile._workbenchwin = window
        cff_view = CFFView(cfile=cfile)
        tui_engine_view = TraitsUIView(obj=cff_view,
                                       id=CFFVIEW,
                                       name='Connectome File View',
                                       window=window,
                                       position='left',
                                       **traits
                                       )
        return tui_engine_view
Example #24
0
class WorkbenchPlugin(Plugin):
    """ The Envisage workbench plugin.

    The workbench plugin uses the PyFace workbench to provide the basis of an
    IDE-like user interface. The interface is made up of perspectives, views
    and editors.

    Note that this is not intended to be a 'general-purpose' plugin for user
    interfaces - it provides an IDE-like style and that is all. If your
    application requires another style of interface then write another plugin
    (you can still re-use all the menu, group and action contribution stuff!).

    """

    # The Ids of the extension points that this plugin offers.
    ACTION_SETS = PKG + '.action_sets'
    PERSPECTIVES = PKG + '.perspectives'
    PREFERENCES_PAGES = PKG + '.preferences_pages'
    WORKBENCH_SERVICE_OFFERS = PKG + '.service_offers'
    VIEWS = PKG + '.views'

    # The Ids of the extension points that this plugin contributes to.
    PREFERENCES = 'envisage.preferences'
    SERVICE_OFFERS = 'envisage.service_offers'

    #### 'IPlugin' interface ##################################################

    # The plugin's unique identifier.
    id = 'envisage.ui.workbench'

    # The plugin's name (suitable for displaying to the user).
    name = 'Workbench'

    #### Extension points offered by this plugin ##############################

    action_sets = ExtensionPoint(List(Callable),
                                 id=ACTION_SETS,
                                 desc="""

        An action set contains the toobars, menus, groups and actions that you
        would like to add to top-level workbench windows (i.e. the main
        application window). You can create new toolbars, menus and groups
        and/or add to existing ones.

        Each contribution to this extension point must be a factory that
        creates an action set, where 'factory' means any callable with the
        following signature::

          callable(**traits) -> IActionSet

        The easiest way to contribute such a factory is to create a class
        that derives from 'envisage.ui.action.api.ActionSet'.

        """)

    perspectives = ExtensionPoint(List(Callable),
                                  id=PERSPECTIVES,
                                  desc="""

        A perspective is simply an arrangment of views around the (optionally
        hidden) editor area.

        Each contribution to this extension point must be a factory that
        creates a perspective, where 'factory' means any callable with the
        following signature::

          callable(**traits) -> IPerspective

        The easiest way to contribute such a factory is to create a class
        that derives from 'pyface.workbench.api.IPerspective'.

        """)

    preferences_pages = ExtensionPoint(List(Callable),
                                       id=PREFERENCES_PAGES,
                                       desc="""

        A preferences page appears in the preferences dialog to allow the user
        to manipulate some preference values.

        Each contribution to this extension point must be a factory that
        creates a preferences page, where 'factory' means any callable with the
        following signature::

          callable(**traits) -> IPreferencesPage

        The easiest way to contribute such a factory is to create a class
        that derives from 'apptools.preferences.ui.api.IPreferencesPage'.

        """)

    service_offers = ExtensionPoint(List(ServiceOffer),
                                    id=WORKBENCH_SERVICE_OFFERS,
                                    desc="""

        Services are simply objects that a plugin wants to make available to
        other plugins. This extension point allows you to offer 'per
        window' services that are created 'on-demand' (where 'on demand' means
        the first time somebody looks up a service of the appropriate
        protocol).
        .

        e.g.

        my_service_offer = ServiceOffer(
            protocol   = 'acme.IMyService',
            factory    = an_object_or_a_callable_that_creates_one,
            properties = {'a dictionary' : 'that is passed to the factory'}
        )

        Any properties specified are passed as keywrod arguments to the
        factory, i.e. the factory signature is::

          callable(**properties)

        """)

    views = ExtensionPoint(List(Callable),
                           id=VIEWS,
                           desc="""

        A view provides information to the user to support their current
        task. Views can contain anything you like(!) and are arranged around
        the (optionally hidden) editor area. The user can re-arrange views as
        he/she sees fit.

        Each contribution to this extension point must be a factory that
        creates a view, where 'factory' means any callable with the following
        signature::

          callable(**traits) -> IView

        The easiest way to contribute such a factory is to create a class
        that derives from 'pyface.workbench.api.View'.

        It is also common to use a simple function (especially when a view
        is a representation of a service) e.g::

            def foo_view_factory(**traits):
                ' Create a view that is a representation of a service. '
                foo = self.application.get_service('IFoo')

                return FooView(foo=foo, **traits)

        """)

    #### Contributions to extension points made by this plugin ################

    my_action_sets = List(contributes_to=ACTION_SETS)

    def _my_action_sets_default(self):
        """ Trait initializer. """

        from .default_action_set import DefaultActionSet

        return [DefaultActionSet]

    my_preferences = List(contributes_to=PREFERENCES)

    def _my_preferences_default(self):
        """ Trait initializer. """

        return ['pkgfile://envisage.ui.workbench/preferences.ini']

    my_preferences_pages = List(contributes_to=PREFERENCES_PAGES)

    def _my_preferences_pages_default(self):
        """ Trait initializer. """

        from .workbench_preferences_page import WorkbenchPreferencesPage

        return [WorkbenchPreferencesPage]

    my_service_offers = List(contributes_to=SERVICE_OFFERS)

    def _my_service_offers_default(self):
        """ Trait initializer. """

        preferences_manager_service_offer = ServiceOffer(
            protocol='apptools.preferences.ui.preferences_manager'
            '.PreferencesManager',
            factory=self._create_preferences_manager_service)

        workbench_service_offer = ServiceOffer(
            protocol='envisage.ui.workbench.workbench.Workbench',
            factory=self._create_workbench_service)

        return [preferences_manager_service_offer, workbench_service_offer]

    ###########################################################################
    # Private interface.
    ###########################################################################

    def _create_preferences_manager_service(self, **properties):
        """ Factory method for the preferences manager service. """

        from apptools.preferences.ui.api import PreferencesManager

        preferences_manager = PreferencesManager(
            pages=[factory() for factory in self.preferences_pages])

        return preferences_manager

    def _create_workbench_service(self, **properties):
        """ Factory method for the workbench service. """

        # We don't actually create the workbench here, we just return a
        # reference to it.
        #
        # fixme: This guard is really just for testing when we have the
        # workbench plugin as a source egg (i.e. if the egg is on our path
        # then we get the plugin for any egg-based application, even if it is
        # not a workbench application!).
        return getattr(self.application, 'workbench', None)
class TasksPlugin(Plugin):
    """ The Envisage Tasks plugin.

    The Tasks plugin uses Pyface Tasks to provide an extensible framework for
    building user interfaces. For more information, see the Tasks User Manual.
    """

    # The IDs of the extension point that this plugin offers.
    PREFERENCES_CATEGORIES = PKG + '.preferences_categories'
    PREFERENCES_PANES = PKG + '.preferences_panes'
    TASKS = PKG + '.tasks'
    TASK_EXTENSIONS = PKG + '.task_extensions'

    # The IDs of the extension points that this plugin contributes to.
    SERVICE_OFFERS = 'envisage.service_offers'

    #### 'IPlugin' interface ##################################################

    #: The plugin's unique identifier.
    id = 'envisage.ui.tasks'

    #: The plugin's name (suitable for displaying to the user).
    name = 'Tasks'

    #### Extension points offered by this plugin ##############################

    #: Contributed preference categories. Contributions to this extension
    #: point must have type ``PreferencesCategory``. Preference categories
    #: will be created automatically if necessary; this extension point is
    #: useful when ensuring that a category is inserted at a specific location.
    preferences_categories = ExtensionPoint(List(PreferencesCategory),
                                            id=PREFERENCES_CATEGORIES,
                                            desc="""

        This extension point makes preference categories available to the
        application. Note that preference categories will be created
        automatically if necessary; this extension point is useful when one
        wants to ensure that a category is inserted at a specific location.
        """)

    #: Contributed preference pane factories. Each contribution to this
    #: extension point must be a callable with the signature
    #: ``callable(**traits) -> PreferencePane``.
    preferences_panes = ExtensionPoint(List(Callable),
                                       id=PREFERENCES_PANES,
                                       desc="""

        A preferences pane appears in the preferences dialog to allow the user
        manipulate certain preference values.

        Each contribution to this extension point must be a factory that
        creates a preferences pane, where 'factory' means any callable with the
        following signature::

          callable(**traits) -> PreferencesPane

        The easiest way to contribute such a factory is to create a class
        that derives from 'envisage.ui.tasks.api.PreferencesPane'.
        """)

    #: Contributed task factories. Contributions to this extension point
    #: must have type ``TaskFactory``.
    tasks = ExtensionPoint(List(
        Instance('envisage.ui.tasks.task_factory.TaskFactory')),
                           id=TASKS,
                           desc="""

        This extension point makes tasks avaiable to the application.

        Each contribution to the extension point must be an instance of
        'envisage.tasks.api.TaskFactory.
        """)

    #: Contributed task extensions. Contributions to this extension point
    #: must have type ``TaskExtension``.
    task_extensions = ExtensionPoint(List(
        Instance('envisage.ui.tasks.task_extension.TaskExtension')),
                                     id=TASK_EXTENSIONS,
                                     desc="""

        This extension point permits the contribution of new actions and panes
        to existing tasks (without creating a new task).

        Each contribution to the extension point must be an instance of
        'envisage.tasks.api.TaskExtension'.
        """)

    #### Contributions to extension points made by this plugin ################

    my_service_offers = List(contributes_to=SERVICE_OFFERS)

    def _my_service_offers_default(self):
        preferences_dialog_service_offer = ServiceOffer(
            protocol='envisage.ui.tasks.preferences_dialog.PreferencesDialog',
            factory=self._create_preferences_dialog_service)

        return [preferences_dialog_service_offer]

    my_task_extensions = List(contributes_to=TASK_EXTENSIONS)

    def _my_task_extensions_default(self):
        from .action.exit_action import ExitAction
        from .action.preferences_action import PreferencesGroup
        from .task_extension import TaskExtension
        from pyface.tasks.action.api import DockPaneToggleGroup, SchemaAddition

        actions = [
            SchemaAddition(id='Exit', factory=ExitAction, path='MenuBar/File'),
            SchemaAddition(id='Preferences',
                           factory=PreferencesGroup,
                           path='MenuBar/Edit'),
            SchemaAddition(id='DockPaneToggleGroup',
                           factory=DockPaneToggleGroup,
                           path='MenuBar/View')
        ]

        return [TaskExtension(actions=actions)]

    ###########################################################################
    # Private interface.
    ###########################################################################

    def _create_preferences_dialog_service(self):
        """ Factory method for preferences dialog service.
        """
        from .preferences_dialog import PreferencesDialog

        dialog = PreferencesDialog(application=self.application)
        dialog.trait_set(categories=self.preferences_categories,
                         panes=[
                             factory(dialog=dialog)
                             for factory in self.preferences_panes
                         ])
        return dialog
Example #26
0
class WorkbenchWindow(pyface.WorkbenchWindow):
    """ An extensible workbench window. """

    # Extension point Ids.
    ACTION_SETS = "envisage.ui.workbench.action_sets"
    VIEWS = "envisage.ui.workbench.views"
    PERSPECTIVES = "envisage.ui.workbench.perspectives"
    SERVICE_OFFERS = "envisage.ui.workbench.service_offers"

    #### 'WorkbenchWindow' interface ##########################################

    # The application that the window is part of.
    #
    # This is equivalent to 'self.workbench.application', and is provided just
    # as a convenience since windows often want access to the application.
    application = Delegate("workbench", modify=True)

    # The action sets that provide the toolbars, menus groups and actions
    # used in the window.
    action_sets = List(Instance(ActionSet))

    # The service registry for 'per window' services.
    service_registry = Instance(IServiceRegistry, factory=ServiceRegistry)

    #### 'IExtensionPointUser' interface ######################################

    # The extension registry that the object's extension points are stored in.
    extension_registry = Property(Instance(IExtensionRegistry))

    #### Private interface ####################################################

    # The workbench menu and tool bar builder.
    #
    # The builder is used to create the window's tool bar and menu bar by
    # combining all of the contributed action sets.
    _action_manager_builder = Instance(WorkbenchActionManagerBuilder)

    # Contributed action sets (each contribution is actually a factory).
    _action_sets = ExtensionPoint(id=ACTION_SETS)

    # Contributed views (each contribution is actually a factory).
    _views = ExtensionPoint(id=VIEWS)

    # Contributed perspectives (each contribution is actually a factory).
    _perspectives = ExtensionPoint(id=PERSPECTIVES)

    # Contributed service offers.
    _service_offers = ExtensionPoint(id=SERVICE_OFFERS)

    # The Ids of the services that were automatically registered.
    _service_ids = List

    ###########################################################################
    # 'IExtensionPointUser' interface.
    ###########################################################################

    def _get_extension_registry(self):
        """ Trait property getter. """

        return self.application

    ###########################################################################
    # 'pyface.Window' interface.
    ###########################################################################

    #### Trait initializers ###################################################

    def _menu_bar_manager_default(self):
        """ Trait initializer. """

        return self._action_manager_builder.create_menu_bar_manager("MenuBar")

    def _status_bar_manager_default(self):
        """ Trait initializer. """

        return StatusBarManager()

    def _tool_bar_managers_default(self):
        """ Trait initializer. """

        return self._action_manager_builder.create_tool_bar_managers("ToolBar")

    #### Trait change handlers ################################################

    def _opening_changed(self):
        """ Static trait change handler. """

        self._service_ids = self._register_service_offers(self._service_offers)

        return

    def _closed_changed(self):
        """ Static trait change handler. """

        self._unregister_service_offers(self._service_ids)

        return

    ###########################################################################
    # 'pyface.WorkbenchWindow' interface.
    ###########################################################################

    #### Trait initializers ###################################################

    def _editor_manager_default(self):
        """ Trait initializer. """

        return WorkbenchEditorManager(window=self)

    def _icon_default(self):
        """ Trait initializer. """

        return self.workbench.application.icon

    def _perspectives_default(self):
        """ Trait initializer. """

        return [factory() for factory in self._perspectives]

    def _title_default(self):
        """ Trait initializer. """

        return self.workbench.application.name

    def _views_default(self):
        """ Trait initializer. """

        return [factory(window=self) for factory in self._views]

    ###########################################################################
    # 'WorkbenchWindow' interface.
    ###########################################################################

    def _action_sets_default(self):
        """ Trait initializer. """

        return [factory(window=self) for factory in self._action_sets]

    ###########################################################################
    # 'IServiceRegistry' interface.
    ###########################################################################

    def get_service(self, protocol, query="", minimize="", maximize=""):
        """ Return at most one service that matches the specified query. """

        service = self.service_registry.get_service(protocol, query, minimize,
                                                    maximize)

        return service

    def get_service_properties(self, service_id):
        """ Return the dictionary of properties associated with a service. """

        return self.service_registry.get_service_properties(service_id)

    def get_services(self, protocol, query="", minimize="", maximize=""):
        """ Return all services that match the specified query. """

        services = self.service_registry.get_services(protocol, query,
                                                      minimize, maximize)

        return services

    def register_service(self, protocol, obj, properties=None):
        """ Register a service. """

        service_id = self.service_registry.register_service(
            protocol, obj, properties)

        return service_id

    def set_service_properties(self, service_id, properties):
        """ Set the dictionary of properties associated with a service. """

        self.service_registry.set_service_properties(service_id, properties)

        return

    def unregister_service(self, service_id):
        """ Unregister a service. """

        self.service_registry.unregister_service(service_id)

        return

    ###########################################################################
    # Private interface.
    ###########################################################################

    def __action_manager_builder_default(self):
        """ Trait initializer. """

        action_manager_builder = WorkbenchActionManagerBuilder(
            window=self, action_sets=self.action_sets)

        return action_manager_builder

    def _register_service_offers(self, service_offers):
        """ Register all service offers. """

        return list(map(self._register_service_offer, service_offers))

    def _register_service_offer(self, service_offer):
        """ Register a service offer. """

        # Add the window to the service offer properties (this is so that it
        # is available to the factory when it is called to create the actual
        # service).
        service_offer.properties["window"] = self

        service_id = self.register_service(
            protocol=service_offer.protocol,
            obj=service_offer.factory,
            properties=service_offer.properties,
        )

        return service_id

    def _unregister_service_offers(self, service_ids):
        """ Unregister all service offers. """

        # Unregister the services in the reverse order that we registered
        # them.
        service_ids_copy = service_ids[:]
        service_ids_copy.reverse()

        for service_id in service_ids_copy:
            self.unregister_service(service_id)

        return
Example #27
0
 class Foo(TestBase):
     x = ExtensionPoint(List(Int))
Example #28
0
 class PluginA(Plugin):
     id = "A"
     x = ExtensionPoint(List, id="bob")
Example #29
0
    def _create_extension_point(self, id, trait_type=List, desc=''):
        """ Create an extension point. """

        return ExtensionPoint(id=id, trait_type=trait_type, desc=desc)
Example #30
0
class IPythonShellPlugin(Plugin):
    """ An IPython shell plugin. """

    # Extension point Ids.
    BANNER = 'envisage.plugins.ipython_shell.banner'
    BINDINGS = 'envisage.plugins.python_shell.bindings'
    COMMANDS = 'envisage.plugins.python_shell.commands'
    VIEWS = 'envisage.ui.workbench.views'
    ACTION_SETS = 'envisage.ui.workbench.action_sets'

    #### 'IPlugin' interface ##################################################

    # The plugin's unique identifier.
    id = 'envisage.plugins.python_shell'

    # The plugin's name (suitable for displaying to the user).
    name = 'Python Shell'

    #### Extension points offered by this plugin ##############################

    banner = ExtensionPoint(List(Str),
                            id=BANNER,
                            desc="""

        This extension point allows you to contribute a string that
        is printed as a banner when the IPython shell is started.
        """)

    bindings = ExtensionPoint(List(Dict),
                              id=BINDINGS,
                              desc="""

        This extension point allows you to contribute name/value pairs that
        will be bound when the interactive Python shell is started.

        e.g. Each item in the list is a dictionary of name/value pairs::

        {'x' : 10, 'y' : ['a', 'b', 'c']}

        """)

    commands = ExtensionPoint(List(Str),
                              id=COMMANDS,
                              desc="""

        This extension point allows you to contribute commands that are
        executed when the interactive Python shell is started.

        e.g. Each item in the list is a string of arbitrary Python code::

          'import os, sys'
          'from traits.api import *'

        Yes, I know this is insecure but it follows the usual Python rule of
        'we are all consenting adults'.

        """)

    #### Contributions to extension points made by this plugin ################

    # Our action sets.
    action_sets = List(contributes_to=ACTION_SETS)

    def _action_sets_default(self):
        """ Trait initializer. """
        from envisage.plugins.ipython_shell.actions.ipython_shell_actions \
            import IPythonShellActionSet
        return [IPythonShellActionSet]

    # Bindings.
    contributed_bindings = List(contributes_to=BINDINGS)

    def _contributed_bindings_default(self):
        """ Trait initializer. """

        return [{'application': self.application}]

    # Views.
    contributed_views = List(contributes_to=VIEWS)

    def _contributed_views_default(self):
        """ Trait initializer. """

        # Local imports.
        from view.ipython_shell_view import IPythonShellView
        from view.namespace_view \
                    import NamespaceView

        return [IPythonShellView, NamespaceView]
Example #31
0
class ProjectPlugin(Plugin):
    """
    The single-project plugin.

    """

    # The Ids of the extension points that this plugin offers.
    ACTION_SETS = 'envisage.ui.workbench.action_sets'
    FACTORY_DEFINITIONS = 'envisage.ui.single_project.factory_definitions'
    UI_SERVICE_FACTORY = 'envisage.ui.single_project.ui_service_factory'

    # The Ids of the extension points that this plugin contributes to.
    PERSPECTIVES = 'envisage.ui.workbench.perspectives'
    PREFERENCES = 'envisage.preferences'
    PREFERENCES_PAGES = 'envisage.ui.workbench.preferences_pages'
    SERVICE_OFFERS = 'envisage.service_offers'
    VIEWS = 'envisage.ui.workbench.views'

    #### 'IPlugin' interface ##################################################

    # The plugin's unique identifier.
    id = 'envisage.ui.single_project'

    # The plugin's name (suitable for displaying to the user).
    name = 'Single Project'

    #### Extension points offered by this plugin ##############################

    # Factory definitions.
    factory_definitions = ExtensionPoint(List(Callable),
                                         id=FACTORY_DEFINITIONS,
                                         desc="""

        A project factory definition.

        An instance of the specified class is used to open and/or create new
        projects.

        The extension with the highest priority wins!  In the event of a tie,
        the first instance wins.

        """)

    # Ui service factories.
    ui_service_factory = ExtensionPoint(List(Callable),
                                        id=UI_SERVICE_FACTORY,
                                        desc="""

        A ui service factory definition.

        """)

    #### Contributions to extension points made by this plugin ################

    # Action sets.
    action_sets = List(contributes_to=ACTION_SETS)

    def _action_sets_default(self):
        """
        Default project actions.

        """

        return [ProjectActionSet]

    # Factory definitions.
    my_factory_definitions = List(contributes_to=FACTORY_DEFINITIONS)

    def _my_factory_definitions_default(self):
        """
        Default factory definition.

        """

        factory_definition = FactoryDefinition(
            class_name=PKG + '.project_factory.ProjectFactory',
            priority=0,
        )

        return [factory_definition]

    # Perspectives.
    perspectives = List(contributes_to=PERSPECTIVES)

    def _perspectives_default(self):
        """
        Default project perspective.

        """

        return [ProjectPerspective]

    # Service offers.
    service_offers = List(contributes_to=SERVICE_OFFERS)

    def _service_offers_default(self):
        """
        Our service contributions.

        """

        model_service = ServiceOffer(protocol=IPROJECT_MODEL,
                                     factory=self._create_model_service)

        ui_service = ServiceOffer(protocol=IPROJECT_UI,
                                  factory=self._create_ui_service)

        # FIXME: Eventually we will register the services here intead
        # of in the plugin's start() method.
        #return [model_service, ui_service]
        return []

    # Ui service factories.
    my_ui_service_factory = List(contributes_to=UI_SERVICE_FACTORY)

    def _my_ui_service_factory_default(self):
        """
        Default ui service factory.

        """

        ui_service_factory = UIServiceFactory(
            class_name=PKG + '.ui_service_factory.UIServiceFactory',
            priority=0,
        )

        return [ui_service_factory]

    # Preferences.
    my_preferences = List(contributes_to=PREFERENCES)

    def _my_preferences_default(self):
        """
        Default preferences.

        """
        return ['pkgfile://%s/preferences.ini' % PKG]

    # Preference pages.
    my_preferences_pages = List(contributes_to=PREFERENCES_PAGES)

    def _my_preferences_pages_default(self):
        """
        Default preference page.

        """

        from .default_path_preference_page import DefaultPathPreferencePage

        return [DefaultPathPreferencePage]

    # Views.
    views = List(contributes_to=VIEWS)

    def _views_default(self):
        """
        Add our project views.

        """

        return [self._project_view_factory]

    ### protected interface ##################################################

    def start(self):
        """
        Starts the plugin.

        Overridden here to start up our services and load the project
        that was open when we were last shut-down.

        """

        super(ProjectPlugin, self).start()

        # FIXME: We eventually won't have to explicitly register the
        # services ourselves, since we contribute them as service offers
        # so they are instantiated when they are invoked, but since they are
        # not used anywhere else yet, I had to use this same old approach
        # just to test and make sure they were working correctly.
        # Create and register the model service we offer
        model_service = self._create_model_service()
        self.application.register_service(IPROJECT_MODEL, model_service)

        # Create and register the ui service we offer
        ui_service = self._create_ui_service(model_service)
        self.application.register_service(IPROJECT_UI, ui_service)

        # Set up any listeners interested in the current project selection
        # FIXME: Register the selection listeners for the current project selection.
        #self._register_selection_listeners(model_service)

        return

    ######################################################################
    # Private methods.
    def _project_view_factory(self, window, **traits):
        """
        Factory method for project views.

        """
        from pyface.workbench.traits_ui_view import \
                TraitsUIView
        from envisage.ui.single_project.api import \
                            ProjectView

        project_view = ProjectView(application=window.application)
        tui_project_view = TraitsUIView(
            obj=project_view,
            id='envisage.ui.single_project.view.project_view.ProjectView',
            name='Project View',
            window=window,
            position='left',
            **traits)
        return tui_project_view

    def _create_model_service(self):
        """
        Creates a model service for this plugin.

        """

        # Determine which contributed project factory to use.
        factory = self._get_contributed_project_factory()

        # Make sure the factory has a reference to our Envisage application.
        factory.application = self.application

        # Create the project service instance.
        result = ModelService(self.application, factory)

        return result

    def _create_ui_service(self, model_service):
        """
        Creates a UI service for this plugin.

        """

        # Create the menu manager representing the context menu we show when
        # nothing is selected in the project view.
        menu_manager = self._get_no_selection_context_menu_manager()

        # Get the UI service factory.
        ui_service_factory = self._get_contributed_ui_service_factory()

        # Create the ui service instance
        ui_service = ui_service_factory.create_ui_service(
            model_service, menu_manager)

        return ui_service

    def _get_contributed_project_factory(self):
        """
        Retrieves the instance of the project factory to use with this
        plugin.

        The instance is generated from the contributed factory definition
        that was the first one with the highest priority.

        """

        # Retrieve all the factory definition contributions
        extensions = self.application.get_extensions(
            'envisage.ui.single_project.factory_definitions')

        # Find the winning contribution
        definition = None
        for extension in extensions:
            if not definition or extension.priority > definition.priority:
                definition = extension

        # Create an instance of the winning project factory
        logger.info("Using ProjectFactory [%s]", definition.class_name)
        klass = self.application.import_symbol(definition.class_name)
        factory = klass()

        return factory

    def _get_contributed_ui_service_factory(self):
        """
        Retrieves the instance of the UiService factory to use with this
        plugin.

        The instance is generated from the contributed factory definition
        that was the first one with the highest priority.

        """

        # Retrieve all the factory definition contributions
        extensions = self.get_extensions(
            'envisage.ui.single_project.ui_service_factory')

        # Find the winning contribution
        definition = None
        for extension in extensions:
            if not definition or extension.priority > definition.priority:
                definition = extension

        # Create an instance of the winning factory
        logger.info("Using UiService Factory [%s]", definition.class_name)
        class_name = definition.class_name
        klass = self.application.import_symbol(class_name)
        factory = klass()

        return factory

    def _get_no_selection_context_menu_manager(self):
        """
        Generates a menu manager representing the context menu shown when
        nothing is selected within the project view.  That is, when the
        user right clicks on any empty space within our associated UI.

        """

        # Retrieve all contributions for the no-selection context menu.
        extensions = self.get_extensions(ProjectActionSet)

        # Populate a menu manager from the extensions.
        menu_manager = MenuManager()
        if len(extensions) > 0:
            action_set_manager = ActionSetManager(action_sets=extensions)
            menu_builder = DefaultMenuBuilder(application=self.application)
            menu_builder.initialize_menu_manager(menu_manager,
                                                 action_set_manager,
                                                 NO_SELECTION_MENU_ID)

        return menu_manager

    def _register_selection_listeners(self, model_service):
        """
        Registers any extension-requested listeners on the project
        selection.

        """

        for sps in self.get_extensions(SyncProjectSelection):
            object = self.application.lookup_application_object(sps.uol)
            if object is not None:
                name = sps.name
                self._register_selection_handler(object, name, model_service)
            else:
                logger.error('Could not resolve the SyncProjectSelection ' + \
                    'UOL: "%s"', sps.uol )

        return

    def _register_selection_handler(self, object, name, model_service):
        """
        Creates a handler and registers it.

        """
        def handler():
            # The key to this method is to realize that our goal is to
            # make it as easy as possible to create recipients for
            # notification.  Using traits as the recipients makes
            # creation very simple because we can rely on the type
            # knowledge within that trait to ensure only valid values
            # get assigned to the recipient.  That is the recipient
            # doesn't need to do anything complex to validate the
            # values they get assigned.  This method also works if the
            # recipient isn't a trait, but in that case, they will
            # have to handle multiple selection of the project
            # bindings.
            #
            # First, try to provide the recipient with a multiple
            # selection type value i.e. a list of bindings.
            try:
                setattr(object, name, model_service.selection)
                return
            except:
                pass

            # If that didn't work, remove the binding wrappers and try
            # notification of the resulting list.
            selection = [s.obj for s in model_service.selection]
            try:
                setattr(object, name, selection)
                return
            except:
                pass

            # If that didn't work, and only a single item is selected,
            # then try to provide that item to the recipient.
            if len(selection) == 1:
                try:
                    setattr(object, name, selection[0])
                    return
                except:
                    pass

            # The recipient must not be accepting the type of the
            # current selection, so let's clear its current selection
            # instead.  If this fails, then something has gone wrong
            # with the declaration of the recipient.
            try:
                setattr(object, name, None)
            except:
                logger.debug(
                    'Error informing object [%s] of project '
                    'selection change via attribute [%s]', object, name)

        model_service.on_trait_change(handler, 'selection')
        model_service.on_trait_change(handler, 'selection_items')

        return