コード例 #1
0
class GUIApplication(Application):
    """ The entry point for an Envisage GUI application.

    This class handles the life-cycle of a Pyface GUI.  Plugins can
    display windows via mechinisms such as edit_traits().
    
    This is intended to be a very simple shell for lifting an existing
    pure TraitsUI or Pyface (or even Qt) app into an Envisage app.
    
    More sophisticated applications should use Tasks.
    
    """

    #### 'GUIApplication' interface #########################################

    #: The PyFace GUI for the application.
    gui = Supports('pyface.i_gui.IGUI')

    #: The splash screen for the application. By default, there is no splash
    #: screen.
    splash_screen = Supports('pyface.i_splash_screen.ISplashScreen')

    #### Application lifecycle events #########################################

    #: Fired after the GUI event loop has been started.
    application_initialized = Event

    ###########################################################################
    # 'IApplication' interface.
    ###########################################################################

    def run(self):
        """ Run the application.

        Returns:
        --------
        Whether the application started successfully (i.e., without a veto).
        
        """

        # Make sure the GUI has been created (so that, if required, the splash
        # screen is shown).
        gui = self.gui

        started = self.start()
        if started:
            gui.set_trait_later(self, 'application_initialized', self)
            # Start the GUI event loop.  The application will block here.
            gui.start_event_loop()

            # clean up plugins once event loop stops
            self.stop()

        return started

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

    def _gui_default(self):
        from pyface.api import GUI
        return GUI(splash_screen=self.splash_screen)
コード例 #2
0
class SurveyLineGroup(HasTraits):
    """ An interface representing a group of survey lines """

    #: the user-defined name of the group
    name = Str

    #: the survey lines in the group
    survey_lines = List

    #: the lake depth algorithm to apply to the group
    lake_depth_algorithm = Supports(IAlgorithm)

    #: the preimpoundment depth algorithm to apply to the group
    preimpoundment_depth_algorithm = Supports(IAlgorithm)

    # XXX may want to add some analysis data here that is applied to the lines
    # in this group (eg. contrast settings, data view, etc.) so users can have
    # consistent settings for viewing a collection of lines

    def add_survey_lines(self, lines):
        """ Add lines to the group """
        lines_added = [line for line in lines if line not in self.survey_lines]
        self.survey_lines += lines_added

    def remove_survey_lines(self, lines):
        """ Remove lines from the group """
        lines_removed = [line for line in lines if line in self.survey_lines]
        self.survey_lines[:] = [
            line for line in self.survey_lines if line not in lines_removed
        ]
コード例 #3
0
class TraitsHolder(HasTraits):

    a_no = Instance(IAverage, adapt="no")
    a_yes = Instance(IAverage, adapt="yes")
    a_default = Instance(IAverage, adapt="default")
    list_adapted_to = Supports(IList)
    foo_adapted_to = Supports(IFoo)
    foo_plus_adapted_to = Supports(IFooPlus)
    list_adapts_to = AdaptsTo(IList)
    foo_adapts_to = AdaptsTo(IFoo)
    foo_plus_adapts_to = AdaptsTo(IFooPlus)
コード例 #4
0
class ISurveyLineGroup(Interface):
    """ An interface representing a group of survey lines """

    #: the user-defined name of the group
    name = Str

    #: the survey lines in the group
    survey_lines = List

    #: the lake depth algorithm to apply to the group
    lake_depth_algorithm = Supports(IAlgorithm)

    #: the preimpoundment depth algorithm to apply to the group
    preimpoundment_depth_algorithm = Supports(IAlgorithm)
コード例 #5
0
ファイル: survey.py プロジェクト: bklappauf/hydropick
class Survey(HasTraits):
    """ The a basic implementation of the ISurvey interface

    A survey has a lake, a set of survey lines, and a collection of
    user-assigned line groups.

    """
    # XXX there should probably be some other survey metadata saved in this object

    #: The name of the survey
    name = Str

    #: Notes about the survey as a whole
    comments = Str

    #: The lake being surveyed
    lake = Supports(ILake)

    #: The lines in the survey
    survey_lines = List(Supports(ISurveyLine))

    #: The groupings of survey lines
    survey_line_groups = List(Supports(ISurveyLineGroup))

    #: The core samples taken in the survey
    core_samples = List(Supports(ICoreSample))

    #: backend hdf5 file
    hdf5_file = File

    def add_survey_line_group(self, group):
        """ Create a new line group, optionally with a set of lines """
        self.survey_line_groups.append(group)
        logger.debug("Added survey line group '{}'".format(group.name))

    def insert_survey_line_group(self, index, group):
        """ Create a new line group, optionally with a set of lines """
        self.survey_line_groups.insert(index, group)
        logger.debug("Inserted survey line group '{}' at index {}".format(
            group.name, index))

    def delete_survey_line_group(self, group):
        """ Delete a line group, returning its index """
        index = self.survey_line_groups.index(group)
        self.survey_line_groups.remove(group)
        logger.debug("Removed survey line group '{}' from index {}".format(
            group.name, index))
        return index
コード例 #6
0
class MOTDModelView(ModelView):
    
    #: the current message we are displaying
    message = Supports('motd.model.i_message.IMessage')
    
    #: the text of the message, formatted using HTML
    html = Property(HTML, depends_on=['message.text', 'message.author'])
    
    @cached_property
    def _get_html(self):
        html = html_template.format(message=self.message)
        return html

    def _model_changed(self, new):
        if new:
            self.message = self.model.get_message()

    def _model_default(self):
        from motd.model.motd import MOTD
        return MOTD()

    def _message_default(self):
        message = self.model.get_message()
        return message

    def on_new_message(self, event=None):
        """ Get a new message from the MOTD instance """
        self.message = self.model.get_message()

    view = View(
        Item('html', show_label=False),
        width=400, height=200,
        title="Message of the day",
        buttons=[new_message_action],
    )
コード例 #7
0
ファイル: motd.py プロジェクト: cardsrock10/Python-Training
class MOTD(HasTraits):
    """ A 'Message of the Day' implementation """

    # The list of possible messages.
    messages = List(Supports(IMessage), minlen=1)

    ###########################################################################
    # 'IMOTD' interface.
    ###########################################################################

    def get_message(self):
        """ Prints a random message. """
        from random import choice

        message = choice(self.messages)
        return message

    ###########################################################################
    # 'MOTD' interface.
    ###########################################################################

    def _messages_default(self):
        """ The default message list to use when no other messages are provided """
        from message import Message
        messages = [
            Message(author='Anon',
                    text='Work hard and be good to your Mother'),
        ]
        return messages
コード例 #8
0
ファイル: i_survey.py プロジェクト: bklappauf/hydropick
class ISurvey(Interface):
    """ The abstract interface for a survey object.

    A survey has a lake, a set of survey lines, and a collection of
    user-assigned line groups.

    """
    # XXX there should probably be some other survey metadata saved in this object

    #: The name of the survey
    name = Str

    #: Notes about the survey as a whole
    comments = Str

    #: The lake being surveyed
    lake = Supports(ILake)

    #: The lines in the survey
    survey_lines = List(Supports(ISurveyLine))

    #: The groupings of survey lines
    survey_line_groups = List(Supports(ISurveyLineGroup))

    #: The core samples taken in the survey
    core_samples = List(Supports(ICoreSample))

    def add_survey_line_group(self, group):
        """ Create a new line group, optionally with a set of lines """
        raise NotImplementedError

    def insert_survey_line_group(self, index, group):
        """ Create a new line group, optionally with a set of lines """
        raise NotImplementedError

    def delete_survey_line_group(self, group):
        """ Delete a line group, returning its index """
        raise NotImplementedError
コード例 #9
0
class MOTDStartupPlugin(Plugin):
    """ The 'Message of the Day' gui plugin.

    This plugin prints a MOTD on application startup.

    """

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

    # The plugin's unique identifier.
    id = 'motd.motd_startup'

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

    @on_trait_change("application:started")
    def on_application_started(self):
        """ Print the 'Message of the Day' to stdout! """

        from motd.util import print_message

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

        # ... and print it.
        print_message(message)

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

    motd = Supports('motd.model.i_motd.IMOTD')

    def _motd_default(self):
        """  Create MOTD instance using list of messages """

        # Only do imports when you need to! This makes sure that the import
        # only happens when somebody needs the motd attribute.
        from motd.model.i_motd import IMOTD

        # ask the application for an IMOTD instance
        motd = self.application.get_service(IMOTD)
        return motd
コード例 #10
0
class SurveyDataPane(TraitsDockPane):
    """ The dock pane holding the data view of the survey """

    id = 'hydropick.survey_data'
    name = 'Survey'

    #: reference to the task's undo manager
    undo_manager = DelegatesTo('task')

    #: proxy for the task's current survey line
    current_survey_line = DelegatesTo('task')

    #: proxy for the task's current survey line
    current_survey_line_group = DelegatesTo('task')

    #: reference to the task's selected survey lines
    selected_survey_lines = DelegatesTo('task')

    #: reference to the survey lines
    survey = Supports(ISurvey)

    #: proxy for the survey's name
    survey_name = UndoingDelegate('survey',
                                  'name',
                                  'undo_manager',
                                  trait=Str,
                                  name='Survey Name',
                                  mergeable=True)

    #: proxy for the survey's comments field
    survey_comments = UndoingDelegate('survey',
                                      'comments',
                                      'undo_manager',
                                      trait=Str,
                                      name='Survey Comments',
                                      mergeable=True)

    #: the currently selected items in the view
    selection = List

    #: the item which has just been activated by double-clicking
    activated = Event

    def _selected_survey_lines_changed(self):
        self.selection = self.selected_survey_lines

    @on_trait_change('selection,selection_items')
    def _selection_updated(self):
        selection = self.selection
        if all(isinstance(item, ISurveyLine) for item in self.selection):
            self.selected_survey_lines = selection
        selected_lines = set()
        for item in selection:
            if isinstance(item, ISurveyLine):
                selected_lines.add(item)
            elif isinstance(item, (ISurveyLineGroup, ISurvey)):
                selected_lines |= set(item.survey_lines)
        if len(selection) == 1 and isinstance(selection[0], ISurveyLineGroup):
            self.current_survey_line_group = selection[0]
        self.selected_survey_lines = list(sorted(selected_lines))

    def _activated_changed(self, item):
        print 'activated', item
        if isinstance(item, ISurveyLine):
            self.current_survey_line = item
        elif isinstance(item, ISurveyLineGroup):
            self.current_survey_line_group = item
            self.selected_survey_lines = item.survey_lines

    view = View(Item('survey', editor=survey_line_tree, show_label=False), )
