Example #1
0
class PychronTasksPlugin(BasePlugin):
    id = 'pychron.tasks.plugin'
    name = 'Tasks'
    preferences_panes = List(
        contributes_to='envisage.ui.tasks.preferences_panes')
    task_extensions = List(contributes_to='envisage.ui.tasks.task_extensions')

    actions = ExtensionPoint(List, id='pychron.actions')
    file_defaults = ExtensionPoint(List(Tuple), id='pychron.plugin.file_defaults')
    help_tips = ExtensionPoint(List, id='pychron.plugin.help_tips')
    available_task_extensions = ExtensionPoint(List, id='pychron.available_task_extensions')

    my_tips = List(contributes_to='pychron.plugin.help_tips')

    # def _application_changed(self):
    #     # defaults = (('use_advanced_ui', False), ('show_random_tip', True))
    #     defaults = (('show_random_tip', True),)
    #     try:
    #         self._set_preference_defaults(defaults, 'pychron.general')
    #     except AttributeError, e:
    #         print 'exception', e

    def start(self):
        self.info('Writing plugin file defaults')
        paths.write_file_defaults(self.file_defaults)

        self._random_tip()

    def _random_tip(self):
        if globalv.random_tip_enabled and to_bool(self.application.preferences.get('pychron.general.show_random_tip')):
            from pychron.envisage.tasks.tip_view import TipView

            t = random.choice(self.help_tips)

            tv = TipView(text=t)
            tv.edit_traits()

    def _my_tips_default(self):
        return ["Use <b>Help>What's New</b> to view the official ChangeLog for the current version",
                'Turn Off Random Tip two ways:<br><b>1. Preferences>General></b> Uncheck "Random Tip".</b><br>'
                '<b>2.</b> Set the flag <i>random_tip_enabled</i> to False in the initialization file']

    def _preferences_panes_default(self):
        return [GeneralPreferencesPane, BrowserPreferencesPane]

    def _task_extensions_default(self):
        actions = [SchemaAddition(factory=EditInitializationAction,
                                  id='edit_plugins',
                                  path='MenuBar/help.menu')]
        return [TaskExtension(actions=actions)]
Example #2
0
class PychronTasksPlugin(BasePlugin):
    id = 'pychron.tasks.plugin'
    name = 'Tasks'
    preferences_panes = List(
        contributes_to='envisage.ui.tasks.preferences_panes')
    task_extensions = List(contributes_to='envisage.ui.tasks.task_extensions')

    actions = ExtensionPoint(List, id='pychron.actions')
    file_defaults = ExtensionPoint(List(Tuple),
                                   id='pychron.plugin.file_defaults')
    help_tips = ExtensionPoint(List, id='pychron.plugin.help_tips')
    available_task_extensions = ExtensionPoint(
        List, id='pychron.available_task_extensions')

    my_tips = List(contributes_to='pychron.plugin.help_tips')

    def _application_changed(self):
        # defaults = (('use_advanced_ui', False), ('show_random_tip', True))
        defaults = (('show_random_tip', True), )
        try:
            self._set_preference_defaults(defaults, 'pychron.general')
        except AttributeError, e:
            print 'exception', e
Example #3
0
class VideoPlugin(BaseTaskPlugin):
    '''
        a list of name, url (file:///abs/path or pvs://host[:port=8080]) tuples
        
        if file then should be a path to an image
        if pvs than should be address to a Pychron Video Server
    '''
    id = 'pychron.video'
    sources = ExtensionPoint(List, id='pychron.video.sources')

    def _tasks_default(self):
        ts = [
            TaskFactory(id='pychron.video',
                        name='Video Display',
                        factory=self._video_task_factory,
                        task_group='hardware')
        ]
        return ts

    def _video_task_factory(self):
        t = VideoTask(available_connections=self.sources)
        return t
Example #4
0
class BaseTasksApplication(TasksApplication, Loggable):
    about_dialog = Instance(Dialog)
    startup_tester = Instance(StartupTester)
    uis = List
    available_task_extensions = ExtensionPoint(
        id='pychron.available_task_extensions')

    def _started_fired(self):
        st = self.startup_tester
        if st.results:
            v = ResultsView(model=st)
            open_view(v)

        if globalv.use_testbot:
            from pychron.testbot.testbot import TestBot

            testbot = TestBot(application=self)
            testbot.run()

    def get_task_extensions(self, pid):
        import yaml

        p = paths.task_extensions_file
        with open(p, 'r') as rfile:
            yl = yaml.load(rfile)
            for yi in yl:
                # print yi['plugin_id'], pid
                if yi['plugin_id'].startswith(pid):
                    tid = yi.get('task_id', '')
                    for ai in yi['actions']:
                        a, e = ai.split(',')
                        # print tid, a, e
                        if to_bool(e):
                            yield tid, a

    def about(self):
        self.about_dialog.open()

    def start(self):
        if globalv.open_logger_on_launch:
            self._load_state()
            self.open_task('pychron.logger')

        self.startup_tester = StartupTester()

        return super(BaseTasksApplication, self).start()

    def get_open_task(self, tid):
        for win in self.windows:
            if win.active_task:
                if win.active_task.id == tid:
                    return win, win.active_task, True
        else:
            win = self.create_window(TaskWindowLayout(tid))
            return win, win.active_task, False

    def task_is_open(self, tid):
        for win in self.windows:
            if win.active_task and win.active_task.id == tid:
                return win.active_task

    def is_open(self, win):
        return win in self.windows

    def get_task(self, tid, activate=True):
        for win in self.windows:
            if win.active_task:
                if win.active_task.id == tid:
                    if activate and win.control:
                        win.activate()
                    break
        else:
            w = TaskWindowLayout(tid)
            win = self.create_window(w)
            if activate:
                win.open()

        if win:
            win.active_task.window = win

            return win.active_task

    def open_task(self, tid, **kw):
        return self.get_task(tid, True, **kw)

        # def add_view(self, ui):
        #     self.uis.append(weakref.ref(ui)())

        # def open_view(self, obj, **kw):
        # open_view(obj, **kw)
        # info = obj.edit_traits(**kw)
        # self.add_view(info)
        # return info

    def exit(self, **kw):
        report_view_stats()
        close_views()

        self._cleanup_services()

        super(BaseTasksApplication, self).exit()

    def _cleanup_services(self):
        for si in self.get_services(ICoreDevice):
            si.close()
