Exemple #1
0
class Bool(Variable):
    """A variable wrapper for a boolean variable.
       """
    
    def __init__(self, default_value=False, iotype=None, desc=None, 
                 **metadata):

        # Put iotype in the metadata dictionary
        if iotype is not None:
            metadata['iotype'] = iotype
            
        # Put desc in the metadata dictionary
        if desc is not None:
            metadata['desc'] = desc

        self._validator = Enthought_Bool(value=default_value, **metadata)
            
        super(Bool, self).__init__(default_value=default_value, **metadata)

    def validate(self, obj, name, value):
        """ Use the Enthought trait's validate.
        """
        return self._validator.validate(obj, name, value)

    def create_editor(self):
        """ User the one in the Enthought trait.
        """
        return self._validator.create_editor()

    def get_attribute(self, name, value, trait, meta):
        """Return the attribute dictionary for this variable. This dict is
        used by the GUI to populate the edit UI. Bools need to turn
        their value into a string for compatibility.
        
        name: str
          Name of variable
          
        value: object
          The value of the variable
          
        trait: CTrait
          The variable's trait
          
        meta: dict
          Dictionary of metadata for this variable
        """
        
        attr = {}
        
        attr['name'] = name
        attr['type'] = type(value).__name__
        attr['value'] = str(value)
        
        for field in meta:
            if field not in gui_excludes:
                attr[field] = meta[field]
        
        return attr, None
Exemple #2
0
    def __init__(self, default_value=False, iotype=None, desc=None, 
                 **metadata):

        # Put iotype in the metadata dictionary
        if iotype is not None:
            metadata['iotype'] = iotype
            
        # Put desc in the metadata dictionary
        if desc is not None:
            metadata['desc'] = desc

        self._validator = Enthought_Bool(value=default_value, **metadata)
            
        super(Bool, self).__init__(default_value=default_value, **metadata)
    def __init__(self, default_value=False, iotype=None, desc=None,
                 **metadata):

        # Put iotype in the metadata dictionary
        if iotype is not None:
            metadata['iotype'] = iotype

        # Put desc in the metadata dictionary
        if desc is not None:
            metadata['desc'] = desc

        assumed_default = (default_value is None)
        if 'assumed_default' in metadata:
            del metadata['assumed_default']

        self._validator = Enthought_Bool(value=default_value, **metadata)

        super(Bool, self).__init__(default_value=default_value,
                                   assumed_default=assumed_default, **metadata)
Exemple #4
0
class TFunPWLInteractive(MFnLineArray, BMCSLeafNode, Vis2D):
    '''Interactive time function.
    '''
    node_name = 'time function'
    t_values = List(Float, [0])
    f_values = List(Float, [0])

    def reset(self):
        self.f_values = [0]
        self.t_values = [0]
        self.f_value = self.f_min

    n_f_values = Int(10,
                     input=True,
                     auto_set=False, enter_set=True)

    f_min = Float(0.0, input=True,
                  auto_set=False, enter_set=True,
                  label='F minimum')

    f_max = Float(1.0,
                  input=True,
                  auto_set=False, enter_set=True,
                  label='F maximum')

    t_ref = Float(1.0, auto_set=False, enter_set=True,
                  label='Initial time range')

    f_value = Range(low='f_min', high='f_max', value=0,
                    input=True,
                    auto_set=False, enter_set=True)

    enable_slider = Bool(True, disable_on_run=True)

    run_eagerly = Bool(True, label='Run eagerly')

    t_snap = Float(0.1, label='Time step to snap to',
                   auto_set=False, enter_set=True)

    def __init__(self, *arg, **kw):
        super(TFunPWLInteractive, self).__init__(*arg, **kw)
        self.xdata = np.array(self.t_values)
        self.ydata = np.array(self.f_values)

    d_t = Property(depends_on='t_ref,n_f_values')

    @cached_property
    def _get_d_t(self):
        return self.t_ref / self.n_f_values

    def _update_xy_arrays(self):
        delta_f = self.f_value - self.f_values[-1]
        self.f_values.append(self.f_value)
        rel_step = delta_f / (self.f_max - self.f_min)
        delta_t = rel_step * self.t_ref
        t_value = np.fabs(delta_t) + self.t_values[-1]
        n_steps = int(t_value / self.t_snap) + 1
        t_value = n_steps * self.t_snap
        self.t_values.append(t_value)
        self.xdata = np.array(self.t_values)
        self.ydata = np.array(self.f_values)
        self.replot()

    def _f_value_changed(self):
        self._update_xy_arrays()
        t_value = self.t_values[-1]
        f_value = self.f_values[-1]
        if self.ui:
            self.ui.model.set_tmax(t_value)
            if self.run_eagerly:
                print('LS-run', t_value, f_value)
                self.ui.run()

    def get_ty_data(self, vot):
        return self.t_values, self.f_values

    viz2d_classes = {
        'time_function': TFViz2D,
    }

    tree_view = View(
        VGroup(
            VSplit(
                VGroup(
                    VGroup(
                        Include('actions'),
                    ),
                    Tabbed(
                        VGroup(
                            VGroup(
                                UItem('f_value',
                                      full_size=True, resizable=True,
                                      enabled_when='enable_slider'
                                      ),
                            ),
                            VGroup(
                                Item('f_max',
                                     full_size=True, resizable=True),
                                Item('f_min',
                                     full_size=True),
                                Item('n_f_values',
                                     full_size=True),
                                Item('t_snap', tooltip='Snap value to round off'
                                     'the value to',
                                     full_size=True),
                            ),
                            spring,
                            label='Steering',
                        ),
                        VGroup(
                            Item('run_eagerly',
                                 full_size=True, resizable=True,
                                 tooltip='True - run calculation immediately'
                                 'after moving the value slider; \nFalse - user must'
                                 'start calculation by clicking Run button'),
                            spring,
                            label='Mode',
                        ),
                    ),
                ),
                UItem('figure', editor=MPLFigureEditor(),
                      height=300,
                      resizable=True,
                      springy=True),
            ),
        )
    )

    traits_view = tree_view
Exemple #5
0
class MplPlot(BasePlot, HasTraitsGroup):
    figure = Instance(Figure, ())
    _draw_pending = Bool(False)

    scale = Enum('linear', 'log', 'sqrt')('linear')
    scale_values = [
        'linear', 'log', 'sqrt'
    ]  # There's probably a way to exract this from the Enum trait but I don't know how
    azimuth = Range(-90, 90, -70)
    elevation = Range(0, 90, 30)
    quality = Range(1, MAX_QUALITY, 1)
    flip_order = Bool(False)
    x_lower = Float(0.0)
    x_upper = Float
    x_label = Str('Angle (2$\Theta$)')
    y_label = Str('Dataset')
    z_lower = Float(0.0)
    z_upper = Float
    z_label = Str
    z_labels = {}  # A dictionary to hold edited labels for each scaling type

    group = VGroup(
        HGroup(
            VGroup(
                Item('azimuth',
                     editor=DefaultOverride(mode='slider',
                                            auto_set=False,
                                            enter_set=True)),
                Item('elevation',
                     editor=DefaultOverride(mode='slider',
                                            auto_set=False,
                                            enter_set=True)),
                Item('quality'),
                Item('flip_order'),
            ),
            VGroup(
                HGroup(
                    Item('x_label',
                         editor=DefaultOverride(auto_set=False,
                                                enter_set=True)),
                    Item('x_lower',
                         editor=DefaultOverride(auto_set=False,
                                                enter_set=True)),
                    Item('x_upper',
                         editor=DefaultOverride(auto_set=False,
                                                enter_set=True)),
                ),
                HGroup(Item('y_label'), ),
                HGroup(
                    Item('z_label',
                         editor=DefaultOverride(auto_set=False,
                                                enter_set=True)),
                    Item('z_lower',
                         editor=DefaultOverride(auto_set=False,
                                                enter_set=True)),
                    Item('z_upper',
                         editor=DefaultOverride(auto_set=False,
                                                enter_set=True)),
                ),
            ),
        ),
        UItem('figure', editor=MPLFigureEditor()),
    )

    def __init__(self, callback_obj=None, *args, **kws):
        super(MplPlot, self).__init__(*args, **kws)
        self.figure = plt.figure()
        self.figure.subplots_adjust(bottom=0.05, left=0, top=1, right=0.95)
        self.ax = None
        for s in self.scale_values:
            self.z_labels[s] = 'Intensity - ' + get_value_scale_label(s,
                                                                      mpl=True)
        # This must be a weak reference, otherwise the entire app will
        # hang on exit.
        from weakref import proxy
        if callback_obj:
            self._callback_object = proxy(callback_obj)
        else:
            self._callback_object = lambda *args, **kw: None

    def close(self):
        del self._callback_object
        plt.close()

    def __del__(self):
        plt.close()

    @on_trait_change('azimuth, elevation')
    def _perspective_changed(self):
        if self.ax:
            self.ax.view_init(azim=self.azimuth, elev=self.elevation)
            self.redraw()

    def _quality_changed(self):
        self.redraw(replot=True)

    @on_trait_change(
        'x_label, y_label, x_lower, x_upper, z_lower, z_upper, flip_order')
    def _trigger_redraw(self):
        self.quality = 1
        self.redraw(replot=True)

    def _z_label_changed(self):
        self.z_labels[self.scale] = self.z_label
        self._trigger_redraw()

    def redraw(self, replot=False, now=False):
        if not now and self._draw_pending:
            self._redraw_timer.Restart()
            return
        #import wx
        canvas = self.figure.canvas
        if canvas is None:
            return

        def _draw():
            self._callback_object._on_redraw(drawing=True)
            if replot:
                self._plot(self.x, self.y, self.z, self.scale)
            else:
                canvas.draw()
            self._draw_pending = False
            self._callback_object._on_redraw(drawing=False)

        if now:
            _draw()
        else:
            _draw()
            #self._redraw_timer = wx.CallLater(250, _draw)
            #self._draw_pending = True
            #self._redraw_timer.Start()

#    def _prepare_data(self, datasets):

    def _prepare_data(self, stack):
        #        stack = stack_datasets(datasets)

        x = stack[:, :, 0]
        z = stack[:, :, 1]
        #        y = array([ [i]*z.shape[1] for i in range(1, len(datasets) + 1) ])
        y = array([[i] * z.shape[1] for i in range(1, stack.shape[1] + 1)])

        if x[0, 0] < x[0, -1]:
            self.x_lower = x[0, 0]
            self.x_upper = x[0, -1]
        else:
            self.x_lower = x[0, -1]
            self.x_upper = x[0, 0]
        self.z_upper = z.max()
        return x, y, z

    def _plot(self, x, y, z, scale='linear'):
        self.x, self.y, self.z = x, y, z
        x, y, z = x.copy(), y.copy(), z.copy()

        if self.flip_order:
            z = z[::-1]
        self.scale = scale
        self.figure.clear()
        self.figure.set_facecolor('white')
        ax = self.ax = self.figure.add_subplot(111, projection='3d')
        ax.set_xlabel(self.x_label)
        ax.set_ylabel(self.y_label)
        self.z_label = self.z_labels[self.scale]
        ax.set_zlabel(self.z_label)

        y_rows = z.shape[0]
        ax.locator_params(axis='y', nbins=10, integer=True)
        ax.view_init(azim=self.azimuth, elev=self.elevation)

        if self.quality != MAX_QUALITY:
            # map quality from 1->5 to 0.05->0.5 to approx. no. of samples
            samples = int(z.shape[1] * ((self.quality - 1) * (0.5 - 0.05) /
                                        (5 - 1) + 0.05))
            z, truncate_at, bins = rebin_preserving_peaks(z, samples / 2)
            # Take the x's from the original x's to maintain visual x-spacing
            # We need to calculate the x's for the rebinned data
            x0_row = x[0, :truncate_at]
            old_xs = np.linspace(x0_row.min(), x0_row.max(), bins * 2)
            new_xs = np.interp(
                old_xs, np.linspace(x0_row.min(), x0_row.max(), len(x0_row)),
                x0_row)
            x = np.tile(new_xs, (y.shape[0], 1))

        # Set values to inf to avoid rendering by matplotlib
        x[(x < self.x_lower) | (x > self.x_upper)] = np.inf
        z[(z < self.z_lower) | (z > self.z_upper)] = np.inf

        # separate series with open lines
        ys = y[:, 0]
        points = []
        for x_row, z_row in zip(x, z):
            points.append(zip(x_row, z_row))
        lines = LineCollection(points)
        ax.add_collection3d(lines, zs=ys, zdir='y')
        ax.set_xlim3d(self.x_lower, self.x_upper)
        ax.set_ylim3d(1, y_rows)
        ax.set_zlim3d(self.z_lower, self.z_upper)
        self.figure.canvas.draw()
        return None

    def copy_to_clipboard(self):
        self.figure.canvas.Copy_to_Clipboard()

    def save_as(self, filename):
        self.figure.canvas.print_figure(filename)
        logger.logger.info('Saved plot {}'.format(filename))

    def _reset_view(self):
        self.azimuth = -70
        self.elevation = 30
Exemple #6
0
class JumpmanViewer(BitmapViewer):
    name = "jumpman"

    pretty_name = "Jumpman Level Editor"

    control_cls = JumpmanGridControl

    has_bitmap = True

    has_colors = True

    has_zoom = True

    zoom_text = "bitmap zoom factor"

    has_caret = False

    ##### Traits

    can_select_objects = Bool(False)

    can_erase_objects = Bool(False)

    ##### class attributes

    valid_mouse_modes = [jm.AnticDSelectMode, jm.DrawGirderMode, jm.DrawLadderMode, jm.DrawUpRopeMode, jm.DrawDownRopeMode, jm.DrawPeanutMode, jm.EraseGirderMode, jm.EraseLadderMode, jm.EraseRopeMode, jm.JumpmanRespawnMode]

    default_mouse_mode_cls = jm.AnticDSelectMode

    #### Default traits

    def _machine_default(self):
        return JumpmanMachine()

    def _draw_pattern_default(self):
        return [0]

    ##### Properties

    @property
    def window_title(self):
        return "Jumpman Level Editor"

    @property
    def current_level(self):
        return self.linked_base.jumpman_playfield_model

    ##### Initialization and serialization

    def from_metadata_dict_post(self, e):
        # ignore bitmap renderer in restore because we always want to use the
        # JumpmanPlayfieldRenderer in Jumpman level edit mode
        if 'assembly_source' in e:
            self.current_level.assembly_source = e['assembly_source']
        if 'old_trigger_mapping' in e:
            self.current_level.old_trigger_mapping = e['old_trigger_mapping']

    def to_metadata_dict_post(self, mdict, document):
        mdict["assembly_source"] = self.current_level.assembly_source
        mdict["old_trigger_mapping"] = dict(self.current_level.old_trigger_mapping)  # so we don't try to pickle a TraitDictObject

    def get_extra_segment_savers(self, segment):
        """Hook to provide additional ways to save the data based on this view
        of the data
        """
        return [js.JumpmanSaveAsATR, js.JumpmanSaveAsXEX]

    ##### Trait change handlers

    def recalc_data_model(self):
        self.current_level.init_level_builder(self)
        self.machine.update_colors(self.current_level.level_colors)

    @on_trait_change('machine.bitmap_shape_change_event,machine.bitmap_color_change_event')
    def update_bitmap(self, evt):
        log.debug("BitmapViewer: machine bitmap changed for %s" % self.control)
        if evt is not Undefined:
            self.control.recalc_view()
            self.linked_base.editor.update_pane_names()

    @on_trait_change('linked_base.editor.document.byte_values_changed')
    def byte_values_changed(self, index_range):
        log.debug("byte_values_changed: %s index_range=%s" % (self, str(index_range)))
        if index_range is not Undefined:
            self.control.recalc_view()

    @on_trait_change('linked_base.jumpman_trigger_selected_event')
    def do_jumpman_trigger_selected_event(self, new_trigger_root):
        log.debug("jumpman_trigger_selected_changed: %s selected=%s" % (self, str(new_trigger_root)))
        if new_trigger_root is not Undefined:
            self.set_trigger_view(new_trigger_root)

    ##### Jumpman level construction

    def update_harvest_state(self):
        if not self.current_level.valid_level:
            return
        harvest_state = self.current_level.level_builder.get_harvest_state()
        self.num_ladders = len(harvest_state.ladder_positions)
        self.num_downropes = len(harvest_state.downrope_positions)
        self.num_peanuts = len(harvest_state.peanuts)

        # FIXME: force redraw of level data here because it depends on the
        # level builder objects so it can count the number of items
        #self.level_data.refresh_view()
        #self.trigger_list.refresh_view()

    def set_trigger_view(self, trigger_root):
        level = self.current_level
        mouse_mode = level.mouse_mode
        if mouse_mode.can_paste:
            mouse_mode.objects = []
        level.set_trigger_root(trigger_root)
        self.can_erase_objects = trigger_root is not None
        self.control.refresh_view()