コード例 #11
0
class MyView(HasTraits):
    tree_node = Supports(ITreeNode)
コード例 #12
0
ファイル: survey_line.py プロジェクト: bklappauf/hydropick
class SurveyLine(HasTraits):
    """ A class representing a single survey line """

    #: the user-visible name for the line
    name = Str

    #: sample locations, an Nx2 array (example: easting/northing?)
    locations = Array(shape=(None, 2))

    #: specifies unit for values in locations array
    locations_unit = Str('feet')

    #: array of associated lat/long available for display
    lat_long = Array(shape=(None, 2))

    #: a dictionary mapping frequencies to intensity arrays
    frequencies = Dict

    #: complete trace_num set. array = combined freq_trace_num arrays
    trace_num = Array

    #: array of trace numbers corresponding to each intensity pixel/column
    #: ! NOTE ! starts at 1, not 0, so need to subtract 1 to use as index
    freq_trace_num = Dict

    #: relevant core samples
    core_samples = List(Supports(ICoreSample))

    #: depth of the lake at each location as generated by various soruces
    lake_depths = Dict(Str, Supports(IDepthLine))

    #: name of final choice for line used as current lake depth for volume calculations
    final_lake_depth = Str

    # and event fired when the lake depths are updated
    lake_depths_updated = Event

    #: The navigation track of the survey line in map coordinates
    navigation_line = Instance(LineString)

    #: pre-impoundment depth at each location as generated by various soruces
    preimpoundment_depths = Dict(Str, Supports(IDepthLine))

    #: name of final choice for pre-impoundment depth to track sedimentation
    final_preimpoundment_depth = Str

    # and event fired when the lake depth is updated
    preimpoundment_depths_updated = Event

    # power values for entire trace set
    power = Array

    # gain values for entire trace set
    gain = Array

    #: Depth corrections:
    #:  depth = (pixel_number_from_top * pixel_resolution) + draft - heave
    #: distance from sensor to water. Constant offset added to depth
    draft = CFloat

    #: array of depth corrections.  Changes vertical offset of each column.
    heave = Array

    #: pixel resolution, depth/pixel
    pixel_resolution = CFloat

    # XXX probably other metadata should be here
    # if some check results in a bad survey line then some text should be
    # put here stating why or where the check was.
    bad_survey_line = Str('')

    def load_data(self, hdf5_file):
        ''' Called by UI to load this survey line when selected to edit
        '''
        # read in sdi dictionary.  Only use 'frequencies' item.
        # sdi_dict_separated = binary.read(self.data_file_path)
        # sdi_dict_raw = binary.read(self.data_file_path, separate=False)
        # freq_dict_list = sdi_dict_separated['frequencies']

        from ..io import survey_io

        # read frequency dict from hdf5 file.
        sdi_dict_raw = survey_io.read_sdi_data_unseparated_from_hdf(hdf5_file,
                                                                    self.name)
        freq_dict_list = survey_io.read_frequency_data_from_hdf(hdf5_file,
                                                                self.name)

        # fill frequncies and freq_trace_num dictionaries with freqs as keys.
        for freq_dict in freq_dict_list:
            key = freq_dict['kHz']
            # transpose array to go into image plot correctly oriented
            intensity = freq_dict['intensity'].T
            self.frequencies[str(key)] = intensity
            self.freq_trace_num[str(key)] = freq_dict['trace_num']

        # for all other traits, use un-freq-sorted values
        self.trace_num = sdi_dict_raw['trace_num']
        self.locations = np.vstack([sdi_dict_raw['interpolated_easting'],
                                   sdi_dict_raw['interpolated_northing']]).T
        self.lat_long = np.vstack([sdi_dict_raw['latitude'],
                                  sdi_dict_raw['longitude']]).T
        self.draft = (np.mean(sdi_dict_raw['draft']))
        self.heave = sdi_dict_raw['heave']
        self.pixel_resolution = (np.mean(sdi_dict_raw['pixel_resolution']))
        self.power = sdi_dict_raw['power']
        self.gain = sdi_dict_raw['gain']
        self.array_sizes_ok()
        filename = os.path.basename(sdi_dict_raw['filepath'])
        sdi_surface = DepthLine(
            name='current_surface_from_bin',
            survey_line_name=self.name,
            line_type='current surface',
            source='sdi_file',
            source_name=filename,
            index_array=self.trace_num - 1,
            depth_array=sdi_dict_raw['depth_r1']            
        )
        survey_io.write_depth_line_to_hdf(hdf5_file, sdi_surface, self.name)
        # depth lines stored separately
        self.lake_depths = survey_io.read_pick_lines_from_hdf(
                                     hdf5_file, self.name, 'current')
        self.preimpoundment_depths = survey_io.read_pick_lines_from_hdf(
                                     hdf5_file, self.name, 'preimpoundment')

    def nearby_core_samples(self, core_samples, dist_tol=100):
        """ Find core samples from a list of CoreSample instances
        that lie within dist_tol units of this survey line.
        """
        def distance(core, line):
            """ Calculate distance between a core sample and a survey line
            """
            from shapely.geometry import Point
            return self.navigation_line.distance(Point(core.location))
        cores = [core for core in core_samples
                 if distance(core, self) < dist_tol]
        return cores
    
    def array_sizes_ok(self):
        ''' this is an check that the arrays for this line make sense
        All the non-separated arrays should be the same size and the
        trace_num array should be range(1,N).  This could be slightly
        more general by assuming and order array instead of contiguous'''
        name = self.name
        logger.info('Checking all array integrity for line {}'.format(name))
        arrays = ['trace_num', 'locations', 'lat_long', 'heave', 'power',
                  'gain']
        # N = self.trace_num.shape[0]
        # check = self.trace_num - np.arange(N) - 1
        # if np.any(check != 0):
        #     bad_traces = np.nonzero(check)[0] + 1
        #     values = self.trace_num[bad_traces - 1]
        #     print check, bad_traces, values
        #     s = '''trace_num not contiguous for array: {}.
        #     values of {} at traces {}
        #     '''.format(name, values, bad_traces)
        #     logger.warn(s)
        #     self.fix_trace_num(N, bad_traces, values)
        # now check rest of arrays
        
        from ..io import survey_io
        bad_indices, bad_vals = survey_io.check_trace_num_array(self.trace_num,
                                                                self.name)
        tn, fn = survey_io.fix_trace_num_arrays(self.trace_num,
                                                bad_indices,
                                                self.freq_trace_num)
        self.trace_num = tn
        self.freq_trace_num = fn
        N = len(tn)
        for a in arrays:
            if getattr(self, a).shape[0] != N:
                s = '{} is not size {}'.format(a, N)
                logger.warn(s)
                self.bad_survey_line = "Array sizes don't match on load"

    def fix_trace_num(self, N, bad_traces, values):
        for freq, trace_array in self.freq_trace_num.items():
            for t, v in zip(bad_traces, values):
                if v in trace_array:
                    i = np.floor((t - 1) / 3.0)
                    print 'freq trace i value is',freq, i, trace_array[i], i+1
                    trace_array[i] = t
            self.freq_trace_num[freq] = trace_array
        for f, v in self.freq_trace_num.items():
            print 'max is ', f, v.max(), v.shape
        self.trace_num = np.arange(1, N + 1)
コード例 #13
0
class DataContext(ListenableMixin, PersistableMixin, DictMixin):
    """ A simple context which fires events.
    """

    # The name of the context.
    name = Str()

    # The underlying dictionary.
    subcontext = Supports(IContext, factory=dict)

    #### IContext interface ####################################################

    def __contains__(self, key):
        return key in self.subcontext

    def __getitem__(self, key):
        return self.subcontext[key]

    def __setitem__(self, key, value):
        if not self.allows(value, key):
            raise ValueError("cannot assign value: %s = %s" % (key, value))
        # Figure out if the item was added or modified
        added = []
        modified = []
        if key in self.subcontext.keys():
            modified = [key]
        else:
            added = [key]

        self.subcontext[key] = value

        # Event fired so that GUI listeners can update
        self._fire_event(added=added, modified=modified)

    def __delitem__(self, key):
        if key in self.subcontext:
            del self.subcontext[key]
            self._fire_event(removed=[key])
        else:
            raise KeyError(key)

    def keys(self):
        """ Returns the list of keys available in the context.

        Returns
        -------
        keys : list of str
        """
        return self.subcontext.keys()

    # Expose DictMixin's get method over HasTraits'.
    get = DictMixin.get

    def __str__(self):
        # Maybe a good default string
        return '%s(name=%r)' % (type(self).__name__, self.name)

    def __repr__(self):
        # Maybe a good default representation
        return '%s(name=%r)' % (type(self).__name__, self.name)

    #### DictMixin interface ##################################################

    def __cmp__(self, other):
        # Dont allow objects of different inherited classes to be equal.
        # This WILL ALLOW different instances with different names but the
        # same keys and values to be equal
        #
        # Subclasses may wish to override this to compare different attributes
        #

        cls_cmp = cmp(self.__class__, other.__class__)
        if cls_cmp != 0:
            return cls_cmp
        return DictMixin.__cmp__(self, other)

    #### IRestrictedContext interface ##########################################

    def allows(self, value, name=None):
        """ Determines whether this value is allowed in this context. Only
        strings are allowed for 'name'.

        Typically, this is used to limit the types of objects allowed into the
        context.  It could also be used to restrict specific values (ie. the
        shape of an array) and even on the name...

        Parameters
        ----------
        value : object
        name : str, optional

        Returns
        -------
        allowed : bool
        """
        # WORKAROUND: subclassing from DataContext can cause the adapter to put the real
        # context in subcontext if used in a class which adapts. In this case
        # call allows on the real context. This only happens occasionally and is a bug.
        if self.subcontext is not None and isinstance(self.subcontext,
                                                      DataContext):
            return self.subcontext.allows(value, name)
        return True

    #### ICheckpointable interface ############################################

    def checkpoint(self):
        """ Make a shallow copy of the context.

        Technically, this is actually a fairly deep copy. All of the object
        structure should be replicated, but the actual dictionary storage will
        be shallowly copied::

            copy = context.shallow_copy()
            copy[key] is context[key] for key in context.keys()

        These semantics are useful for saving out checkpointed versions of the
        context for implementing an undo/redo stack. They may not be useful for
        other purposes.

        Returns
        -------
        copy : IContext
        """
        copy = self.clone_traits()
        checkpointable_subcontext = adapt(self.subcontext, ICheckpointable)
        copy.subcontext = checkpointable_subcontext.checkpoint()
        return copy