class CorePlugin(Plugin):
    """ The Envisage core plugin.

    The core plugin offers facilities that are generally useful when building
    extensible applications such as adapters and hooks etc. It does
    not contain anything to do with user interfaces!

    The core plugin should be started before any other plugin. It is up to
    the plugin manager to do this.

    """

    #: Extension point ID for preferences
    PREFERENCES = "envisage.preferences"

    #: Extension point ID for service offers
    SERVICE_OFFERS = "envisage.service_offers"

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

    #: The plugin's unique identifier.
    id = "envisage.core"

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

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

    #: preferences ExtensionPoint
    preferences = ExtensionPoint(
        List(Str),
        id=PREFERENCES,
        desc="""

        Preferences files allow plugins to contribute default values for
        user preferences. Each contributed string must be the URL of a
        file-like object that contains preferences values.

        e.g.

        'pkgfile://envisage/preferences.ini'

        - this looks for the 'preferences.ini' file in the 'envisage'
        package.

        'file://*****:*****@on_trait_change("preferences_items")
    def _preferences_changed(self, event):
        """ React to new preferencess being *added*.

        Note that we don't currently do anything if preferences are *removed*.

        """

        self._load_preferences(event.added)

        return

    #: service offers ExtensionPoint
    service_offers = ExtensionPoint(
        List(ServiceOffer),
        id=SERVICE_OFFERS,
        desc="""

        Services are simply objects that a plugin wants to make available to
        other plugins. This extension point allows you to offer services
        that are created 'on-demand'.

        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'}
        )

        See the documentation for 'ServiceOffer' for more details.

        """,
    )

    @on_trait_change("service_offers_items")
    def _service_offers_changed(self, event):
        """ React to new service offers being *added*.

        Note that we don't currently do anything if services are *removed* as
        we have no facility to let users of the service know that the offer
        has been retracted.

        """
        for service in event.added:
            self._register_service_offer(service)

        return

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

    # None.

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

    def start(self):
        """ Start the plugin. """
        # Load all contributed preferences files into the application's root
        # preferences node.
        self._load_preferences(self.preferences)

        # Register all service offers.
        #
        # These services are unregistered by the default plugin activation
        # strategy (due to the fact that we store the service ids in this
        # specific trait!).
        self._service_ids = self._register_service_offers(self.service_offers)

        return

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

    def _load_preferences(self, preferences):
        """ Load all contributed preferences into a preferences node. """

        # Enthought library imports.
        from envisage.resource.api import ResourceManager

        # We add the plugin preferences to the default scope. The default scope
        # is a transient scope which means that (quite nicely ;^) we never
        # save the actual default plugin preference values. They will only get
        # saved if a value has been set in another (persistent) scope - which
        # is exactly what happens in the preferences UI.
        default = self.application.preferences.node("default/")

        # The resource manager is used to find the preferences files.
        resource_manager = ResourceManager()
        for resource_name in preferences:
            f = resource_manager.file(resource_name)
            try:
                default.load(f)

            finally:
                f.close()

        return

    def _register_service_offers(self, service_offers):
        """ Register a list of service offers. """

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

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

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

        return service_id
Example #6
0
class PychronTasksPlugin(BasePlugin):
    id = 'pychron.tasks.plugin'
    name = 'Tasks'
    preferences_panes = List(
        contributes_to='envisage.ui.tasks.preferences_panes')
    task_extensions = List(contributes_to='envisage.ui.tasks.task_extensions')

    actions = ExtensionPoint(List, id='pychron.actions')
    file_defaults = ExtensionPoint(List(Tuple), id='pychron.plugin.file_defaults')
    help_tips = ExtensionPoint(List, id='pychron.plugin.help_tips')
    available_task_extensions = ExtensionPoint(List, id='pychron.available_task_extensions')

    my_tips = List(contributes_to='pychron.plugin.help_tips')
    background_processes = ExtensionPoint(List, id='pychron.background_processes')

    def start(self):
        self.info('Writing plugin file defaults')
        paths.write_file_defaults(self.file_defaults)

        self._set_user()
        self._random_tip()
        self._start_background_processes()

    def _start_background_processes(self):

        self.info('starting background processes disabled')
        return

        for i, p in enumerate(self.background_processes):
            if isinstance(p, tuple):
                name, func = p
            else:
                func = p
                name = 'Background{:02n}'.format(i)

            if hasattr(func, '__call__'):
                t = Thread(target=func, name=name)
                t.setDaemon(True)
                t.start()

    def _set_user(self):
        self.application.preferences.set('pychron.general.username', globalv.username)
        self.application.preferences.save()

    def _random_tip(self):
        if globalv.random_tip_enabled and self.application.get_boolean_preference('pychron.general.show_random_tip'):
            from pychron.envisage.tasks.tip_view import TipView

            t = random.choice(self.help_tips)

            tv = TipView(text=t)
            tv.edit_traits()

    def _my_tips_default(self):
        return ["Use <b>Help>What's New</b> to view the official ChangeLog for the current version",
                'Turn Off Random Tip two ways:<br><b>1. Preferences>General></b> Uncheck "Random Tip".</b><br>'
                '<b>2.</b> Set the flag <i>random_tip_enabled</i> to False in the initialization file',
                'Use <b>Window/Reset Layout</b> to change the current window back to its default "Look"',
                'Submit bugs or issues to the developers manually using <b>Help/Add Request/Report Bug</b>',
                'The current version of Pychron contains over 156K lines of code in 1676 files',
                'If menu actions are missing first check that the desired "Plugin" is enabled using <b>Help/Edit '
                'Initialization</b>. If "Plugin" is enabled, check that the desired action is enabled using '
                '<b>Help/Edit UI</b>.']

    def _preferences_panes_default(self):
        return [GeneralPreferencesPane, BrowserPreferencesPane]

    def _task_extensions_default(self):
        actions = [SchemaAddition(factory=EditInitializationAction,
                                  id='edit_plugins',
                                  path='MenuBar/help.menu')]
        return [TaskExtension(actions=actions)]
Example #7
0
class EntryPlugin(BaseTaskPlugin):
    id = 'pychron.entry.plugin'

    data_sources = ExtensionPoint(List, id='pychron.entry.data_sources')

    def _help_tips_default(self):
        return [
            'Use <b>Entry>Labnumber</b> to add/edit irradiation information including '
            'Irradiation, Level, Sample, Project, Labnumber, etc...',
            'Use <b>Entry>Sensitivity</b> to add/edit the sensitivity table.',
            'Once the Labnumber window is activated additional Menu actions are available including, '
            'Transfer J and Generate Labbook.'
        ]

    def _actions_default(self):
        return [
            ('pychron.labnumber_entry', 'Ctrl+Shift+l',
             'Open Labnumber Entry Window'),
            ('pychron.sensitivity', 'Ctrl+Shift+\\',
             'Open Sensistivity Window'),
        ]

    def _service_offers_default(self):
        def factory2():
            dvc = self.application.get_service(DVC_PROTOCOL)
            e = FluxMonitorEditor(dvc=dvc)
            return e

        so2 = self.service_offer_factory(factory=factory2,
                                         protocol=FluxMonitorEditor)
        return [so2]

    def _task_extensions_default(self):
        extensions = [
            TaskExtension(actions=actions, task_id=eid)
            for eid, actions in self._get_extensions()
        ]
        additions = [
            SchemaAddition(
                id='entry',
                factory=lambda: SMenu(id='entry.menu', name='Entry'),
                path='MenuBar',
                before='tools.menu',
                after='view.menu')
        ]

        eflag = False
        eeflag = False
        for eid, actions in self._get_extensions():
            # print 'b', eid, len(actions)
            for ai in actions:
                # print 'c',ai,ai.id
                if not eflag and ai.id.startswith('pychron.entry1'):
                    eflag = True
                    additions.append(
                        SchemaAddition(
                            id='entry_group',
                            factory=lambda: SGroup(id='entry.group'),
                            path='MenuBar/entry.menu'))
                    additions.append(
                        SchemaAddition(
                            id='entry_sample_group',
                            absolute_position='first',
                            factory=lambda: SGroup(id='entry.sample.group'),
                            path='MenuBar/entry.menu'))
                elif not eeflag and ai.id.startswith('pychron.entry2'):
                    eeflag = True
                    additions.append(
                        SchemaAddition(
                            id='entry_group2',
                            factory=lambda: SGroup(id='entry.group2'),
                            after='entry_group',
                            path='MenuBar/entry.menu'), )

        extensions.append(TaskExtension(actions=additions))

        return extensions

    def _available_task_extensions_default(self):
        g2path = 'MenuBar/entry.menu/entry.group2'
        gpath = 'MenuBar/entry.menu/entry.group'
        spath = 'MenuBar/entry.menu/entry.sample.group'

        return [
            ('{}.entry2'.format(self.id), 'pychron.entry.irradiation.task',
             'Entry Tools', [
                 SchemaAddition(id='pychron.entry2.transfer_j',
                                factory=TransferJAction,
                                path=g2path),
                 SchemaAddition(id='pychron.entry2.get_igsns',
                                factory=GetIGSNAction,
                                path=g2path),
                 SchemaAddition(id='pychron.entry2.export_irradiation',
                                factory=ExportIrradiationAction,
                                path=g2path),
                 SchemaAddition(id='pychron.entry2.import_samples_from_file',
                                factory=ImportSamplesAction,
                                path=g2path),
                 SchemaAddition(
                     id='pychron.entry2.import_irradiations_from_file',
                     factory=ImportIrradiationFileAction,
                     path=g2path),
                 SchemaAddition(id='pychron.entry2.generate_tray',
                                factory=GenerateTrayAction,
                                path=g2path),
                 SchemaAddition(id='pychron.entry2.run_report',
                                factory=GenerateStatusReportAction,
                                path=gpath),
                 SchemaAddition(id='pychron.entry2.save_labbook',
                                factory=MakeIrradiationBookPDFAction,
                                path=g2path)
             ]),
            (
                self.id,
                '',
                'Entry',
                [
                    SchemaAddition(id='pychron.entry1.sample_entry',
                                   factory=SampleEntryAction,
                                   path=spath,
                                   absolute_position='first'),
                    SchemaAddition(id='pychron.entry1.sample_prep',
                                   factory=SamplePrepAction,
                                   path=spath,
                                   after='pychron.entry1.sample_edit'),
                    SchemaAddition(id='pychron.entry1.labnumber_entry',
                                   factory=LabnumberEntryAction,
                                   path=spath,
                                   after='pychron.entry1.sample_prep'),
                    SchemaAddition(id='pychron.entry1.project',
                                   factory=ProjectAction,
                                   path=gpath),
                    SchemaAddition(id='pychron.entry2.import_analyses',
                                   factory=ImportAnalysesAction,
                                   path=g2path),
                    SchemaAddition(id='pychron.entry2.import_irradiation',
                                   factory=ImportIrradiationAction,
                                   path=g2path),
                    SchemaAddition(id='pychron.entry1.make_template',
                                   factory=MakeIrradiationTemplateAction,
                                   path=g2path),
                    SchemaAddition(
                        id='pychron.entry1.generate_irradiation_table',
                        factory=GenerateIrradiationTableAction,
                        path=gpath),
                    SchemaAddition(id='pychron.entry1.import_irradiation_geom',
                                   factory=ImportIrradiationGeometryAction,
                                   path=gpath),
                    SchemaAddition(id='pychron.entry1.edit_irradiation_geom',
                                   factory=EditIrradiationGeometryAction,
                                   path=gpath),
                    SchemaAddition(id='pychron.entry1.sensitivity_entry',
                                   factory=SensitivityEntryAction,
                                   path=gpath),
                    # SchemaAddition(id='pychron.entry1.molecular_weight_entry', factory=AddMolecularWeightAction,
                    #                path=gpath),
                    SchemaAddition(id='pychron.entry1.flux_monitor',
                                   factory=AddFluxMonitorAction,
                                   path=gpath)
                ])
        ]

    def _tasks_default(self):
        return [
            TaskFactory(id='pychron.entry.irradiation.task',
                        factory=self._labnumber_entry_task_factory,
                        include_view_menu=False),
            TaskFactory(id='pychron.entry.sensitivity.task',
                        factory=self._sensitivity_entry_task_factory,
                        include_view_menu=False),
            TaskFactory(id='pychron.entry.sample.task',
                        factory=self._sample_entry_task_factory,
                        include_view_menu=False),
            TaskFactory(id='pychron.entry.sample.prep.task',
                        factory=self._sample_prep_task_factory,
                        include_view_menu=False),
            TaskFactory(id='pychron.entry.project.task',
                        factory=self._project_task_factory,
                        include_view_menu=False)
        ]

    def _project_task_factory(self):
        from pychron.entry.tasks.project.task import ProjectTask
        return ProjectTask(application=self.application)

    def _sample_prep_task_factory(self):
        from pychron.entry.tasks.sample_prep.task import SamplePrepTask

        return SamplePrepTask(application=self.application)

    def _sample_entry_task_factory(self):
        from pychron.entry.tasks.sample.task import SampleEntryTask

        return SampleEntryTask(application=self.application)

    def _labnumber_entry_task_factory(self):
        from pychron.entry.tasks.labnumber.task import LabnumberEntryTask
        return LabnumberEntryTask(application=self.application)

    def _sensitivity_entry_task_factory(self):
        from pychron.entry.tasks.sensitivity.task import SensitivityEntryTask

        return SensitivityEntryTask(application=self.application)

    def _preferences_panes_default(self):
        return [
            LabnumberEntryPreferencesPane, SamplePrepPreferencesPane,
            SampleEntryPreferencesPane
        ]
class BaseTasksApplication(TasksApplication, Loggable):
    about_dialog = Instance(Dialog)
    startup_tester = Instance(StartupTester)
    uis = List
    available_task_extensions = ExtensionPoint(id='pychron.available_task_extensions')

    def __init__(self, *args, **kw):
        super().__init__(*args, **kw)
        self.init_logger()

    def _application_initialized_fired(self):
        if globalv.use_startup_tests:
            st = StartupTester()
            for plugin in iter(self.plugin_manager):
                st.test_plugin(plugin)

            if st.results:
                if globalv.show_startup_results or not st.all_passed:
                    v = ResultsView(model=st)
                    open_view(v)

        if globalv.use_testbot:
            from pychron.testbot.testbot import TestBot

            testbot = TestBot(application=self)
            testbot.run()

    def get_boolean_preference(self, pid, default=None):
        return to_bool(self.preferences.get(pid, default))

    def get_task_extensions(self, pid):
        import yaml

        p = paths.task_extensions_file
        with open(p, 'r') as rfile:
            yl = yaml.load(rfile)
            for yi in yl:
                # print yi['plugin_id'], pid
                if yi['plugin_id'].startswith(pid):
                    tid = yi.get('task_id', '')
                    for ai in yi['actions']:
                        a, e = ai.split(',')
                        # print tid, a, e
                        if to_bool(e):
                            yield tid, a

    def about(self):
        self.about_dialog.open()

    # def start(self):
    #     # if globalv.open_logger_on_launch:
    #     #     self.debug('load_state')
    #     #     self._load_state()
    #     #     self.debug('open logger')
    #     #     self.open_task('pychron.logger')
    #
    #     self.startup_tester = StartupTester()
    #
    #     return super(BaseTasksApplication, self).start()

    def get_open_task(self, tid):
        for win in self.windows:
            if win.active_task:
                if win.active_task.id == tid:
                    return win, win.active_task, True
        else:
            win = self.create_window(TaskWindowLayout(tid))
            return win, win.active_task, False

    def task_is_open(self, tid):
        for win in self.windows:
            if win.active_task and win.active_task.id == tid:
                return win.active_task

    def is_open(self, win):
        return win in self.windows

    def get_task(self, tid, activate=True):
        for win in self.windows:
            if win.active_task:
                if win.active_task.id == tid:
                    if activate and win.control:
                        win.activate()
                    break
        else:
            w = TaskWindowLayout(tid)
            win = self.create_window(w)
            if activate:
                win.open()

        if win:
            if win.active_task:
                win.active_task.window = win

            return win.active_task

    def open_task(self, tid, **kw):
        return self.get_task(tid, True, **kw)

        # def add_view(self, ui):
        #     self.uis.append(weakref.ref(ui)())

        # def open_view(self, obj, **kw):
        # open_view(obj, **kw)
        # info = obj.edit_traits(**kw)
        # self.add_view(info)
        # return info

    def exit(self, **kw):
        report_view_stats()
        close_views()

        self._cleanup_services()

        super(BaseTasksApplication, self).exit(**kw)

    def _cleanup_services(self):
        for si in self.get_services(ICoreDevice):
            si.close()

    def _load_state(self):
        """ Loads saved application state, if possible.
        """
        state = TasksApplicationState()
        filename = os.path.join(self.state_location, 'application_memento')
        if os.path.exists(filename):
            # Attempt to unpickle the saved application state.
            try:
                with open(filename, 'rb') as f:
                    try:
                        restored_state = pickle.load(f)
                        if state.version == restored_state.version:
                            state = restored_state
                        else:
                            logger.warn('Discarding outdated application layout')
                    except EOFError:
                        logger.exception('EOFerror: Restoring application layout from %s',
                                         filename)
            except:
                # If anything goes wrong, log the error and continue.
                logger.exception('Restoring application layout from %s',
                                 filename)
        self._state = state

    def _save_state(self):
        """ Saves the application state.
        """
        # Grab the current window layouts.
        window_layouts = [w.get_window_layout() for w in self.windows]
        self._state.previous_window_layouts = window_layouts

        # Attempt to pickle the application state.
        filename = os.path.join(self.state_location, 'application_memento')
        try:
            with open(filename, 'wb') as f:
                pickle.dump(self._state, f)
        except:
            # If anything goes wrong, log the error and continue.
            logger.exception('Saving application layout')

    def _on_window_closing(self, window, trait_name, event):
        # Event notification.
        self.window_closing = window_event = VetoableTaskWindowEvent(
            window=window)

        if window_event.veto:
            event.veto = True
        else:
            # Store the layout of the window.
            window_layout = window.get_window_layout()
            self._state.push_window_layout(window_layout)

            # If we're exiting implicitly and this is the last window, save
            # state, because we won't get another chance.
            if globalv.quit_on_last_window:
                if len(self.windows) == 1 and not self._explicit_exit:
                    self._prepare_exit()
            else:
                if len(self.windows) == 1:
                    if not self.confirmation_dialog('Closing the last open window will quit Pychron. '
                                                    'Are you sure you want to continue?'):
                        window_event.veto = True
                        event.veto = True
                    else:
                        self._prepare_exit()

    def _on_window_closed(self, window, trait_name, event):
        self.windows.remove(window)

        # Event notification.
        self.window_closed = TaskWindowEvent(window=window)

        # Was this the last window?
        if len(self.windows) == 0:
            self.stop()
Example #9
0
class PipelinePlugin(BaseTaskPlugin):
    name = 'Pipeline'
    id = 'pychron.pipeline.plugin'
    nodes = ExtensionPoint(List, id='pychron.pipeline.nodes')
    node_factories = ExtensionPoint(List, id='pychron.pipeline.node_factories')
    predefined_templates = ExtensionPoint(
        List, id='pychron.pipeline.predefined_templates')

    def _help_tips_default(self):
        return []

    def _file_defaults_default(self):
        files = [('flux_constants', 'FLUX_CONSTANTS_DEFAULT', False)]
        return files

    def _pipeline_factory(self):
        model = self.application.get_service(SampleBrowserModel)
        iamodel = self.application.get_service(InterpretedAgeBrowserModel)
        dvc = self.application.get_service(DVC)

        from pychron.pipeline.tasks.task import PipelineTask

        t = PipelineTask(browser_model=model,
                         dvc=dvc,
                         interpreted_age_browser_model=iamodel,
                         application=self.application)
        t.engine.nodes = self.nodes
        t.engine.node_factories = self.node_factories
        t.engine.predefined_templates = self.predefined_templates
        t.engine.load_predefined_templates()
        return t

    def _browser_model_factory(self):
        return SampleBrowserModel(application=self.application)

    def _interpreted_age_browser_model_factory(self):
        dvc = self.application.get_service(DVC)
        return InterpretedAgeBrowserModel(application=self.application,
                                          dvc=dvc)

    # defaults
    def _service_offers_default(self):
        so = self.service_offer_factory(protocol=SampleBrowserModel,
                                        factory=self._browser_model_factory)

        so1 = self.service_offer_factory(
            protocol=InterpretedAgeBrowserModel,
            factory=self._interpreted_age_browser_model_factory)
        return [so, so1]

    def _preferences_panes_default(self):
        return [PipelinePreferencesPane]

    def _task_extensions_default(self):
        def data_menu():
            return SMenu(id='data.menu', name='Data')

        def ideogram_menu():
            return SMenu(id='ideogram.menu', name='Ideogram')

        def plot_group():
            return SGroup(id='plot.group')

        def reduction_group():
            return SGroup(id='reduction.group')

        def recall_group():
            return SGroup(id='recall.group')

        exts = self._get_extensions()
        extensions = [
            TaskExtension(actions=actions, task_id=eid)
            for eid, actions in exts
        ]

        additions = [
            SchemaAddition(
                factory=data_menu,
                path='MenuBar',
                before='tools.menu',
                after='view.menu',
            )
        ]

        for s, f, p in (('ideogram', ideogram_menu,
                         'MenuBar/data.menu/plot.group'),
                        ('plot', plot_group, 'MenuBar/data.menu'),
                        ('fit', reduction_group, 'MenuBar/data.menu'),
                        ('recall', recall_group, 'MenuBar/data.menu')):

            for eid, actions in exts:
                for ai in actions:
                    if ai.id.startswith('pychron.pipeline.{}'.format(s)):
                        additions.append(SchemaAddition(factory=f, path=p))
                        break

        extensions.append(TaskExtension(actions=additions))
        return extensions

    def _available_task_extensions_default(self):
        def idformat(tag):
            return 'pychron.pipeline.{}'.format(tag)

        pg = 'MenuBar/data.menu/plot.group'
        rg = 'MenuBar/data.menu/reduction.group'
        ig = 'MenuBar/data.menu/plot.group/ideogram.menu'
        reg = 'MenuBar/data.menu/recall.group'

        fit_actions = []
        for f, t in ((IsoEvolutionAction, 'iso_evo'), (BlanksAction, 'blanks'),
                     (ICFactorAction, 'icfactor'), (FluxAction, 'flux'),
                     (AnalysisTableAction, 'analysis_table'),
                     (FreezeProductionRatios, 'freeze_production'),
                     (MassSpecReducedAction, 'mass_spec_reduced')):
            fit_actions.append(
                SchemaAddition(factory=f,
                               id='pychron.pipeline.fit.{}'.format(t),
                               path=rg))
        plot_actions = []
        for f, t in ((IdeogramAction, 'ideogram'), (SubgroupIdeogramAction,
                                                    'subgroup_ideogram'),
                     (HybridIdeogramAction, 'hybrid_ideogram'),
                     (HistoryIdeogramAction, 'history_ideogram')):
            plot_actions.append(
                SchemaAddition(factory=f,
                               id='pychron.pipeline.ideogram.{}'.format(t),
                               path=ig))

        for f, t in ((SpectrumAction, 'spectrum'), (InverseIsochronAction,
                                                    'inverse_isochron'),
                     (SeriesAction, 'series'), (ExtractionAction,
                                                'extraction')):
            plot_actions.append(
                SchemaAddition(factory=f,
                               id='pychron.pipeline.plot.{}'.format(t),
                               path=pg))

        recall_actions = [
            SchemaAddition(factory=ConfigureRecallAction,
                           id='pychron.pipeline.recall.configure',
                           path='MenuBar/edit.menu')
        ]

        for f, t in ((RecallAction, 'recall'), (InterpretedAgeRecallAction,
                                                'interpreted_age_recall')):
            recall_actions.append(
                SchemaAddition(factory=f,
                               id='pychron.pipeline.recall.{}'.format(t),
                               path=reg))

        return [
            (self.id, '', 'Pipeline Tools', [
                SchemaAddition(id=idformat('reset_factory_defaults'),
                               factory=ResetFactoryDefaultsAction,
                               path='MenuBar/help.menu'),
                SchemaAddition(id=idformat('clear_analysis_sets'),
                               factory=ClearAnalysisSetsAction,
                               path='MenuBar/help.menu')
            ]),
            ('{}.plot'.format(self.id), '', 'Plot', plot_actions),
            ('{}.fit'.format(self.id), '', 'Fit', fit_actions),
            ('{}.recall'.format(self.id), '', 'Recall', recall_actions),
        ]

    def _tasks_default(self):
        return [
            TaskFactory(id='pychron.pipeline.task',
                        name='Pipeline',
                        accelerator='Ctrl+p',
                        factory=self._pipeline_factory)
        ]
Example #10
0
class HardwarePlugin(BaseTaskPlugin):
    id = 'pychron.hardware.plugin'
    managers = ExtensionPoint(List(Dict), id='pychron.hardware.managers')

    my_managers = List(contributes_to='pychron.hardware.managers')

    sources = List(contributes_to='pychron.video.sources')

    def _sources_default(self):
        return [('pvs://localhost:1081', 'Hardware')]

    def _my_task_extensions_default(self):
        return [
            TaskExtension(actions=[
                SchemaAddition(id='Flag Manager',
                               factory=OpenFlagManagerAction,
                               path='MenuBar/Tools'),
            ])
        ]

    def _tasks_default(self):
        return [
            TaskFactory(id='tasks.hardware',
                        name='Hardware',
                        factory=self._factory,
                        task_group='hardware')
        ]

    def _factory(self):
        man = self.application.get_service(HardwareManager)
        task = HardwareTask(manager=man)
        return task

    def _service_offers_default(self):

        so_hm = self.service_offer_factory(
            protocol=HardwareManager, factory=self._hardware_manager_factory)

        so_rhm = self.service_offer_factory(
            protocol=RemoteHardwareManager,
            factory=self._remote_hardware_manager_factory)

        so_fm = self.service_offer_factory(protocol=FlagManager,
                                           factory=self._flag_manager_factory)
        #        return [so, so1, so2]
        return [so_hm, so_rhm, so_fm]

    def _flag_manager_factory(self):
        return FlagManager(application=self.application)

    def _hardware_manager_factory(self):
        return HardwareManager(application=self.application)

    def _remote_hardware_manager_factory(self):
        return RemoteHardwareManager(application=self.application)

    def _preferences_panes_default(self):
        return [HardwarePreferencesPane]

    def _my_managers_default(self):
        return [
            dict(name='hardware', manager=self._hardware_manager_factory())
        ]

    #    def _system_lock_manager_factory(self):
    #        return SystemLockManager(application=self.application)

    def start(self):
        # if self.managers:
        from pychron.initializer import Initializer

        dp = DevicePreferences()
        afh = self.application.preferences.get(
            'pychron.hardware.auto_find_handle')
        awh = self.application.preferences.get(
            'pychron.hardware.auto_write_handle')
        if afh is not None:
            toBool = lambda x: True if x == 'True' else False
            dp.serial_preference.auto_find_handle = toBool(afh)
            dp.serial_preference.auto_write_handle = toBool(awh)

        ini = Initializer(device_prefs=dp)
        for m in self.managers:
            ini.add_initialization(m)

        # any loaded managers will be registered as services
        if not ini.run(application=self.application):
            self.application.exit()
            return

        # create the hardware server
        rhm = self.application.get_service(RemoteHardwareManager)
        bind_preference(rhm, 'enable_hardware_server',
                        'pychron.hardware.enable_hardware_server')
        bind_preference(rhm, 'enable_directory_server',
                        'pychron.hardware.enable_directory_server')

        rhm.bootstrap()

    def stop(self):

        #        rhm = self.application.get_service(RemoteHardwareManager)
        #        rhm.stop()

        if self.managers:
            for m in self.managers:
                man = m['manager']
                if man:
                    man.kill()
                    man.close_ui()

        for s in self.application.get_services(ICoreDevice):
            if s.is_scanable:
                s.stop_scan()
Example #11
0
class ExperimentPlugin(BaseTaskPlugin):
    id = 'pychron.experiment.plugin'

    events = ExtensionPoint(List(ExperimentEventAddition),
                            id='pychron.experiment.events')
    dock_pane_factories = ExtensionPoint(
        List, id='pychron.experiment.dock_pane_factories')
    activations = ExtensionPoint(List(Callable),
                                 id='pychron.experiment.activations')
    deactivations = ExtensionPoint(List(Callable),
                                   id='pychron.experiment.deactivations')

    def _signal_calculator_factory(self, *args, **kw):
        return SignalCalculator()

    def _sens_selector_factory(self, *args, **kw):
        return SensitivitySelector()

    def _run_history_factory(self, *args, **kw):
        dvc = self.application.get_service('pychron.dvc.dvc.DVC')

        rhm = RunHistoryModel(dvc=dvc)
        rhm.load()
        rh = RunHistoryView(model=rhm)

        return rh

    def _tasks_default(self):
        return [
            TaskFactory(id='pychron.experiment.task',
                        factory=self._task_factory,
                        name='Experiment',
                        image='applications-science',
                        task_group='experiment')
        ]

    def _task_factory(self):
        return ExperimentEditorTask(
            application=self.application,
            events=self.events,
            dock_pane_factories=self.dock_pane_factories,
            activations=self.activations,
            deactivations=self.deactivations)

    def _preferences_default(self):
        return self._preferences_factory('experiment')

    def _preferences_panes_default(self):
        return [
            ExperimentPreferencesPane, ConsolePreferencesPane,
            UserNotifierPreferencesPane, HumanErrorCheckerPreferencesPane
        ]

    def _file_defaults_default(self):
        return [('experiment_defaults', 'EXPERIMENT_DEFAULTS', False),
                ('ratio_change_detection', 'RATIO_CHANGE_DETECTION', False)]

    # def _actions_default(self):
    #     return [('pychron.open_experiment', 'Ctrl+O', 'Open Experiment'),
    #             ('pychron.new_experiment', 'Ctrl+N', 'New Experiment'),
    #             ('pychron.deselect', 'Ctrl+Shift+D', 'Deselect'),
    #             ('pychron.open_last_experiment', 'Alt+Ctrl+O', 'Open Last Experiment')]

    def _help_tips_default(self):
        return [
            'You can set the Analysis State colors in Preferences>Experiment',
            'You can set the color for Sniff, Signal, and Baseline datapoints in Preferences>Experiment',
            'If the last analysis fails to save you can recover it using Tools/Recover Last Analysis'
        ]

    def _task_extensions_default(self):
        exts = self._get_extensions()
        extensions = [
            TaskExtension(actions=actions, task_id=eid)
            for eid, actions in exts
        ]

        additions = []

        # eflag = False
        for eid, actions in exts:
            for ai in actions:
                # if not eflag and ai.id.startswith('pychron.experiment.edit'):
                if ai.id.startswith('pychron.experiment.edit'):
                    # eflag = True
                    additions.append(
                        SchemaAddition(
                            id='experiment.edit',
                            factory=lambda: SGroup(id='experiment.group'),
                            path='MenuBar/edit.menu'), )
                    break

        if additions:
            extensions.append(TaskExtension(actions=additions, task_id=''))

        sr_actions = [
            SchemaAddition(id='experiment.acquire_spectrometer',
                           factory=AcquireSpectrometerAction,
                           path='MenuBar/Tools'),
            SchemaAddition(id='experiment.release_spectrometer',
                           factory=ReleaseSpectrometerAction,
                           path='MenuBar/Tools')
        ]
        extensions.append(TaskExtension(actions=sr_actions, task_id=''))
        return extensions

    def _available_task_extensions_default(self):
        def idformat(t):
            return 'pychron.experiment.{}'.format(t)

        def eidformat(t):
            return 'pychron.experiment.edit.{}'.format(t)

        actions = []
        for path, fs in (('edit.menu', ((QueueConditionalsAction,
                                         'open_queue_conditionals'),
                                        (SystemConditionalsAction,
                                         'open_system_conditionals'))),
                         ('file.menu/Open',
                          ((OpenExperimentQueueAction, 'open_experiment'),
                           (OpenCurrentExperimentQueueAction,
                            'open_current_experiment'),
                           (OpenLastExperimentQueueAction,
                            'open_last_experiment'), (OpenPatternAction,
                                                      'open_pattern'))),
                         ('file.menu/New',
                          ((NewExperimentQueueAction, 'new_experiment'),
                           (NewPatternAction, 'new_pattern'))),
                         ('tools.menu',
                          ((OpenExperimentHistoryAction, 'launch_history'),
                           (SignalCalculatorAction, 'signal_calculator'),
                           (LastAnalysisRecoveryAction,
                            'last_analysis_recovery'), (RunHistoryAction,
                                                        'run_history_view'),
                           (MeltingPointCalibrationAction,
                            'melting_point_calibrator')))):

            path = 'MenuBar/{}'.format(path)
            for f, t in fs:
                actions.append(
                    SchemaAddition(id=idformat(t), factory=f, path=path))

        eactions = []
        for path, fs in (('edit.menu/experiment.group',
                          ((DeselectAction, 'deselect'), (ResetQueuesAction,
                                                          'reset'),
                           (SyncQueueAction, 'sync'), (UndoAction, 'undo'),
                           (ConfigureEditorTableAction, 'configure'))),
                         ('file.menu/Save',
                          ((SaveAsCurrentExperimentAction,
                            'save_as_current_experiment'), ))):
            for f, t in fs:
                eactions.append(
                    SchemaAddition(id=eidformat(t), factory=f, path=path))

        return [(self.id, '', 'Experiment', actions),
                ('{}.edit'.format(self.id), 'pychron.experiment.task',
                 'Experiment Edit', eactions)]

    def _service_offers_default(self):
        so_signal_calculator = self.service_offer_factory(
            protocol=SignalCalculator, factory=self._signal_calculator_factory)

        # so_image_browser = self.service_offer_factory(
        #     protocol=ImageBrowser,
        #     factory=self._image_browser_factory)

        so_sens_selector = self.service_offer_factory(
            protocol=SensitivitySelector, factory=self._sens_selector_factory)

        so_run_history = self.service_offer_factory(
            protocol=RunHistoryView, factory=self._run_history_factory)
        return [
            so_signal_calculator,
            # so_image_browser,
            so_sens_selector,
            so_run_history
        ]
class PychronTasksPlugin(BasePlugin):
    id = 'pychron.tasks.plugin'
    name = 'Tasks'
    preferences_panes = List(
        contributes_to='envisage.ui.tasks.preferences_panes')
    task_extensions = List(contributes_to='envisage.ui.tasks.task_extensions')

    actions = ExtensionPoint(List, id='pychron.actions')
    file_defaults = ExtensionPoint(List(Tuple),
                                   id='pychron.plugin.file_defaults')
    help_tips = ExtensionPoint(List, id='pychron.plugin.help_tips')
    available_task_extensions = ExtensionPoint(
        List, id='pychron.available_task_extensions')

    my_tips = List(contributes_to='pychron.plugin.help_tips')

    # def _application_changed(self):
    #     # defaults = (('use_advanced_ui', False), ('show_random_tip', True))
    #     defaults = (('show_random_tip', True),)
    #     try:
    #         self._set_preference_defaults(defaults, 'pychron.general')
    #     except AttributeError, e:
    #         print 'exception', e

    def start(self):
        self.info('Writing plugin file defaults')
        paths.write_file_defaults(self.file_defaults)

        self._set_user()
        self._random_tip()

    def _set_user(self):
        self.application.preferences.set('pychron.general.username',
                                         globalv.username)
        self.application.preferences.save()

    def _random_tip(self):
        if globalv.random_tip_enabled and to_bool(
                self.application.preferences.get(
                    'pychron.general.show_random_tip')):
            from pychron.envisage.tasks.tip_view import TipView

            t = random.choice(self.help_tips)

            tv = TipView(text=t)
            tv.edit_traits()

    def _my_tips_default(self):
        return [
            "Use <b>Help>What's New</b> to view the official ChangeLog for the current version",
            'Turn Off Random Tip two ways:<br><b>1. Preferences>General></b> Uncheck "Random Tip".</b><br>'
            '<b>2.</b> Set the flag <i>random_tip_enabled</i> to False in the initialization file',
            'Use <b>Window/Reset Layout</b> to change the current window back to its default "Look"',
            'Submit bugs or issues to the developers manually using <b>Help/Add Request/Report Bug</b>',
            'The current version of Pychron contains over 147K lines of code',
            'If menu actions are missing first check that the desired "Plugin" is enabled using <b>Help/Edit '
            'Initialization</b>. If "Plugin" is enabled, check that the desired action is enabled using '
            '<b>Help/Edit UI</b>.'
        ]

    def _preferences_panes_default(self):
        return [GeneralPreferencesPane, BrowserPreferencesPane]

    def _task_extensions_default(self):
        actions = [
            SchemaAddition(factory=EditInitializationAction,
                           id='edit_plugins',
                           path='MenuBar/help.menu')
        ]
        return [TaskExtension(actions=actions)]
Example #13
0
class HardwarePlugin(BaseTaskPlugin):
    id = 'pychron.hardware.plugin'
    managers = ExtensionPoint(List(Dict),
                              id='pychron.hardware.managers')

    # my_managers = List(contributes_to='pychron.hardware.managers')

    sources = List(contributes_to='pychron.video.sources')

    # def _my_managers_default(self):
    #     return [dict(name='hardware', manager=self._hardware_manager_factory())]

    #    def _system_lock_manager_factory(self):
    #        return SystemLockManager(application=self.application)
    _remote_hardware_manager = None
    # _remote_hardware_manager = Instance('pychron.remote_hardware.remote_hardware_manager.RemoteHardwareManager')
    # _hardware_manager = Instance('pychron.managers.hardware_manager.HardwareManager')

    def start(self):
        # if self.managers:
        from pychron.envisage.initialization.initializer import Initializer

        dp = DevicePreferences()
        afh = self.application.preferences.get('pychron.hardware.auto_find_handle')
        awh = self.application.preferences.get('pychron.hardware.auto_write_handle')
        if afh is not None:
            dp.serial_preference.auto_find_handle = to_bool(afh)
            dp.serial_preference.auto_write_handle = to_bool(awh)

        ini = Initializer(device_prefs=dp)
        for m in self.managers:
            ini.add_initialization(m)

        # any loaded managers will be registered as services
        if not ini.run(application=self.application):
            self.application.exit()
            # self.application.starting
            return

        # create the hardware proxy server
        ehs = to_bool(self.application.preferences.get('pychron.hardware.enable_hardware_server'))
        if ehs:
            # use_tx = to_bool(self.application.preferences.get('pychron.hardware.use_twisted', True))
            use_tx = True
            if use_tx:
                from pychron.tx.server import TxServer
                rhm = TxServer()
                node = self.application.preferences.node('pychron.hardware')
                ports = eval(node.get('ports', '[]'))
                factories = eval(node.get('factories', '[]'))

                for protocol in eval(node.get('pnames', '[]')):
                    factory = import_klass(factories[protocol])
                    port = int(ports[protocol])

                    exc = rhm.add_endpoint(port, factory(self.application))
                    if exc:
                        msg = 'Failed starting Command Server for "{}:{}". Please check that multiple ' \
                              'instances of pychron are not running on this computer. ' \
                              'Exception: {}'.format(protocol, port, exc)
                        self.warning_dialog(msg)
                    else:
                        self.info('Added Pychron Proxy Service: {}:{}'.format(protocol, port))

            # else:
            #     from pychron.remote_hardware.remote_hardware_manager import RemoteHardwareManager
            #     rhm = RemoteHardwareManager(application=self.application)

            self._remote_hardware_manager = rhm
            rhm.bootstrap()

    def stop(self):
        if self._remote_hardware_manager:
            self._remote_hardware_manager.kill()

        if self.managers:
            for m in self.managers:
                man = m['manager']
                if man:
                    man.kill()

        for s in self.application.get_services(ICoreDevice):
            if s.is_scanable:
                s.stop_scan()

    def _factory(self):
        task = HardwareTask(application=self.application)
        return task

    def _flag_manager_factory(self):
        return FlagManager(application=self.application)

    # def _hardware_manager_factory(self):
    #     return HardwareManager(application=self.application)

    # def _remote_hardware_manager_factory(self):
    #     return RemoteHardwareManager(application=self.application)

    def _service_offers_default(self):

        # so_hm = self.service_offer_factory(
        #     protocol=HardwareManager,
        #     factory=self._hardware_manager_factory)
        #
        # so_rhm = self.service_offer_factory(
        #     protocol=RemoteHardwareManager,
        #     factory=self._remote_hardware_manager_factory)

        so_fm = self.service_offer_factory(
            protocol=FlagManager,
            factory=self._flag_manager_factory)
        #        return [so, so1, so2]
        # return [so_hm, so_rhm, so_fm]
        # return [so_hm, so_fm]
        return [so_fm]

    def _preferences_panes_default(self):
        return [HardwarePreferencesPane]

    def _sources_default(self):
        return [('pvs://localhost:1081', 'Hardware')]

    def _task_extensions_default(self):
        return [TaskExtension(actions=[SchemaAddition(id='Flag Manager',
                                                      factory=OpenFlagManagerAction,
                                                      path='MenuBar/tools.menu'), ])]

    def _tasks_default(self):
        return [TaskFactory(id='tasks.hardware',
                            name='Hardware',
                            factory=self._factory,
                            image='configure-2',
                            task_group='hardware')]
class ExtractionLinePlugin(BaseTaskPlugin):
    id = 'pychron.extraction_line'
    name = 'ExtractionLine'
    extraction_line_manager_klass = ExtractionLineManager
    plugin_canvases = ExtensionPoint(
        List(Dict), id='pychron.extraction_line.plugin_canvases')

    def _preferences_default(self):
        return self._preferences_factory('extractionline')

    # def set_preference_defaults(self):
    #     self._set_preference_defaults((('canvas_path', os.path.join(paths.canvas2D_dir, 'canvas.xml')),
    #                                    ('canvas_config_path', os.path.join(paths.canvas2D_dir, 'canvas_config.xml')),
    #                                    ('valves_path', os.path.join(paths.extraction_line_dir, 'valves.xml'))),
    #                                    'pychron.extraction_line')

    def test_gauge_communication(self):
        return self._test('test_gauge_communication')

    def test_valve_communication(self):
        return self._test('test_valve_communication')

    def _test(self, func):

        man = self.application.get_service(ExtractionLineManager)
        return getattr(man, func)()

    def _factory(self):
        elm = self.extraction_line_manager_klass(application=self.application)
        elm.bind_preferences()
        elm.plugin_canvases = self.plugin_canvases

        return elm

    def _runner_factory(self):
        runner = PyScriptRunner()
        return runner

    # defaults
    def _task_extensions_default(self):
        ex = [
            TaskExtension(actions=[
                SchemaAddition(id='refresh_canvas',
                               factory=RefreshCanvasAction,
                               path='MenuBar/tools.menu')
            ])
        ]

        if self.application.get_plugin('pychron.pyscript.plugin'):

            actions = []
            for f in list_directory2(paths.procedures_dir,
                                     extension='.py',
                                     remove_extension=True):
                actions.append(
                    SchemaAddition(id='procedure.{}'.format(f),
                                   factory=procedure_action(
                                       f, self.application),
                                   path='MenuBar/procedures.menu'))

            if actions:
                actions.insert(
                    0,
                    SchemaAddition(id='procedures.menu',
                                   before='window.menu',
                                   after='tools.menu',
                                   factory=lambda: SMenu(name='Procedures',
                                                         id='procedures.menu'),
                                   path='MenuBar'))

                ex.append(TaskExtension(actions=actions))
            else:
                self.warning('no procedure scripts located in "{}"'.format(
                    paths.procedures_dir))
        return ex

    def _service_offers_default(self):
        """
        """
        so = self.service_offer_factory(protocol=ExtractionLineManager,
                                        factory=self._factory)
        so1 = self.service_offer_factory(protocol=IPyScriptRunner,
                                         factory=self._runner_factory)

        return [so, so1]

    def _managers_default(self):
        """
        """
        return [
            dict(name='extraction_line',
                 plugin_name=self.name,
                 manager=self.application.get_service(ExtractionLineManager))
        ]

    def _tasks_default(self):
        ts = [
            TaskFactory(id='pychron.extraction_line',
                        name='Extraction Line',
                        factory=self._task_factory,
                        accelerator='Ctrl+E',
                        task_group='hardware')
        ]
        return ts

    def _task_factory(self):
        elm = self.application.get_service(ExtractionLineManager)
        t = ExtractionLineTask(manager=elm)
        return t

    def _preferences_panes_default(self):
        return [ExtractionLinePreferencesPane, ConsolePreferencesPane]