Exemple #7
0
class TraitGridModel(GridModel):
    """ A TraitGridModel builds a grid from a list of traits objects. Each row
    represents on object, each column one trait from those objects. All the
    objects must be of the same type. Optionally a user may pass in a list of
    trait names defining which traits will be shown in the columns and in
    which order. If this list is not passed in, then the first object is
    inspected and every trait from that object gets a column."""

    # A 2-dimensional list/array containing the grid data.
    data = List()  #HasTraits)

    # The column definitions
    columns = Trait(None, None, List(Trait(None, Str, TraitGridColumn)))

    # The trait to look at to get the row name
    row_name_trait = Trait(None, None, Str)

    # Allow column sorting?
    allow_column_sort = Bool(True)

    # A factory to generate new rows. If this is not None then it must
    # be a no-argument function.
    row_factory = Trait(None, None, Function)

    #########################################################################
    # 'object' interface.
    #########################################################################
    def __init__(self, **traits):
        """ Create a TraitGridModel object. """

        # Base class constructor
        super(TraitGridModel, self).__init__(**traits)

        # if no columns are pass in then create the list of names
        # from the first trait in the list. if the list is empty,
        # the columns should be an empty list as well.
        self._auto_columns = self.columns

        if self.columns is None or len(self.columns) == 0:
            if self.data is not None and len(self.data) > 0:
                self._auto_columns = []

                # we only add traits that aren't events, since events
                # are write-only
                for name, trait in self.data[0].traits().items():
                    if trait.type != 'event':
                        self._auto_columns.append(TraitGridColumn(name=name))
            else:
                self._auto_columns = []

        # attach trait handlers to the list object
        self.on_trait_event(self._on_data_changed, 'data')
        self.on_trait_event(self._on_data_items_changed, 'data_items')

        # attach appropriate trait handlers to objects in the list
        self.__manage_data_listeners(self.data)

        # attach a listener to the column definitions so we refresh when
        # they change
        self.on_trait_change(self._on_columns_changed, 'columns')
        self.on_trait_event(self._on_columns_items_changed, 'columns_items')
        # attach listeners to the column definitions themselves
        self.__manage_column_listeners(self.columns)

        # attach a listener to the row_name_trait
        self.on_trait_change(self._on_row_name_trait_changed, 'row_name_trait')

        return

    #########################################################################
    # 'GridModel' interface.
    #########################################################################

    def get_column_count(self):
        """ Return the number of columns for this table. """

        return len(self._auto_columns)

    def get_column_name(self, index):
        """ Return the label of the column specified by the
        (zero-based) index. """

        try:
            name = col = self._auto_columns[index]
            if isinstance(col, TraitGridColumn):
                if col.label is not None:
                    name = col.label
                else:
                    name = col.name
        except IndexError:
            name = ''

        return name

    def get_column_size(self, index):
        """ Return the size in pixels of the column indexed by col.
            A value of -1 or None means use the default. """

        size = -1
        try:
            col = self._auto_columns[index]
            if isinstance(col, TraitGridColumn):
                size = col.size
        except IndexError:
            pass

        return size

    def get_cols_drag_value(self, cols):
        """ Return the value to use when the specified columns are dragged or
        copied and pasted. cols is a list of column indexes. """

        # iterate over every column, building a list of the values in that
        # column
        value = []
        for col in cols:
            value.append(self.__get_data_column(col))

        return value

    def get_cols_selection_value(self, cols):
        """ Returns a list of TraitGridSelection objects containing the
        object corresponding to the grid rows and the traits corresponding
        to the specified columns. """

        values = []
        for obj in self.data:
            for col in cols:
                values.append(
                    TraitGridSelection(obj=obj,
                                       trait_name=self.__get_column_name(col)))

        return values

    def sort_by_column(self, col, reverse=False):
        """ Sort model data by the column indexed by col. """

        # first check to see if we allow sorts by column
        if not self.allow_column_sort:
            return

        # see if a sorter is specified for this column
        try:
            column = self._auto_columns[col]
            name = self.__get_column_name(col)
            # by default we use cmp to sort on the traits
            sorter = cmp
            if isinstance(column, TraitGridColumn) and \
                   column.sorter is not None:
                sorter = column.sorter
        except IndexError:
            return

        # now sort the data appropriately
        def sort_function(a, b):
            if hasattr(a, name):
                a_trait = getattr(a, name)
            else:
                a_trait = None

            if hasattr(b, name):
                b_trait = getattr(b, name)
            else:
                b_trait = None

            return sorter(a_trait, b_trait)

        self.data.sort(sort_function)

        if reverse:
            self.data.reverse()

        # now fire an event to tell the grid we're sorted
        print 'firing sort event'
        self.column_sorted = GridSortEvent(index=col, reversed=reverse)

        return

    def is_column_read_only(self, index):
        """ Return True if the column specified by the zero-based index
        is read-only. """

        return self.__get_column_readonly(index)

    def get_row_count(self):
        """ Return the number of rows for this table. """

        if self.data is not None:
            count = len(self.data)
        else:
            count = 0

        return count

    def get_row_name(self, index):
        """ Return the name of the row specified by the
        (zero-based) index. """

        if self.row_name_trait is not None:
            try:
                row = self._get_row(index)
                if hasattr(row, self.row_name_trait):
                    name = getattr(row, self.row_name_trait)
            except IndexError:
                name = str(index + 1)

        else:
            name = str(index + 1)

        return name

    def get_rows_drag_value(self, rows):
        """ Return the value to use when the specified rows are dragged or
        copied and pasted. rows is a list of row indexes. If there is only
        one row listed, return the corresponding trait object. If more than
        one row is listed then return a list of objects. """

        # return a list of objects
        value = []

        for index in rows:
            try:
                # note that we can't use get_value for this because it
                # sometimes returns strings instead of the actual value,
                # e.g. in cases where a float_format is specified
                value.append(self._get_row(index))
            except IndexError:
                value.append(None)

        return value

    def get_rows_selection_value(self, rows):
        """ Returns a list of TraitGridSelection objects containing the
        object corresponding to the selected rows. """

        values = []
        for row_index in rows:
            values.append(TraitGridSelection(obj=self.data[row_index]))

        return values

    def is_row_read_only(self, index):
        """ Return True if the row specified by the zero-based index
        is read-only. """

        return False

    def get_cell_editor(self, row, col):
        """ Return the editor for the specified cell. """

        # print 'TraitGridModel.get_cell_editor row: ', row, ' col: ', col

        obj = self.data[row]
        trait_name = self.__get_column_name(col)
        trait = obj.base_trait(trait_name)
        if trait is None:
            return None

        factory = trait.get_editor()

        return TraitGridCellAdapter(factory, obj, trait_name, '')

    def get_cell_drag_value(self, row, col):
        """ Return the value to use when the specified cell is dragged or
        copied and pasted. """

        # find the name of the column indexed by col
        # note that this code is the same as the get_value code but without
        # the potential string formatting
        column = self.__get_column(col)
        obj = self._get_row(row)

        value = self._get_data_from_row(obj, column)

        return value

    def get_cell_selection_value(self, row, col):
        """ Returns a TraitGridSelection object specifying the data stored
        in the table at (row, col). """

        obj = self.data[row]
        trait_name = self.__get_column_name(col)

        return TraitGridSelection(obj=obj, trait_name=trait_name)

    def resolve_selection(self, selection_list):
        """ Returns a list of (row, col) grid-cell coordinates that
        correspond to the objects in objlist. For each coordinate, if the
        row is -1 it indicates that the entire column is selected. Likewise
        coordinates with a column of -1 indicate an entire row that is
        selected. For the TraitGridModel, the objects in objlist must
        be TraitGridSelection objects. """

        cells = []
        for selection in selection_list:
            try:
                row = self.data.index(selection.obj)
            except ValueError:
                continue

            column = -1
            if selection.trait_name is not None:
                column = self._get_column_index_by_trait(selection.trait_name)
                if column is None:
                    continue

            cells.append((row, column))

        return cells

    def get_type(self, row, col):
        """ Return the value stored in the table at (row, col). """

        typename = self.__get_column_typename(col)

        return typename

    def get_value(self, row, col):
        """ Return the value stored in the table at (row, col). """

        value = self.get_cell_drag_value(row, col)
        formats = self.__get_column_formats(col)

        if value is not None and formats is not None and \
               formats.has_key(type(value)) and \
               formats[type(value)] is not None:
            try:
                format = formats[type(value)]
                if callable(format):
                    value = format(value)
                else:
                    value = format % value
            except TypeError:
                # not enough arguments? wrong kind of arguments?
                pass

        return value

    def is_cell_empty(self, row, col):
        """ Returns True if the cell at (row, col) has a None value,
        False otherwise."""

        value = self.get_value(row, col)

        return value is None

    def is_cell_editable(self, row, col):
        """ Returns True if the cell at (row, col) is editable,
        False otherwise. """
        return not self.is_column_read_only(col)

    #########################################################################
    # protected 'GridModel' interface.
    #########################################################################
    def _insert_rows(self, pos, num_rows):
        """ Inserts num_rows at pos and fires an event iff a factory method
        for new rows is defined. Otherwise returns 0. """

        count = 0
        if self.row_factory is not None:
            new_data = []
            for i in range(num_rows):
                new_data.append(self.row_factory())

            count = self._insert_rows_into_model(pos, new_data)
            self.rows_added = ('added', pos, new_data)

        return count

    def _delete_rows(self, pos, num_rows):
        """ Removes rows pos through pos + num_rows from the model. """

        if pos + num_rows >= self.get_row_count():
            num_rows = self.get_rows_count() - pos

        return self._delete_rows_from_model(pos, num_rows)

    def _set_value(self, row, col, value):
        """ Sets the value of the cell at (row, col) to value.

        Raises a ValueError if the value is vetoed or the cell at
        (row, col) does not exist. """

        #print 'TraitGridModel._set_value: new: ', value

        new_rows = 0
        # find the column indexed by col
        column = self.__get_column(col)
        obj = self._get_row(row)
        success = False
        if obj is not None:
            success = self._set_data_on_row(obj, column, value)
        else:
            # Add a new row.
            new_rows = self._insert_rows(self.get_row_count(), 1)
            if new_rows > 0:
                # now set the value on the new object
                obj = self._get_row(self.get_row_count() - 1)
                success = self._set_data_on_row(obj, column, value)

        if not success:
            # fixme: what do we do in this case? veto the set somehow? raise
            #        an exception?
            pass

        return new_rows

    #########################################################################
    # protected interface.
    #########################################################################
    def _get_row(self, index):
        """ Return the object that corresponds to the row at index. Override
        this to handle very large data sets. """

        return self.data[index]

    def _get_data_from_row(self, row, column):
        """ Retrieve the data specified by column for this row. Attribute
        can be either a member of the row object, or a no-argument method
        on that object. Override this method to provide alternative ways
        of accessing the data in the object. """

        value = None

        if row is not None and column is not None:
            if not isinstance(column, TraitGridColumn):
                # first handle the case where the column
                # definition might be just a string
                if hasattr(row, column):
                    value = getattr(row, column)
            elif column.name is not None and hasattr(row, column.name):
                # this is the case when the trait name is specified
                value = getattr(row, column.name)
            elif column.method is not None and hasattr(row, column.method):
                # this is the case when an object method is specified
                value = getattr(row, column.method)()

        return value

    def _set_data_on_row(self, row, column, value):
        """ Retrieve the data specified by column for this row. Attribute
        can be either a member of the row object, or a no-argument method
        on that object. Override this method to provide alternative ways
        of accessing the data in the object. """

        success = False

        if row is not None and column is not None:
            if not isinstance(column, TraitGridColumn):
                if hasattr(row, column):
                    # sometimes the underlying grid gives us 0/1 instead
                    # of True/False. do some conversion here to make that
                    # case worl.
                    #if type(getattr(row, column)) == bool and \
                    #       type(value) != bool:
                    # convert the value to a boolean
                    #    value = bool(value)

                    setattr(row, column, value)
                    success = True
            elif column.name is not None and hasattr(row, column.name):
                # sometimes the underlying grid gives us 0/1 instead
                # of True/False. do some conversion here to make that
                # case worl.
                #if type(getattr(row, column.name)) == bool and \
                #       type(value) != bool:
                # convert the value to a boolean
                #    value = bool(value)
                setattr(row, column.name, value)
                success = True

            # do nothing in the method case as we don't allow rows
            # defined to return a method value to set the value

        return success

    def _insert_rows_into_model(self, pos, new_data):
        """ Insert the given new rows into the model. Override this method
        to handle very large data sets. """

        for data in new_data:
            self.data.insert(pos, data)
            pos += 1

        return

    def _delete_rows_from_model(self, pos, num_rows):
        """ Delete the specified rows from the model. Override this method
        to handle very large data sets. """
        del self.data[pos, pos + num_rows]

        return num_rows

    ###########################################################################
    # trait handlers
    ###########################################################################

    def _on_row_name_trait_changed(self, new):
        """ Force the grid to refresh when any underlying trait changes. """
        self.fire_content_changed()
        return

    def _on_columns_changed(self, object, name, old, new):
        """ Force the grid to refresh when any underlying trait changes. """
        self.__manage_column_listeners(old, remove=True)
        self.__manage_column_listeners(self.columns)
        self._auto_columns = self.columns
        self.fire_structure_changed()
        return

    def _on_columns_items_changed(self, event):
        """ Force the grid to refresh when any underlying trait changes. """

        self.__manage_column_listeners(event.removed, remove=True)
        self.__manage_column_listeners(event.added)
        self.fire_structure_changed()
        return

    def _on_contained_trait_changed(self, new):
        """ Force the grid to refresh when any underlying trait changes. """
        self.fire_content_changed()
        return

    def _on_data_changed(self, object, name, old, new):
        """ Force the grid to refresh when the underlying list changes. """

        self.__manage_data_listeners(old, remove=True)
        self.__manage_data_listeners(self.data)
        self.fire_structure_changed()
        return

    def _on_data_items_changed(self, event):
        """ Force the grid to refresh when the underlying list changes. """

        # if an item was removed then remove that item's listener
        self.__manage_data_listeners(event.removed, remove=True)

        # if items were added then add trait change listeners on those items
        self.__manage_data_listeners(event.added)

        self.fire_content_changed()
        return

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

    def __get_data_column(self, col):
        """ Return a 1-d list of data from the column indexed by col. """

        row_count = self.get_row_count()

        coldata = []
        for row in range(row_count):
            try:
                coldata.append(self.get_value(row, col))
            except IndexError:
                coldata.append(None)

        return coldata

    def __get_column(self, col):

        try:
            column = self._auto_columns[col]
        except IndexError:
            column = None

        return column

    def __get_column_name(self, col):

        name = column = self.__get_column(col)
        if isinstance(column, TraitGridColumn):
            name = column.name

        return name

    def __get_column_typename(self, col):

        name = column = self.__get_column(col)
        typename = None
        if isinstance(column, TraitGridColumn):
            typename = column.typename

        return typename

    def __get_column_readonly(self, col):

        read_only = False
        column = self.__get_column(col)
        if isinstance(column, TraitGridColumn):
            read_only = column.read_only

        return read_only

    def __get_column_formats(self, col):

        formats = None
        column = self.__get_column(col)
        if isinstance(column, TraitGridColumn):
            formats = column.formats

        return formats

    def _get_column_index_by_trait(self, trait_name):

        cols = self._auto_columns
        for i in range(len(cols)):
            col = cols[i]
            if isinstance(col, TraitGridColumn):
                col_name = col.name
            else:
                col_name = col

            if col_name == trait_name:
                return i

        return None

    def __manage_data_listeners(self, list, remove=False):
        # attach appropriate trait handlers to objects in the list
        if list is not None:
            for item in list:
                item.on_trait_change(self._on_contained_trait_changed,
                                     remove=remove)
        return

    def __manage_column_listeners(self, collist, remove=False):

        if collist is not None:
            for col in collist:
                if isinstance(col, TraitGridColumn):
                    col.on_trait_change(self._on_columns_changed,
                                        remove=remove)

        return
Exemple #8
0
class NonInterruptibleTaskExample(HasStrictTraits):
    #: The executor to submit tasks to.
    traits_executor = Instance(TraitsExecutor)

    #: The future object returned on task submission.
    future = Instance(CallFuture)

    #: Number of points to use.
    sample_count = Int(10**8)

    #: Message about state of calculation.
    message = Str("No previous calculation runs")

    #: Button to calculate, plus its enabled state.
    calculate = Button()
    can_calculate = Property(Bool(), observe="future")

    #: Button to cancel, plus its enabled state.
    cancel = Button()
    can_cancel = Property(Bool(), observe="future.cancellable")

    @observe("calculate")
    def _submit_calculation(self, event):
        self.message = "Calculating π"
        self.future = submit_call(self.traits_executor, approximate_pi,
                                  self.sample_count)

    @observe("cancel")
    def _request_cancellation(self, event):
        self.future.cancel()
        self.message = "Cancelling"

    @observe("future:done")
    def _report_result(self, event):
        if self.future.state == CANCELLED:
            self.message = "Cancelled"
        elif self.future.state == FAILED:
            self.message = f"Unexpected error: {self.future.exception[1]}"
        elif self.future.state == COMPLETED:
            self.message = f"Complete: π ≈ {self.future.result:.6f}"
        else:
            # Shouldn't ever get here: CANCELLED, FAILED and COMPLETED
            # are the only possible final states of a future.
            assert False, f"Impossible state: {self.future.state}"
        self.future = None

    def _get_can_calculate(self):
        return self.future is None

    def _get_can_cancel(self):
        return self.future is not None and self.future.cancellable

    traits_view = View(
        Item("sample_count"),
        UItem("message", style="readonly"),
        HGroup(
            UItem("calculate", enabled_when="can_calculate"),
            UItem("cancel", enabled_when="can_cancel"),
        ),
        resizable=True,
    )
Exemple #9
0
class PlotAxis(AbstractOverlay):
    """
    The PlotAxis is a visual component that can be rendered on its own as
    a standalone component or attached as an overlay to another component.
    (To attach it as an overlay, set its **component** attribute.)

    When it is attached as an overlay, it draws into the padding around
    the component.
    """

    # The mapper that drives this axis.
    mapper = Instance(AbstractMapper)

    # The text of the axis title.
    title = Trait('', Str, Unicode)  #May want to add PlotLabel option

    # The font of the title.
    title_font = KivaFont('modern 12')

    # The spacing between the axis line and the title
    title_spacing = Trait('auto', 'auto', Float)

    # The color of the title.
    title_color = ColorTrait("black")

    # The thickness (in pixels) of each tick.
    tick_weight = Float(1.0)

    # The color of the ticks.
    tick_color = ColorTrait("black")

    # The font of the tick labels.
    tick_label_font = KivaFont('modern 10')

    # The color of the tick labels.
    tick_label_color = ColorTrait("black")

    # The rotation of the tick labels.
    tick_label_rotate_angle = Float(0)

    # Whether to align to corners or edges (corner is better for 45 degree rotation)
    tick_label_alignment = Enum('edge', 'corner')

    # The margin around the tick labels.
    tick_label_margin = Int(2)

    # The distance of the tick label from the axis.
    tick_label_offset = Float(8.)

    # Whether the tick labels appear to the inside or the outside of the plot area
    tick_label_position = Enum("outside", "inside")

    # A callable that is passed the numerical value of each tick label and
    # that returns a string.
    tick_label_formatter = Callable(DEFAULT_TICK_FORMATTER)

    # The number of pixels by which the ticks extend into the plot area.
    tick_in = Int(5)

    # The number of pixels by which the ticks extend into the label area.
    tick_out = Int(5)

    # Are ticks visible at all?
    tick_visible = Bool(True)

    # The dataspace interval between ticks.
    tick_interval = Trait('auto', 'auto', Float)

    # A callable that implements the AbstractTickGenerator interface.
    tick_generator = Instance(AbstractTickGenerator)

    # The location of the axis relative to the plot.  This determines where
    # the axis title is located relative to the axis line.
    orientation = Enum("top", "bottom", "left", "right")

    # Is the axis line visible?
    axis_line_visible = Bool(True)

    # The color of the axis line.
    axis_line_color = ColorTrait("black")

    # The line thickness (in pixels) of the axis line.
    axis_line_weight = Float(1.0)

    # The dash style of the axis line.
    axis_line_style = LineStyle('solid')

    # A special version of the axis line that is more useful for geophysical
    # plots.
    small_haxis_style = Bool(False)

    # Does the axis ensure that its end labels fall within its bounding area?
    ensure_labels_bounded = Bool(False)

    # Does the axis prevent the ticks from being rendered outside its bounds?
    # This flag is off by default because the standard axis *does* render ticks
    # that encroach on the plot area.
    ensure_ticks_bounded = Bool(False)

    # Fired when the axis's range bounds change.
    updated = Event

    #------------------------------------------------------------------------
    # Override default values of inherited traits
    #------------------------------------------------------------------------

    # Background color (overrides AbstractOverlay). Axes usually let the color of
    # the container show through.
    bgcolor = ColorTrait("transparent")

    # Dimensions that the axis is resizable in (overrides PlotComponent).
    # Typically, axes are resizable in both dimensions.
    resizable = "hv"

    #------------------------------------------------------------------------
    # Private Traits
    #------------------------------------------------------------------------

    # Cached position calculations

    _tick_list = List  # These are caches of their respective positions
    _tick_positions = Any  #List
    _tick_label_list = Any
    _tick_label_positions = Any
    _tick_label_bounding_boxes = List
    _major_axis_size = Float
    _minor_axis_size = Float
    _major_axis = Array
    _title_orientation = Array
    _title_angle = Float
    _origin_point = Array
    _inside_vector = Array
    _axis_vector = Array
    _axis_pixel_vector = Array
    _end_axis_point = Array

    ticklabel_cache = List
    _cache_valid = Bool(False)

    #------------------------------------------------------------------------
    # Public methods
    #------------------------------------------------------------------------

    def __init__(self, component=None, **kwargs):
        # TODO: change this back to a factory in the instance trait some day
        self.tick_generator = DefaultTickGenerator()
        # Override init so that our component gets set last.  We want the
        # _component_changed() event handler to get run last.
        super(PlotAxis, self).__init__(**kwargs)
        if component is not None:
            self.component = component

    def invalidate(self):
        """ Invalidates the pre-computed layout and scaling data.
        """
        self._reset_cache()
        self.invalidate_draw()
        return

    def traits_view(self):
        """ Returns a View instance for use with Traits UI.  This method is
        called automatically be the Traits framework when .edit_traits() is
        invoked.
        """
        from axis_view import AxisView
        return AxisView

    #------------------------------------------------------------------------
    # PlotComponent and AbstractOverlay interface
    #------------------------------------------------------------------------

    def _do_layout(self, *args, **kw):
        """ Tells this component to do layout at a given size.

        Overrides Component.
        """
        if self.use_draw_order and self.component is not None:
            self._layout_as_overlay(*args, **kw)
        else:
            super(PlotAxis, self)._do_layout(*args, **kw)
        return

    def overlay(self, component, gc, view_bounds=None, mode='normal'):
        """ Draws this component overlaid on another component.

        Overrides AbstractOverlay.
        """
        if not self.visible:
            return
        self._draw_component(gc, view_bounds, mode, component)
        return

    def _draw_overlay(self, gc, view_bounds=None, mode='normal'):
        """ Draws the overlay layer of a component.

        Overrides PlotComponent.
        """
        self._draw_component(gc, view_bounds, mode)
        return

    def _draw_component(self,
                        gc,
                        view_bounds=None,
                        mode='normal',
                        component=None):
        """ Draws the component.

        This method is preserved for backwards compatibility. Overrides
        PlotComponent.
        """
        if not self.visible:
            return

        if not self._cache_valid:
            if component is not None:
                self._calculate_geometry_overlay(component)
            else:
                self._calculate_geometry()
            self._compute_tick_positions(gc, component)
            self._compute_labels(gc)

        with gc:
            # slight optimization: if we set the font correctly on the
            # base gc before handing it in to our title and tick labels,
            # their set_font() won't have to do any work.
            gc.set_font(self.tick_label_font)

            if self.axis_line_visible:
                self._draw_axis_line(gc, self._origin_point,
                                     self._end_axis_point)
            if self.title:
                self._draw_title(gc)

            self._draw_ticks(gc)
            self._draw_labels(gc)

        self._cache_valid = True
        return

    #------------------------------------------------------------------------
    # Private draw routines
    #------------------------------------------------------------------------

    def _layout_as_overlay(self, size=None, force=False):
        """ Lays out the axis as an overlay on another component.
        """
        if self.component is not None:
            if self.orientation in ("left", "right"):
                self.y = self.component.y
                self.height = self.component.height
                if self.orientation == "left":
                    self.width = self.component.padding_left
                    self.x = self.component.outer_x
                elif self.orientation == "right":
                    self.width = self.component.padding_right
                    self.x = self.component.x2 + 1
            else:
                self.x = self.component.x
                self.width = self.component.width
                if self.orientation == "bottom":
                    self.height = self.component.padding_bottom
                    self.y = self.component.outer_y
                elif self.orientation == "top":
                    self.height = self.component.padding_top
                    self.y = self.component.y2 + 1
        return

    def _draw_axis_line(self, gc, startpoint, endpoint):
        """ Draws the line for the axis.
        """
        with gc:
            gc.set_antialias(0)
            gc.set_line_width(self.axis_line_weight)
            gc.set_stroke_color(self.axis_line_color_)
            gc.set_line_dash(self.axis_line_style_)
            gc.move_to(*around(startpoint))
            gc.line_to(*around(endpoint))
            gc.stroke_path()
        return

    def _draw_title(self, gc, label=None, axis_offset=None):
        """ Draws the title for the axis.
        """
        if label is None:
            title_label = Label(text=self.title,
                                font=self.title_font,
                                color=self.title_color,
                                rotate_angle=self.title_angle)
        else:
            title_label = label

        # get the _rotated_ bounding box of the label
        tl_bounds = array(title_label.get_bounding_box(gc), float64)
        text_center_to_corner = -tl_bounds / 2.0
        # which axis are we moving away from the axis line along?
        axis_index = self._major_axis.argmin()

        if self.title_spacing != 'auto':
            axis_offset = self.title_spacing

        if (self.title_spacing) and (axis_offset is None):
            if not self.ticklabel_cache:
                axis_offset = 25
            else:
                axis_offset = max([
                    l._bounding_box[axis_index] for l in self.ticklabel_cache
                ]) * 1.3

        offset = (self._origin_point + self._end_axis_point) / 2
        axis_dist = self.tick_out + tl_bounds[axis_index] / 2.0 + axis_offset
        offset -= self._inside_vector * axis_dist
        offset += text_center_to_corner

        gc.translate_ctm(*offset)
        title_label.draw(gc)
        gc.translate_ctm(*(-offset))
        return

    def _draw_ticks(self, gc):
        """ Draws the tick marks for the axis.
        """
        if not self.tick_visible:
            return
        gc.set_stroke_color(self.tick_color_)
        gc.set_line_width(self.tick_weight)
        gc.set_antialias(False)
        gc.begin_path()
        tick_in_vector = self._inside_vector * self.tick_in
        tick_out_vector = self._inside_vector * self.tick_out
        for tick_pos in self._tick_positions:
            gc.move_to(*(tick_pos + tick_in_vector))
            gc.line_to(*(tick_pos - tick_out_vector))
        gc.stroke_path()
        return

    def _draw_labels(self, gc):
        """ Draws the tick labels for the axis.
        """
        # which axis are we moving away from the axis line along?
        axis_index = self._major_axis.argmin()

        inside_vector = self._inside_vector
        if self.tick_label_position == "inside":
            inside_vector = -inside_vector

        for i in range(len(self._tick_label_positions)):
            #We want a more sophisticated scheme than just 2 decimals all the time
            ticklabel = self.ticklabel_cache[i]
            tl_bounds = self._tick_label_bounding_boxes[i]

            #base_position puts the tick label at a point where the vector
            #extending from the tick mark inside 8 units
            #just touches the rectangular bounding box of the tick label.
            #Note: This is not necessarily optimal for non
            #horizontal/vertical axes.  More work could be done on this.

            base_position = self._tick_label_positions[i].copy()
            axis_dist = self.tick_label_offset + tl_bounds[axis_index] / 2.0
            base_position -= inside_vector * axis_dist
            base_position -= tl_bounds / 2.0

            if self.tick_label_alignment == 'corner':
                if self.orientation in ("top", "bottom"):
                    base_position[0] += tl_bounds[0] / 2.0
                elif self.orientation == "left":
                    base_position[1] -= tl_bounds[1] / 2.0
                elif self.orientation == "right":
                    base_position[1] += tl_bounds[1] / 2.0

            if self.ensure_labels_bounded:
                bound_idx = self._major_axis.argmax()
                if i == 0:
                    base_position[bound_idx] = max(
                        base_position[bound_idx],
                        self._origin_point[bound_idx])
                elif i == len(self._tick_label_positions) - 1:
                    base_position[bound_idx] = min(base_position[bound_idx],
                                                   self._end_axis_point[bound_idx] - \
                                                   tl_bounds[bound_idx])

            tlpos = around(base_position)
            gc.translate_ctm(*tlpos)
            ticklabel.draw(gc)
            gc.translate_ctm(*(-tlpos))
        return

    #------------------------------------------------------------------------
    # Private methods for computing positions and layout
    #------------------------------------------------------------------------

    def _reset_cache(self):
        """ Clears the cached tick positions, labels, and label positions.
        """
        self._tick_positions = []
        self._tick_label_list = []
        self._tick_label_positions = []
        return

    def _compute_tick_positions(self, gc, overlay_component=None):
        """ Calculates the positions for the tick marks.
        """
        if (self.mapper is None):
            self._reset_cache()
            self._cache_valid = True
            return

        datalow = self.mapper.range.low
        datahigh = self.mapper.range.high
        screenhigh = self.mapper.high_pos
        screenlow = self.mapper.low_pos
        if overlay_component is not None:
            origin = getattr(overlay_component, 'origin', 'bottom left')
            if self.orientation in ("top", "bottom"):
                if "right" in origin:
                    flip_from_gc = True
                else:
                    flip_from_gc = False
            elif self.orientation in ("left", "right"):
                if "top" in origin:
                    flip_from_gc = True
                else:
                    flip_from_gc = False

            if flip_from_gc:
                screenlow, screenhigh = screenhigh, screenlow

        if (datalow == datahigh) or (screenlow == screenhigh) or \
           (datalow in [inf, -inf]) or (datahigh in [inf, -inf]):
            self._reset_cache()
            self._cache_valid = True
            return

        if datalow > datahigh:
            raise RuntimeError, "DataRange low is greater than high; unable to compute axis ticks."

        if not self.tick_generator:
            return

        if hasattr(self.tick_generator, "get_ticks_and_labels"):
            # generate ticks and labels simultaneously
            tmp = self.tick_generator.get_ticks_and_labels(
                datalow, datahigh, screenlow, screenhigh)
            if len(tmp) == 0:
                tick_list = []
                labels = []
            else:
                tick_list, labels = tmp
            # compute the labels here
            self.ticklabel_cache = [Label(text=lab,
                                          font=self.tick_label_font,
                                          color=self.tick_label_color) \
                                    for lab in labels]
            self._tick_label_bounding_boxes = [array(ticklabel.get_bounding_box(gc), float64) \
                                               for ticklabel in self.ticklabel_cache]
        else:
            scale = 'log' if isinstance(self.mapper, LogMapper) else 'linear'
            if self.small_haxis_style:
                tick_list = array([datalow, datahigh])
            else:
                tick_list = array(
                    self.tick_generator.get_ticks(datalow,
                                                  datahigh,
                                                  datalow,
                                                  datahigh,
                                                  self.tick_interval,
                                                  use_endpoints=False,
                                                  scale=scale), float64)

        mapped_tick_positions = (array(self.mapper.map_screen(tick_list))-screenlow) / \
                                            (screenhigh-screenlow)
        self._tick_positions = around(array([self._axis_vector*tickpos + self._origin_point \
                                for tickpos in mapped_tick_positions]))
        self._tick_label_list = tick_list
        self._tick_label_positions = self._tick_positions
        return

    def _compute_labels(self, gc):
        """Generates the labels for tick marks.

        Waits for the cache to become invalid.
        """
        # tick labels are already computed
        if hasattr(self.tick_generator, "get_ticks_and_labels"):
            return

        formatter = self.tick_label_formatter

        def build_label(val):
            tickstring = formatter(val) if formatter is not None else str(val)
            return Label(text=tickstring,
                         font=self.tick_label_font,
                         color=self.tick_label_color,
                         rotate_angle=self.tick_label_rotate_angle,
                         margin=self.tick_label_margin)

        self.ticklabel_cache = [
            build_label(val) for val in self._tick_label_list
        ]
        self._tick_label_bounding_boxes = [
            array(ticklabel.get_bounding_box(gc), float)
            for ticklabel in self.ticklabel_cache
        ]
        return

    def _calculate_geometry(self):
        screenhigh = self.mapper.high_pos
        screenlow = self.mapper.low_pos

        if self.orientation in ('top', 'bottom'):
            self._major_axis_size = self.bounds[0]
            self._minor_axis_size = self.bounds[1]
            self._major_axis = array([1., 0.])
            self._title_orientation = array([0., 1.])
            self.title_angle = 0.0
            if self.orientation == 'top':
                self._origin_point = array(
                    self.position) + self._major_axis * screenlow
                self._inside_vector = array([0., -1.])
            else:  #self.oriention == 'bottom'
                self._origin_point = array(self.position) + array(
                    [0., self.bounds[1]]) + self._major_axis * screenlow
                self._inside_vector = array([0., 1.])

        elif self.orientation in ('left', 'right'):
            self._major_axis_size = self.bounds[1]
            self._minor_axis_size = self.bounds[0]
            self._major_axis = array([0., 1.])
            self._title_orientation = array([-1., 0])
            if self.orientation == 'left':
                self._origin_point = array(self.position) + array(
                    [self.bounds[0], 0.]) + self._major_axis * screenlow
                self._inside_vector = array([1., 0.])
                self.title_angle = 90.0
            else:  #self.orientation == 'right'
                self._origin_point = array(
                    self.position) + self._major_axis * screenlow
                self._inside_vector = array([-1., 0.])
                self.title_angle = 270.0

        if self.ensure_ticks_bounded:
            self._origin_point -= self._inside_vector * self.tick_in

        self._end_axis_point = (
            screenhigh - screenlow) * self._major_axis + self._origin_point
        self._axis_vector = self._end_axis_point - self._origin_point
        # This is the vector that represents one unit of data space in terms of screen space.
        self._axis_pixel_vector = self._axis_vector / sqrt(
            dot(self._axis_vector, self._axis_vector))
        return

    def _calculate_geometry_overlay(self, overlay_component=None):
        if overlay_component is None:
            overlay_component = self
        component_origin = getattr(overlay_component, "origin", 'bottom left')

        screenhigh = self.mapper.high_pos
        screenlow = self.mapper.low_pos

        if self.orientation in ('top', 'bottom'):
            self._major_axis_size = overlay_component.bounds[0]
            self._minor_axis_size = overlay_component.bounds[1]
            self._major_axis = array([1., 0.])
            self._title_orientation = array([0., 1.])
            self.title_angle = 0.0
            if self.orientation == 'top':
                self._origin_point = array(
                    [overlay_component.x, overlay_component.y2])
                self._inside_vector = array([0.0, -1.0])
            else:
                self._origin_point = array(
                    [overlay_component.x, overlay_component.y])
                self._inside_vector = array([0.0, 1.0])
            if "right" in component_origin:
                screenlow, screenhigh = screenhigh, screenlow

        elif self.orientation in ('left', 'right'):
            self._major_axis_size = overlay_component.bounds[1]
            self._minor_axis_size = overlay_component.bounds[0]
            self._major_axis = array([0., 1.])
            self._title_orientation = array([-1., 0])
            if self.orientation == 'left':
                self._origin_point = array(
                    [overlay_component.x, overlay_component.y])
                self._inside_vector = array([1.0, 0.0])
                self.title_angle = 90.0
            else:
                self._origin_point = array(
                    [overlay_component.x2, overlay_component.y])
                self._inside_vector = array([-1.0, 0.0])
                self.title_angle = 270.0
            if "top" in component_origin:
                screenlow, screenhigh = screenhigh, screenlow

        if self.ensure_ticks_bounded:
            self._origin_point -= self._inside_vector * self.tick_in

        self._end_axis_point = (
            screenhigh - screenlow) * self._major_axis + self._origin_point
        self._axis_vector = self._end_axis_point - self._origin_point
        # This is the vector that represents one unit of data space in terms of screen space.
        self._axis_pixel_vector = self._axis_vector / sqrt(
            dot(self._axis_vector, self._axis_vector))
        return

    #------------------------------------------------------------------------
    # Event handlers
    #------------------------------------------------------------------------

    def _bounds_changed(self, old, new):
        super(PlotAxis, self)._bounds_changed(old, new)
        self._layout_needed = True
        self._invalidate()

    def _bounds_items_changed(self, event):
        super(PlotAxis, self)._bounds_items_changed(event)
        self._layout_needed = True
        self._invalidate()

    def _mapper_changed(self, old, new):
        if old is not None:
            old.on_trait_change(self.mapper_updated, "updated", remove=True)
        if new is not None:
            new.on_trait_change(self.mapper_updated, "updated")
        self._invalidate()

    def mapper_updated(self):
        """
        Event handler that is bound to this axis's mapper's **updated** event
        """
        self._invalidate()

    def _position_changed(self, old, new):
        super(PlotAxis, self)._position_changed(old, new)
        self._cache_valid = False

    def _position_items_changed(self, event):
        super(PlotAxis, self)._position_items_changed(event)
        self._cache_valid = False

    def _position_changed_for_component(self):
        self._cache_valid = False

    def _position_items_changed_for_component(self):
        self._cache_valid = False

    def _bounds_changed_for_component(self):
        self._cache_valid = False
        self._layout_needed = True

    def _bounds_items_changed_for_component(self):
        self._cache_valid = False
        self._layout_needed = True

    def _origin_changed_for_component(self):
        self._invalidate()

    def _updated_fired(self):
        """If the axis bounds changed, redraw."""
        self._cache_valid = False
        return

    def _invalidate(self):
        self._cache_valid = False
        self.invalidate_draw()
        if self.component:
            self.component.invalidate_draw()
        return

    def _component_changed(self):
        if self.mapper is not None:
            # If there is a mapper set, just leave it be.
            return

        # Try to pick the most appropriate mapper for our orientation
        # and what information we can glean from our component.
        attrmap = {
            "left": ("ymapper", "y_mapper", "value_mapper"),
            "bottom": ("xmapper", "x_mapper", "index_mapper"),
        }
        attrmap["right"] = attrmap["left"]
        attrmap["top"] = attrmap["bottom"]

        component = self.component
        attr1, attr2, attr3 = attrmap[self.orientation]
        for attr in attrmap[self.orientation]:
            if hasattr(component, attr):
                self.mapper = getattr(component, attr)
                break
        return

    #------------------------------------------------------------------------
    # The following event handlers just invalidate our previously computed
    # Label instances and backbuffer if any of our visual attributes change.
    # TODO: refactor this stuff and the caching of contained objects (e.g. Label)
    #------------------------------------------------------------------------

    def _title_changed(self):
        self.invalidate_draw()
        if self.component:
            self.component.invalidate_draw()
        return

    def _anytrait_changed(self, name, old, new):
        """ For every trait that defines a visual attribute
            we just call _invalidate() when a change is made.
        """
        invalidate_traits = [
            'title_font',
            'title_spacing',
            'title_color',
            'tick_weight',
            'tick_color',
            'tick_label_font',
            'tick_label_color',
            'tick_label_rotate_angle',
            'tick_label_alignment',
            'tick_label_margin',
            'tick_label_offset',
            'tick_label_position',
            'tick_label_formatter',
            'tick_in',
            'tick_out',
            'tick_visible',
            'tick_interval',
            'tick_generator',
            'orientation',
            'axis_line_visible',
            'axis_line_color',
            'axis_line_weight',
            'axis_line_style',
            'small_haxis_style',
            'ensure_labels_bounded',
            'ensure_ticks_bounded',
        ]
        if name in invalidate_traits:
            self._invalidate()

    #------------------------------------------------------------------------
    # Persistence-related methods
    #------------------------------------------------------------------------

    def __getstate__(self):
        dont_pickle = [
            '_tick_list', '_tick_positions', '_tick_label_list',
            '_tick_label_positions', '_tick_label_bounding_boxes',
            '_major_axis_size', '_minor_axis_size', '_major_axis',
            '_title_orientation', '_title_angle', '_origin_point',
            '_inside_vector', '_axis_vector', '_axis_pixel_vector',
            '_end_axis_point', '_ticklabel_cache', '_cache_valid'
        ]

        state = super(PlotAxis, self).__getstate__()
        for key in dont_pickle:
            if state.has_key(key):
                del state[key]

        return state

    def __setstate__(self, state):
        super(PlotAxis, self).__setstate__(state)
        self._mapper_changed(None, self.mapper)
        self._reset_cache()
        self._cache_valid = False
        return