コード例 #14
0
class Application(HasTraits):
    """ The main Hydropick application object """

    #: application data directory
    application_home = Directory

    #: the root logger instance
    logger = Instance('logging.Logger')

    #: application data directory
    logging_handler = Instance('logging.Handler')

    #: the PyFace GUI for the application
    gui = Supports('pyface.i_gui.IGUI')

    #: the splash-screen for the application
    splash_screen = Supports('pyface.i_splash_screen.ISplashScreen')

    #: the main task window
    task_window = Supports('pyface.tasks.task_window.TaskWindow')

    #: the main task window
    task = Instance('pyface.tasks.task.Task')

    def exception_handler(self, exc_type, exc_value, exc_traceback):
        """ Handle un-handled exceptions """
        if not isinstance(exc_value, Exception):
            # defer to usual exception handler
            sys.__excepthook__(exc_type, exc_value, exc_traceback)

        logging.error('Unhandled exception:', exc_info=(exc_type, exc_value, exc_traceback))

        from traceback import format_tb
        from pyface.api import MessageDialog

        informative = "{0}: {1}".format(exc_type.__name__, str(exc_value))
        detail = '\n'.join(format_tb(exc_traceback))
        dlg = MessageDialog(severity='error', message="Unhandled Exception",
                            informative=informative, detail=detail,
                            size=(800,600))
        dlg.open()

    def parse_arguments(self):
        import argparse
        parser = argparse.ArgumentParser(description="Hydropick: a hydrological survey editor")
        parser.add_argument('--import', help='survey data to import',
                            dest='import_', metavar='DIR')
        parser.add_argument('--with-picks', help='if included, then pre and pick files will be imported',
                            dest='with_picks_', action='store_true')
        parser.add_argument('-v', '--verbose', action='store_const', dest='logging',
                            const=logging.INFO, help='verbose logging')
        parser.add_argument('-q', '--quiet', action='store_const', dest='logging',
                            const=logging.WARNING, help='quiet logging')
        parser.add_argument('-d', '--debug', action='store_const', dest='logging',
                            const=logging.DEBUG, help='debug logging')
        args = parser.parse_args()
        return args

    def init(self):
        # set up logging
        self.logger.addHandler(self.logging_handler)

        # parse commandline arguments
        args = self.parse_arguments()
        if args.import_:
            from ..io.import_survey import import_survey
            survey = import_survey(args.import_, args.with_picks_)
            self.task.survey = survey
        if args.logging is not None:
            self.logger.setLevel(args.logging)

    def start(self):
        self.logger.info('Starting application')

        # override the exceptionhook to display MessageDialog
        sys.excepthook = self.exception_handler

        # set up tasks
        self.task_window.add_task(self.task)
        self.task_window.open()

        # and we're done successfully
        return True

    def run(self):
        # ensure GUI instance is created
        gui = self.gui

        started = self.start()
        if started:
            self.logger.info('Starting event loop')
            gui.start_event_loop()
            self.logger.info('Event loop finished')

        self.stop()

    def stop(self):
        self.logger.info('Stopping application')

    def cleanup(self):
        logging.shutdown()

    def _application_home_default(self):
        home = ETSConfig.application_home
        if not os.path.exists(home):
            os.makedirs(home)
        return home

    def _logger_default(self):
        return logging.getLogger()

    def _logging_handler_default(self):
        from logging.handlers import RotatingFileHandler
        logfile = os.path.join(self.application_home, 'hydropick.log')
        print logfile, self.application_home
        handler = RotatingFileHandler(logfile, backupCount=5)
        handler.doRollover()
        # format output
        datefmt = '%Y%m%d:%H%M%S'
        line_fmt = '%(asctime)s :: %(name)s : %(levelname)s : %(message)s'
        formatter = logging.Formatter(line_fmt, datefmt=datefmt)
        handler.setFormatter(formatter)
        return handler

    def _gui_default(self):
        from pyface.api import GUI
        return GUI(splash_screen=self.splash_screen)

    def _task_window_default(self):
        from pyface.tasks.api import TaskWindow
        window = TaskWindow(size=(960, 720))
        return window

    def _task_default(self):
        from .tasks.survey_task import SurveyTask
        return SurveyTask()
コード例 #15
0
class MultiContext(ListenableMixin, PersistableMixin, DictMixin):
    """ Wrap several subcontexts.
    """

    #: The name of the context.
    name = Str("multidummy")

    #: The underlying dictionary.
    subcontexts = List(Supports(IRestrictedContext, factory=DataContext))

    #: Suppress subcontext modified events
    veto_subcontext_modified = Bool(True)

    def __init__(self, *subcontexts, **traits):
        subcontexts = list(subcontexts)
        super(MultiContext, self).__init__(subcontexts=subcontexts, **traits)

    #### IContext interface ####################################################

    def __contains__(self, key):
        for c in self.subcontexts:
            if key in c:
                return True
        return False

    def __delitem__(self, key):
        """ Remove the given key with [] access.

        Only deletes the first instance of the key.

        Parameters
        ----------
        key : str

        Raises
        ------
        KeyError if the kew is not available in the context.
        """
        for c in self.subcontexts:
            try:
                del c[key]
                return
            except KeyError:
                continue
        raise KeyError(key)

    def __getitem__(self, key):
        for c in self.subcontexts:
            try:
                return c[key]
            except KeyError:
                continue
        raise KeyError(key)

    def __setitem__(self, key, value):
        """ Set item with [] access.

        The first subcontext which allows the key/value pair will get it. If an
        earlier subcontext has the key, but does not allow the assignment, then
        that key will be deleted. Later contexts with the key will be untouched.

        If the key/value pair cannot be assigned to anything, no deletion will
        take place.

        Parameters
        ----------
        key : str
        value : object

        Raises
        ------
        ValueError if the key is not permitted to be assigned that value.
        """

        # Let subtypes dictate compatibility independently of contained contexts
        if not self.allows(value, key):
            raise ValueError('Disallowed mapping: %s = %s' %
                             (key, safe_repr(value)))

        set = False
        blocking_contexts = []
        for c in self.subcontexts:
            if not set:
                if c.allows(value, key):
                    if key in c:
                        added = []
                        current_value = c[key]
                        try:
                            is_modified = bool(current_value != value)
                        except Exception:
                            is_modified = current_value is not value
                        if is_modified:
                            modified = [key]
                            c[key] = value
                        else:
                            modified = []
                    else:
                        added = [key]
                        modified = []
                        c[key] = value

                    set = True
                    break
                elif key in c:
                    # Record this context as blocking access to the final
                    # location of the value.
                    blocking_contexts.append(c)

        # Remove all blocking instances.
        for c in blocking_contexts:
            del c[key]

        if not set:
            raise ValueError('Disallowed mapping: %s = %s' %
                             (key, safe_repr(value)))

    def keys(self):
        return list(set(chain(*[c.keys() for c in self.subcontexts])))

    # Expose DictMixin's get method over HasTraits'.
    get = DictMixin.get

    def __str__(self):
        # Maybe a good default string
        subcontext_str = '[%s]' % ', '.join([str(x) for x in self.subcontexts])
        return '%s(name=%r, subcontexts=%s)' % (type(self).__name__, self.name,
                                                subcontext_str)

    def __repr__(self):
        # Maybe a good default representation
        return '%s(name=%r)' % (type(self).__name__, self.name)

    #### IRestrictedContext interface ##########################################

    def allows(self, value, name=None):
        for c in self.subcontexts:
            if c.allows(value, name=name):
                return True
        return False

    #### Trait Event Handlers ##################################################

    @on_trait_change('subcontexts:items_modified')
    def subcontexts_items_modified(self, event):
        """ Pass events up.
        """
        if event is Undefined:
            # Nothing to do.
            return

        event.veto = self.veto_subcontext_modified

        self._fire_event(added=event.added,
                         removed=event.removed,
                         modified=event.modified,
                         context=event.context)

    def _subcontexts_items_changed(self, event):
        """ Trait listener for items of subcontexts list.
        """
        added = []
        removed = []

        # Add to the list of items added
        if len(event.added):
            for context in event.added:
                added.extend(context.keys())

        # Add to the list of items removed
        if len(event.removed):
            for context in event.removed:
                removed.extend(context.keys())

        self._fire_event(added=added, removed=removed)

    #### ICheckpointable interface ############################################

    def checkpoint(self):
        """ Make a shallow copy of the context.

        Technically, this is actually a fairly deep copy. All of the object
        structure should be replicated, but the actual dictionary storage will
        be shallowly copied::

            copy = context.shallow_copy()
            copy[key] is context[key] for key in context.keys()

        These semantics are useful for saving out checkpointed versions of the
        context for implementing an undo/redo stack. They may not be useful for
        other purposes.

        Returns
        -------
        copy : IContext
        """
        copy = self.clone_traits()
        new_subcontexts = []
        for context in self.subcontexts:
            checkpointable_subcontext = adapt(context, ICheckpointable)
            new_subcontexts.append(checkpointable_subcontext.checkpoint())
        copy.subcontexts = new_subcontexts
        return copy
コード例 #16
0
class DepthLineView(HasTraits):
    """ View Class for working with survey line data to find depth profile.

    Uses a Survey class as a model and allows for viewing of various depth
    picking algorithms and manual editing of depth profiles.
    """

    #==========================================================================
    # Traits Attributes
    #==========================================================================

    # current data session with relevant info for the current line
    data_session = Instance(SurveyDataSession)

    # name of current line in editor
    survey_line_name = Property(depends_on=['data_session']
                                )

    # list of available depth lines extracted from survey line
    depth_lines = Property(depends_on=['data_session',
                                       'data_session.depth_lines_updated']
                           )

    # name of depth_line to view chosen from pulldown of all available lines.
    selected_depth_line_name = Str

    # name of hdf5_file for this survey in case we need to load survey lines
    hdf5_file = Str

    # current depth line object
    model = Instance(DepthLine)

    # set of arguments for algorithms.  Assume keyword.  makes dict
    args = Property(Str, depends_on=['model.args', 'model'])

    # arrays to plot
    index_array_size = Property(Int, depends_on=['model.index_array, model'])
    depth_array_size = Property(Int, depends_on=['model.depth_array, model'])

    # changes model to empty DepthLine for creating new line
    new_button = Button('New Line')

    # updates the data arrays for the selected line.  Apply does not do this
    update_arrays_button = Button('Update Data')

    # applys settings to  DepthLine updating object and updating survey line
    apply_button = Button('Apply')

    # applys settings each survey line in selected lines
    apply_to_group = Button('Apply to Group')

    # create local traits so that these options can be dynamically changed
    source_name = Str
    source_names = Property(depends_on=['model.source'])

    # flag allows line creation/edit to continue in apply method
    no_problem = Bool(False)

    # determines whether to show the list of selected groups and lines
    show_selected = Bool(False)

    # list of selected groups and lines by name str for information only
    selected = Property(List, depends_on=['current_survey_line_group',
                                    'selected_survey_lines'])

    # currently selected group
    current_survey_line_group = Supports(ISurveyLineGroup)

    # Set of selected survey lines (including groups) to apply algorithm to
    selected_survey_lines = List(Supports(ISurveyLine))

    # dict of algorithms
    algorithms = Dict

    #==========================================================================
    # Define Views
    #==========================================================================

    traits_view = View(
        'survey_line_name',
        HGroup(
            Item('show_selected', label='Selected(show)'),
            UItem('selected',
                  editor=ListEditor(style='readonly'),
                  style='readonly',
                  visible_when='show_selected')
                   ),
        Item('selected_depth_line_name', label='View Depth Line',
             editor=EnumEditor(name='depth_lines')),
        Item('_'),
        VGroup(Item('object.model.survey_line_name', style='readonly'),
               Item('object.model.name'),
               Item('object.model.line_type'),
               Item('object.model.source'),
               Item('source_name',
                    editor=EnumEditor(name='source_names')),
               Item('args',
                    editor=TextEditor(auto_set=False, enter_set=False),
                    tooltip=ARG_TOOLTIP,
                    visible_when='object.model.source=="algorithm"'
                    ),
               Item('index_array_size', style='readonly'),
               Item('depth_array_size', style='readonly'),
               Item('object.model.edited', style='readonly'),
               Item('object.model.color'),
               Item('object.model.notes',
                    editor=TextEditor(auto_set=False, enter_set=False),
                    style='custom',
                    height=75, resizable=True
                    ),
               Item('object.model.lock'),
               ),
        HGroup(UItem('new_button'),
               UItem('update_arrays_button',
                     tooltip=UPDATE_ARRAYS_TOOLTIP),
               UItem('apply_button',
                     tooltip=APPLY_TOOLTIP),
               UItem('apply_to_group',
                     tooltip=APPLY_TOOLTIP)
               ),
        height=500,
        resizable=True,
    )

    #==========================================================================
    # Defaults
    #==========================================================================

    def _selected_depth_line_name_default(self):
        ''' provide initial value for selected depth line in view'''
        return 'none'

    #==========================================================================
    # Notifications or Callbacks
    #==========================================================================
    def update_plot(self):
        self.data_session.depth_lines_updated = True

    @on_trait_change('new_button')
    def load_new_blank_line(self):
        ''' prepare for creation of new line
        if "none" is already selected, change depth line as if view_depth_line
        was "changed" to "none" (call change depth line with "none"). Otherwise
        change selected line to none and listener will handle it'''
        self.no_problem = True
        if self.selected_depth_line_name == 'none':
            self.change_depth_line(new='none')
        else:
            self.selected_depth_line_name = 'none'

    def depth_line_name_new(self, proposed_line, data_session=None):
        '''check that name is not in survey line depth lines already.
        Allow same name for PRE and POST lists since these are separate
        '''
        if data_session is None:
            data_session = self.data_session
        p = proposed_line
        # new names should begin and end with printable characters.
        p.name = p.name.strip()
        if p.line_type == 'current surface':
            used = p.name in data_session.lake_depths.keys()
        elif p.line_type == 'pre-impoundment surface':
            used = p.name in data_session.preimpoundment_depths.keys()
        else:
            self.log_problem('problem checking depth_line_name_new')
            used = True
        if used:
            s = 'name already used. Unlock to edit existing line'
            self.log_problem(s)
            self.model.lock = True
        return not used

    @on_trait_change('update_arrays_button')
    def update_arrays(self, new):
        ''' apply chosen method to fill line arrays
        '''
        logger.info('applying arrays update {}'.format(self.no_problem))
        model = self.model
        if model.lock:
            self.log_problem('locked so cannot change/create anything')

        # if line is 'none' then this is a new line --'added line'--
        # not a changed line. check name is new
        if self.selected_depth_line_name == 'none':
            self.depth_line_name_new(model)

        if self.no_problem:
            logger.info('no problem in update. try update')
            # name valid.  Try to update data.
            if model.source == 'algorithm':
                alg_name = model.source_name
                args = model.args
                logger.info('applying algorithm : {}'.format(alg_name))
                self.make_from_algorithm(alg_name, args)

            elif model.source == 'previous depth line':
                line_name = model.source_name
                self.make_from_depth_line(line_name)

            else:
                # source is sdi line.  create only from sdi data
                s = 'source "sdi" only available at survey load'
                self.log_problem(s)

    @on_trait_change('apply_button')
    def apply(self, new):
        ''' save current setting and data to current line'''
        model = self.model
        no_depth_array = self.depth_array_size == 0
        no_index_array = self.index_array_size == 0
        depth_notequal_index = self.depth_array_size != self.index_array_size
        if no_depth_array or no_index_array or depth_notequal_index:
            self.no_problem = False
            s = 'data arrays sizes are 0 or not equal'
            self.log_problem(s)
        if self.model.name.strip() == '':
            self.no_problem = False
            s = 'depth line has no printable name'
            self.log_problem(s)
        if self.model.name != self.selected_depth_line_name:
            self.depth_line_name_new(model)
        if model.lock:
            self.log_problem('locked so cannot change/create anything')
        # add to the survey line's appropriate dictionary
        if self.no_problem:
            logger.info('saving new line')
            ds = self.data_session
            if model.line_type == 'current surface':
                ds.lake_depths[self.model.name] = model
                ds.final_lake_depth = self.model.name
                key = 'POST_' + model.name
            else:
                ds.preimpoundment_depths[self.model.name] = model
                ds.final_preimpoundment_depth = self.model.name
                key = 'PRE_' + model.name
                       
            # set form to new line
            self.selected_depth_line_name = key
            self.update_plot()
        else:
            s = '''Could not make new line.
            Did you update Data?
            Check log for details'''
            self.log_problem(s)

    def check_name_and_arrays(self, depth_line=None):
        if depth_line is None:
            depth_line = self.model
        d_array_size = self._array_size(depth_line.depth_array)
        i_array_size = self._array_size(depth_line.index_array)
        no_depth_array = d_array_size == 0
        no_index_array = i_array_size == 0
        depth_notequal_index = d_array_size != i_array_size
        name = self.survey_line_name
        if no_depth_array or no_index_array or depth_notequal_index:
            self.no_problem = False
            s = 'data arrays sizes are 0 or not equal for {}'.format(name)
            self.log_problem(s)
        if self.model.name.strip() == '':
            self.no_problem = False
            s = 'depth line has no printable name'
            self.log_problem(s)

    @on_trait_change('apply_to_group')
    def apply_to_selected(self, new):
        ''' Apply current settings to all selected survey lines

        the will step through selected lines list and
        - check that valid algorithm selected
        - check if depth line exists (overwrite?)
        - check if line is approved (apply?)
        - check if line is bad
        - create line with name and algorithm, args color etc.
        - apply data and apply to make line
        - set as final (?)
        '''
        # save current model to duplicate
        model = self.model
        # list of selected lines
        selected = self.selected_survey_lines
        # check that algorithm is selected and valid
        not_alg = self.model.source != 'algorithm'
        alg_choices = self.algorithms.keys()
        good_alg_name = self.model.source_name in alg_choices
        if not_alg or not good_alg_name:
            self.no_problem = False
            self.log_problem('must select valid algorithm')
        else:
            self.no_problem = True

        # apply to each survey line
        if self.no_problem:
            # log parameters
            lines_str = '\n'.join([line.name for line in selected])
            s = '''Creating depth line for the following surveylines:
            {lines}
            with the following parameters:
            name = {name}
            algorithm = {algorithm}
            args = {args}
            color = {color}
            '''.format(lines=lines_str,
                   name=self.model.name,
                   algorithm=self.source_name,
                   args=self.model.args,
                   color=self.model.color)
            logger.info(s)
            for line in self.selected_survey_lines:
                if line.trace_num.size == 0:
                    # need to load line
                    line.load_data(self.hdf5_file)
                    
                self.model = deepcopy(model)
                self.model.survey_line_name = line.name
                alg_name = model.source_name
                args = model.args
                logger.info('applying algorithm : {}'.format(alg_name))
                self.make_from_algorithm(alg_name, args, survey_line=line)
                self.check_name_and_arrays(self.model)
                if self.no_problem:
                    lname = line.name
                    s = 'saving new depth line to surveyline {}'.format(lname)
                    logger.info(s)
                    print 'saving', self.model, line.name
                    if model.line_type == 'current surface':
                        line.lake_depths[self.model.name] = self.model
                        line.final_lake_depth = self.model.name
                    else:
                        line.preimpoundment_depths[self.model.name] = self.model
                        print line.preimpoundment_depths.keys()
                        line.final_preimpoundment_depth = self.model.name
                    
        self.model = model

    @on_trait_change('selected_depth_line_name')
    def change_depth_line(self, new):
        ''' selected line has changed so use the selection to change the
        current model to selected or create new one if none'''
        if new != 'none':
            # Existing line: edit copy of line until apply button clicked
            new_line = self.data_session.depth_dict[new]
            selected_line = deepcopy(new_line)
        else:
            selected_line = self.create_new_line()
        self.model = selected_line
        self.source_name = selected_line.source_name

    @on_trait_change('source_name')
    def _update_source_name(self):
        self.model.source_name = self.source_name

    #==========================================================================
    # Helper functions
    #==========================================================================
    def message(self, msg='my message'):
        dialog = MsgView(msg=msg)
        dialog.configure_traits()

    def log_problem(self, msg):
        ''' if there is a problem with any part of creating/updating a line,
        log it and notify user and set no_problem flag false'''
        self.no_problem = False
        logger.error(msg)
        self.message(msg)

    def make_from_algorithm(self, alg_name, args, model=None, survey_line=None):
        if model is None:
            model = self.model
        if survey_line is None:
            survey_line = self.data_session.survey_line
        algorithm = self.data_session.algorithms[alg_name]()
        trace_array, depth_array = algorithm.process_line(survey_line,
                                                          **args)
        model.index_array = np.asarray(trace_array, dtype=np.int32) - 1
        model.depth_array = np.asarray(depth_array, dtype=np.float32)
        return model

    def make_from_depth_line(self, line_name):
        source_line = self.data_session.depth_dict[line_name]
        self.model.index_array = source_line.index_array
        self.model.depth_array = source_line.depth_array

    def create_new_line(self):
        ''' fill in some default value and return new depth line object'''
        new_dline = DepthLine(
            survey_line_name=self.survey_line_name,
            name='Type New Name',
            line_type='pre-impoundment surface',
            source='algorithm',
            edited=False,
            lock=False
            )
        logger.info('creating new depthline template')
        return new_dline

    def _array_size(self, array=None):
        if array is not None:
            size = len(array)
        else:
            size = 0
        return size
    
    #==========================================================================
    # Get/Set methods
    #==========================================================================
    def _get_source_names(self):
        source = self.model.source
        if source == 'algorithm':
            names = self.data_session.algorithms.keys()
        elif source == 'previous depth line':
            names = self.data_session.depth_dict.keys()
        else:
            # if source is sdi the source name is just the file it came from
            names = [self.model.source_name]
        return names

    def _get_survey_line_name(self):
        if self.data_session:
            name = self.data_session.survey_line.name
        else:
            name = 'No Survey Line Selected'
        return name

    def _get_depth_lines(self):
        # get list of names of depthlines for the UI
        if self.data_session:
            lines = ['none'] + self.data_session.depth_dict.keys()
        else:
            lines = []
        return lines

    def _get_index_array_size(self):
        a = self.model.index_array
        if a is not None:
            size = len(a)
        else:
            size = 0
        return size

    def _get_depth_array_size(self):
        a = self.model.depth_array
        if a is not None:
            size = len(a)
        else:
            size = 0
        return size

    
    def _get_args(self):
        d = self.model.args
        s = ','.join(['{}={}'.format(k, v) for k, v in d.items()])
        return s

    def _set_args(self, args):
        ''' This is currently not very safe.  Probably better and easier just
        to pass a string that each algorithm will parse appropriately'''
        s = 'dict({})'.format(args)
        d = eval('dict({})'.format(args))
        mod_args = self.model.args
        if isinstance(d, dict):
            if mod_args != d:
                self.model.args = d
        else:
            s = '''Cannot make dictionary out of these arguments,
            Please check the format -- x=1, key=True, ...'''
            self.log_problem(s)
            if mod_args != {}:
                self.model.args = {}

    def _get_selected(self):
        '''make list of selected lines with selected group on top and all lines
        '''
        group_string = 'No Group Selected'
        all_lines = []
        if self.current_survey_line_group:
            group_name = self.current_survey_line_group.name
            group_string = 'GROUP: ' + group_name
        if self.selected_survey_lines:
            all_lines = [line.name for line in self.selected_survey_lines]
            num_lines = len(all_lines)
        else:
            num_lines = 0
        return [group_string] + ['LINES: {}'.format(num_lines)] + all_lines