class _DataFrameEditor(UIEditor):
    """ TraitsUI-based editor implementation for data frames """

    #: Indicate that the editor is scrollable/resizable:
    scrollable = True

    #: Should column titles be displayed:
    show_titles = Bool(True)

    #: The tabular adapter being used for the editor view:
    adapter = Instance(DataFrameAdapter)

    # -- Private Methods ------------------------------------------------------

    def _target_name(self, name):
        if name:
            return "object.object." + name
        else:
            return ""

    def _data_frame_view(self):
        """ Return the view used by the editor.
        """

        return View(
            Item(
                self._target_name(self.name),
                id="tabular_editor",
                show_label=False,
                editor=TabularEditor(
                    show_titles=self.factory.show_titles,
                    editable=self.factory.editable,
                    adapter=self.adapter,
                    selected=self._target_name(self.factory.selected),
                    selected_row=self._target_name(self.factory.selected_row),
                    selectable=self.factory.selectable,
                    multi_select=self.factory.multi_select,
                    activated=self._target_name(self.factory.activated),
                    activated_row=self._target_name(
                        self.factory.activated_row
                    ),  # noqa
                    clicked=self._target_name(self.factory.clicked),
                    dclicked=self._target_name(self.factory.dclicked),
                    scroll_to_row=self._target_name(
                        self.factory.scroll_to_row
                    ),  # noqa
                    scroll_to_row_hint=self.factory.scroll_to_row_hint,
                    scroll_to_column=self._target_name(
                        self.factory.scroll_to_column
                    ),  # noqa
                    right_clicked=self._target_name(
                        self.factory.right_clicked
                    ),  # noqa
                    right_dclicked=self._target_name(
                        self.factory.right_dclicked
                    ),  # noqa
                    column_clicked=self._target_name(
                        self.factory.column_clicked
                    ),  # noqa
                    column_right_clicked=self._target_name(
                        self.factory.column_right_clicked
                    ),  # noqa
                    operations=self.factory.operations,
                    update=self._target_name(self.factory.update),
                    refresh=self._target_name(self.factory.refresh),
                ),
            ),
            id="data_frame_editor",
            resizable=True,
        )

    def init_ui(self, parent):
        """ Creates the Traits UI for displaying the array.
        """
        factory = self.factory
        if factory.columns != []:
            columns = []
            for column in factory.columns:
                if isinstance(column, str):
                    title = column
                    column_id = column
                else:
                    title, column_id = column
                if column_id not in self.value.columns:
                    continue
                columns.append((title, column_id))
        else:
            columns = [
                (column_id, column_id) for column_id in self.value.columns
            ]

        if factory.show_index:
            index_name = self.value.index.name
            if index_name is None:
                index_name = ""
            columns.insert(0, (index_name, "index"))

        if factory.adapter is not None:
            self.adapter = factory.adapter
            self.adapter._formats = factory.formats
            self.adapter._fonts = factory.fonts
            if not self.adapter.columns:
                self.adapter.columns = columns
        else:
            self.adapter = DataFrameAdapter(
                columns=columns, _formats=factory.formats, _fonts=factory.fonts
            )

        return self.edit_traits(
            view="_data_frame_view", parent=parent, kind="subpanel"
        )
class TreeItem(HasTraits):
    """ A generic base-class for items in a tree data structure. """

    #### 'TreeItem' interface #################################################

    # Does this item allow children?
    allows_children = Bool(True)

    # The item's children.
    children = List(Instance('TreeItem'))

    # Arbitrary data associated with the item.
    data = Any

    # Does the item have any children?
    has_children = Property(Bool)

    # The item's parent.
    parent = Instance('TreeItem')

    ###########################################################################
    # 'object' interface.
    ###########################################################################

    def __str__(self):
        """ Returns the informal string representation of the object. """

        if self.data is None:
            s = ''

        else:
            s = str(self.data)

        return s

    ###########################################################################
    # 'TreeItem' interface.
    ###########################################################################

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

    # has_children
    def _get_has_children(self):
        """ True iff the item has children. """

        return len(self.children) != 0

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

    def append(self, child):
        """ Appends a child to this item.

        This removes the child from its current parent (if it has one).

        """

        return self.insert(len(self.children), child)

    def insert(self, index, child):
        """ Inserts a child into this item at the specified index.

        This removes the child from its current parent (if it has one).

        """

        if child.parent is not None:
            child.parent.remove(child)

        child.parent = self
        self.children.insert(index, child)

        return child

    def remove(self, child):
        """ Removes a child from this item. """

        child.parent = None
        self.children.remove(child)

        return child

    def insert_before(self, before, child):
        """ Inserts a child into this item before the specified item.

        This removes the child from its current parent (if it has one).

        """

        index = self.children.index(before)

        self.insert(index, child)

        return (index, child)

    def insert_after(self, after, child):
        """ Inserts a child into this item after the specified item.

        This removes the child from its current parent (if it has one).

        """

        index = self.children.index(after)

        self.insert(index + 1, child)

        return (index, child)
class RemoteCommandServer(ConfigLoadable):
    '''
    '''
    simulation = False
    _server = None
    repeater = Instance(CommandRepeater)
    processor = Instance(
        'pychron.remote_hardware.command_processor.CommandProcessor')

    host = Str(enter_set=True, auto_set=False)
    port = Int(enter_set=True, auto_set=False)
    klass = Str

    loaded_port = None
    loaded_host = None

    packets_received = Int
    packets_sent = Int
    repeater_fails = Int

    cur_rpacket = String
    cur_spacket = String

    server_button = Event
    server_label = Property(depends_on='_running')
    _running = Bool(False)
    _connected = Bool(False)

    save = Button
    _dirty = Bool(False)

    run_time = Str
    led = Instance(LED, ())

    use_ipc = True

    def _repeater_default(self):
        """
        """
        if globalv.use_ipc:
            c = CommandRepeater(logger_name='{}_repeater'.format(self.name),
                                name=self.name,
                                config_path=os.path.join(
                                    paths.root, 'servers',
                                    '{}.cfg'.format(self.name)))
            if c.bootstrap():
                return c

    def _repeater_fails_changed(self, old, new):
        if new != 0:
            self.repeater.led.state = 0

    def load(self, *args, **kw):
        """
        """

        config = self.get_configuration()
        if config:

            server_class = self.config_get(config, 'General', 'class')
            if server_class is None:
                return

            if server_class == 'IPCServer':
                path = self.config_get(config, 'General', 'path')
                if path is None:
                    self.warning('Path not set. use path config value')
                    return
                addr = path
                self.host = path
                if os.path.exists(path):
                    os.remove(path)
            else:
                if LOCAL:
                    host = 'localhost'
                else:
                    host = self.config_get(config,
                                           'General',
                                           'host',
                                           optional=True)
                port = self.config_get(config, 'General', 'port', cast='int')

                if host is None:
                    host = socket.gethostbyname(socket.gethostname())
                if port is None:
                    self.warning('Host or Port not set {}:{}'.format(
                        host, port))
                    return
                elif port < 1024:
                    self.warning('Port Numbers < 1024 not allowed')
                    return
                addr = (host, port)

                self.host = host
                self.port = port if port else 0

                self.loaded_host = host
                self.loaded_port = port
            self.klass = server_class[:3]

            ds = None
            if config.has_option('Requests', 'datasize'):
                ds = config.getint('Requests', 'datasize')

            ptype = self.config_get(config, 'Requests', 'type', optional=False)
            if ptype is None:
                return

            self.datasize = ds
            self.processor_type = ptype

            self._server = self.server_factory(server_class, addr, ptype, ds)

            # add links
            for link in self.config_get_options(config, 'Links'):
                # note links cannot be stopped
                self._server.add_link(link,
                                      self.config_get(config, 'Links', link))

            if self._server is not None and self._server.connected:
                addr = self._server.server_address
                #                saddr = '({})'.format(','.join(addr if isinstance(addr, tuple) else (addr,)))
                saddr = '({})'.format(addr)
                msg = '%s - %s' % (server_class, saddr)
                self.info(msg)
                self._connected = True
                return True
            else:
                self._connected = False
                self.warning('Cannot connect to {}'.format(addr))

    def server_factory(self, klass, addr, ptype, ds):
        '''
        '''
        # from tcp_server import TCPServer
        # from udp_server import UDPServer

        module = __import__('pychron.messaging.{}_server'.format(
            klass[:3].lower()),
                            fromlist=[klass])
        factory = getattr(module, klass)

        #        gdict = globals()
        #        if handler in gdict:
        #            handler_klass = gdict[handler]

        #        server = gdict[server_class]
        if ds is None:
            ds = 2**10
        #        return server(self, ptype, ds, addr, handler_klass)
        return factory(self, ptype, ds, addr)

    def open(self):
        '''
        '''
        self._running = True
        # t = threading.Thread(target = self._server.serve_forever)
        t = threading.Thread(target=self.start_server)
        t.start()

        return True

    def start_server(self):
        SELECT_TIMEOUT = 1
        #        THREAD_LIMIT = 15
        while self._running:
            try:
                readySocket = select.select([self._server.socket], [], [],
                                            SELECT_TIMEOUT)
                if readySocket[0]:
                    self._server.handle_request()
                #                    if threading.activeCount() < THREAD_LIMIT:
                #                        self._server.handle_request()

            except:
                pass
            #        self._server.socket.close()

    def shutdown(self):
        """
        """
        self._connected = False
        if self._server is not None:
            #            self._server.shutdown()
            self._server.socket.close()

            self._running = False

    def traits_view(self):
        """
        """
        cparams = VGroup(
            HGroup(
                Item('led', show_label=False, editor=LEDEditor()),
                Item('server_button',
                     show_label=False,
                     editor=ButtonEditor(label_value='server_label'),
                     enabled_when='_connected'),
            ),
            Item('host', visible_when='not _running'),
            Item('port', visible_when='not _running'),
            show_border=True,
            label='Connection',
        )
        stats = Group(Item('packets_received', style='readonly'),
                      Item('cur_rpacket', label='Received', style='readonly'),
                      Item('packets_sent', style='readonly'),
                      Item('cur_spacket', label='Sent', style='readonly'),
                      Item('repeater_fails', style='readonly'),
                      Item('run_time', style='readonly'),
                      show_border=True,
                      label='Statistics',
                      visible_when='_connected')

        buttons = HGroup(Item('save', show_label=False, enabled_when='_dirty'))
        v = View(
            VGroup(cparams, stats, buttons),
            handler=RCSHandler,
        )
        return v

    def _run_time_update(self):
        '''
        '''

        #        t = datetime.datetime.now() - self.start_time

        #        h = t.seconds / 3600
        #        m = (t.seconds % 3600) / 60
        #        s = (t.seconds % 3600) % 60

        t, h, m, s = diff_timestamp(datetime.datetime.now(), self.start_time)

        rt = '{:02d}:{:02d}:{:02d}'.format(h, m, s)
        if t.days:
            rt = '{} {:02d}:{:02d}:{:02d}'.format(t.days, h, m, s)
        self.run_time = rt

    def __running_changed(self):
        '''
        '''
        if self._running:
            self.start_time = datetime.datetime.now()
            self.timer = Timer(1000, self._run_time_update)
        else:
            self.timer.Stop()

    def _anytrait_changed(self, name, value):
        '''

        '''
        if name in ['host', 'port']:
            attr = 'loaded_{}'.format(name)
            a = getattr(self, attr)
            if value != a and a is not None:
                self._dirty = True

    def _save_fired(self):
        '''
        '''

        self.shutdown()
        config = self.get_configuration()
        for attr in ['host', 'port']:
            a = getattr(self, attr)
            setattr(self, 'loaded_{}'.format(attr), a)
            config.set('General', attr, a)
            self.write_configuration(config)
            self.load()
        self._dirty = False

    def _server_button_fired(self):
        '''
        '''
        if self._running:
            self.shutdown()
        else:
            # reset the stats
            self.packets_received = 0
            self.packets_sent = 0
            self.cur_rpacket = ''
            self.cur_spacket = ''
            self.repeater_fails = 0

            #            self._server = self.server_factory('TCPServer',
            #                                               (self.host, self.port),
            #                                               self.handler,
            #                                               self.processor_type,
            #                                               self.datasize
            #                                             )
            #            self.open()
            self.bootstrap()

    def _get_server_label(self):
        '''
        '''
        return 'Start' if not self._running else 'Stop'
Exemple #13
0
class TestCase(Mayavi):
    """
    This class is to be subclassed when you write a test.
    """

    # Interact with the user after test is done?  Normally tests just
    # exit after completion, this prevents that.
    interact = Bool(False)

    # Always use offscreen rendering to generate images -- even if
    # `self.compare_image` was called in the test..
    offscreen = Bool(False)

    # Use the standalone mode. This mode does not use the envisage Mayavi
    # application.
    standalone = Bool(True)

    app_window = Instance('pyface.api.ApplicationWindow')

    gui = Instance('pyface.gui.GUI')

    # An exception info if an exception was raised by a test.
    exception_info = Any

    ######################################################################
    # `Mayavi` interface.
    ######################################################################
    def main(self, argv=None, plugins=None):
        """Overridden main method that sets the argv to sys.argv[1:] by
        default.  Call this to run the test.
        """
        if argv is None:
            argv = sys.argv[1:]

        if not is_running_with_nose():
            self.parse_command_line(argv)

        if self.standalone:
            self.run_standalone()
        else:
            # Call the superclass main method.
            super(TestCase, self).main(argv, plugins)

    def setup_logger(self):
        """Overridden logger setup."""
        if self.standalone:
            path = os.path.join(ETSConfig.application_data, 'mayavi_e3',
                                'mayavi-test.log')
            path = os.path.abspath(path)
            log_path = os.path.dirname(path)
            if not os.path.exists(log_path):
                os.makedirs(log_path)
        else:
            path = 'mayavi-test.log'
        setup_logger(logger, path, mode=self.log_mode)

    def run_standalone(self):
        from mayavi.core.engine import Engine
        from mayavi.plugins.script import Script
        from pyface.api import ApplicationWindow, GUI

        self.setup_logger()
        if self.offscreen:
            engine = Engine(scene_factory=off_screen_viewer)
        else:
            engine = Engine()
        engine.start()

        self.exception_info = None
        self.script = Script(engine=engine)
        self.gui = g = GUI()
        self.app_window = a = ApplicationWindow()
        a.open()
        a.show(False)
        g.invoke_later(self.run)
        g.start_event_loop()
        if self.exception_info is not None:
            type, value, tb = self.exception_info
            raise type, value, tb

    def run(self):
        """This starts everything up and runs the test.  Call main to
        run the test."""
        # Calls the users test code.
        try:
            self.do()
        except Exception, e:
            type, value, tb = sys.exc_info()
            if is_running_with_nose():
                self.exception_info = type, value, tb
            else:
                # To mimic behavior of unittest.
                sys.stderr.write('\nfailures=1\n')
                info = traceback.extract_tb(tb)
                filename, lineno, function, text = info[-1]  # last line only
                exc_msg = "%s\nIn %s:%d\n%s: %s (in %s)" %\
                        ('Exception', filename, lineno, type.__name__, str(value),
                        function)
                sys.stderr.write(exc_msg + '\n')
                # Log the message.
                logger.exception(exc_msg)
                if not self.interact:
                    sys.exit(1)
        finally:
Exemple #14
0
class SettingsView(HasTraits):
    """Traits-defined console settings view.

    link : object
      Serial driver object.
    read_finished_functions : list
      Callbacks to call on finishing a settings read.
    name_of_yaml_file : str
      Settings to read from (defaults to settings.yaml)
    expert : bool
      Show expert settings (defaults to False)
    gui_mode : bool
      ??? (defaults to True)
    skip : bool
      Skip reading of the settings (defaults to False). Intended for
      use when reading from network connections.
    """
    show_auto_survey = Bool(False)
    settings_yaml = list()
    auto_survey = SVGButton(
        label='Auto Survey',
        tooltip='Auto populate surveyed lat, lon and alt fields',
        filename='',
        width=16,
        height=20)
    settings_read_button = SVGButton(
        label='Reload',
        tooltip='Reload settings from Piksi',
        filename=resource_filename('console/images/fontawesome/refresh.svg'),
        width=16,
        height=20)
    settings_save_button = SVGButton(
        label='Save to Flash',
        tooltip='Save settings to Flash',
        filename=resource_filename('console/images/fontawesome/download.svg'),
        width=16,
        height=20)
    factory_default_button = SVGButton(
        label='Reset to Defaults',
        tooltip='Reset to Factory Defaults',
        filename=resource_filename(
            'console/images/fontawesome/exclamation-triangle.svg'),
        width=16,
        height=20)
    settings_list = List(SettingBase)
    expert = Bool()
    selected_setting = Instance(SettingBase)
    traits_view = View(
        HSplit(
            Item(
                'settings_list',
                editor=TabularEditor(adapter=SimpleAdapter(),
                                     editable_labels=False,
                                     auto_update=True,
                                     editable=False,
                                     selected='selected_setting'),
                show_label=False,
            ),
            VGroup(
                HGroup(
                    Item('settings_read_button', show_label=False),
                    Item('settings_save_button', show_label=False),
                    Item('factory_default_button', show_label=False),
                    Item('auto_survey',
                         show_label=False,
                         visible_when='show_auto_survey'),
                ),
                HGroup(
                    Item('expert',
                         label="Show Advanced Settings",
                         show_label=True)),
                Item('selected_setting', style='custom', show_label=False),
            ),
        ))

    def _selected_setting_changed(self):
        if self.selected_setting:
            if (self.selected_setting.name in [
                    'surveyed_position', 'broadcast', 'surveyed_lat',
                    'surveyed_lon', 'surveyed_alt'
            ] and self.lat != 0 and self.lon != 0):
                self.show_auto_survey = True
            else:
                self.show_auto_survey = False

    def _expert_changed(self, info):
        try:
            self.settings_display_setup(do_read_finished=False)
        except AttributeError:
            pass

    def _settings_read_button_fired(self):
        self.enumindex = 0
        self.ordering_counter = 0
        self.link(MsgSettingsReadByIndexReq(index=self.enumindex))

    def _settings_save_button_fired(self):
        self.link(MsgSettingsSave())

    def _factory_default_button_fired(self):
        confirm_prompt = prompt.CallbackPrompt(
            title="Reset to Factory Defaults?",
            actions=[prompt.close_button, prompt.reset_button],
            callback=self.reset_factory_defaults)
        confirm_prompt.text = "This will erase all settings and then reset the device.\n" \
            + "Are you sure you want to reset to factory defaults?"
        confirm_prompt.run(block=False)

    def reset_factory_defaults(self):
        # Reset the Piksi, with flag set to restore default settings
        self.link(MsgReset(flags=1))

    def _auto_survey_fired(self):
        confirm_prompt = prompt.CallbackPrompt(
            title="Auto populate surveyed position?",
            actions=[prompt.close_button, prompt.auto_survey_button],
            callback=self.auto_survey_fn)
        confirm_prompt.text = "\n" \
            + "This will set the Surveyed Position section to the \n" \
            + "mean position of the last 1000 position solutions.\n \n" \
            + "The fields that will be auto-populated are: \n" \
            + "Surveyed Lat \n" \
            + "Surveyed Lon \n" \
            + "Surveyed Alt \n \n" \
            + "The surveyed position will be an approximate value. \n" \
            + "This may affect the relative accuracy of Piksi. \n \n" \
            + "Are you sure you want to auto-populate the Surveyed Position section?"
        confirm_prompt.run(block=False)

    def auto_survey_fn(self):
        lat_value = str(self.lat)
        lon_value = str(self.lon)
        alt_value = str(self.alt)
        self.settings['surveyed_position']['surveyed_lat'].value = lat_value
        self.settings['surveyed_position']['surveyed_lon'].value = lon_value
        self.settings['surveyed_position']['surveyed_alt'].value = alt_value
        self.settings_display_setup(do_read_finished=False)

    # Callbacks for receiving messages
    def settings_display_setup(self, do_read_finished=True):
        self.settings_list = []
        sections = sorted(self.settings.keys())
        for sec in sections:
            this_section = []
            for name, setting in sorted(self.settings[sec].iteritems(),
                                        key=lambda n_s: n_s[1].ordering):
                if not setting.expert or (self.expert and setting.expert):
                    this_section.append(setting)
            if this_section:
                self.settings_list.append(SectionHeading(sec))
                self.settings_list += this_section
        # call read_finished_functions as needed
        if do_read_finished:
            for cb in self.read_finished_functions:
                if self.gui_mode:
                    GUI.invoke_later(cb)
                else:
                    cb()

    def settings_read_by_index_done_callback(self, sbp_msg, **metadata):
        self.settings_display_setup()

    def settings_read_resp_callback(self, sbp_msg, **metadata):
        confirmed_set = True
        settings_list = sbp_msg.setting.split("\0")
        if len(settings_list) <= 3:
            print("Received malformed settings read response {0}".format(
                sbp_msg))
            confirmed_set = False
        try:
            if self.settings[settings_list[0]][
                    settings_list[1]].value != settings_list[2]:
                try:
                    float_val = float(self.settings[settings_list[0]][
                        settings_list[1]].value)
                    float_val2 = float(settings_list[2])
                    if abs(float_val - float_val2) > 0.000001:
                        confirmed_set = False
                except ValueError:
                    confirmed_set = False
            if not confirmed_set:
                pass
                # We pass if the new value doesn't match current console value.  It would be nice to update it, but that may cause side effects.
            self.settings[settings_list[0]][
                settings_list[1]].confirmed_set = confirmed_set
        except KeyError:
            return

    def settings_read_by_index_callback(self, sbp_msg, **metadata):
        section, setting, value, format_type = sbp_msg.payload[2:].split(
            '\0')[:4]
        self.ordering_counter += 1
        if format_type == '':
            format_type = None
        else:
            setting_type, setting_format = format_type.split(':')
        if section not in self.settings:
            self.settings[section] = {}
        if format_type is None:
            # Plain old setting, no format information
            self.settings[section][setting] = Setting(
                setting,
                section,
                value,
                ordering=self.ordering_counter,
                settings=self)
        else:
            if setting_type == 'enum':
                enum_values = setting_format.split(',')
                self.settings[section][setting] = EnumSetting(
                    setting,
                    section,
                    value,
                    ordering=self.ordering_counter,
                    values=enum_values,
                    settings=self)
            else:
                # Unknown type, just treat is as a string
                self.settings[section][setting] = Setting(setting,
                                                          section,
                                                          value,
                                                          settings=self)
        if self.enumindex == sbp_msg.index:
            self.enumindex += 1
            self.link(MsgSettingsReadByIndexReq(index=self.enumindex))

    def piksi_startup_callback(self, sbp_msg, **metadata):
        self.settings.clear()
        self._settings_read_button_fired()

    def set(self, section, name, value):
        self.link(
            MsgSettingsWrite(setting='%s\0%s\0%s\0' % (section, name, value)))

    def cleanup(self):
        """ Remove callbacks from serial link. """
        self.link.remove_callback(self.piksi_startup_callback, SBP_MSG_STARTUP)
        self.link.remove_callback(self.settings_read_by_index_callback,
                                  SBP_MSG_SETTINGS_READ_BY_INDEX_REQ)
        self.link.remove_callback(self.settings_read_by_index_callback,
                                  SBP_MSG_SETTINGS_READ_BY_INDEX_RESP)
        self.link.remove_callback(self.settings_read_by_index_done_callback,
                                  SBP_MSG_SETTINGS_READ_BY_INDEX_DONE)

    def __enter__(self):
        return self

    def __exit__(self, *args):
        self.cleanup()

    def __init__(self,
                 link,
                 read_finished_functions=[],
                 name_of_yaml_file="settings.yaml",
                 expert=False,
                 gui_mode=True,
                 skip=False):
        super(SettingsView, self).__init__()
        self.expert = expert
        self.show_auto_survey = False
        self.gui_mode = gui_mode
        self.enumindex = 0
        self.settings = {}
        self.link = link
        self.link.add_callback(self.piksi_startup_callback, SBP_MSG_STARTUP)
        self.link.add_callback(self.settings_read_by_index_callback,
                               SBP_MSG_SETTINGS_READ_BY_INDEX_REQ)
        self.link.add_callback(self.settings_read_by_index_callback,
                               SBP_MSG_SETTINGS_READ_BY_INDEX_RESP)
        self.link.add_callback(self.settings_read_by_index_done_callback,
                               SBP_MSG_SETTINGS_READ_BY_INDEX_DONE)
        self.link.add_callback(self.settings_read_resp_callback,
                               SBP_MSG_SETTINGS_READ_RESP)
        # Read in yaml file for setting metadata
        self.settings_yaml = SettingsList(name_of_yaml_file)
        # List of functions to be executed after all settings are read.
        # No support for arguments currently.
        self.read_finished_functions = read_finished_functions
        self.setting_detail = SettingBase()
        if not skip:
            try:
                self._settings_read_button_fired()
            except IOError:
                print(
                    "IOError in settings_view startup call of _settings_read_button_fired."
                )
                print("Verify that write permissions exist on the port.")
        self.python_console_cmds = {'settings': self}
Exemple #15
0
class Action(HasTraits):
    """ The base class for all actions.

    An action is the non-UI side of a command which can be triggered by the end
    user.  Actions are typically associated with buttons, menu items and tool
    bar tools.

    When the user triggers the command via the UI, the action's 'perform'
    method is invoked to do the actual work.

    """

    #### 'Action' interface ###################################################

    # Keyboard accelerator (by default the action has NO accelerator).
    accelerator = Unicode

    # Is the action checked?  This is only relevant if the action style is
    # 'radio' or 'toggle'.
    checked = Bool(False)

    # A longer description of the action (used for context sensitive help etc).
    # If no description is specified, the tooltip is used instead (and if there
    # is no tooltip, then well, maybe you just hate your users ;^).
    description = Unicode

    # Is the action enabled?
    enabled = Bool(True)

    # Is the action visible?
    visible = Bool(True)

    # The action's unique identifier (may be None).
    id = Str

    # The action's image (displayed on tool bar tools etc).
    image = Image

    # The action's name (displayed on menus/tool bar tools etc).
    name = Unicode

    # An (optional) callable that will be invoked when the action is performed.
    on_perform = Callable

    # The action's style.
    style = Enum('push', 'radio', 'toggle')

    # A short description of the action used for tooltip text etc.
    tooltip = Unicode

    ###########################################################################
    # 'Action' interface.
    ###########################################################################

    #### Initializers #########################################################

    def _id_default(self):
        """ Initializes the 'id' trait. """

        return self.name

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

    def destroy(self):
        """ Called when the action is no longer required.

        By default this method does nothing, but this would be a great place to
        unhook trait listeners etc.

        """

        return

    def perform(self, event):
        """ Performs the action. """

        if self.on_perform is not None:
            self.on_perform()

        return
Exemple #16
0
class IsotopeAnalysisSelector(DatabaseSelector):
    title = 'Recall Analyses'
    #    orm_path = 'pychron.database.orms.isotope_orm'

    query_table = meas_AnalysisTable
    record_view_klass = IsotopeRecordView
    #     record_klass = IsotopeRecord
    #    record_klass = DummyIsotopeRecord
    query_klass = IsotopeQuery
    tabular_adapter = IsotopeResultsAdapter

    lookup = {
        'Labnumber': ([gen_LabTable], gen_LabTable.identifier),
        'Step': ([], meas_AnalysisTable.step),
        'Aliquot': ([], meas_AnalysisTable.aliquot),
        'Sample': ([gen_LabTable, gen_SampleTable], gen_SampleTable.name),
        'Irradiation': ([
            gen_LabTable, irrad_PositionTable, irrad_LevelTable,
            irrad_IrradiationTable
        ], irrad_IrradiationTable.name),
        'Irradiation Level': ([
            gen_LabTable, irrad_PositionTable, irrad_LevelTable,
            irrad_IrradiationTable
        ], irrad_LevelTable.name),
        'Irradiation Position': ([
            gen_LabTable, irrad_PositionTable, irrad_LevelTable,
            irrad_IrradiationTable
        ], irrad_PositionTable.position),
        'Run Date/Time': ([], meas_AnalysisTable.analysis_timestamp),
        'Project': ([
            gen_LabTable,
            gen_SampleTable,
            gen_ProjectTable,
        ], gen_ProjectTable.name),
        'Mass Spectrometer':
        ([meas_MeasurementTable,
          gen_MassSpectrometerTable], gen_MassSpectrometerTable.name),
        'Analysis Type': ([meas_MeasurementTable,
                           gen_AnalysisTypeTable], gen_AnalysisTypeTable.name)
    }

    mass_spectrometer = Str('Spectrometer')
    mass_spectrometers = Property
    analysis_type = Str('Analysis Type')
    analysis_types = Property

    omit_invalid = Bool(True)

    def _record_factory(self, idn):
        if isinstance(idn, meas_AnalysisTable):
            dbr = idn
        elif isinstance(idn, str):
            uuid = idn
        else:
            uuid = idn.uuid
            dbr = self.db.get_analysis(uuid, key='uuid')

        return self.record_klass(_dbrecord=dbr)

    def _get_selector_records(self,
                              queries=None,
                              limit=None,
                              use_filters=True,
                              **kw):
        with self.db.session_ctx() as sess:
            #             sess = self.db.get_session()
            q = sess.query(meas_AnalysisTable)

            if self.omit_invalid:
                q = q.filter(meas_AnalysisTable.tag != 'invalid')

            #             q = q.filter(meas_AnalysisTable.status != -1)

            if queries and use_filters:
                qs = self._build_filters()
                if qs:
                    queries = queries + qs
                    #                queries.extend(qs)

            return self._get_records(q,
                                     queries,
                                     limit,
                                     timestamp='analysis_timestamp')

    def _get_mass_spectrometers(self):
        db = self.db
        mas = ['Spectrometer', LINE_STR]
        ms = db.get_mass_spectrometers()
        if ms:
            mas += [m.name for m in ms]
        return mas

    def _get_analysis_types(self):
        db = self.db
        ats = ['Analysis Type', LINE_STR]
        ai = db.get_analysis_types()
        if ai:
            ats += [aii.name.capitalize() for aii in ai]
        return ats

    def _analysis_type_changed(self):
        self._refresh_results()

    def _mass_spectrometer_changed(self):
        self._refresh_results()

    def _refresh_results(self):
        import inspect

        stack = inspect.stack()
        self.debug('refresh results by {}'.format(stack[1][3]))

        self.execute_query(load=False)

    def _build_filters(self):
        ma = self.mass_spectrometer
        an = self.analysis_type
        qs = []
        #        if pr != NULL_STR:
        #            q = selector.query_factory(parameter='Project', criterion=pr)
        #            qs.append(q)
        if ma not in ('Spectrometer', LINE_STR):
            q = self.query_factory(parameter='Mass Spectrometer', criterion=ma)
            qs.append(q)

        if an not in ('Analysis Type', LINE_STR):
            q = self.query_factory(parameter='Analysis Type', criterion=an)
            qs.append(q)

        return qs
class ObservationView(HasTraits):
  python_console_cmds = Dict()

  _obs_table_list = List()
  obs = Dict()

  name = 'Rover'

  recording = Bool(False)

  record_button = SVGButton(
    label='Record', tooltip='Record Raw Observations',
    toggle_tooltip='Stop Recording', toggle=True,
    filename=os.path.join(os.path.dirname(__file__), 'images', 'fontawesome', 'floppy-o.svg'),
    toggle_filename=os.path.join(os.path.dirname(__file__), 'images', 'fontawesome', 'stop.svg'),
    width=16, height=16
  )

  def trait_view(self, view):
    return View(
      HGroup(
        Item('_obs_table_list', style = 'readonly', editor = TabularEditor(adapter=SimpleAdapter()), show_label=False),
        VGroup(
          Item('record_button', show_label=False),
        ),
        label = self.name,
        show_border = True
      )
    )

  def _record_button_fired(self):
    self.recording = not self.recording
    if not self.recording:
      if self.rinex_file is not None:
        self.rinex_file.close()
      self.rinex_file = None

  def update_obs(self):
    self._obs_table_list = [(prn + 1,) + obs for prn, obs in sorted(self.obs.items(), key=lambda x: x[0])]

  def obs_callback(self, data, sender=None):
    if (sender is not None and
        (self.relay ^ (sender == 0))):
      return

    if self.rinex_file is None and self.recording:
      self.rinex_file = open(self.name+self.t.strftime("-%Y%m%d-%H%M%S.obs"),  'w')
      header = """     2.11           OBSERVATION DATA    G (GPS)             RINEX VERSION / TYPE
pyNEX                                   %s UTC PGM / RUN BY / DATE 
                                                            MARKER NAME         
                                                            OBSERVER / AGENCY   
                                                            REC # / TYPE / VERS 
                                                            ANT # / TYPE        
   808673.9171 -4086658.5368  4115497.9775                  APPROX POSITION XYZ 
        0.0000        0.0000        0.0000                  ANTENNA: DELTA H/E/N
     1     0                                                WAVELENGTH FACT L1/2
     4    C1    L1    S1                                    # / TYPES OF OBSERV 
%s%13.7f     GPS         TIME OF FIRST OBS   
                                                            END OF HEADER       
""" % (
          datetime.datetime.utcnow().strftime("%Y%m%d %H%M%S"),
          self.t.strftime("  %Y    %m    %d    %H    %M"), self.t.second + self.t.microsecond * 1e-6,
      )
      self.rinex_file.write(header)
      self.rinex_file.flush()


    hdr_fmt = "<dH"
    hdr_size = struct.calcsize(hdr_fmt)
    tow, wn = struct.unpack("<dH", data[:hdr_size])
    self.gps_tow = tow
    self.gps_week = wn
    self.t = datetime.datetime(1980, 1, 5) + \
             datetime.timedelta(weeks=self.gps_week) + \
             datetime.timedelta(seconds=self.gps_tow)

    obs_fmt = '<ddfB'
    """
  double P;      /**< Pseudorange (m) */
  double L;      /**< Carrier-phase (cycles) */
  float snr;     /**< Signal-to-Noise ratio */
  u8 prn;        /**< Satellite number. */
    """

    obs_size = struct.calcsize(obs_fmt)
    self.n_obs = (len(data) - hdr_size) / obs_size
    obs_data = data[hdr_size:]

    self.obs = {}
    for i in range(self.n_obs):
      P, L, snr, prn = struct.unpack(obs_fmt, obs_data[:obs_size])
      obs_data = obs_data[obs_size:]
      self.obs[prn] = (P, L, snr)

    if self.recording:
        prns = list(self.obs.iterkeys())
        self.rinex_file.write("%s %10.7f  0 %2d" % (self.t.strftime(" %y %m %d %H %M"),
                                                    self.t.second + self.t.microsecond*1e-6,
                                                    len(prns)))
        while len(prns) > 0:
            prns_ = prns[:12]
            prns = prns[12:]
            for prn in prns_:
                self.rinex_file.write('G%2d' % (prn+1))
            self.rinex_file.write('   ' * (12 - len(prns_)))
            self.rinex_file.write('\n')

        for prn in list(self.obs.iterkeys()):
            # G    3 C1C L1C D1C
            self.rinex_file.write("%14.3f  " % self.obs[prn][0])
            self.rinex_file.write("%14.3f  " % self.obs[prn][1])
            self.rinex_file.write("%14.3f  \n" % self.obs[prn][2])

        self.rinex_file.flush()

    self.update_obs()

  def __init__(self, link, name='Rover', relay=False):
    super(ObservationView, self).__init__()

    self.obs_count = 0
    self.n_obs = 1

    self.relay = relay
    self.name = name

    self.rinex_file = None

    self.link = link
    self.link.add_callback(ids.NEW_OBS, self.obs_callback)

    self.python_console_cmds = {
      'obs': self
    }
Exemple #18
0
class TraitBrowser(HasTraits):
    """ View value heirarchy in traits.  Selection not working at the 
     moment, so use is limitd.  Uses monkeypatching through hackedvtree.py
     to change output style.
     """

    traits_tree = Any  #<-- Instance of stack
    use_default = Bool(False)
    monkeyfunction = Function

    # For display/view only
    _allowed = Str  #<-- defined in subclasses
    _infostr = Property(Str, depends_on='use_default')

    def _get__infostr(self):
        """ View message. """
        if not self.use_default:
            return 'Showing %s Traits only' % self._allowed
        return 'Showing Everything'

    traits_view = View(HGroup(
        Item('use_default', label='Show All Traits'),
        Item('_infostr', style='readonly', show_label=False)),
                       Item('traits_tree',
                            editor=ValueEditor(),
                            show_label=False),
                       title='Trait Browser',
                       buttons=OKCancelButtons,
                       resizable=True,
                       width=.4,
                       height=.4)

    def __init__(self, *args, **kwargs):
        super(TraitBrowser, self).__init__(*args, **kwargs)
        TraitsNode.tno_get_children = self.monkeyfunction

    #def _hide_changed(self):
    #""" Hide or show various attributes in ValueEditor.  Constructs
    #a partial function based on values of self.hide and monkeypatches
    #TraitNode.  Very hacky.
    #"""
    #if self.use_default:
    #return

    #hide = self.hide
    #if 'private' in hide:
    #hide_privates = True
    #else:
    #hide_privates = False
    ## Could make this more "general" with a mapper, but it's 1am
    #allowed = []
    #if 'strings' not in hide:
    #allowed.append(basestring)
    #if 'scalars' not in hide:
    #allowed += [int, float]
    #if 'arrays' not in hide:
    #allowed.append(np.ndarray)

    # X MONKEY PATCH
    # http://stackoverflow.com/questions/28185336/monkey-patching-with-a-partial-function
    #outfcn = functools.partial(hackednode,
    #hide_privates = hide_privates,
    #allowed = allowed)

    #TraitsNode.tno_get_children = outfcn

    ##XXX
    ## SINCE MONKEY PATCHING DOES WORK, JUST GOING TO CALL THIS WITH DEFAULTS
    #TraitsNode.tno_get_children = self.monkeyfunction

    ##Force Refresh view
    #self.trait_view().updated = True

    def _use_default_changed(self):
        """ Restore ValueEditor to unchanged values. """
        if self.use_default:
            _RESTORE()
        else:
            TraitsNode.tno_get_children = self.monkeyfunction
        # Force Refresh view
        self.trait_view().updated = True
Exemple #19
0
class UItem(Item):
    """ An Item that has no label.
    """

    show_label = Bool(False)