コード例 #17
0
class SurveyTask(Task):
    """ A task for viewing and editing hydrological survey data """

    #### Task interface #######################################################

    id = 'hydropick.survey_task'
    name = 'Survey Editor'

    #### SurveyTask interface #################################################

    # XXX perhaps bundle the survey specific things into survey manager object?

    #: the survey object that we are viewing
    survey = Supports(ISurvey)

    #: the currently active survey line group
    current_survey_line_group = Supports(ISurveyLineGroup)

    #: the currently active survey line that we are viewing
    current_survey_line = Supports(ISurveyLine)  # Instance(SurveyLine)#

    # data object for maninpulating data for survey view and depth lines
    current_data_session = Instance(SurveyDataSession)

    #: the selected survey lines
    selected_survey_lines = List(Supports(ISurveyLine))

    #: reference to dictionary of available depth pic algorithms
    # (IAlgorithm Classes)
    algorithms = Dict

    # selected depth line
    selected_depth_line_name = Str

    # traits for managing Action state ########################################

    #: whether the undo stack is "clean"
    dirty = Property(Bool, depends_on='command_stack.clean')

    #: whether or not there are selected lines
    have_selected_lines = Property(Bool, depends_on='selected_survey_lines')

    #: whether or not there is a current group
    have_current_group = Property(Bool, depends_on='current_survey_line_group')

    #: the object that manages Undo/Redo stacks
    undo_manager = Supports(IUndoManager)

    #: the object that holds the Task's commands
    command_stack = Supports(ICommandStack)

    ###########################################################################
    # 'Task' interface.
    ###########################################################################

    def _default_layout_default(self):
        return TaskLayout(left=VSplitter(
            PaneItem('hydropick.survey_data'),
            PaneItem('hydropick.survey_map'),
            PaneItem('hydropick.survey_depth_line'),
        ))

    def _menu_bar_default(self):
        from apptools.undo.action.api import UndoAction, RedoAction
        menu_bar = SMenuBar(
            SMenu(
                SGroup(TaskAction(name="Import",
                                  method='on_import',
                                  accelerator='Ctrl+I'),
                       id='New',
                       name='New'),
                SGroup(TaskAction(name="Open",
                                  method='on_open',
                                  accelerator='Ctrl+O'),
                       id='Open',
                       name='Open'),
                SGroup(TaskAction(name="Save",
                                  method='on_save',
                                  accelerator='Ctrl+S',
                                  enabled_name='dirty'),
                       TaskAction(name="Save As...",
                                  method='on_save_as',
                                  accelerator='Ctrl+Shift+S',
                                  enabled_name='survey'),
                       id='Save',
                       name='Save'),
                id='File',
                name="&File",
            ),
            SMenu(
                # XXX can't integrate easily with TraitsUI editors :P
                SGroup(
                    UndoAction(undo_manager=self.undo_manager,
                               accelerator='Ctrl+Z'),
                    RedoAction(undo_manager=self.undo_manager,
                               accelerator='Ctrl+Shift+Z'),
                    id='UndoGroup',
                    name="Undo Group",
                ),
                SGroup(
                    TaskCommandAction(name='New Group',
                                      method='on_new_group',
                                      accelerator='Ctrl+Shift+N',
                                      command_stack_name='command_stack'),
                    TaskCommandAction(name='Delete Group',
                                      method='on_delete_group',
                                      accelerator='Ctrl+Delete',
                                      enabled_name='have_current_group',
                                      command_stack_name='command_stack'),
                    id='LineGroupGroup',
                    name="Line Group Group",
                ),
                id='Edit',
                name="&Edit",
            ),
            SMenu(
                SGroup(
                    TaskAction(name='Next Line',
                               method='on_next_line',
                               enabled_name='survey.survey_lines',
                               accelerator='Ctrl+Right'),
                    TaskCommandAction(name='Previous Line',
                                      method='on_previous_line',
                                      enabled_name='survey.survey_lines',
                                      accelerator='Ctrl+Left'),
                    id='LineGroup',
                    name='Line Group',
                ),
                SGroup(
                    CentralPaneAction(name='Location Data',
                                      method='on_show_location_data',
                                      enabled_name='show_view',
                                      accelerator='Ctrl+Shift+D'),
                    CentralPaneAction(name='Plot View Selection',
                                      method='on_show_plot_view_selection',
                                      enabled_name='show_view',
                                      accelerator='Ctrl+Shift+S'),
                    id='DataGroup',
                    name='Data Group',
                ),
                DockPaneToggleGroup(),
                id='View',
                name="&View",
            ),
            SMenu(
                SGroup(
                    CentralPaneAction(name='Image Adjustment',
                                      method='on_image_adjustment',
                                      enabled_name='show_view',
                                      accelerator='Ctrl+Shift+I'),
                    CentralPaneAction(name='Change Colormap',
                                      method='on_change_colormap',
                                      enabled_name='show_view'),
                    CentralPaneAction(name='Cursor Freeze Key = Alt+c',
                                      method='on_cursor_freeze',
                                      enabled_name='show_view'),
                    CentralPaneAction(name='Box zoom enable = z'),
                    id='ToolGroup',
                    name='Tool Group',
                ),
                id='Tools',
                name="&Tools",
            ),
        )
        return menu_bar

    def _tool_bars_default(self):
        toolbars = [
            SToolBar(TaskAction(name="Import",
                                method='on_import',
                                image=ImageResource('import')),
                     TaskAction(name="Open",
                                method='on_open',
                                image=ImageResource('survey')),
                     TaskAction(name="Save",
                                method='on_save',
                                enabled_name='dirty',
                                image=ImageResource('save')),
                     id='File',
                     name="File",
                     show_tool_names=False,
                     image_size=(24, 24)),
            SToolBar(TaskCommandAction(name='New Group',
                                       method='on_new_group',
                                       command_stack_name='command_stack',
                                       image=ImageResource('new-group')),
                     TaskCommandAction(name='Delete Group',
                                       method='on_delete_group',
                                       enabled_name='have_current_group',
                                       command_stack_name='command_stack',
                                       image=ImageResource('delete-group')),
                     TaskAction(name='Previous Line',
                                method='on_previous_line',
                                enabled_name='survey.survey_lines',
                                image=ImageResource("arrow-left")),
                     TaskAction(name='Next Line',
                                method='on_next_line',
                                enabled_name='survey.survey_lines',
                                image=ImageResource("arrow-right")),
                     id='Survey',
                     name="Survey",
                     show_tool_names=False,
                     image_size=(24, 24)),
        ]
        return toolbars

    def activated(self):
        """ Overriden to set the window's title.
        """
        self.window.title = self._window_title()

    def create_central_pane(self):
        """ Create the central pane: the editor pane.
        """
        from .survey_line_pane import SurveyLinePane
        pane = SurveyLinePane(survey_task=self)
        # listen for changes to the current survey line
        self.on_trait_change(lambda new: setattr(pane, 'survey_line', new),
                             'current_survey_line')
        return pane

    def create_dock_panes(self):
        """ Create the map pane and hook up listeners
        """
        from .survey_data_pane import SurveyDataPane
        from .survey_map_pane import SurveyMapPane
        from .survey_depth_pane import SurveyDepthPane

        data = SurveyDataPane(survey=self.survey)
        self.on_trait_change(lambda new: setattr(data, 'survey', new),
                             'survey')

        map = SurveyMapPane(survey=self.survey)
        self.on_trait_change(lambda new: setattr(map, 'survey', new), 'survey')

        depth = SurveyDepthPane()

        return [data, map, depth]

    def _survey_changed(self):
        from apptools.undo.api import CommandStack
        self.current_survey_line = None
        self.current_survey_line_group = None
        self.selected_survey_lines = []
        # reset undo stack
        self.command_stack = CommandStack(undo_manager=self.undo_manager)
        self.undo_manager.active_stack = self.command_stack

    @on_trait_change('survey.name')
    def update_title(self):
        if self.window and self.window.active_task is self:
            self.window.title = self._window_title()

    @on_trait_change('survey.survey_lines')
    def survey_lines_updated(self):
        if self.current_survey_line not in self.survey.survey_lines:
            self.current_survey_line = None
        self.selected_survey_lines[:] = [
            line for line in self.selected_survey_lines
            if line in self.survey_lines
        ]

    @on_trait_change('survey.survey_line_groups')
    def survey_line_groups_updated(self):
        if self.current_survey_line_group not in self.survey.survey_line_groups:
            self.current_survey_line_group = None

    ###########################################################################
    # 'SurveyTask' interface.
    ###########################################################################

    def on_import(self):
        """ Imports hydrological survey data """
        from pyface.api import DirectoryDialog, OK
        from ...io.import_survey import import_survey

        # ask the user for save if needed
        self._prompt_for_save()

        survey_directory = DirectoryDialog(message="Select survey to import:",
                                           new_directory=False)
        if survey_directory.open() == OK:
            survey = import_survey(survey_directory.path)
            self.survey = survey

    def on_open(self):
        """ Opens a hydrological survey file """
        self._prompt_for_save()
        raise NotImplementedError

    def on_save(self):
        """ Saves a hydrological survey file """
        raise NotImplementedError

    def on_save_as(self):
        """ Saves a hydrological survey file in a different location """
        raise NotImplementedError

    def on_new_group(self):
        """ Adds a new survey line group to a survey """
        from ...model.survey_line_group import SurveyLineGroup
        from ...model.survey_commands import AddSurveyLineGroup

        group = SurveyLineGroup(name='Untitled',
                                survey_lines=self.selected_survey_lines)
        command = AddSurveyLineGroup(data=self.survey, group=group)
        return command

    def on_delete_group(self):
        """ Deletes a survey line group from a survey """
        from ...model.survey_line_group import SurveyLineGroup
        from ...model.survey_commands import DeleteSurveyLineGroup

        group = self.current_survey_line_group
        command = DeleteSurveyLineGroup(data=self.survey, group=group)
        return command

    def on_next_line(self):
        """ Move to the next selected line """
        self.current_survey_line = self._get_next_survey_line()

    def on_previous_line(self):
        """ Move to the previous selected line """
        self.current_survey_line = self._get_previous_survey_line()

    def _get_dirty(self):
        return not self.command_stack.clean

    def _get_have_selected_lines(self):
        return len(self.selected_survey_lines) != 0

    def _get_have_current_group(self):
        return self.current_survey_line_group is not None

    def _command_stack_default(self):
        """ Return the default undo manager """
        from apptools.undo.api import CommandStack
        command_stack = CommandStack()
        return command_stack

    def _undo_manager_default(self):
        """ Return the default undo manager """
        from apptools.undo.api import UndoManager
        undo_manager = UndoManager(active_stack=self.command_stack)
        self.command_stack.undo_manager = undo_manager
        return undo_manager

    def _survey_default(self):
        from ...model.survey import Survey
        return Survey(name='New Survey')

    def _algorithms_default(self):
        name_list = algorithms.ALGORITHM_LIST
        classes = [getattr(algorithms, cls_name) for cls_name in name_list]
        names = [cls().name for cls in classes]
        logger.debug('found these algorithms: {}'.format(names))
        return dict(zip(names, classes))

    ###########################################################################
    # private interface.
    ###########################################################################

    def _window_title(self):
        """ Get the title of the window """
        name = self.survey.name
        return name if name else 'Untitled'

    def _prompt_for_save(self):
        """ Check if the user wants to save changes """
        from pyface.api import ConfirmationDialog, CANCEL, YES
        if not self.command_stack.clean:
            message = 'The current survey has unsaved changes. ' \
                      'Do you want to save your changes?'
            dialog = ConfirmationDialog(parent=self.window.control,
                                        message=message,
                                        cancel=True,
                                        default=CANCEL,
                                        title='Save Changes?')
            result = dialog.open()
            if result == CANCEL:
                return False
            elif result == YES:
                if not self._save():
                    return self._prompt_for_save()
        return True

    def _save(self):
        """ Save changes to a survey file """
        raise NotImplementedError

    def _get_next_survey_line(self):
        """ Get the next selected survey line,
            or next line if nothing selected """
        survey_lines = self.selected_survey_lines[:]
        previous_survey_line = self.current_survey_line

        # if nothing selected, use all survey lines
        if len(survey_lines) == 0:
            survey_lines = self.survey.survey_lines[:]

        # if still nothing, can't do anything reasonable, but we shouldn't
        # have been called
        if len(survey_lines) == 0:
            return None

        if previous_survey_line in survey_lines:
            index = (survey_lines.index(previous_survey_line)+1) % \
                len(survey_lines)
            return survey_lines[index]
        else:
            return survey_lines[0]

    def _get_previous_survey_line(self):
        """ Get the previous selected survey line,
            or previous line if nothing selected """
        survey_lines = self.selected_survey_lines[:]
        previous_survey_line = self.current_survey_line

        # if nothing selected, use all survey lines
        if len(survey_lines) == 0:
            survey_lines = self.survey.survey_lines[:]

        # if still nothing, can't do anything reasonable, but we shouldn't
        # have been called
        if len(survey_lines) == 0:
            return None

        if previous_survey_line in survey_lines:
            index = (survey_lines.index(previous_survey_line)-1) % \
                len(survey_lines)
            return survey_lines[index]
        else:
            return survey_lines[-1]
コード例 #18
0
class SurveyLinePane(TraitsTaskPane):
    """ The dock pane holding the map view of the survey """

    id = 'hydropick.survey_line'
    name = "Survey Line"

    survey = DelegatesTo('task')

    # current survey line viewed in editing pane.
    # listener is set up in 'task.create_central_pane' to change line.

    survey_line = Instance(ISurveyLine)

    current_data_session = DelegatesTo('task')

    core_samples = List(Supports(ICoreSample))

    # provides string with name of line for keys or info.
    line_name = Property(depends_on='survey_line.name')

    def _get_line_name(self):
        if self.survey_line:
            return self.survey_line.name
        else:
            return 'None'

    # instance of survey_line view which displays selected surveyline
    survey_line_view = Instance(SurveyLineView)

    # once a valid survey line is selected a datasession will
    # created and stored for quick retrieval on line changes
    data_session_dict = Dict(Str, Instance(SurveyDataSession))

    #: dictionary of (name, class) pairs for available depth pic algorithms
    algorithms = DelegatesTo('task')

    # set when survey_line is none to prevent showing invalid view.
    show_view = Bool(False)

    def on_image_adjustment(self):
        ''' Open dialog to adjust image (B&C : task menu)'''
        self.survey_line_view.image_adjustment_dialog()

    def on_cursor_freeze(self):
        ''' Currently just shows Key Binding to freeze cursor'''
        pass

    def on_change_colormap(self):
        ''' Open dialog to adjust image (B&C : task menu)'''
        self.survey_line_view.cmap_edit_dialog()

    def on_show_location_data(self):
        ''' Open dialog to show location data (task menu)'''
        self.survey_line_view.show_data_dialog()

    def on_show_plot_view_selection(self):
        ''' Open dialog to change which plots to view (task menu)'''
        self.survey_line_view.plot_view_selection_dialog()

    def _survey_line_changed(self):
        ''' handle loading of survey line view if valid line provide or else
        provide an empty view.
        '''
        if self.survey_line is None:
            logger.warning('current survey line is None')
            self.show_view = False
            self.survey_line_view = None
        else:
            data_session = self.data_session_dict.get(self.line_name, None)
            if data_session is None:
                # create new datasession object and entry for this surveyline.
                if self.survey_line.trace_num.size == 0:
                    # need to load data for this line
                    self.survey_line.load_data(self.survey.hdf5_file)
                data_session = SurveyDataSession(survey_line=self.survey_line,
                                                 algorithms=self.algorithms)
                self.data_session_dict[self.line_name] = data_session
            self.current_data_session = data_session

            # load relevant core samples into survey line
            # must do this before creating survey line view
            all_samples = self.survey.core_samples
            near_samples = self.survey_line.nearby_core_samples(all_samples)
            self.survey_line.core_samples = near_samples

            # create survey line view
            self.survey_line_view = SurveyLineView(model=data_session)
            self.show_view = True

    view = View(
        Item('survey_line_view',
             style='custom',
             show_label=False,
             visible_when='show_view'))