Exemple #20
0
class DataSourceFactory(HasStrictTraits):
    """ Factory for creating data sources. The information about the
        organisation of the data is given by setting the public traits.
    """

    # Whether the position is implicitly inferred from the array indices
    position_implicit = Bool(False)

    # Whether the data is on an orthogonal grid
    orthogonal_grid = Bool(False)

    # If the data is unstructured
    unstructured = Bool(False)

    # If the factory should attempt to connect the data points
    connected = Bool(True)

    # The position of the data points
    position_x = ArrayOrNone
    position_y = ArrayOrNone
    position_z = ArrayOrNone

    # Connectivity array. If none, it is implicitly inferred from the array
    # indices
    connectivity_triangles = ArrayOrNone

    # Whether or not the data points should be connected.
    lines = Bool(False)

    # The scalar data array
    scalar_data = ArrayOrNone

    # Whether there is vector data
    has_vector_data = Bool(False)

    # The vector components
    vector_u = ArrayOrNone
    vector_v = ArrayOrNone
    vector_w = ArrayOrNone

    #----------------------------------------------------------------------
    # Private traits
    #----------------------------------------------------------------------
    _vtk_source = Instance(tvtk.DataSet)

    _mayavi_source = Instance(Source)

    #----------------------------------------------------------------------
    # Private interface
    #----------------------------------------------------------------------

    def _add_scalar_data(self):
        """ Adds the scalar data to the vtk source.
        """
        if self.scalar_data is not None:
            scalars = self.scalar_data.ravel()
            self._vtk_source.point_data.scalars = scalars

    def _add_vector_data(self):
        """ Adds the vector data to the vtk source.
        """
        if self.has_vector_data:
            vectors = c_[self.vector_u.ravel(),
                         self.vector_v.ravel(),
                         self.vector_w.ravel(), ]
            self._vtk_source.point_data.vectors = vectors

    def _mk_polydata(self):
        """ Creates a PolyData vtk data set using the factory's
            attributes.
        """
        points = c_[self.position_x.ravel(),
                    self.position_y.ravel(),
                    self.position_z.ravel(), ]
        lines = None
        if self.lines:
            np = len(points) - 1
            lines = zeros((np, 2), 'l')
            lines[:, 0] = arange(0, np - 0.5, 1, 'l')
            lines[:, 1] = arange(1, np + 0.5, 1, 'l')
        self._vtk_source = tvtk.PolyData(points=points, lines=lines)
        if (self.connectivity_triangles is not None and self.connected):
            assert self.connectivity_triangles.shape[1] == 3, \
                    "The connectivity list must be Nx3."
            self._vtk_source.polys = self.connectivity_triangles
        self._mayavi_source = VTKDataSource(data=self._vtk_source)

    def _mk_image_data(self):
        """ Creates an ImageData VTK data set and the associated ArraySource
            using the factory's attributes.
        """
        self._mayavi_source = ArraySource(transpose_input_array=True,
                                          scalar_data=self.scalar_data,
                                          origin=[0., 0., 0],
                                          spacing=[1, 1, 1])
        self._vtk_source = self._mayavi_source.image_data

    def _mk_rectilinear_grid(self):
        """ Creates a RectilinearGrid VTK data set using the factory's
            attributes.
        """
        rg = tvtk.RectilinearGrid()
        x = self.position_x.squeeze()
        if x.ndim == 3:
            x = x[:, 0, 0]
        y = self.position_y.squeeze()
        if y.ndim == 3:
            y = y[0, :, 0]
        z = self.position_z.squeeze()
        if z.ndim == 3:
            z = z[0, 0, :]
        # FIXME: We should check array size here.
        rg.dimensions = (x.size, y.size, z.size)
        rg.x_coordinates = x
        rg.y_coordinates = y
        rg.z_coordinates = z
        self._vtk_source = rg
        self._mayavi_source = VTKDataSource(data=self._vtk_source)

    def _mk_structured_grid(self):
        """ Creates a StructuredGrid VTK data set using the factory's
            attributes.
        """
        # FIXME: We need to figure out the dimensions of the data
        # here, if any.
        sg = tvtk.StructuredGrid(dimensions=self.scalar_data.shape)
        sg.points = c_[self.position_x.ravel(),
                       self.position_y.ravel(),
                       self.position_z.ravel(), ]
        self._vtk_source = sg
        self._mayavi_source = VTKDataSource(data=self._vtk_source)

    #----------------------------------------------------------------------
    # Public interface
    #----------------------------------------------------------------------

    def build_data_source(self, **traits):
        """ Uses all the information given by the user on his data
            structure to figure out the right data structure.
        """
        self.trait_set(**traits)
        if not self.lines:
            if self.position_implicit:
                self._mk_image_data()
            elif self.orthogonal_grid:
                self._mk_rectilinear_grid()
            elif self.connectivity_triangles is None:
                if self.unstructured:
                    self._mk_polydata()
                else:
                    self._mk_structured_grid()
            else:
                self._mk_polydata()
        else:
            self._mk_polydata()
        self._add_scalar_data()
        self._add_vector_data()
        return self._mayavi_source
Exemple #21
0
class UCustom(Custom):
    """ An Item using a 'custom' style with no label.
    """

    show_label = Bool(False)
Exemple #22
0
class AbstractDataExporter(ABCHasStrictTraits):
    """ ABC for classes that export data from a data view.

    Concrete classes should implement the ``get_data`` method so that
    it produces a value that can be serialized using the provided
    ``format``.  Some convenience methods are provided to get
    text values, as that is a common use-case.
    """

    #: The DataFormat used to serialize the exported data.
    format = Instance(DataFormat)

    #: Whether to get item data from the text channel, if available.
    is_text = Bool()

    def add_data(self, data_wrapper, model, indices):
        """ Add data to the data wrapper from the model and indices.

        Parameters
        ----------
        data_wrapper : DataWrapper instance
            The data wrapper that will be used to export data.
        model : AbstractDataModel instance
            The data model holding the data.
        indices : list of (row, column) index pairs
            The indices where the data is to be stored.
        """
        try:
            data = self.get_data(model, indices)
            data_wrapper.set_format(self.format, data)
        except DataViewGetError:
            pass

    @abstractmethod
    def get_data(self, model, indices):
        """ Get the data to be exported from the model and indices.

        Parameters
        ----------
        model : AbstractDataModel instance
            The data model holding the data.
        indices : list of (row, column) index pairs
            The indices where the data is to be stored.

        Returns
        -------
        data : any
            The data, of a type that can be serialized by the format.
        """
        raise NotImplementedError()

    def get_value(self, model, row, column):
        """ Utility method to extract a value at a given index.

        If ``is_text`` is True, it will use the ``get_text()`` method
        to extract the value, otherwise it will try to use the
        editor value if it exists, and failing that the raw value
        returned from the model.
        """
        value_type = model.get_value_type(row, column)
        if self.is_text:
            if value_type.has_text(model, row, column):
                value = value_type.get_text(model, row, column)
            else:
                value = ""
        elif value_type.has_editor_value(model, row, column):
            value = value_type.get_editor_value(model, row, column)
        else:
            value = model.get_value(row, column)
        return value

    def _is_text_default(self):
        return self.format.mimetype.startswith('text/')
Exemple #23
0
class UReadonly(Readonly):
    """ An Item using a 'readonly' style with no label.
    """

    show_label = Bool(False)
class DataFrameEditor(BasicEditorFactory):
    """ Editor factory for basic data frame editor """

    #: The editor implementation class.
    klass = Property()

    #: Should an index column be displayed.
    show_index = Bool(True)

    #: Should column headers be displayed.
    show_titles = Bool(True)

    #: Optional list of either column ID or pairs of (column title, column ID).
    columns = List(Either(Str, Tuple(Str, Str)))

    #: The format for each element, or a mapping column ID to format.
    formats = Either(Str, Dict, default="%s")

    #: The font for each element, or a mapping column ID to font.
    fonts = Either(Font, Dict, default="Courier 10")

    #: The optional extended name of the trait to synchronize the selection
    #: values with:
    selected = Str()

    #: The optional extended name of the trait to synchronize the selection rows
    #: with:
    selected_row = Str()

    #: Whether or not to allow selection.
    selectable = Bool(True)

    #: Whether or not to allow for multiple selections
    multi_select = Bool(False)

    #: The optional extended name of the trait to synchronize the activated
    #: value with:
    activated = Str()

    #: The optional extended name of the trait to synchronize the activated
    #: value's row with:
    activated_row = Str()

    #: The optional extended name of the trait to synchronize left click data
    #: with. The data is a TabularEditorEvent:
    clicked = Str()

    #: The optional extended name of the trait to synchronize left double click
    #: data with. The data is a TabularEditorEvent:
    dclicked = Str()

    #: The optional extended name of the Event trait that should be used to
    #: trigger a scroll-to command. The data is an integer giving the row.
    scroll_to_row = Str()

    #: Controls behavior of scroll to row
    scroll_to_row_hint = Enum("center", "top", "bottom", "visible")

    #: The optional extended name of the Event trait that should be used to
    #: trigger a scroll-to command. The data is an integer giving the column.
    scroll_to_column = Str()

    #: The optional extended name of the trait to synchronize right click data
    #: with. The data is a TabularEditorEvent:
    right_clicked = Str()

    #: The optional extended name of the trait to synchronize right double
    #: clicked data with. The data is a TabularEditorEvent:
    right_dclicked = Str()

    #: The optional extended name of the trait to synchronize column
    #: clicked data with. The data is a TabularEditorEvent:
    column_clicked = Str()

    #: The optional extended name of the trait to synchronize column
    #: right clicked data with. The data is a TabularEditorEvent:
    column_right_clicked = Str()

    #: Whether or not the entries can be edited.
    editable = Bool(False)

    #: What type of operations are allowed on the list:
    operations = List(
        Enum("delete", "insert", "append", "edit", "move"),
        ["delete", "insert", "append", "edit", "move"],
    )

    #: The optional extended name of the trait used to indicate that a complete
    #: table update is needed:
    update = Str()

    #: The optional extended name of the trait used to indicate that the table
    #: just needs to be repainted.
    refresh = Str()

    #: Set to override the default dataframe adapter
    adapter = Instance(DataFrameAdapter)

    def _get_klass(self):
        """ The class used to construct editor objects.
        """
        return toolkit_object("data_frame_editor:_DataFrameEditor")