コード例 #19
0
class AsyncExecutingContext(ExecutingContext):
    """Sequential, threaded pipeline for execution of a Block.

    This uses a (possibly shared) Executor to asynchronously execute a block
    within a context. This will only consume a single worker at a time from the
    Executor. Changes to the context are made with the '__setitem__' method
    (dictionary update) and may trigger an execution of the block, with inputs
    restricted to the current, cumulative set of context changes.  Updates made
    while the block is being executed will result in an accumulation of the
    changes and execution of the block once the current execution completes.

    """

    # The code string
    code = Code

    # The codeblock that contains the pipeline code
    executable = Any

    # The local execution namespace
    subcontext = Supports(IListenableContext, factory=DataContext,
        comparison_mode=OBJECT_IDENTITY_COMPARE)

    # Fired when an exception occurs during execution.
    # Carries the exception.
    exception = Event

    # An Executor with which to dispatch work.
    executor = Instance(Executor)

    # The cumulative changes in the context since the last successful execution
    _context_delta = Dict

    # Flag used to suppress events when internally modifying subcontext
    _suppress_events = Bool(False)

    # A lock for shared data
    _data_lock = Instance(threading.Lock, ())

    _full_update = Bool(False)

    ###########################################################################
    #### AsyncExecutingContext Interface
    ###########################################################################

    def execute(self):
        """Update the context by executing the full block.
        This executes asynchronously.

        """
        self._full_update = True
        self._update()

    ###########################################################################
    #### ExecutingContext Interface
    ###########################################################################

    def execute_for_names(self, names=None):
        """ Possibly execute for a set of names which have been changed.

        Parameters
        ----------
        names : list of str, optional
            If not provided, then we'll just re-execute the whole thing.
        """

        if names is None or None in names:
            # A total re-execution has been requested.
            self.execute()
        else:
            # Copy the affected names into the delta to signal that the block
            # should be executed as though they changed
            affected_names = list(set(names))
            context = {}
            with self._data_lock:
                for key in affected_names:
                    context[key] = self.subcontext[key]
            with self._data_lock:
                self._context_delta.update(context)

            self._update()

    def __setitem__(self, name, value):
        """Assign a new value into the namespace.

        This triggers an asyncronous update of the context via execution of the
        block.  The 'updated' event will be fired when the execution completes.

        Parameters
        ----------
        name : str
            The name of the variable to assign
        value : any
            The value to be assigned

        """

        with self._data_lock:
            self._context_delta[name] = value
            context_copy = dict(self.subcontext)
            if name in context_copy:
                added = []
                modified = [name]
            else:
                modified = []
                added = [name]
            context_copy.update(self._context_delta)
        self._fire_event(added=added, modified=modified, context=context_copy)

        self._update()

    def __delitem__(self, name):
        del self.subcontext[name]

    ###########################################################################
    #### Concurrency methods and state machine.
    ###########################################################################

    # A lock for state changes.
    _state_lock = Instance(threading.Condition, ())

    # State flag: true iff execution is currently deferred.
    _execution_deferred = Bool(False)

    # Flag indicating whether there's a need to do a new update at
    # some point.
    _update_pending = Bool(False)

    # The current Future instance, or None.
    _future = Instance(Future)

    @contextlib.contextmanager
    def _update_state(self):
        """Helper for state updates.

        Applies the state update in the body of the associated with block;
        starts a new future execution if necessary, and notifies all listeners
        of the state change.

        """
        with self._state_lock:
            yield
            submit_new = (
                self._future is None and self._update_pending and
                not self._execution_deferred
            )
            if submit_new:
                self._update_pending = False
                self._future = self.executor.submit(self._worker)
            self._state_lock.notify_all()

        if submit_new:
            self._future.add_done_callback(self._callback)

    # Transition methods.

    def _update(self):
        """Update based on changes in _context_delta."""
        with self._update_state():
            self._update_pending = True

    def _callback(self, future):
        """Callback for the _worker"""
        with self._update_state():
            exception = future.exception()
            if exception is not None:
                self.exception = exception
            self._future = None

    def _pause(self):
        """Temporarily pause execution."""
        with self._update_state():
            self._execution_deferred = True

    def _resume(self):
        """Resume execution."""
        with self._update_state():
            self._execution_deferred = False

    def _wait(self):
        """Wait until all previous assignments are finished, possibly longer."""
        with self._state_lock:
            while self._future is not None:
                self._state_lock.wait()

    def _worker(self):
        """Worker for the _update method. """

        # Get the current context, apply then delete the delta
        with self._data_lock:
            updated_vars = set(self._context_delta.keys())
            self._suppress_events = True
            self.subcontext.update(self._context_delta)
            self._suppress_events = False
            context_delta = self._context_delta.copy()
            self._context_delta.clear()

        if not updated_vars and not self._full_update:
            # don't execute if no context delta or full update requested
            return

        if self._full_update:
            # signal to self.executable that we want the unrestricted block to
            # be executed and then clear the flag
            updated_vars = []
            self._full_update = False

        try:
            self.subcontext.defer_events = True
            self.executable.execute(self.subcontext, inputs=updated_vars)
            self.subcontext.defer_events = False
        except Exception:
            self.subcontext.defer_events = False
            # If we failed to execute, put changes back into _context_delta
            with self._data_lock:
                context_delta.update(self._context_delta)
                self._context_delta = context_delta
            raise

    ###########################################################################
    #### Trait defaults
    ###########################################################################

    def _executor_default(self):
        return ThreadPoolExecutor(max_workers=2)

    def _executable_default(self):
        return RestrictingCodeExecutable(code=self.code)

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

    def _code_changed(self, old, new):
        try:
            self.executable.code = new
        except Exception as e:
            self.exception = e
            return
        self.execute()

    def _defer_execution_changed(self, new):
        if new:
            self._pause()
        else:
            self._resume()

    @on_trait_change('subcontext.items_modified')
    def subcontext_items_modified(self, event):
        if event is Undefined:
            # Nothing to do.
            return

        if self._suppress_events:
            # These events were already manually fired in __setitem__()
            return

        event.veto = True
        self._fire_event(added=event.added, removed=event.removed,
            modified=event.modified, context=event.context)
コード例 #20
0
class FormulaExecutingContext(DataContext):
    """A class that manages execution between a code block, and spreadsheet like
    expressions that can be assigned to variables"""

    # The underlying context
    data_context = Supports(IListenableContext)

    # The block that is generated by the expressions in the context
    external_code = Str("pass\n")

    # The user-supplied block that is merged with the expressions
    external_block = Instance(Block)

    # Whether data has changed so as to need execution -- we may want
    # to store up a list of variables that have changed to aid this.
    execution_needed = Bool(False)

    # Whether to automatically re-execute when a variable changes
    auto_execute = Bool(True)

    # Whether to continue on exceptions from execution
    continue_on_errors = Bool(True)

    # Whether to swallow exceptions from the block
    swallow_exceptions = Bool(False)

    # A block representing the external block plus all of the expressions
    _composite_block = Instance(Block)
    # A block representing just the expressions
    _expression_block = Instance(Block)
    # The expressions to execute
    _expressions = Dict

    _globals_context = Instance(
        DataContext)  # May want to change to an interface spec
    # Whether we are currently executing on the DataContext
    _executing = Bool(False)

    # Dict interface
    def __setitem__(self, key, value):
        # FIXME this heuristic of looking for an = is somewhat brittle, but will work for our application
        if isinstance(value, str) and '=' in value:
            #This is a formula
            self._expressions[key] = value.split('=')[1]
            self._regenerate_expression_block()
            self._regenerate_composite_block()
            self.execute_block(outputs=[key])
        # FIXME we should be caching these restrictions
        else:
            self.data_context[key] = value

    def __getitem__(self, key):
        try:
            if key in self._expressions:
                return repr(
                    self.data_context[key]) + '=' + self._expressions[key]
            else:
                return self.data_context[key]
        except:
            import pdb
            pdb.set_trace()

    def keys(self):
        return list(self.data_context.keys())

    # FIXME implement __delitem__

    # Public interface

    def __init__(self, **kwtraits):
        if 'external_block' in kwtraits:
            self.external_block = kwtraits.pop('external_block')

        super(FormulaExecutingContext, self).__init__(**kwtraits)
        self._regenerate_expression_block()

        if self.external_block is None:
            self._external_code_changed(self.external_code)
        else:
            self.external_block = Block(
                [self.external_block,
                 Block(self.external_code)])

        self._regenerate_composite_block()

    def execute_block_if_auto(self, inputs=(), outputs=()):
        if self.auto_execute:
            self.execute_block(inputs=inputs, outputs=outputs)
        else:
            self.execution_needed = True
        return

    def execute_if_needed(self):
        if self.execution_needed:
            self.execute_block()
        return

    def execute_block(self, inputs=(), outputs=()):
        if self.data_context is None:
            return

        self._executing = True
        with self.data_context.deferred_events():
            if self._composite_block is None:
                self._regenerate_composite_block()
            if inputs != () or outputs != ():
                block = self._composite_block.restrict(inputs=inputs,
                                                       outputs=outputs)
            else:
                block = self._composite_block

        if self.swallow_exceptions:
            with self.data_context.deferred_events():
                try:
                    block.execute(self.data_context,
                                  self._globals_context,
                                  continue_on_errors=self.continue_on_errors)
                except Exception:
                    pass

        else:
            with self.data_context.deferred_events():
                block.execute(self.data_context,
                              self._globals_context,
                              continue_on_errors=self.continue_on_errors)

        self._executing = False

        execution_needed = False
        return

    def copy(self):
        """Make a deep copy of this FormulaExecutingContext.  Useful for plot shadowing."""
        new_datacontext = DataContext()
        for key in list(self.data_context.keys()):
            try:
                new_datacontext[key] = copy(self.data_context[key])
            except:
                new_datacontext[key] = self.data_context[key]

        # turn off auto-firing of events during construction, then turn it back on
        # after everything is set up

        new = FormulaExecutingContext(data_context=new_datacontext,
                                      external_block=self.external_block,
                                      execution_needed=self.execution_needed,
                                      auto_execute=False,
                                      _expressions=self._expressions)

        new._regenerate_expression_block()
        new._regenerate_composite_block()

        new.auto_execute = self.auto_execute

        return new

    # Trait listeners
    def _data_context_changed(self):
        self.execution_needed = True

    def _external_code_changed(self, new):
        self.external_block = Block(new)
        return

    def _external_block_changed(self, new):
        self._regenerate_composite_block()
        self.execute_block_if_auto()

    def _expression_block_changed(self, new):
        self._regenerate_composite_block()
        self.execute_block_if_auto()

    def _regenerate_composite_block(self):
        self._composite_block = Block(
            (self.external_block, self._expression_block))
        return

    def _regenerate_expression_block(self):
        exprs = [
            '%s = %s' % (var, expr)
            for var, expr in list(self._expressions.items())
        ]
        expression_code = '\n'.join(exprs) + '\n'
        self._expression_block = Block(expression_code)

    @on_trait_change('data_context:items_modified')
    def _data_context_items_modified(self, event):
        if not self._executing and isinstance(event, ItemsModified):
            inputs = set(self._composite_block.inputs).intersection(
                set(event.added + event.removed + event.modified))
            if len(inputs) == 0:
                return
            self.execute_block_if_auto(inputs=inputs)
        return

    def __composite_block_default(self):
        return Block('')

    def __expression_block_default(self):
        return Block('')
コード例 #21
0
class ExecutingContext(DataContext):
    """ A context which will execute code in response to changes in its
    namespace.
    """

    # Override to provide a more specific requirement.
    subcontext = Supports(IListenableContext,
                          factory=DataContext,
                          rich_compare=False)

    executable = Supports(IExecutable, factory=CodeExecutable)

    defer_execution = Bool(False)

    # When execution is deferred, we need to keep a list of these events.
    _deferred_execution_names = List(Str, transient=True)

    def execute_for_names(self, names):
        """ Possibly execute for a set of names which have been changed.

        Parameters
        ----------
        names : list of str, optional
            If not provided, then we'll just re-execute the whole thing.
        """
        if self.defer_execution:
            if names is None:
                names = [None]
            self._deferred_execution_names.extend(names)
            return

        if names is None or None in names:
            # A total re-execution has been requested.
            affected_names = None
        else:
            affected_names = list(set(names))
        with self.subcontext.deferred_events():
            self.executable.execute(self.subcontext, inputs=affected_names)

    #### IContext interface ####################################################

    def __setitem__(self, key, value):
        self.subcontext[key] = value
        self.execute_for_names([key])

    def __delitem__(self, key):
        del self.subcontext[key]
        self.execute_for_names([key])

    #### Trait Event Handlers ##################################################

    @on_trait_change('defer_execution')
    def _defer_execution_changed(self, old, new):
        self._defer_execution_changed_refire(new)

    def _defer_execution_changed_refire(self, new):
        if not new:
            self.execute_for_names(self._deferred_execution_names)

            # Reset the deferred names.
            self._deferred_execution_names = []

    @on_trait_change('subcontext.items_modified')
    def subcontext_items_modified(self, event):
        if event is Undefined:
            # Nothing to do.
            return

        event.veto = True

        self._fire_event(added=event.added,
                         removed=event.removed,
                         modified=event.modified,
                         context=event.context)
コード例 #22
0
ファイル: i_survey_line.py プロジェクト: bklappauf/hydropick
class ISurveyLine(Interface):
    """ A class representing a single survey line """

    #: the user-visible name for the line
    name = Str

    #: file location for this surveyline.  Used to load data when needed.
    data_file_path = Str

    #: sample locations, an Nx2 array (example: easting/northing?)
    locations = Array(shape=(None, 2))

    #: specifies unit for values in locations array
    locations_unit = Str

    #: array of associated lat/long available for display
    lat_long = Array(shape=(None, 2))

    #: a dictionary mapping frequencies to intensity arrays
    frequencies = Dict

    #: array of trace numbers corresponding to each intensity pixel columns
    freq_trace_num = Dict

    #: complete trace_num set. array = combined freq_trace_num arrays
    trace_num = Array

    #: relevant core samples
    core_samples = List(Supports(ICoreSample))

    #: depth of the lake at each location as generated by various soruces
    lake_depths = Dict(Str, Supports(IDepthLine))

    #: final choice for line used as current lake depth for volume calculations
    final_lake_depth = Instance(IDepthLine)

    # and event fired when the lake depths are updated
    lake_depths_updated = Event

    #: The navigation track of the survey line in map coordinates
    navigation_line = Instance(LineString)

    #: pre-impoundment depth at each location as generated by various soruces
    preimpoundment_depths = Dict(Str, Supports(IDepthLine))

    #: final choice for pre-impoundment depth to track sedimentation
    final_pre_imp_depth = Instance(IDepthLine)

    # and event fired when the lake depth is updated
    preimpoundment_depths_updated = Event

    # power values for entire trace set
    power = Array

    # gain values for entire trace set
    gain = Array

    ##: Depth corrections:
    ##:     depth = (pixel_number_from_top * pixel_resolution) + draft - heave

    #: distance from sensor to water. Constant offset added to depth
    draft = CFloat

    #: array of depth corrections to vertical offset of each column (waves etc)
    heave = Array

    #: pixel resolution, depth/pixel
    pixel_resolution = CFloat
コード例 #23
0
class TraitslikeContextWrapper(HasTraits):
    """ Wrap a context with traits, primarily for use with Traits UI.
    """

    # These traits are _private so as to avoid name conflicts with the traits
    # that mirror the context.

    # The context we are wrapping.
    _context = Supports(IListenableContext,
                        comparison_mode=OBJECT_IDENTITY_COMPARE)

    # Whether the communication between traits and context is currently active
    # or not.
    _synched = Bool(True)

    def add_traits(self, *args, **kwds):
        """ Add a set of traits to this object.

        Each trait corresponds to a name in the context. Each CTrait which is
        added will have .in_context metadata attribute as True.

        Parameters
        ----------
        ``*args`` : strs
            These attributes will be created as Any traits.
        ``**kwds`` : str -> Trait
            These attributes will be created as the specified Traits.

        Example
        -------
        >>> from traits.api import Int
        >>> from codetools.contexts.traitslike_context_wrapper import TraitsLikeContextWrapper
        >>> tcw = TraitsLikeContextWrapper(_context={})
        >>> tcw.add_traits('a', 'b', c=Int)
        """
        if self._context is not None:
            keys = list(self._context.keys())
        else:
            keys = []

        with self._synch_off():
            for name, trait in [(x, Any()) for x in args] + list(kwds.items()):
                self.add_trait(name, Trait(trait, in_context=True))
                # Set the value to that in the context.
                if name in keys:
                    setattr(self, name, self._context[name])
                else:
                    # ... or vice-versa.
                    self._context[name] = getattr(self, name)

    def _in_context_traits(self):
        """ Return a list of names of all of the traits which mirror context
        variables.
        """
        in_context_traits = []
        for name in self.trait_names():
            ctrait = self.trait(name)
            if ctrait.in_context:
                in_context_traits.append(name)
        return in_context_traits

    @on_trait_change('_context')
    def _context_changed(self, object, name, old, new):
        if old is not None:
            old.on_trait_change(self._context_items_modified,
                                'items_modified',
                                remove=True)
        if new is not None:
            new.on_trait_change(self._context_items_modified, 'items_modified')

    #@on_trait_change('_context.items_modified')
    def _context_items_modified(self, event):
        """ Receive change notifications from the context.
        """
        if not self._synched or event is Undefined:
            # Nothing to do.
            return
        with self._synch_off():
            in_context_traits = self._in_context_traits()
            for name in event.added + event.modified:
                if name in in_context_traits:
                    setattr(self, name, self._context[name])

    # The decorator doesn't work. See #1327
    #@on_trait_change('+in_context')
    def _in_context_trait_changed(self, name, new):
        """ Generic trait change handler to propogate mirrored trait changes
        into the context.
        """
        if not self._synched:
            # Nothing to do.
            return

        with self._context.deferred_events():
            # Set the trait with synching off, then turn synching on before
            # firing events
            with self._synch_off():
                self._context[name] = new

    # Instead, do this.
    def _anytrait_changed(self, name, old, new):
        trait = self.trait(name)
        if trait.in_context:
            self._in_context_trait_changed(name, new)

    @contextmanager
    def _synch_off(self):
        # XXX not thread-safe
        _old_synched = self._synched
        self._synched = False
        try:
            yield
        finally:
            self._synched = _old_synched
コード例 #24
0
class SurveyMapPane(TraitsDockPane):
    """ The dock pane holding the map view of the survey """

    id = 'hydropick.survey_map'
    name = "Map"

    survey = Supports(ISurvey)

    #: proxy for the task's current survey line
    current_survey_line = DelegatesTo('task')

    #: proxy for the task's current survey line group
    current_survey_line_group = DelegatesTo('task')

    #: reference to the task's selected survey lines
    selected_survey_lines = DelegatesTo('task')

    #: model view for map pane
    survey_map_view = Instance(ModelView)

    #: plot for map view
    plot = Property(Instance(Plot), depends_on='survey_map_view')

    def _survey_map_view_default(self):
        return self._get_survey_map_view()

    #########  Notifications  ###########

    def _current_survey_line_changed(self):
        self.survey_map_view.current_survey_line = self.current_survey_line

    def _selected_survey_lines_changed(self):
        self.survey_map_view.selected_survey_lines = self.selected_survey_lines

    @on_trait_change('survey')
    def _set_survey_map_view(self):
        self.survey_map_view = self._get_survey_map_view()

    @on_trait_change('survey_map_view.current_survey_line')
    def map_selection_changed(self, new):
        if new:  # survey line cannot be None
            line = self.survey_map_view.current_survey_line
            self.current_survey_line = line

    @on_trait_change('survey_map_view.selected_survey_lines')
    def lines_selection_changed(self, new):
        if new:  # selected lines list cannot be None
            lines = self.survey_map_view.selected_survey_lines
            self.selected_survey_lines = lines

    #########  Pane methods ###########
    def _get_survey_map_view(self):
        if self.task is None:
            selected_survey_lines = []
        else:
            selected_survey_lines = self.selected_survey_lines
        return SurveyMapView(model=self.survey,
                             selected_survey_lines=selected_survey_lines)

    def _get_plot(self):
        return self.survey_map_view.plot

    view = View(
        Item('plot',
             editor=ComponentEditor(),
             width=400,
             height=200,
             show_label=False))
コード例 #25
0
class MOTDServicePlugin(Plugin):
    """ The 'Message of the Day' plugin.

    This plugin provides an MOTD object as a service.

    """

    # The Ids of the extension points that this plugin offers.
    MESSAGES = '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 = 'motd.motd_service'

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

    #### 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(Supports('motd.model.i_message.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
        # (do not use 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.
        motd_service_offer = ServiceOffer(protocol='motd.model.i_motd.IMOTD',
                                          factory=self._create_motd)

        return [motd_service_offer]

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

    def _create_motd(self):
        """  Create MOTD instance using list of messages """

        # Only do imports when you need to! This makes sure that the import
        # only happens when somebody needs the motd attribute.
        from motd.model.motd import MOTD

        motd = MOTD(messages=self.messages)
        return motd
コード例 #26
0
class MOTDStartupPlugin(Plugin):
    """ The 'Message of the Day' plugin.

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

    """

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

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

    # The plugin's unique identifier.
    id = 'motd.motd_startup'

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

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

        from motd.util import print_message

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

        # ... and print it.
        print_message(message)

    #### 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(Supports('motd.model.i_message.IMessage')),
                              id=MESSAGES,
                              desc="""

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

        """)

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

    #: The motd instance
    motd = Supports('motd.model.i_motd.IMOTD')

    def _motd_default(self):
        """  Create MOTD instance using list of messages """

        # Only do imports when you need to! This makes sure that the import
        # only happens when somebody needs the motd attribute.
        from motd.model.motd import MOTD

        motd = MOTD(messages=self.messages)
        return motd