Exemple #25
0
class Editor(HasPrivateTraits):
    """ Represents an editing control for an object trait in a Traits-based
        user interface.
    """

    #---------------------------------------------------------------------------
    #  Trait definitions:
    #---------------------------------------------------------------------------

    # The UI (user interface) this editor is part of:
    ui = Instance('traitsui.ui.UI')

    # Full name of the object the editor is editing (e.g. 'object.link1.link2'):
    object_name = Str('object')

    # The object this editor is editing (e.g. object.link1.link2):
    object = Instance(HasTraits)

    # The name of the trait this editor is editing (e.g. 'value'):
    name = ReadOnly

    # The context object the editor is editing (e.g. object):
    context_object = Property

    # The extended name of the object trait being edited. That is,
    # 'object_name.name' minus the context object name at the beginning. For
    # example: 'link1.link2.value':
    extended_name = Property

    # Original value of object.name (e.g. object.link1.link2.value):
    old_value = Any

    # Text description of the object trait being edited:
    description = ReadOnly

    # The Item object used to create this editor:
    item = Instance(Item, ())

    # The GUI widget defined by this editor:
    control = Any

    # The GUI label (if any) defined by this editor:
    label_control = Any

    # Is the underlying GUI widget enabled?
    enabled = Bool(True)

    # Is the underlying GUI widget visible?
    visible = Bool(True)

    # Is the underlying GUI widget scrollable?
    scrollable = Bool(False)

    # The EditorFactory used to create this editor:
    factory = factory_trait

    # Is the editor updating the object.name value?
    updating = Bool(False)

    # Current value for object.name:
    value = Property

    # Current value of object trait as a string:
    str_value = Property

    # The trait the editor is editing (not its value, but the trait itself):
    value_trait = Property

    # The current editor invalid state status:
    invalid = Bool(False)

    #---------------------------------------------------------------------------
    #  Initializes the object:
    #---------------------------------------------------------------------------

    def __init__(self, parent, **traits):
        """ Initializes the editor object.
        """
        HasPrivateTraits.__init__(self, **traits)
        try:
            self.old_value = getattr(self.object, self.name)
        except AttributeError:
            ctrait = self.object.base_trait(self.name)
            if ctrait.type == 'event' or self.name == 'spring':
                # Getting the attribute will fail for 'Event' traits:
                self.old_value = Undefined
            else:
                raise

        # Synchronize the application invalid state status with the editor's:
        self.sync_value(self.factory.invalid, 'invalid', 'from')

    #---------------------------------------------------------------------------
    #  Finishes editor set-up:
    #---------------------------------------------------------------------------

    def prepare(self, parent):
        """ Finishes setting up the editor.
        """
        name = self.extended_name
        if name != 'None':
            self.context_object.on_trait_change(self._update_editor,
                                                name,
                                                dispatch='ui')
        self.init(parent)
        self._sync_values()
        self.update_editor()

    #---------------------------------------------------------------------------
    #  Finishes initializing the editor by creating the underlying toolkit
    #  widget:
    #---------------------------------------------------------------------------

    def init(self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        raise NotImplementedError

    #---------------------------------------------------------------------------
    #  Assigns focus to the editor's underlying toolkit widget:
    #---------------------------------------------------------------------------

    def set_focus(self):
        """ Assigns focus to the editor's underlying toolkit widget.
        """
        raise NotImplementedError

    #---------------------------------------------------------------------------
    #  Disposes of the contents of an editor:
    #---------------------------------------------------------------------------

    def dispose(self):
        """ Disposes of the contents of an editor.
        """
        if self.ui is None:
            return

        name = self.extended_name
        if name != 'None':
            self.context_object.on_trait_change(self._update_editor,
                                                name,
                                                remove=True)

        if self._user_from is not None:
            for name, handler in self._user_from:
                self.on_trait_change(handler, name, remove=True)

        if self._user_to is not None:
            for object, name, handler in self._user_to:
                object.on_trait_change(handler, name, remove=True)

        # Break linkages to references we no longer need:
        self.object = self.ui = self.item = self.factory = self.control = \
        self.label_control = self.old_value = self._context_object = None

    #---------------------------------------------------------------------------
    #  Returns the context object the editor is using (Property implementation):
    #---------------------------------------------------------------------------

    @cached_property
    def _get_context_object(self):
        """ Returns the context object the editor is using (Property
            implementation).
        """
        object_name = self.object_name
        context_key = object_name.split('.', 1)[0]
        if (object_name != '') and (context_key in self.ui.context):
            return self.ui.context[context_key]

        # This handles the case of a 'ListItemProxy', which is not in the
        # ui.context, but is the editor 'object':
        return self.object

    #---------------------------------------------------------------------------
    #  Returns the extended trait name being edited (Property implementation):
    #---------------------------------------------------------------------------

    @cached_property
    def _get_extended_name(self):
        """ Returns the extended trait name being edited (Property
            implementation).
        """
        return ('%s.%s' % (self.object_name, self.name)).split('.', 1)[1]

    #---------------------------------------------------------------------------
    #  Returns the trait the editor is editing (Property implementation):
    #---------------------------------------------------------------------------

    def _get_value_trait(self):
        """ Returns the trait the editor is editing (Property implementation).
        """
        return self.object.trait(self.name)

    #---------------------------------------------------------------------------
    #  Gets/Sets the associated object trait's value:
    #---------------------------------------------------------------------------

    def _get_value(self):
        return getattr(self.object, self.name, Undefined)

    def _set_value(self, value):
        if self.ui and self.name != 'None':
            self.ui.do_undoable(self.__set_value, value)

    def __set_value(self, value):
        self._no_update = True
        try:
            try:
                handler = self.ui.handler
                obj_name = self.object_name
                name = self.name
                method = (getattr(handler, '%s_%s_setattr' %
                                  (obj_name, name), None)
                          or getattr(handler, '%s_setattr' % name, None)
                          or getattr(handler, 'setattr'))
                method(self.ui.info, self.object, name, value)
            except TraitError as excp:
                self.error(excp)
                raise
        finally:
            self._no_update = False

    #---------------------------------------------------------------------------
    #  Returns the text representation of a specified object trait value:
    #---------------------------------------------------------------------------

    def string_value(self, value, format_func=None):
        """ Returns the text representation of a specified object trait value.

            If the **format_func** attribute is set on the editor factory, then
            this method calls that function to do the formatting.  If the
            **format_str** attribute is set on the editor factory, then this
            method uses that string for formatting. If neither attribute is
            set, then this method just calls the built-in unicode() function.
        """
        factory = self.factory
        #print "editor", value.decode(encoding='UTF-16',errors='strict')
        if factory.format_func is not None:
            return factory.format_func(value)

        if factory.format_str != '':
            return factory.format_str % value

        if format_func is not None:
            return format_func(value)
        """if value.startswith(b'\xFF\xD8')and value.endswith(b'\xFF\xD9'):
            from base64 import b64encode
            mime='image/jpeg'
            fmt = 'data:{mime};base64,{data}'
            b64 = b64encode(value).decode('ascii')
            return  fmt.format(mime=mime, data=b64)"""

        return unicode(value)

    #---------------------------------------------------------------------------
    #  Returns the text representation of the object trait:
    #---------------------------------------------------------------------------

    def _get_str_value(self):
        """ Returns the text representation of the object trait.
        """
        return self.string_value(getattr(self.object, self.name, Undefined))

    #---------------------------------------------------------------------------
    #  Returns the text representation of a specified value:
    #---------------------------------------------------------------------------

    def _str(self, value):
        """ Returns the text representation of a specified value.
        """
        # In Unicode!
        return unicode(value)

    #---------------------------------------------------------------------------
    #  Handles an error that occurs while setting the object's trait value:
    #
    #  (Should normally be overridden in a subclass)
    #---------------------------------------------------------------------------

    def error(self, excp):
        """ Handles an error that occurs while setting the object's trait value.
        """
        pass

    #---------------------------------------------------------------------------
    #  Performs updates when the object trait changes:
    #---------------------------------------------------------------------------

    def _update_editor(self, object, name, old_value, new_value):
        """ Performs updates when the object trait changes.
        """
        # If background threads have modified the trait the editor is bound to,
        # their trait notifications are queued to the UI thread. It is possible
        # that by the time the UI thread dispatches these events, the UI the
        # editor is part of has already been closed. So we need to check if we
        # are still bound to a live UI, and if not, exit immediately:
        if self.ui is None:
            return

        # If the notification is for an object different than the one actually
        # being edited, it is due to editing an item of the form:
        # object.link1.link2.name, where one of the 'link' objects may have
        # been modified. In this case, we need to rebind the current object
        # being edited:
        if object is not self.object:
            self.object = eval(self.object_name, globals(), self.ui.context)

        # If the editor has gone away for some reason, disconnect and exit:
        if self.control is None:
            self.context_object.on_trait_change(self._update_editor,
                                                self.extended_name,
                                                remove=True)
            return

        # Log the change that was made (as long as it is not for an event):
        if object.base_trait(name).type != 'event':
            self.log_change(self.get_undo_item, object, name, old_value,
                            new_value)

        # If the change was not caused by the editor itself:
        if not self._no_update:
            # Update the editor control to reflect the current object state:
            self.update_editor()

    #---------------------------------------------------------------------------
    #  Logs a change made in the editor:
    #---------------------------------------------------------------------------

    def log_change(self, undo_factory, *undo_args):
        """ Logs a change made in the editor.
        """
        # Indicate that the contents of the user interface have been changed:
        ui = self.ui
        ui.modified = True

        # Create an undo history entry if we are maintaining a history:
        undoable = ui._undoable
        if undoable >= 0:
            history = ui.history
            if history is not None:
                item = undo_factory(*undo_args)
                if item is not None:
                    if undoable == history.now:
                        # Create a new undo transaction:
                        history.add(item)
                    else:
                        # Extend the most recent undo transaction:
                        history.extend(item)

    #---------------------------------------------------------------------------
    #  Updates the editor when the object trait changes external to the editor:
    #
    #  (Should normally be overridden in a subclass)
    #---------------------------------------------------------------------------

    def update_editor(self):
        """ Updates the editor when the object trait changes externally to the
            editor.
        """
        pass

    #---------------------------------------------------------------------------
    #  Creates an undo history entry:
    #
    #  (Can be overridden in a subclass for special value types)
    #---------------------------------------------------------------------------

    def get_undo_item(self, object, name, old_value, new_value):
        """ Creates an undo history entry.
        """
        return UndoItem(object=object,
                        name=name,
                        old_value=old_value,
                        new_value=new_value)

    #---------------------------------------------------------------------------
    #  Returns a tuple of the form ( context_object, name[.name...], callable )
    #  for a specified extended name of the form: name or
    #  context_object_name.name[.name...]:
    #---------------------------------------------------------------------------

    def parse_extended_name(self, name):
        """ Returns a tuple of the form ( context_object, 'name[.name...],
            callable ) for a specified extended name of the form: 'name' or
            'context_object_name.name[.name...]'.
        """
        col = name.find('.')
        if col < 0:
            object = self.context_object
        else:
            object, name = self.ui.context[name[:col]], name[col + 1:]

        return (object, name, eval("lambda obj=object: obj." + name))

    #---------------------------------------------------------------------------
    #  Initializes and synchronizes (as needed) editor traits with the value of
    #  corresponding factory traits:
    #---------------------------------------------------------------------------

    def _sync_values(self):
        """ Initializes and synchronizes (as needed) editor traits with the
            value of corresponding factory traits.
        """
        factory = self.factory
        for name, trait in factory.traits(sync_value=not_none):
            value = getattr(factory, name)
            if isinstance(value, ContextValue):
                self_trait = self.trait(name)
                self.sync_value(value.name, name, self_trait.sync_value
                                or trait.sync_value,
                                self_trait.is_list is True)
            elif value is not Undefined:
                setattr(self, name, value)

    #---------------------------------------------------------------------------
    #  Sets/Unsets synchronization between an editor trait and a user object
    #  trait:
    #---------------------------------------------------------------------------

    def sync_value(self, user_name, editor_name, mode='both', is_list=False):
        """ Sets or unsets synchronization between an editor trait and a user
            object trait.
        """
        if user_name != '':
            key = '%s:%s' % (user_name, editor_name)

            if self._no_trait_update is None:
                self._no_trait_update = {}

            user_ref = 'user_object'
            col = user_name.find('.')
            if col < 0:
                user_object = self.context_object
                xuser_name = user_name
            else:
                user_object = self.ui.context[user_name[:col]]
                user_name = xuser_name = user_name[col + 1:]
                col = user_name.rfind('.')
                if col >= 0:
                    user_ref += ('.' + user_name[:col])
                    user_name = user_name[col + 1:]

            user_value = compile('%s.%s' % (user_ref, user_name), '<string>',
                                 'eval')
            user_ref = compile(user_ref, '<string>', 'eval')

            if mode in ('from', 'both'):

                def user_trait_modified(new):
                    # Need this to include 'user_object' in closure:
                    user_object
                    if key not in self._no_trait_update:
                        self._no_trait_update[key] = None
                        try:
                            setattr(self, editor_name, new)
                        except:
                            from traitsui.api import raise_to_debug
                            raise_to_debug()
                        del self._no_trait_update[key]

                user_object.on_trait_change(user_trait_modified, xuser_name)

                if self._user_to is None:
                    self._user_to = []
                self._user_to.append(
                    (user_object, xuser_name, user_trait_modified))

                if is_list:

                    def user_list_modified(event):
                        if isinstance(event, TraitListEvent):
                            if key not in self._no_trait_update:
                                self._no_trait_update[key] = None
                                n = event.index
                                try:
                                    getattr(self, editor_name
                                            )[n:n +
                                              len(event.removed)] = event.added
                                except:
                                    from traitsui.api import raise_to_debug
                                    raise_to_debug()
                                del self._no_trait_update[key]

                    user_object.on_trait_change(user_list_modified,
                                                xuser_name + '_items')
                    self._user_to.append((user_object, xuser_name + '_items',
                                          user_list_modified))

                try:
                    setattr(self, editor_name, eval(user_value))
                except:
                    from traitsui.api import raise_to_debug
                    raise_to_debug()

            if mode in ('to', 'both'):

                def editor_trait_modified(new):
                    # Need this to include 'user_object' in closure:
                    user_object
                    if key not in self._no_trait_update:
                        self._no_trait_update[key] = None
                        try:
                            setattr(eval(user_ref), user_name, new)
                        except:
                            from traitsui.api import raise_to_debug
                            raise_to_debug()
                        del self._no_trait_update[key]

                self.on_trait_change(editor_trait_modified, editor_name)

                if self._user_from is None:
                    self._user_from = []
                self._user_from.append((editor_name, editor_trait_modified))

                if is_list:

                    def editor_list_modified(event):
                        # Need this to include 'user_object' in closure:
                        user_object
                        if key not in self._no_trait_update:
                            self._no_trait_update[key] = None
                            n = event.index
                            try:
                                eval(user_value
                                     )[n:n + len(event.removed)] = event.added
                            except:
                                from traitsui.api import raise_to_debug
                                raise_to_debug()
                            del self._no_trait_update[key]

                    self.on_trait_change(editor_list_modified,
                                         editor_name + '_items')
                    self._user_from.append(
                        (editor_name + '_items', editor_list_modified))

                if mode == 'to':
                    try:
                        setattr(eval(user_ref), user_name,
                                getattr(self, editor_name))
                    except:
                        from traitsui.api import raise_to_debug
                        raise_to_debug()

    #-- UI preference save/restore interface -----------------------------------

    #---------------------------------------------------------------------------
    #  Restores any saved user preference information associated with the
    #  editor:
    #---------------------------------------------------------------------------

    def restore_prefs(self, prefs):
        """ Restores any saved user preference information associated with the
            editor.
        """
        pass

    #---------------------------------------------------------------------------
    #  Returns any user preference information associated with the editor:
    #---------------------------------------------------------------------------

    def save_prefs(self):
        """ Returns any user preference information associated with the editor.
        """
        return None
Exemple #26
0
class TextGrid(Component):
    """
    A 2D grid of string values
    """

    # A 2D array of strings
    string_array = Array

    # The cell size can be set to a tuple (w,h) or to "auto".
    cell_size = Property

    #------------------------------------------------------------------------
    # Appereance traits
    #------------------------------------------------------------------------

    # The font to use for the text of the grid
    font = KivaFont("modern 14")

    # The color of the text
    text_color = black_color_trait

    # The padding around each cell
    cell_padding = Int(5)

    # The thickness of the border between cells
    cell_border_width = Int(1)

    # The color of the border between cells
    cell_border_color = black_color_trait

    # The dash style of the border between cells
    cell_border_style = LineStyle("solid")

    # Text color of highlighted items
    highlight_color = ColorTrait("red")

    # Cell background color of highlighted items
    highlight_bgcolor = ColorTrait("lightgray")

    # A list of tuples of the (i,j) of selected cells
    selected_cells = List

    #------------------------------------------------------------------------
    # Private traits
    #------------------------------------------------------------------------

    # Are our cached extent values still valid?
    _cache_valid = Bool(False)

    # The maximum width and height of all cells, as a tuple (w,h)
    _cached_cell_size = Tuple

    # The maximum (leading, descent) of all the text strings (positive value)
    _text_offset = Array

    # An array NxMx2 of the x,y positions of the lower-left coordinates of
    # each cell
    _cached_cell_coords = Array

    # "auto" or a tuple
    _cell_size = Trait("auto", Any)

    #------------------------------------------------------------------------
    # Public methods
    #------------------------------------------------------------------------

    def __init__(self, **kwtraits):
        super(Component, self).__init__(**kwtraits)
        self.selected_cells = []
        return

    #------------------------------------------------------------------------
    # AbstractComponent interface
    #------------------------------------------------------------------------

    def _draw_mainlayer(self, gc, view_bounds=None, mode="default"):
        text_color = self.text_color_
        highlight_color = self.highlight_color_
        highlight_bgcolor = self.highlight_bgcolor_
        padding = self.cell_padding
        border_width = self.cell_border_width

        with gc:
            gc.set_stroke_color(text_color)
            gc.set_fill_color(text_color)
            gc.set_font(self.font)
            gc.set_text_position(0, 0)

            width, height = self._get_actual_cell_size()
            numrows, numcols = self.string_array.shape

            # draw selected backgrounds
            # XXX should this be in the background layer?
            for j, row in enumerate(self.string_array):
                for i, text in enumerate(row):
                    if (i, j) in self.selected_cells:
                        gc.set_fill_color(highlight_bgcolor)
                        ll_x, ll_y = self._cached_cell_coords[i, j + 1]
                        # render this a bit big, but covered by border
                        gc.rect(ll_x, ll_y, width + 2 * padding + border_width,
                                height + 2 * padding + border_width)
                        gc.fill_path()
                        gc.set_fill_color(text_color)

            self._draw_grid_lines(gc)

            for j, row in enumerate(self.string_array):
                for i, text in enumerate(row):
                    x,y = self._cached_cell_coords[i,j+1] + self._text_offset + \
                        padding + border_width/2.0

                    if (i, j) in self.selected_cells:
                        gc.set_fill_color(highlight_color)
                        gc.set_stroke_color(highlight_color)
                        gc.set_text_position(x, y)
                        gc.show_text(text)
                        gc.set_stroke_color(text_color)
                        gc.set_fill_color(text_color)
                    else:
                        gc.set_text_position(x, y)
                        gc.show_text(text)

        return

    #------------------------------------------------------------------------
    # Private methods
    #------------------------------------------------------------------------

    def _draw_grid_lines(self, gc):
        gc.set_stroke_color(self.cell_border_color_)
        gc.set_line_dash(self.cell_border_style_)
        gc.set_line_width(self.cell_border_width)

        # Skip the leftmost and bottommost cell coords (since Y axis is reversed,
        # the bottommost coord is the last one)
        x_points = self._cached_cell_coords[:, 0, 0]
        y_points = self._cached_cell_coords[0, :, 1]

        for x in x_points:
            gc.move_to(x, self.y)
            gc.line_to(x, self.y + self.height)
            gc.stroke_path()

        for y in y_points:
            gc.move_to(self.x, y)
            gc.line_to(self.x + self.width, y)
            gc.stroke_path()
        return

    def _compute_cell_sizes(self):
        if not self._cache_valid:
            gc = font_metrics_provider()
            max_w = 0
            max_h = 0
            min_l = 0
            min_d = 0
            for text in self.string_array.ravel():
                gc.set_font(self.font)
                l, d, w, h = gc.get_text_extent(text)
                if -l + w > max_w:
                    max_w = -l + w
                if -d + h > max_h:
                    max_h = -d + h
                if l < min_l:
                    min_l = l
                if d < min_d:
                    min_d = d

            self._cached_cell_size = (max_w, max_h)
            self._text_offset = array([-min_l, -min_d])
            self._cache_valid = True
        return

    def _compute_positions(self):

        if self.string_array is None or len(self.string_array.shape) != 2:
            return

        width, height = self._get_actual_cell_size()
        numrows, numcols = self.string_array.shape

        cell_width = width + 2 * self.cell_padding + self.cell_border_width
        cell_height = height + 2 * self.cell_padding + self.cell_border_width

        x_points = arange(
            numcols + 1) * cell_width + self.cell_border_width / 2.0 + self.x
        y_points = arange(
            numrows + 1) * cell_height + self.cell_border_width / 2.0 + self.y

        tmp = dstack(
            (repeat(x_points[:, newaxis], numrows + 1,
                    axis=1), repeat(y_points[:, newaxis].T,
                                    numcols + 1,
                                    axis=0)))

        # We have to reverse the y-axis (e.g. the 0th row needs to be at the
        # highest y-position).
        self._cached_cell_coords = tmp[:, ::-1]
        return

    def _update_bounds(self):
        if self.string_array is not None and len(self.string_array.shape) == 2:
            rows, cols = self.string_array.shape
            margin = 2 * self.cell_padding + self.cell_border_width
            width, height = self._get_actual_cell_size()
            self.bounds = [
                cols * (width + margin) + self.cell_border_width,
                rows * (height + margin) + self.cell_border_width
            ]

        else:
            self.bounds = [0, 0]

    def _get_actual_cell_size(self):
        if self._cell_size == "auto":
            if not self._cache_valid:
                self._compute_cell_sizes()
            return self._cached_cell_size

        else:
            if not self._cache_valid:
                # actually computing the text offset
                self._compute_cell_sizes()
            return self._cell_size

    #------------------------------------------------------------------------
    # Event handlers
    #------------------------------------------------------------------------

    def normal_left_down(self, event):
        self.selected_cells = [self._get_index_for_xy(event.x, event.y)]
        self.request_redraw()

    def _get_index_for_xy(self, x, y):
        width, height = array(self._get_actual_cell_size()) + 2*self.cell_padding \
                            + self.cell_border_width

        numrows, numcols = self.string_array.shape
        i = int((x - self.padding_left) / width)
        j = numrows - (int((y - self.padding_bottom) / height) + 1)
        shape = self.string_array.shape
        if 0 <= i < shape[1] and 0 <= j < shape[0]:
            return i, j
        else:
            return None

    #------------------------------------------------------------------------
    # Trait events, property setters and getters
    #------------------------------------------------------------------------

    def _string_array_changed(self, old, new):
        if self._cell_size == "auto":
            self._cache_valid = False
            self._compute_cell_sizes()
        self._compute_positions()
        self._update_bounds()

    @on_trait_change('cell_border_width,cell_padding')
    def cell_properties_changed(self):
        self._compute_positions()
        self._update_bounds()

    def _set_cell_size(self, newsize):
        self._cell_size = newsize
        if newsize == "auto":
            self._compute_cell_sizes()
        self._compute_positions()
        self._update_bounds()

    def _get_cell_size(self):
        return self._cell_size
Exemple #27
0
class Recorder(HasTraits, ICameraListener):
    """Container to save all records of a experiment"""

    #=========================================================================
    # Important components to interact
    #=========================================================================

    plotview = Instance(PlotView, ())

    #=========================================================================
    # Properties of the recorder
    #=========================================================================

    input_ports = List(Port)

    container = List(List)

    record_information = List()

    images = List()

    record_mode = Bool()

    logger = getLogger('Application')

    def __init__(self, record_mode):
        self.logger.debug('Initialize Recorder')
        self.record_mode = record_mode
        self.plotview.model = self
        self.result_viewer = ResultViewer(self)

    def append(self, info, rec):
        """Appends new values to the recorder.

        :param info: Values of the input ports.
        :type info: List(Float)
        :param rec: Recording time.
        :type rec: str.
        """
        pid, value = info
        for i in range(self.n):
            p = self.input_ports[i]
            if p.id == pid:
                self.container[i].append((rec, value))

    def reset(self, input_ports):
        """Reset all information of the recorder

        :param input_ports: All input ports of the measuring card
        :type input_ports: List(Port)
        """
        del self.input_ports[:]
        del self.container[:]
        del self.record_information[:]
        self.plotview.update_subplots(input_ports)
        self.n = len(input_ports)
        for i in range(self.n):
            self.input_ports.append(input_ports[i])
            self.container.append([])

    def get_values(self):
        """Returns all recorded values"""
        res = []
        for i in range(len(self.container[0])):
            row = []
            for j in range(self.n):
                rec, val = self.container[j][i]
                row.append(val)
            row.insert(0, time_to_secs(rec))
            res.append(row)
        return array(res)

    #=========================================================================
    # Update methods
    #=========================================================================

    def _update_record_information(self, index):
        """Update the record information by the given index

        :param index: Index of the values which should show
        :type index: int
        """
        self.index = index
        del self.record_information[:]
        for i in range(self.n):
            rec, val = self.container[i][index]
            label = '{0}: {1:.3f}'.format(self.input_ports[i].name, val)
            self.record_information.append(label)
        self.record_information.append('Time: {0}'.format(rec))
        if not self.show_reference:
            files = get_all_files(images_dir)
            self.update_reference(files[0])
        self.plotview.update_focus_point(index)
        self.result_viewer.update_image(index)

    def update_reference(self, f):
        """Load the reference image from the reference dir"""
        self.logger.debug("Update reference image [Recorder]")
        self.reference_image = ImageResource(join(resized_images_dir, f))
        self.show_reference = True

    def _update_plots(self):
        # Update all values with the currently values of the recorder.
        self.plotview.update_values(self.get_values())
    
    def update_image(self, index):
        self.result_viewer.show_recorded_image(index)
    #=========================================================================
    # Methods to save and load the data
    #=========================================================================

    def save(self):
        self.logger.debug("Save recorded values [Recorder]")
        Recorder.save_values(self.get_values())
        Recorder.save_images(self.images)
    
    @staticmethod
    def save_values(values):
        save(join(recorder_dir, recording_file), values)
        
    @staticmethod
    def save_images(images):
        data = {}
        data["images"] = []
        for img in images:
            data["images"].append(img)
        with open(join(recorder_dir, recorder_file), 'w') as f:
            dump(data, f, indent=2)
    
    def load(self, input_ports):
        self.logger.debug("Load recorded values [Recorder]")
        with open(join(recorder_dir, recorder_file), 'r') as f:
            data = jload(f)
        self.images = data["images"]
        values = load(join(recorder_dir, recording_file))
        self.reset(input_ports)
        nv = len(values)
        nip = len(input_ports)
        self.plotview.update_subplots(input_ports)
        self.plotview.update_values(values)
        for i in range(nv):
            t = secs_to_time(values[i, 0])
            for j in range(nip):
                self.container[j].append((t, values[i, j + 1]))
        self.result_viewer.show_images=False
        self._update_record_information(0)
        self.show_images=True
        

    #=========================================================================
    # Traitsview + Traitsevent
    #=========================================================================

    result_viewer = Instance(ResultViewer)

    scroll_able = Bool(True)

    show_images = Bool(False)

    show_reference = Bool(False)

    reference_image = Image()
    
    def _show_images_changed(self):
        self.result_viewer.show_images=self.show_images
        
    view = View(
        VGroup(
            HGroup(
                VGroup(
                    InstanceUItem('plotview', style='custom', width=400),
                    VGroup(
                        UItem('record_information',
                              editor=ListStrEditor(auto_add=False, editable=False)),
                        label="Recorded values:",
                        visible_when='show_images'
                    )
                ),
                VGroup(
                    VGroup(
                        UItem('reference_image', width=300, height=225),
                        label='Reference Image:',
                        visible_when='show_reference'
                    ),
                    VGroup(
                        UItem('result_viewer', style='custom',
                              visible_when='show_images'),
                    ),
                    layout='normal'
                ),
            )
        )
    )
Exemple #28
0
class SolutionView(HasTraits):
    python_console_cmds = Dict()
    # we need to doubleup on Lists to store the psuedo absolutes separately
    # without rewriting everything
    """
  logging_v : toggle logging for velocity files
  directory_name_v : location and name of velocity files
  logging_p : toggle logging for position files
  directory_name_p : location and name of velocity files
  """
    plot_history_max = Int(1000)
    logging_v = Bool(False)
    directory_name_v = File

    logging_p = Bool(False)
    directory_name_p = File

    lats_psuedo_abs = List()
    lngs_psuedo_abs = List()
    alts_psuedo_abs = List()

    table = List()
    dops_table = List()
    pos_table = List()
    vel_table = List()

    rtk_pos_note = Str(
        "It is necessary to enter the \"Surveyed Position\" settings for the base station in order to view the RTK Positions in this tab."
    )

    plot = Instance(Plot)
    plot_data = Instance(ArrayPlotData)
    # Store plots we care about for legend

    running = Bool(True)
    zoomall = Bool(False)
    position_centered = Bool(False)

    clear_button = SVGButton(label='',
                             tooltip='Clear',
                             filename=os.path.join(determine_path(), 'images',
                                                   'iconic', 'x.svg'),
                             width=16,
                             height=16)
    zoomall_button = SVGButton(label='',
                               tooltip='Zoom All',
                               toggle=True,
                               filename=os.path.join(determine_path(),
                                                     'images', 'iconic',
                                                     'fullscreen.svg'),
                               width=16,
                               height=16)
    center_button = SVGButton(label='',
                              tooltip='Center on Solution',
                              toggle=True,
                              filename=os.path.join(determine_path(), 'images',
                                                    'iconic', 'target.svg'),
                              width=16,
                              height=16)
    paused_button = SVGButton(label='',
                              tooltip='Pause',
                              toggle_tooltip='Run',
                              toggle=True,
                              filename=os.path.join(determine_path(), 'images',
                                                    'iconic', 'pause.svg'),
                              toggle_filename=os.path.join(
                                  determine_path(), 'images', 'iconic',
                                  'play.svg'),
                              width=16,
                              height=16)

    traits_view = View(
        HSplit(
            VGroup(
                Item('table',
                     style='readonly',
                     editor=TabularEditor(adapter=SimpleAdapter()),
                     show_label=False,
                     width=0.3),
                Item('rtk_pos_note',
                     show_label=False,
                     resizable=True,
                     editor=MultilineTextEditor(TextEditor(multi_line=True)),
                     style='readonly',
                     width=0.3,
                     height=-40),
            ),
            VGroup(
                HGroup(
                    Item('paused_button', show_label=False),
                    Item('clear_button', show_label=False),
                    Item('zoomall_button', show_label=False),
                    Item('center_button', show_label=False),
                ),
                Item('plot',
                     show_label=False,
                     editor=ComponentEditor(bgcolor=(0.8, 0.8, 0.8))),
            )))

    def _zoomall_button_fired(self):
        self.zoomall = not self.zoomall

    def _center_button_fired(self):
        self.position_centered = not self.position_centered

    def _paused_button_fired(self):
        self.running = not self.running

    def _reset_remove_current(self):
        self.plot_data.set_data('cur_lat_spp', [])
        self.plot_data.set_data('cur_lng_spp', [])
        self.plot_data.set_data('cur_alt_spp', [])
        self.plot_data.set_data('cur_lat_dgnss', [])
        self.plot_data.set_data('cur_lng_dgnss', [])
        self.plot_data.set_data('cur_alt_dgnss', [])
        self.plot_data.set_data('cur_lat_float', [])
        self.plot_data.set_data('cur_lng_float', [])
        self.plot_data.set_data('cur_alt_float', [])
        self.plot_data.set_data('cur_lat_fixed', [])
        self.plot_data.set_data('cur_lng_fixed', [])
        self.plot_data.set_data('cur_alt_fixed', [])

    def _clear_button_fired(self):
        self.tows = np.empty(self.plot_history_max)
        self.lats = np.empty(self.plot_history_max)
        self.lngs = np.empty(self.plot_history_max)
        self.alts = np.empty(self.plot_history_max)
        self.modes = np.empty(self.plot_history_max)
        self.plot_data.set_data('lat_spp', [])
        self.plot_data.set_data('lng_spp', [])
        self.plot_data.set_data('alt_spp', [])
        self.plot_data.set_data('lat_dgnss', [])
        self.plot_data.set_data('lng_dgnss', [])
        self.plot_data.set_data('alt_dgnss', [])
        self.plot_data.set_data('lat_float', [])
        self.plot_data.set_data('lng_float', [])
        self.plot_data.set_data('alt_float', [])
        self.plot_data.set_data('lat_fixed', [])
        self.plot_data.set_data('lng_fixed', [])
        self.plot_data.set_data('alt_fixed', [])
        self._reset_remove_current()

    def _pos_llh_callback(self, sbp_msg, **metadata):
        # Updating an ArrayPlotData isn't thread safe (see chaco issue #9), so
        # actually perform the update in the UI thread.
        if self.running:
            GUI.invoke_later(self.pos_llh_callback, sbp_msg)

    def update_table(self):
        self._table_list = self.table_spp.items()

    def auto_survey(self):
        if self.counter < 1000:
            self.counter = self.counter + 1
        self.latitude_list.append(self.last_soln.lat)
        self.longitude_list.append(self.last_soln.lon)
        self.altitude_list.append(self.last_soln.height)
        self.latitude_list = self.latitude_list[-1000:]
        self.longitude_list = self.longitude_list[-1000:]
        self.altitude_list = self.altitude_list[-1000:]
        self.latitude = (sum(self.latitude_list)) / self.counter
        self.altitude = (sum(self.altitude_list)) / self.counter
        self.longitude = (sum(self.longitude_list)) / self.counter

    def pos_llh_callback(self, sbp_msg, **metadata):
        if sbp_msg.msg_type == SBP_MSG_POS_LLH_DEP_A:
            soln = MsgPosLLHDepA(sbp_msg)
        else:
            soln = MsgPosLLH(sbp_msg)
        self.last_soln = soln

        self.last_pos_mode = get_mode(soln)
        pos_table = []
        soln.h_accuracy *= 1e-3
        soln.v_accuracy *= 1e-3

        tow = soln.tow * 1e-3
        if self.nsec is not None:
            tow += self.nsec * 1e-9

        if self.week is not None:
            t = datetime.datetime(1980, 1, 6) + \
                datetime.timedelta(weeks=self.week) + \
                datetime.timedelta(seconds=tow)
            tstr = t.strftime('%Y-%m-%d %H:%M')
            secs = t.strftime('%S.%f')

            if (self.directory_name_p == ''):
                filepath_p = time.strftime("position_log_%Y%m%d-%H%M%S.csv")
            else:
                filepath_p = os.path.join(
                    self.directory_name_p,
                    time.strftime("position_log_%Y%m%d-%H%M%S.csv"))

            if self.logging_p == False:
                self.log_file = None

            if self.logging_p:
                if self.log_file is None:
                    self.log_file = sopen(filepath_p, 'w')
                    self.log_file.write(
                        "time,latitude(degrees),longitude(degrees),altitude(meters),"
                        "h_accuracy(meters),v_accuracy(meters),n_sats,flags\n")
                self.log_file.write(
                    '%s,%.10f,%.10f,%.4f,%.4f,%.4f,%d,%d\n' %
                    ("{0}:{1:06.6f}".format(tstr, float(secs)), soln.lat,
                     soln.lon, soln.height, soln.h_accuracy, soln.v_accuracy,
                     soln.n_sats, soln.flags))
                self.log_file.flush()

        if self.last_pos_mode == 0:
            pos_table.append(('GPS Time', EMPTY_STR))
            pos_table.append(('GPS Week', EMPTY_STR))
            pos_table.append(('GPS TOW', EMPTY_STR))
            pos_table.append(('Num. Signals', EMPTY_STR))
            pos_table.append(('Lat', EMPTY_STR))
            pos_table.append(('Lng', EMPTY_STR))
            pos_table.append(('Height', EMPTY_STR))
            pos_table.append(('h_accuracy', EMPTY_STR))
            pos_table.append(('v_accuracy', EMPTY_STR))
        else:
            self.last_stime_update = time.time()
            if self.week is not None:
                pos_table.append(
                    ('GPS Time', "{0}:{1:06.3f}".format(tstr, float(secs))))
                pos_table.append(('GPS Week', str(self.week)))
            pos_table.append(('GPS TOW', "{:.3f}".format(tow)))
            pos_table.append(('Num. Sats', soln.n_sats))
            pos_table.append(('Lat', soln.lat))
            pos_table.append(('Lng', soln.lon))
            pos_table.append(('Height', soln.height))
            pos_table.append(('h_accuracy', soln.h_accuracy))
            pos_table.append(('v_accuracy', soln.v_accuracy))

        pos_table.append(('Pos Flags', '0x%03x' % soln.flags))
        pos_table.append(('Pos Fix Mode', mode_dict[self.last_pos_mode]))

        self.auto_survey()

        # setup_plot variables
        self.lats[1:] = self.lats[:-1]
        self.lngs[1:] = self.lngs[:-1]
        self.alts[1:] = self.alts[:-1]
        self.tows[1:] = self.tows[:-1]
        self.modes[1:] = self.modes[:-1]

        self.lats[0] = soln.lat
        self.lngs[0] = soln.lon
        self.alts[0] = soln.height
        self.tows[0] = soln.tow
        self.modes[0] = self.last_pos_mode

        self.lats = self.lats[-self.plot_history_max:]
        self.lngs = self.lngs[-self.plot_history_max:]
        self.alts = self.alts[-self.plot_history_max:]
        self.tows = self.tows[-self.plot_history_max:]
        self.modes = self.modes[-self.plot_history_max:]

        # SPP
        spp_indexer, dgnss_indexer, float_indexer, fixed_indexer = None, None, None, None
        if np.any(self.modes):
            spp_indexer = (self.modes == SPP_MODE)
            dgnss_indexer = (self.modes == DGNSS_MODE)
            float_indexer = (self.modes == FLOAT_MODE)
            fixed_indexer = (self.modes == FIXED_MODE)

            # make sure that there is at least one true in indexer before setting
            if any(spp_indexer):
                self.plot_data.set_data('lat_spp', self.lats[spp_indexer])
                self.plot_data.set_data('lng_spp', self.lngs[spp_indexer])
                self.plot_data.set_data('alt_spp', self.alts[spp_indexer])
            if any(dgnss_indexer):
                self.plot_data.set_data('lat_dgnss', self.lats[dgnss_indexer])
                self.plot_data.set_data('lng_dgnss', self.lngs[dgnss_indexer])
                self.plot_data.set_data('alt_dgnss', self.alts[dgnss_indexer])
            if any(float_indexer):
                self.plot_data.set_data('lat_float', self.lats[float_indexer])
                self.plot_data.set_data('lng_float', self.lngs[float_indexer])
                self.plot_data.set_data('alt_float', self.alts[float_indexer])
            if any(fixed_indexer):
                self.plot_data.set_data('lat_fixed', self.lats[fixed_indexer])
                self.plot_data.set_data('lng_fixed', self.lngs[fixed_indexer])
                self.plot_data.set_data('alt_fixed', self.alts[fixed_indexer])

            # update our "current solution" icon
            if self.last_pos_mode == SPP_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_lat_spp', [soln.lat])
                self.plot_data.set_data('cur_lng_spp', [soln.lon])
            elif self.last_pos_mode == DGNSS_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_lat_dgnss', [soln.lat])
                self.plot_data.set_data('cur_lng_dgnss', [soln.lon])
            elif self.last_pos_mode == FLOAT_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_lat_float', [soln.lat])
                self.plot_data.set_data('cur_lng_float', [soln.lon])
            elif self.last_pos_mode == FIXED_MODE:
                self._reset_remove_current()
                self.plot_data.set_data('cur_lat_fixed', [soln.lat])
                self.plot_data.set_data('cur_lng_fixed', [soln.lon])
            else:
                pass

        # set-up table variables
        self.pos_table = pos_table
        self.table = self.pos_table + self.vel_table + self.dops_table

        # TODO: figure out how to center the graph now that we have two separate messages
        # when we selectively send only SPP, the centering function won't work anymore

        if not self.zoomall and self.position_centered:
            d = (self.plot.index_range.high - self.plot.index_range.low) / 2.
            self.plot.index_range.set_bounds(soln.lon - d, soln.lon + d)
            d = (self.plot.value_range.high - self.plot.value_range.low) / 2.
            self.plot.value_range.set_bounds(soln.lat - d, soln.lat + d)
        if self.zoomall:
            plot_square_axes(
                self.plot, ('lng_spp', 'lng_dgnss', 'lng_float', 'lng_fixed'),
                ('lat_spp', 'lat_dgnss', 'lat_float', 'lat_fixed'))

    def dops_callback(self, sbp_msg, **metadata):
        flags = 0
        if sbp_msg.msg_type == SBP_MSG_DOPS_DEP_A:
            dops = MsgDopsDepA(sbp_msg)
            flags = 1
        else:
            dops = MsgDops(sbp_msg)
            flags = dops.flags
        if flags != 0:
            self.dops_table = [('PDOP', '%.1f' % (dops.pdop * 0.01)),
                               ('GDOP', '%.1f' % (dops.gdop * 0.01)),
                               ('TDOP', '%.1f' % (dops.tdop * 0.01)),
                               ('HDOP', '%.1f' % (dops.hdop * 0.01)),
                               ('VDOP', '%.1f' % (dops.vdop * 0.01))]
        else:
            self.dops_table = [('PDOP', EMPTY_STR), ('GDOP', EMPTY_STR),
                               ('TDOP', EMPTY_STR), ('HDOP', EMPTY_STR),
                               ('VDOP', EMPTY_STR)]

        self.dops_table.append(('DOPS Flags', '0x%03x' % flags))
        self.table = self.pos_table + self.vel_table + self.dops_table

    def vel_ned_callback(self, sbp_msg, **metadata):
        flags = 0
        if sbp_msg.msg_type == SBP_MSG_VEL_NED_DEP_A:
            vel_ned = MsgVelNEDDepA(sbp_msg)
            flags = 1
        else:
            vel_ned = MsgVelNED(sbp_msg)
            flags = vel_ned.flags
        tow = vel_ned.tow * 1e-3
        if self.nsec is not None:
            tow += self.nsec * 1e-9

        if self.week is not None:
            t = datetime.datetime(1980, 1, 6) + \
                datetime.timedelta(weeks=self.week) + \
                datetime.timedelta(seconds=tow)
            tstr = t.strftime('%Y-%m-%d %H:%M')
            secs = t.strftime('%S.%f')

            if self.directory_name_v == '':
                filepath_v = time.strftime("velocity_log_%Y%m%d-%H%M%S.csv")
            else:
                filepath_v = os.path.join(
                    self.directory_name_v,
                    time.strftime("velocity_log_%Y%m%d-%H%M%S.csv"))

            if self.logging_v == False:
                self.vel_log_file = None

            if self.logging_v:

                if self.vel_log_file is None:
                    self.vel_log_file = sopen(filepath_v, 'w')
                    self.vel_log_file.write(
                        'time,north(m/s),east(m/s),down(m/s),speed(m/s),flags,num_signals\n'
                    )
                self.vel_log_file.write(
                    '%s,%.6f,%.6f,%.6f,%.6f,%d,%d\n' %
                    ("{0}:{1:06.6f}".format(tstr, float(secs)),
                     vel_ned.n * 1e-3, vel_ned.e * 1e-3, vel_ned.d * 1e-3,
                     math.sqrt(vel_ned.n * vel_ned.n + vel_ned.e * vel_ned.e) *
                     1e-3, flags, vel_ned.n_sats))
                self.vel_log_file.flush()
        if flags != 0:
            self.vel_table = [
                ('Vel. N', '% 8.4f' % (vel_ned.n * 1e-3)),
                ('Vel. E', '% 8.4f' % (vel_ned.e * 1e-3)),
                ('Vel. D', '% 8.4f' % (vel_ned.d * 1e-3)),
            ]
        else:
            self.vel_table = [
                ('Vel. N', EMPTY_STR),
                ('Vel. E', EMPTY_STR),
                ('Vel. D', EMPTY_STR),
            ]
        self.vel_table.append(('Vel Flags', '0x%03x' % flags))
        self.table = self.pos_table + self.vel_table + self.dops_table

    def gps_time_callback(self, sbp_msg, **metadata):
        if sbp_msg.msg_type == SBP_MSG_GPS_TIME_DEP_A:
            time_msg = MsgGPSTimeDepA(sbp_msg)
            flags = 1
        elif sbp_msg.msg_type == SBP_MSG_GPS_TIME:
            time_msg = MsgGPSTime(sbp_msg)
            flags = time_msg.flags
            if flags != 0:
                self.week = time_msg.wn
                self.nsec = time_msg.ns

    def __init__(self, link, dirname=''):
        super(SolutionView, self).__init__()

        self.lats = np.zeros(self.plot_history_max)
        self.lngs = np.zeros(self.plot_history_max)
        self.alts = np.zeros(self.plot_history_max)
        self.tows = np.zeros(self.plot_history_max)
        self.modes = np.zeros(self.plot_history_max)
        self.log_file = None
        self.directory_name_v = dirname
        self.directory_name_p = dirname
        self.vel_log_file = None
        self.last_stime_update = 0
        self.last_soln = None

        self.counter = 0
        self.latitude_list = []
        self.longitude_list = []
        self.altitude_list = []
        self.altitude = 0
        self.longitude = 0
        self.latitude = 0
        self.last_pos_mode = 0

        self.plot_data = ArrayPlotData(lat_spp=[],
                                       lng_spp=[],
                                       alt_spp=[],
                                       cur_lat_spp=[],
                                       cur_lng_spp=[],
                                       lat_dgnss=[],
                                       lng_dgnss=[],
                                       alt_dgnss=[],
                                       cur_lat_dgnss=[],
                                       cur_lng_dgnss=[],
                                       lat_float=[],
                                       lng_float=[],
                                       alt_float=[],
                                       cur_lat_float=[],
                                       cur_lng_float=[],
                                       lat_fixed=[],
                                       lng_fixed=[],
                                       alt_fixed=[],
                                       cur_lat_fixed=[],
                                       cur_lng_fixed=[])
        self.plot = Plot(self.plot_data)

        # 1000 point buffer
        self.plot.plot(('lng_spp', 'lat_spp'),
                       type='line',
                       line_width=0.1,
                       name='',
                       color=color_dict[SPP_MODE])
        self.plot.plot(('lng_spp', 'lat_spp'),
                       type='scatter',
                       name='',
                       color=color_dict[SPP_MODE],
                       marker='dot',
                       line_width=0.0,
                       marker_size=1.0)
        self.plot.plot(('lng_dgnss', 'lat_dgnss'),
                       type='line',
                       line_width=0.1,
                       name='',
                       color=color_dict[DGNSS_MODE])
        self.plot.plot(('lng_dgnss', 'lat_dgnss'),
                       type='scatter',
                       name='',
                       color=color_dict[DGNSS_MODE],
                       marker='dot',
                       line_width=0.0,
                       marker_size=1.0)
        self.plot.plot(('lng_float', 'lat_float'),
                       type='line',
                       line_width=0.1,
                       name='',
                       color=color_dict[FLOAT_MODE])
        self.plot.plot(('lng_float', 'lat_float'),
                       type='scatter',
                       name='',
                       color=color_dict[FLOAT_MODE],
                       marker='dot',
                       line_width=0.0,
                       marker_size=1.0)
        self.plot.plot(('lng_fixed', 'lat_fixed'),
                       type='line',
                       line_width=0.1,
                       name='',
                       color=color_dict[FIXED_MODE])
        self.plot.plot(('lng_fixed', 'lat_fixed'),
                       type='scatter',
                       name='',
                       color=color_dict[FIXED_MODE],
                       marker='dot',
                       line_width=0.0,
                       marker_size=1.0)
        # current values
        spp = self.plot.plot(('cur_lng_spp', 'cur_lat_spp'),
                             type='scatter',
                             name=mode_dict[SPP_MODE],
                             color=color_dict[SPP_MODE],
                             marker='plus',
                             line_width=1.5,
                             marker_size=5.0)
        dgnss = self.plot.plot(('cur_lng_dgnss', 'cur_lat_dgnss'),
                               type='scatter',
                               name=mode_dict[DGNSS_MODE],
                               color=color_dict[DGNSS_MODE],
                               marker='plus',
                               line_width=1.5,
                               marker_size=5.0)
        rtkfloat = self.plot.plot(('cur_lng_float', 'cur_lat_float'),
                                  type='scatter',
                                  name=mode_dict[FLOAT_MODE],
                                  color=color_dict[FLOAT_MODE],
                                  marker='plus',
                                  line_width=1.5,
                                  marker_size=5.0)
        rtkfix = self.plot.plot(('cur_lng_fixed', 'cur_lat_fixed'),
                                type='scatter',
                                name=mode_dict[FIXED_MODE],
                                color=color_dict[FIXED_MODE],
                                marker='plus',
                                line_width=1.5,
                                marker_size=5.0)
        plot_labels = ['SPP', 'DGPS', "RTK float", "RTK fixed"]
        plots_legend = dict(zip(plot_labels, [spp, dgnss, rtkfloat, rtkfix]))
        self.plot.legend.plots = plots_legend
        self.plot.legend.labels = plot_labels  # sets order
        self.plot.legend.visible = True

        self.plot.index_axis.tick_label_position = 'inside'
        self.plot.index_axis.tick_label_color = 'gray'
        self.plot.index_axis.tick_color = 'gray'
        self.plot.index_axis.title = 'Longitude (degrees)'
        self.plot.index_axis.title_spacing = 5
        self.plot.value_axis.tick_label_position = 'inside'
        self.plot.value_axis.tick_label_color = 'gray'
        self.plot.value_axis.tick_color = 'gray'
        self.plot.value_axis.title = 'Latitude (degrees)'
        self.plot.value_axis.title_spacing = 5
        self.plot.padding = (25, 25, 25, 25)

        self.plot.tools.append(PanTool(self.plot))
        zt = ZoomTool(self.plot,
                      zoom_factor=1.1,
                      tool_mode="box",
                      always_on=False)
        self.plot.overlays.append(zt)

        self.link = link
        self.link.add_callback(self._pos_llh_callback,
                               [SBP_MSG_POS_LLH_DEP_A, SBP_MSG_POS_LLH])
        self.link.add_callback(self.vel_ned_callback,
                               [SBP_MSG_VEL_NED_DEP_A, SBP_MSG_VEL_NED])
        self.link.add_callback(self.dops_callback,
                               [SBP_MSG_DOPS_DEP_A, SBP_MSG_DOPS])
        self.link.add_callback(self.gps_time_callback,
                               [SBP_MSG_GPS_TIME_DEP_A, SBP_MSG_GPS_TIME])

        self.week = None
        self.nsec = 0

        self.python_console_cmds = {
            'solution': self,
        }
class ThemedTextEditor ( EditorFactory ):
    """ Traits UI simple, single line text editor with a themed (i.e. image)
        background.
    """

    #---------------------------------------------------------------------------
    #  Trait definitions:
    #---------------------------------------------------------------------------

    # The background theme image to display:
    theme = ATheme

    # Dictionary that maps user input to other values:
    mapping = Dict( Str, Any )

    # Is user input set on every keystroke?
    auto_set = Bool( True )

    # Is user input set when the Enter key is pressed?
    enter_set = Bool( False )

    # Is user input unreadable? (e.g., for a password)
    password = Bool( False )

    # Function to evaluate textual user input:
    evaluate = Any

    # The object trait containing the function used to evaluate user input:
    evaluate_name = Str

    #---------------------------------------------------------------------------
    #  'Editor' factory methods:
    #---------------------------------------------------------------------------

    def simple_editor ( self, ui, object, name, description, parent ):
        return _ThemedTextEditor( parent,
                                  factory     = self,
                                  ui          = ui,
                                  object      = object,
                                  name        = name,
                                  description = description )

    def custom_editor ( self, ui, object, name, description, parent ):
        return _ThemedTextEditor( parent,
                                  factory     = self,
                                  ui          = ui,
                                  object      = object,
                                  name        = name,
                                  description = description )

    def text_editor ( self, ui, object, name, description, parent ):
        return _ThemedTextEditor( parent,
                                  factory     = self,
                                  ui          = ui,
                                  object      = object,
                                  name        = name,
                                  description = description )

    def readonly_editor ( self, ui, object, name, description, parent ):
        return _ReadonlyTextEditor( parent,
                                  factory     = self,
                                  ui          = ui,
                                  object      = object,
                                  name        = name,
                                  description = description )
Exemple #30
0
class Item(ViewSubElement):
    """ An element in a Traits-based user interface.

    Magic:

    - Items are rendered as layout elements if :attr:`name` is set to
      special values:

      * name='', the item is rendered as a static label

      * name='_', the item is rendered as a separator

      * name=' ', the item is rendered as a 5 pixel spacer

      * name='23' (any number), the item is rendered as a spacer of
        the size specified (number of pixels)
    """

    # FIXME: all the logic for the name = '', '_', ' ', '23' magic is in
    # _GroupPanel._add_items in qt/ui_panel.py, which is a very unlikely place
    # to look for it. Ideally, that logic should be in this class.

    #---------------------------------------------------------------------------
    #  Trait definitions:
    #---------------------------------------------------------------------------

    # A unique identifier for the item. If not set, it defaults to the value
    # of **name**.
    id = Str

    # User interface label for the item in the GUI. If this attribute is not
    # set, the label is the value of **name** with slight modifications:
    # underscores are replaced by spaces, and the first letter is capitalized.
    # If an item's **name** is not specified, its label is displayed as
    # static text, without any editor widget.
    label = Str

    # Name of the trait the item is editing:
    name = Str

    # Style-sheet to apply to item / group (Qt only)
    style_sheet = Str

    # Help text describing the purpose of the item. The built-in help handler
    # displays this text in a pop-up window if the user clicks the widget's
    # label. View-level help displays the help text for all items in a view.
    # If this attribute is not set, the built-in help handler generates a
    # description based on the trait definition.
    help = Str

    # The HasTraits object whose trait attribute the item is editing:
    object = ContainerDelegate

    # Presentation style for the item:
    style = ContainerDelegate

    # Docking style for the item:
    dock = ContainerDelegate

    # Image to display on notebook tabs:
    image = ContainerDelegate

    # The theme to use for the item itself:
    item_theme = ContainerDelegate

    # The theme to use for the item's label:
    label_theme = ContainerDelegate

    # Category of elements dragged from view:
    export = ContainerDelegate

    # Should a label be displayed for the item?
    show_label = Delegate('container', 'show_labels')

    # Editor to use for the item:
    editor = ItemEditor

    # Additional editor traits to be set if default traits editor to be used:
    editor_args = Dict

    # Should the item use extra space along its Group's non-layout axis? If set to
    # True, the widget expands to fill any extra space that is available in the
    # display. If set to True for more than one item in the same View, any extra
    # space is divided between them. If set to False, the widget uses only
    # whatever space it is explicitly (or implicitly) assigned. The default
    # value of Undefined means that the use (or non-use) of extra space will be
    # determined by the editor associated with the item.
    resizable = Bool(Undefined)

    # Should the item use extra space along its Group's layout axis? For
    # example, it a vertical group, should an item expand vertically to use
    # any extra space available in the group?
    springy = Bool(False)

    # Should the item use any extra space along its Group's non-layout
    # orientation? For example, in a vertical group, should an item expand
    # horizontally to the full width of the group? If left to the default value
    # of Undefined, the decision will be left up to the associated item editor.
    full_size = Bool(Undefined)

    # Should the item's label use emphasized text? If the label is not shown,
    # this attribute is ignored.
    emphasized = Bool(False)

    # Should the item receive focus initially?
    has_focus = Bool(False)

    # Pre-condition for including the item in the display. If the expression
    # evaluates to False, the item is not defined in the display. Conditions
    # for **defined_when** are evaluated only once, when the display is first
    # constructed. Use this attribute for conditions based on attributes that
    # vary from object to object, but that do not change over time. For example,
    # displaying a 'maiden_name' item only for female employees in a company
    # database.
    defined_when = Str

    # Pre-condition for showing the item. If the expression evaluates to False,
    # the widget is not visible (and disappears if it was previously visible).
    # If the value evaluates to True, the widget becomes visible. All
    # **visible_when** conditions are checked each time that any trait value
    # is edited in the display. Therefore, you can use **visible_when**
    # conditions to hide or show widgets in response to user input.
    visible_when = Str

    # Pre-condition for enabling the item. If the expression evaluates to False,
    # the widget is disabled, that is, it does not accept input. All
    # **enabled_when** conditions are checked each time that any trait value
    # is edited in the display. Therefore, you can use **enabled_when**
    # conditions to enable or disable widgets in response to user input.
    enabled_when = Str

    # Amount of extra space, in pixels, to add around the item. Values must be
    # integers between -15 and 15. Use negative values to subtract from the
    # default spacing.
    padding = Padding

    # Tooltip to display over the item, when the mouse pointer is left idle
    # over the widget. Make this text as concise as possible; use the **help**
    # attribute to provide more detailed information.
    tooltip = Str

    # A Callable to use for formatting the contents of the item. This function
    # or method is called to create the string representation of the trait value
    # to be edited. If the widget does not use a string representation, this
    # attribute is ignored.
    format_func = Callable

    # Python format string to use for formatting the contents of the item.
    # The format string is applied to the string representation of the trait
    # value before it is displayed in the widget. This attribute is ignored if
    # the widget does not use a string representation, or if the
    # **format_func** is set.
    format_str = Str

    # Requested width of the editor (in pixels or fraction of available width).
    # For pixel values (i.e. values not in the range from 0.0 to 1.0), the
    # actual displayed width is at least the maximum of **width** and the
    # optimal width of the widget as calculated by the GUI toolkit. Specify a
    # negative value to ignore the toolkit's optimal width. For example, use
    # -50 to force a width of 50 pixels. The default value of -1 ensures that
    # the toolkit's optimal width is used.
    #
    # A value in the range from 0.0 to 1.0 specifies the fraction of the
    # available width to assign to the editor. Note that the value is not an
    # absolute value, but is relative to other item's whose **width** is also
    # in the 0.0 to 1.0 range. For example, if you have two item's with a width
    # of 0.1, and one item with a width of 0.2, the first two items will each
    # receive 25% of the available width, while the third item will receive
    # 50% of the available width. The available width is the total width of the
    # view minus the width of any item's with fixed pixel sizes (i.e. width
    # values not in the 0.0 to 1.0 range).
    width = Float(-1.0)

    # Requested height of the editor (in pixels or fraction of available
    # height). For pixel values (i.e. values not in the range from 0.0 to 1.0),
    # the actual displayed height is at least the maximum of **height** and the
    # optimal height of the widget as calculated by the GUI toolkit. Specify a
    # negative value to ignore the toolkit's optimal height. For example, use
    # -50 to force a height of 50 pixels. The default value of -1 ensures that
    # the toolkit's optimal height is used.
    #
    # A value in the range from 0.0 to 1.0 specifies the fraction of the
    # available height to assign to the editor. Note that the value is not an
    # absolute value, but is relative to other item's whose **height** is also
    # in the 0.0 to 1.0 range. For example, if you have two item's with a height
    # of 0.1, and one item with a height of 0.2, the first two items will each
    # receive 25% of the available height, while the third item will receive
    # 50% of the available height. The available height is the total height of
    # the view minus the height of any item's with fixed pixel sizes (i.e.
    # height values not in the 0.0 to 1.0 range).
    height = Float(-1.0)

    # The extended trait name of the trait containing the item's invalid state
    # status (passed through to the item's editor):
    invalid = Str

    #---------------------------------------------------------------------------
    #  Initialize the object:
    #---------------------------------------------------------------------------

    def __init__(self, value=None, **traits):
        """ Initializes the item object.
        """
        super(Item, self).__init__(**traits)

        if value is None:
            return

        if not isinstance(value, basestring):
            raise TypeError, (
                "The argument to Item must be a string of the "
                "form: [id:][object.[object.]*][name]['['label']']`tooltip`"
                "[<width[,height]>][#^][$|@|*|~|;style]")

        value, empty = self._parse_label(value)
        if empty:
            self.show_label = False

        value = self._parse_style(value)
        value = self._parse_size(value)
        value = self._parse_tooltip(value)
        value = self._option(value, '#', 'resizable', True)
        value = self._option(value, '^', 'emphasized', True)
        value = self._split('id', value, ':', find, 0, 1)
        value = self._split('object', value, '.', rfind, 0, 1)

        if value != '':
            self.name = value

    #---------------------------------------------------------------------------
    #  Returns whether or not the object is replacable by an Include object:
    #---------------------------------------------------------------------------

    def is_includable(self):
        """ Returns a Boolean indicating whether the object is replaceable by an
            Include object.
        """
        return (self.id != '')

    #---------------------------------------------------------------------------
    #  Returns whether or not the Item represents a spacer or separator:
    #---------------------------------------------------------------------------

    def is_spacer(self):
        """ Returns True if the item represents a spacer or separator.
        """
        name = self.name.strip()

        return ((name == '') or (name == '_')
                or (all_digits.match(name) is not None))

    #---------------------------------------------------------------------------
    #  Gets the help text associated with the Item in a specified UI:
    #---------------------------------------------------------------------------

    def get_help(self, ui):
        """ Gets the help text associated with the Item in a specified UI.
        """
        # Return 'None' if the Item is a separator or spacer:
        if self.is_spacer():
            return None

        # Otherwise, it must be a trait Item:
        if self.help != '':
            return self.help

        object = eval(self.object_, globals(), ui.context)

        return object.base_trait(self.name).get_help()

    #---------------------------------------------------------------------------
    #  Gets the label to use for a specified Item in a specified UI:
    #---------------------------------------------------------------------------

    def get_label(self, ui):
        """ Gets the label to use for a specified Item.

        If not specified, the label is set as the name of the
        corresponding trait, replacing '_' with ' ', and capitalizing
        the first letter (see :func:`user_name_for`). This is called
        the *user name*.

        Magic:

        - if attr:`item.label` is specified, and it begins with '...',
          the final label is the user name followed by the item label
        - if attr:`item.label` is specified, and it ends with '...',
          the final label is the item label followed by the user name
        """
        # Return 'None' if the Item is a separator or spacer:
        if self.is_spacer():
            return None

        label = self.label
        if label != '':
            return label

        name = self.name
        object = eval(self.object_, globals(), ui.context)
        trait = object.base_trait(name)
        label = user_name_for(name)
        tlabel = trait.label
        if tlabel is None:
            return label

        if isinstance(tlabel, basestring):
            if tlabel[0:3] == '...':
                return label + tlabel[3:]
            if tlabel[-3:] == '...':
                return tlabel[:-3] + label
            if self.label != '':
                return self.label
            return tlabel

        return tlabel(object, name, label)

    #---------------------------------------------------------------------------
    #  Returns an id used to identify the item:
    #---------------------------------------------------------------------------

    def get_id(self):
        """ Returns an ID used to identify the item.
        """
        if self.id != '':
            return self.id

        return self.name

    #---------------------------------------------------------------------------
    #  Parses a '<width,height>' value from the string definition:
    #---------------------------------------------------------------------------

    def _parse_size(self, value):
        """ Parses a '<width,height>' value from the string definition.
        """
        match = size_pat.match(value)
        if match is not None:
            data = match.group(2)
            value = match.group(1) + match.group(3)
            col = data.find(',')
            if col < 0:
                self._set_float('width', data)
            else:
                self._set_float('width', data[:col])
                self._set_float('height', data[col + 1:])

        return value

    #---------------------------------------------------------------------------
    #  Parses a '`tooltip`' value from the string definition:
    #---------------------------------------------------------------------------

    def _parse_tooltip(self, value):
        """ Parses a *tooltip* value from the string definition.
        """
        match = tooltip_pat.match(value)
        if match is not None:
            self.tooltip = match.group(2)
            value = match.group(1) + match.group(3)

        return value

    #---------------------------------------------------------------------------
    #  Sets a specified trait to a specified string converted to a float:
    #---------------------------------------------------------------------------

    def _set_float(self, name, value):
        """ Sets a specified trait to a specified string converted to a float.
        """
        value = value.strip()
        if value != '':
            setattr(self, name, float(value))

    #---------------------------------------------------------------------------
    #  Returns a 'pretty print' version of the Item:
    #---------------------------------------------------------------------------

    def __repr__(self):
        """ Returns a "pretty print" version of the Item.
        """

        options = self._repr_options('id', 'object', 'label', 'style',
                                     'show_label', 'width', 'height')
        if options is None:
            return "Item( '%s' )" % self.name

        return "Item( '%s'\n%s\n)" % (self.name, self._indent(
            options, '      '))
Exemple #31
0
class TableModel(GridModel):
    """ Model for table data.
    """

    # -------------------------------------------------------------------------
    #  Trait definitions:
    # -------------------------------------------------------------------------

    #: The editor that created this model
    editor = Instance(Editor)

    #: The current filter
    filter = Instance(TableFilter, allow_none=True)

    #: Current filter summary message
    filter_summary = Str("All items")

    #: Display the table items in reverse order?
    reverse = Bool(False)

    #: Event fired when the table has been sorted
    sorted = Event

    #: The current 'auto_add' row
    auto_add_row = Any

    def __init__(self, **traits):
        """ Initializes the object.
        """
        super(TableModel, self).__init__(**traits)

        # Attach trait handlers to the list object:
        editor = self.editor
        object = editor.context_object
        name = " " + editor.extended_name

        # Set up listeners for any of the model data changing:
        object.on_trait_change(self._on_data_changed, name, dispatch="ui")
        object.on_trait_change(
            self.fire_content_changed, name + ".-", dispatch="ui"
        )

        # Set up listeners for any column definitions changing:
        editor.on_trait_change(self.update_columns, "columns", dispatch="ui")
        editor.on_trait_change(
            self.update_columns, "columns_items", dispatch="ui"
        )

        # Initialize the current filter from the editor's default filter:
        self.filter = editor.filter

        # If we are using 'auto_add' mode, create the first 'auto_add' row:
        if editor.auto_add:
            self.auto_add_row = row = editor.create_new_row()
            if row is not None:
                row.on_trait_change(self.on_auto_add_row, dispatch="ui")

    # -- TableModel Interface -------------------------------------------------

    def dispose(self):
        """ Disposes of the model when it is no longer needed.
        """
        editor = self.editor
        object = editor.context_object
        name = " " + editor.extended_name

        # Remove listeners for any of the model data changing:
        object.on_trait_change(self._on_data_changed, name, remove=True)
        object.on_trait_change(
            self.fire_content_changed, name + ".-", remove=True
        )

        # Remove listeners for any column definitions changing:
        editor.on_trait_change(self.update_columns, "columns", remove=True)
        editor.on_trait_change(
            self.update_columns, "columns_items", remove=True
        )

        # Make sure we have removed listeners from the current filter also:
        if self.filter is not None:
            self.filter.on_trait_change(self._filter_modified, remove=True)

        # Clean-up any links that should be broken:
        self.editor = None

    def get_filtered_items(self):
        """ Returns all model items matching the current filter.
        """
        return self.__filtered_items()

    def get_filtered_item(self, index=0):
        """ Returns a single specified item from those items matching the
            current filter.
        """
        try:
            return self.__filtered_items()[index]
        except:
            logger.error(
                "TableModel error: Request for invalid row %d out of "
                "%d" % (index, len(self.__filtered_items()))
            )
            return None

    def raw_index_of(self, row):
        """ Returns the raw, unfiltered index corresponding to a specified
            filtered index.
        """
        if self._filtered_cache is None:
            return row

        return self.editor.filtered_indices[row]

    def insert_filtered_item_after(self, index, item):
        """ Inserts an object after a specified filtered index.
        """
        mapped_index = 0
        n = len(self.editor.filtered_indices)
        if index >= n:
            if (index != 0) or (n != 0):
                raise IndexError
        elif index >= 0:
            mapped_index = self.editor.filtered_indices[index] + 1
        self.__items().insert(mapped_index, item)
        sorted = self._sort_model()
        if sorted:
            mapped_index = self.__items().index(item)
        self._filtered_cache = None
        return (mapped_index, sorted)

    def delete_filtered_item_at(self, index):
        """ Deletes the object at the specified filtered index.
        """
        if index >= len(self.editor.filtered_indices):
            raise IndexError

        mapped_index = self.editor.filtered_indices[index]
        items = self.__items()
        object = items[mapped_index]
        del items[mapped_index]
        self._filtered_cache = None
        return (mapped_index, object)

    def update_columns(self):
        """ Updates the table view when columns have been changed.
        """
        self._columns = None
        self.fire_structure_changed()
        self.editor.refresh()

    def no_column_sort(self):
        """ Resets any sorting being performed on the underlying model.
        """
        self._sorter = self._filtered_cache = None
        self.column_sorted = GridSortEvent(index=-1)
        # self.fire_structure_changed()

    # -- Event Handlers -------------------------------------------------------

    @on_trait_change("filter.+")
    def _filter_modified(self):
        """ Handles the contents of the filter being changed.
        """
        self._filtered_cache = None
        self.fire_structure_changed()
        self.editor.filter_modified()

    def _click_changed(self, event):
        """ Handles the grid firing a 'click' event.
        """
        row, col = event

        # Fire the same event on the editor after mapping it to a model object
        # and column name:
        object = self.get_filtered_item(row)
        column = self.__get_column(col)
        self.editor.click = (object, column)

        # Check to see if the column has a view to display:
        view = column.get_view(object)
        if view is not None:
            column.get_object(object).edit_traits(
                view=view, parent=self._bounds_for(row, col)
            )

        # Invoke the column's click handler:
        column.on_click(object)

    def _dclick_changed(self, event):
        """ Handles the grid firing a 'dclick' event.
        """
        row, col = event

        # Fire the same event on the editor after mapping it to a model object
        # and column name:
        object = self.get_filtered_item(row)
        column = self.__get_column(col)
        self.editor.dclick = (object, column)

        # Invoke the column's double-click handler:
        column.on_dclick(object)

    def on_auto_add_row(self):
        """ Handles the user modifying the current 'auto_add' mode row.
        """
        object = self.auto_add_row
        object.on_trait_change(self.on_auto_add_row, remove=True)

        self.auto_add_row = row = self.editor.create_new_row()
        if row is not None:
            row.on_trait_change(self.on_auto_add_row, dispatch="ui")

        do_later(
            self.editor.add_row, object, len(self.get_filtered_items()) - 2
        )

    # -- GridModel Interface --------------------------------------------------

    def get_column_count(self):
        """ Returns the number of columns for this table.
        """
        return len(self.__get_columns())

    def get_column_name(self, index):
        """ Returns the label of the column specified by the (zero-based) index.
        """
        return self.__get_column(index).get_label()

    def get_column_size(self, index):
        """ Returns the size in pixels of the column indexed by *index*.
            A value of -1 or None means to use the default.
        """
        return self.__get_column(index).get_width()

    def get_cols_drag_value(self, cols):
        """ Returns the value to use when the specified columns are dragged or
            copied and pasted. The parameter *cols* is a list of column indexes.
        """
        return [self.__get_data_column(col) for col in cols]

    def get_cols_selection_value(self, cols):
        """ Returns a list of TraitGridSelection objects containing the
            objects corresponding to the grid rows and the traits corresponding
            to the specified columns.
        """
        values = []
        for obj in self.__items(False):
            values.extend(
                [
                    TraitGridSelection(
                        obj=obj, name=self.__get_column_name(col)
                    )
                    for col in cols
                ]
            )
        return values

    def sort_by_column(self, col, reverse=False):
        """ Sorts the model data by the column indexed by *col*.
        """
        # Make sure we allow sorts by column:
        factory = self.editor.factory
        if not factory.sortable:
            return

        # Flush the object cache:
        self._filtered_cache = None

        # Cache the sorting information for later:
        self._sorter = self.__get_column(col).key
        self._reverse = reverse

        # If model sorting is requested, do it now:
        self._sort_model()

        # Indicate the we have been sorted:
        self.sorted = True

        self.column_sorted = GridSortEvent(index=col, reversed=reverse)

    def is_column_read_only(self, index):
        """ Returns True if the column specified by the zero-based *index* is
            read-only.
        """
        return not self.__get_column(index).editable

    def get_row_count(self):
        """ Return the number of rows for this table.
        """
        return len(self.__filtered_items())

    def get_row_name(self, index):
        """ Return the name of the row specified by the (zero-based) *index*.
        """
        return "<undefined>"

    def get_rows_drag_value(self, rows):
        """ Returns the value to use when the specified rows are dragged or
            copied and pasted. The parameter *rows* is a list of row indexes.
            If there is only one row listed, then return the corresponding trait
            object. If more than one row is listed, then return a list of objects.
        """
        items = self.__filtered_items()
        return [items[row] for row in rows]

    def get_rows_selection_value(self, rows):
        """ Returns a list of TraitGridSelection objects containing the
            object corresponding to the selected rows.
        """
        items = self.__filtered_items()
        return [TraitGridSelection(obj=items[row]) for row in rows]

    def is_row_read_only(self, index):
        """ Returns True if the row specified by the zero-based *index* is
            read-only.
        """
        return False

    def get_cell_editor(self, row, col):
        """ Returns the editor for the specified cell.
        """

        if self.editor is None:
            return None

        column = self.__get_column(col)
        object = self.get_filtered_item(row)
        editor = column.get_editor(object)
        if editor is None:
            return None

        editor._ui = self.editor.ui

        target, name = column.target_name(object)

        return TraitGridCellAdapter(
            editor,
            target,
            name,
            "",
            context=self.editor.ui.context,
            style=column.get_style(object),
            width=column.get_edit_width(object),
            height=column.get_edit_height(object),
        )

    def get_cell_renderer(self, row, col):
        """ Returns the renderer for the specified cell.
        """
        return self.__get_column(col).get_renderer(self.get_filtered_item(row))

    def get_cell_drag_value(self, row, col):
        """ Returns the value to use when the specified cell is dragged or
            copied and pasted.
        """
        return self.__get_column(col).get_drag_value(
            self.get_filtered_item(row)
        )

    def get_cell_selection_value(self, row, col):
        """ Returns a TraitGridSelection object specifying the data stored
            in the table at (*row*, *col*).
        """
        return TraitGridSelection(
            obj=self.get_filtered_item(row), name=self.__get_column_name(col)
        )

    def resolve_selection(self, selection_list):
        """ Returns a list of (row, col) grid-cell coordinates that
            correspond to the objects in *selection_list*. For each coordinate,
            if the row is -1, it indicates that the entire column is selected.
            Likewise coordinates with a column of -1 indicate an entire row
            that is selected. For the TableModel, the objects in
            *selection_list* must be TraitGridSelection objects.
        """
        items = self.__filtered_items()
        cells = []
        for selection in selection_list:
            row = -1
            if selection.obj is not None:
                try:
                    row = items.index(selection.obj)
                except ValueError:
                    continue

            column = -1
            if selection.name != "":
                column = self._get_column_index_by_trait(selection.name)
                if column is None:
                    continue

            cells.append((row, column))

        return cells

    def get_cell_context_menu(self, row, col):
        """ Returns a Menu object that generates the appropriate context
            menu for this cell.
        """
        column = self.__get_column(col)
        menu = column.get_menu(self.get_filtered_item(row))
        editor = self.editor

        if menu is None:
            menu = editor.factory.menu

        if menu is None:
            menu_name = editor.factory.menu_name
            if menu_name:
                menu = getattr(self.editor.object, menu_name, None)

        if menu is not None:
            editor.prepare_menu(row, column)

            return (menu, editor)

        return None

    def get_value(self, row, col):
        """ Returns the value stored in the table at (*row*, *col*).
        """
        object = self.get_filtered_item(row)
        if object is self.auto_add_row:
            return ""

        value = self.__get_column(col).get_value(object)
        formats = self.__get_column_formats(col)

        if (value is not None) and (formats is not None):
            format = formats.get(type(value))
            if format is not None:
                try:
                    if callable(format):
                        value = format(value)
                    else:
                        value = format % value
                except:
                    pass

        return value

    def is_valid_cell_value(self, row, col, value):
        """ Tests whether *value* is valid for the cell at (*row*, *col*).
        Returns True if value is acceptable, and False otherwise. """
        return self.__get_column(col).is_droppable(
            self.get_filtered_item(row), value
        )

    def is_cell_empty(self, row, col):
        """ Returns True if the cell at (*row*, *col*) has a None value, and
            False otherwise.
        """
        return self.get_value(row, col) is None

    def is_cell_read_only(self, row, col):
        """ Returns True if the cell at (*row*, *col*) is read-only, and False
            otherwise.
        """
        return not self.__get_column(col).is_editable(
            self.get_filtered_item(row)
        )

    def get_cell_bg_color(self, row, col):
        """ Returns a wxColour object specifying the background color
            of the specified cell.
        """
        return self.__get_column(col).get_cell_color(
            self.get_filtered_item(row)
        )

    def get_cell_text_color(self, row, col):
        """ Returns a wxColour object specifying the text color of the
            specified cell.
        """
        column = self.__get_column(col)
        item = self.get_filtered_item(row)
        return column.get_text_color(item)

    def get_cell_font(self, row, col):
        """ Returns a wxFont object specifying the font of the specified cell.
        """
        return self.__get_column(col).get_text_font(
            self.get_filtered_item(row)
        )

    def get_cell_halignment(self, row, col):
        """ Returns a string specifying the horizontal alignment of the
            specified cell.

            Returns 'left' for left alignment, 'right' for right alignment,
            or 'center' for center alignment.
        """
        return self.__get_column(col).get_horizontal_alignment(
            self.get_filtered_item(row)
        )

    def get_cell_valignment(self, row, col):
        """ Returns a string specifying the vertical alignment of the
            specified cell.

            Returns 'top' for top alignment, 'bottom' for bottom alignment,
            or 'center' for center alignment.
        """
        return self.__get_column(col).get_vertical_alignment(
            self.get_filtered_item(row)
        )

    def _insert_rows(self, pos, num_rows):
        """ Inserts *num_rows* at *pos*; fires an event only if a factory
        method for new rows is defined or the model is not empty. Otherwise,
        it returns 0.
        """
        count = 0

        factory = self.editor.factory.row_factory
        if factory is None:
            items = self.__items(False)
            if len(items) > 0:
                factory = items[0].__class__

        if factory is not None:
            new_data = [
                x
                for x in [factory() for i in range(num_rows)]
                if x is not None
            ]
            if len(new_data) > 0:
                count = self._insert_rows_into_model(pos, new_data)
                self.rows_added = ("added", pos, new_data)

        return count

    def _delete_rows(self, pos, num_rows):
        """ Removes rows *pos* through *pos* + *num_rows* from the model.
        """
        row_count = self.get_rows_count()
        if (pos + num_rows) > row_count:
            num_rows = row_count - pos

        return self._delete_rows_from_model(pos, num_rows)

    def _set_value(self, row, col, value):
        """ Sets the value of the cell at (*row*, *col*) to *value*.

            Raises a ValueError if the value is vetoed or the cell at
            the specified position does not exist.
        """
        new_rows = 0
        column = self.__get_column(col)
        obj = None
        try:
            obj = self.get_filtered_item(row)
        except:
            # Add a new row:
            new_rows = self._insert_rows(self.get_row_count(), 1)
            if new_rows > 0:
                # Now set the value on the new object:
                try:
                    obj = self.get_filtered_item(self.get_row_count() - 1)
                except:
                    # fixme: what do we do in this case? veto the set somehow?
                    # raise an exception?
                    pass

        if obj is not None:
            self._set_data_on_row(obj, column, value)

        return new_rows

    def _move_column(self, frm, to):
        """ Moves a specified **frm** column to before the specified **to**
            column. Returns **True** if successful; **False** otherwise.
        """
        to_column = None
        if to < len(self.__get_columns()):
            to_column = self.__get_column(to)

        return self.editor.move_column(self.__get_column(frm), to_column)

    def _set_data_on_row(self, row, column, value):
        """ Sets the cell specified by (*row*, *col*) to *value, which
            can be either a member of the row object, or a no-argument method
            on that object.
        """
        column.set_value(row, value)

    def _insert_rows_into_model(self, pos, new_data):
        """ Inserts the given new rows into the model.
        """
        raw_pos = self.raw_index_of(pos)
        self.__items()[raw_pos:raw_pos] = new_data

    def _delete_rows_from_model(self, pos, num_rows):
        """ Deletes the specified rows from the model.
        """
        raw_rows = sorted(
            [self.raw_index_of(i) for i in range(pos, pos + num_rows)]
        )
        raw_rows.reverse()
        items = self.__items()
        for row in raw_rows:
            del items[row]

        return num_rows

    def _on_data_changed(self):
        """ Forces the grid to refresh when the underlying list changes.
        """
        # Invalidate the current cache (if any):
        self._filtered_cache = None

        self.fire_structure_changed()

    def _mouse_cell_changed(self, new):
        """ Handles the user mousing over a specified cell.
        """
        row, col = new
        column = self.__get_column(col)
        object = self.get_filtered_item(row)

        # Update the tooltip if necessary:
        tooltip = column.get_tooltip(object)
        if tooltip != self._tooltip:
            self._tooltip = tooltip
            self.editor.grid._grid_window.SetToolTip(wx.ToolTip(tooltip))

        if column.is_auto_editable(object):
            x, y, dx, dy = self._bounds_for(row, col)
            if column.is_editable(object):
                view = View(
                    Item(
                        name=column.name,
                        editor=column.get_editor(object),
                        style=column.get_style(object),
                        show_label=False,
                        padding=-4,
                    ),
                    kind="info",
                    width=dx,
                    height=dy,
                )
            else:
                view = column.get_view(object)
                if view is None:
                    return

            column.get_object(object).edit_traits(
                view=view, parent=(x, y, dx, dy)
            )

    def _bounds_for(self, row, col):
        """ Returns the coordinates and size of the specified cell in the form:
            ( x, y, dx, dy ).
        """
        grid = self.editor.grid
        coords = wxg.GridCellCoords(row, col)
        x, y, dx, dy = grid._grid.BlockToDeviceRect(coords, coords)
        x, y = grid._grid_window.ClientToScreenXY(x, y)

        return (x, y, dx, dy)

    def _sort_model(self):
        """ Sorts the underlying model if that is what the user requested.
        """
        editor = self.editor
        sorted = editor.factory.sort_model and (self._sorter is not None)
        if sorted:
            items = self.__items(False)[:]
            items.sort(key=self._sorter)
            if self.reverse ^ self._reverse:
                items.reverse()
            editor.value = items
        return sorted

    def __items(self, ordered=True):
        """ Returns the raw list of model objects.
        """
        result = self.editor.value
        if not isinstance(result, SequenceTypes):
            return [result]

        if ordered and self.reverse:
            return ReversedList(result)

        return result

    def __filtered_items(self):
        """ Returns the list of all model objects that pass the current filter.
        """
        fc = self._filtered_cache
        if fc is None:
            items = self.__items()
            filter = self.filter
            if filter is None:
                nitems = [nitem for nitem in enumerate(items)]
                self.filter_summary = "All %s items" % len(nitems)
            else:
                if not callable(filter):
                    filter = filter.filter
                nitems = [
                    nitem for nitem in enumerate(items) if filter(nitem[1])
                ]
                self.filter_summary = "%s of %s items" % (
                    len(nitems),
                    len(items),
                )
            sorter = self._sorter
            if sorter is not None:
                nitems.sort(key=lambda x: sorter(x[1]))
                if self._reverse:
                    nitems.reverse()

            self.editor.filtered_indices = [x[0] for x in nitems]
            self._filtered_cache = fc = [x[1] for x in nitems]
            if self.auto_add_row is not None:
                self._filtered_cache.append(self.auto_add_row)

        return fc

    def __get_data_column(self, col):
        """ Returns a list of model data from the column indexed by *col*.
        """
        column = self.__get_column(col)
        return [column.get_value(item) for item in self.__filtered_items()]

    def __get_columns(self):
        columns = self._columns
        if columns is None:
            self._columns = columns = [
                c for c in self.editor.columns if c.visible
            ]
        return columns

    def __get_column(self, col):
        try:
            return self.__get_columns()[col]
        except:
            return self.__get_columns()[0]

    def __get_column_name(self, col):
        return self.__get_column(col).name

    def __get_column_formats(self, col):
        return None  # Not used/implemented currently

    def _get_column_index_by_trait(self, name):
        for i, col in enumerate(self.__get_columns()):
            if name == col.name:
                return i
Exemple #32
0
class MRISubjectSource(HasPrivateTraits):
    """Find subjects in SUBJECTS_DIR and select one.

    Parameters
    ----------
    subjects_dir : directory
        SUBJECTS_DIR.
    subject : str
        Subject, corresponding to a folder in SUBJECTS_DIR.
    """

    refresh = Event(desc="Refresh the subject list based on the directory "
                    "structure of subjects_dir.")

    # settings
    subjects_dir = Directory(exists=True)
    subjects = Property(List(Str), depends_on=['subjects_dir', 'refresh'])
    subject = Enum(values='subjects')
    use_high_res_head = Bool(True)

    # info
    can_create_fsaverage = Property(Bool,
                                    depends_on=['subjects_dir', 'subjects'])
    subject_has_bem = Property(Bool,
                               depends_on=['subjects_dir', 'subject'],
                               desc="whether the subject has a file matching "
                               "the bem file name pattern")
    bem_pattern = Property(depends_on='mri_dir')

    @cached_property
    def _get_can_create_fsaverage(self):
        if not op.exists(self.subjects_dir) or 'fsaverage' in self.subjects:
            return False
        return True

    @cached_property
    def _get_mri_dir(self):
        if not self.subject:
            return
        elif not self.subjects_dir:
            return
        else:
            return op.join(self.subjects_dir, self.subject)

    @cached_property
    def _get_subjects(self):
        sdir = self.subjects_dir
        is_dir = sdir and op.isdir(sdir)
        if is_dir:
            dir_content = os.listdir(sdir)
            subjects = [s for s in dir_content if _is_mri_subject(s, sdir)]
            if len(subjects) == 0:
                subjects.append('')
        else:
            subjects = ['']

        return sorted(subjects)

    @cached_property
    def _get_subject_has_bem(self):
        if not self.subject:
            return False
        return _mri_subject_has_bem(self.subject, self.subjects_dir)

    def create_fsaverage(self):  # noqa: D102
        if not self.subjects_dir:
            err = ("No subjects directory is selected. Please specify "
                   "subjects_dir first.")
            raise RuntimeError(err)

        fs_home = get_fs_home()
        if fs_home is None:
            err = ("FreeSurfer contains files that are needed for copying the "
                   "fsaverage brain. Please install FreeSurfer and try again.")
            raise RuntimeError(err)

        create_default_subject(fs_home=fs_home, subjects_dir=self.subjects_dir)
        self.refresh = True
        self.use_high_res_head = False
        self.subject = 'fsaverage'

    @on_trait_change('subjects_dir')
    def _emit_subject(self):
        # This silliness is the only way I could figure out to get the
        # on_trait_change('subject_panel.subject') in CoregFrame to work!
        self.subject = self.subject
class EditableValue(AbstractValueType):
    """ A base class for editable values.

    This class provides two things beyond the base AbstractValueType:
    a trait ``is_editable`` which allows toggling editing state on and
    off, and an ``is_valid`` method that is used for validation before
    setting a value.
    """

    #: Whether or not the value is editable, assuming the underlying data can
    #: be set.
    is_editable = Bool(True, update_value_type=True)

    def is_valid(self, model, row, column, value):
        """ Whether or not the value is valid for the data item specified.

        The default implementation returns True for all values.

        Parameters
        ----------
        model : AbstractDataModel
            The data model holding the data.
        row : sequence of int
            The row in the data model being queried.
        column : sequence of int
            The column in the data model being queried.
        value : any
            The value to validate.

        Returns
        -------
        is_valid : bool
            Whether or not the value is valid.
        """
        return True

    # AbstractValueType Interface --------------------------------------------

    def has_editor_value(self, model, row, column):
        """ Return whether or not the value can be edited.

        A cell is editable if the underlying data can be set, and the
        ``is_editable`` flag is set to True

        Parameters
        ----------
        model : AbstractDataModel
            The data model holding the data.
        row : sequence of int
            The row in the data model being queried.
        column : sequence of int
            The column in the data model being queried.

        Returns
        -------
        has_editor_value : bool
            Whether or not the value is editable.
        """
        return model.can_set_value(row, column) and self.is_editable

    def set_editor_value(self, model, row, column, value):
        """ Set the edited value.

        Parameters
        ----------
        model : AbstractDataModel
            The data model holding the data.
        row : sequence of int
            The row in the data model being set.
        column : sequence of int
            The column in the data model being set.
        value : any
            The value being set.

        Raises
        -------
        DataViewSetError
            If the value cannot be set.
        """
        if self.is_valid(model, row, column, value):
            model.set_value(row, column, value)
        else:
            raise DataViewSetError("Invalid value set: {!r}".format(value))