class ObjectWithEqualityComparisonMode(HasTraits):
    """ Class for supporting TestHasTraitsHelpersWarning """

    list_values = List(comparison_mode=2)
    dict_values = Dict(comparison_mode=2)
    set_values = Set(comparison_mode=2)
    property_list = Property(List(comparison_mode=2))
    container_in_union = Union(
        None,
        Set(comparison_mode=1),
        comparison_mode=2,
    )
class Foo(HasTraits):

    alist = List([1, 2, 3])
    adict = Dict({'red': 255, 'blue': 0, 'green': 127})
    aset = Set({1, 2, 3})

    @on_trait_change(["alist_items", "adict_items", "aset_items"])
    def _receive_events(self, event):
        self.event = event
class ObjectWithEqualityComparisonMode(HasTraits):
    """ Class for supporting TestHasTraitsHelpersComparisonMode """

    list_values = List(comparison_mode=ComparisonMode.equality)
    dict_values = Dict(comparison_mode=ComparisonMode.equality)
    set_values = Set(comparison_mode=ComparisonMode.equality)
    number = Any(comparison_mode=ComparisonMode.equality)
    calculated = Property(depends_on="number")

    def _get_calculated(self):
        return None
Esempio n. 4
0
class TraitModel(HasTraits):
    value = Float
    value_delegate = Str()
    value_subscribe = Str()
    value_update = Str()
    value_simple = Str('simple_text')
    value_notify = Event()
    list_values = List(Str)
    dict_values = Dict(Str, Str)
    set_values = Set(Str)
    property_value = Property(depends_on='value')
    typed_property_value = Property(Float, depends_on='value')
    collection_property_value = Property(List, depends_on='value')

    def _get_property_value(self):
        return self.value

    def _get_typed_property_value(self):
        return self.value

    def _get_collection_property_value(self):
        value = self.value
        return [value, value * 10]
Esempio n. 5
0
class ResizeTool(ValueDragTool):
    """ Generic tool for resizing a component
    """

    # Should the resized component be raised to the top of its container's
    # list of components?  This is only recommended for overlaying containers
    # and canvases, but generally those are the only ones in which the
    # ResizeTool will be useful.
    auto_raise = Bool(True)

    #: the hotspots which are active for this tool
    hotspots = Set(hotspot_trait)

    #: the distance in pixels from a hotspot required to register a hit
    threshhold = Int(10)

    #: the minimum bounds that we can resize to
    minimum_bounds = bounds_trait

    #: the hotspot that started the drag
    _selected_hotspot = hotspot_trait

    # 'ValueDragTool' Interface ##############################################

    def get_value(self):
        if self.component is not None:
            c = self.component
            return c.position[:], c.bounds[:]

    def set_delta(self, value, delta_x, delta_y):
        if self.component is not None:
            c = self.component
            position, bounds = value
            x, y = position
            width, height = bounds
            min_width, min_height = self.minimum_bounds
            edges = self._selected_hotspot.split()
            if "left" in edges:
                if delta_x >= width - min_width:
                    delta_x = width - min_width
                c.x = x + delta_x
                c.width = width - delta_x
            if "right" in edges:
                if delta_x <= -width + min_width:
                    delta_x = -width + min_width
                c.width = width + delta_x
            if "bottom" in edges:
                if delta_y >= height - min_height:
                    delta_y = height - min_height
                c.y = y + delta_y
                c.height = height - delta_y
            if "top" in edges:
                if delta_y <= -height + min_height:
                    delta_y = -height + min_height
                c.height = height + delta_y
            c._layout_needed = True
            c.request_redraw()

    # 'DragTool' Interface ###################################################

    def is_draggable(self, x, y):
        return self._find_hotspot(x, y) in self.hotspots

    def drag_start(self, event):
        if self.component is not None:
            self._selected_hotspot = self._find_hotspot(event.x, event.y)
            super().drag_start(event)
            self.component._layout_needed = True
            if self.auto_raise:
                # Push the component to the top of its container's list
                self.component.container.raise_component(self.component)
            event.window.set_mouse_owner(self, event.net_transform())
            event.handled = True
        return True

    # Private Interface ######################################################

    def _find_hotspot(self, x, y):
        hotspot = []
        if self.component is not None:
            c = self.component

            v_threshhold = min(self.threshhold, c.height / 2.0)
            if c.y <= y <= c.y + v_threshhold:
                hotspot.append("bottom")
            elif c.y2 + 1 - v_threshhold <= y <= c.y2 + 1:
                hotspot.append("top")
            elif y < c.y or y > c.y2 + 1:
                return ""

            h_threshhold = min(self.threshhold, c.width / 2.0)
            if c.x <= x <= c.x + h_threshhold:
                hotspot.append("left")
            elif c.x2 + 1 - h_threshhold <= x <= c.x2 + 1:
                hotspot.append("right")
            elif x < c.x or x > c.x2 + 1:
                return ""
        return " ".join(hotspot)

    # Traits Handlers ########################################################

    def _hotspots_default(self):
        return set([
            "top",
            "left",
            "right",
            "bottom",
            "top left",
            "top right",
            "bottom left",
            "bottom right",
        ])

    def _minimum_bounds_default(self):
        return [self.threshhold * 2, self.threshhold * 2]
Esempio n. 6
0
class Editor(HasPrivateTraits):
    """ Represents an editing control for an object trait in a Traits-based
        user interface.
    """

    #: The UI (user interface) this editor is part of:
    ui = Instance("traitsui.ui.UI", clean_up=True)

    #: 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, clean_up=True)

    #: 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(clean_up=True)

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

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

    #: The GUI widget defined by this editor:
    control = Any(clean_up=True)

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

    #: 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 = Instance(EditorFactory, clean_up=True)

    #: 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)

    # -- private trait definitions ------------------------------------------

    #: A set to track values being updated to prevent infinite recursion.
    _no_trait_update = Set(Str)

    #: A list of all values synchronized to.
    _user_to = List(Tuple(Any, Str, Callable))

    #: A list of all values synchronized from.
    _user_from = List(Tuple(Str, Callable))

    # ------------------------------------------------------------------------
    # Editor interface
    # ------------------------------------------------------------------------

    # -- Abstract methods ---------------------------------------------------

    def init(self, parent):
        """ Create and initialize the underlying toolkit widget.

        This method must be overriden by subclasses.  Implementations must
        ensure that the :attr:`control` trait is set to an appropriate
        toolkit object.

        Parameters
        ----------
        parent : toolkit control
            The parent toolkit object of the editor's toolkit objects.
        """
        raise NotImplementedError("This method must be overriden.")

    def update_editor(self):
        """ Updates the editor when the value changes externally to the editor.

        This should normally be overridden in a subclass.
        """
        pass

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

        This should normally be overridden in a subclass.

        Parameters
        ----------
        excp : Exception
            The exception which occurred.
        """
        pass

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

        This method must be overriden by subclasses.
        """
        raise NotImplementedError("This method must be overriden.")

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

        This simply delegates to the factory's `string_value` method.
        Sub-classes may choose to override the default implementation.

        Parameters
        ----------
        value : any
            The value being edited.
        format_func : callable or None
            A function that takes a value and returns a string.
        """
        return self.factory.string_value(value, format_func)

    def restore_prefs(self, prefs):
        """ Restores saved user preference information for the editor.

        Editors with state may choose to override this. It will only be used
        if the editor has an `id` value.

        Parameters
        ----------
        prefs : dict
            A dictionary of preference values.
        """
        pass

    def save_prefs(self):
        """ Returns any user preference information for the editor.

        Editors with state may choose to override this. It will only be used
        if the editor has an `id` value.

        Returns
        -------
        prefs : dict or None
            A dictionary of preference values, or None if no preferences to
            be saved.
        """
        return None

    # -- Editor life-cycle methods ------------------------------------------

    def prepare(self, parent):
        """ Finish setting up the editor.

        Parameters
        ----------
        parent : toolkit control
            The parent toolkit object of the editor's toolkit objects.
        """
        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()

    def dispose(self):
        """ Disposes of the contents of an editor.

        This disconnects any synchronised values and resets references
        to other objects.

        Subclasses may chose to override this method to perform additional
        clean-up.
        """
        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)

        for name, handler in self._user_from:
            self.on_trait_change(handler, name, remove=True)

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

        # Break linkages to references we no longer need:
        for name in self.trait_names(clean_up=True):
            setattr(self, name, None)

    # -- Undo/redo methods --------------------------------------------------

    def log_change(self, undo_factory, *undo_args):
        """ Logs a change made in the editor with undo/redo history.

        Parameters
        ----------
        undo_factory : callable
            Callable that creates an undo item.  Often self.get_undo_item.
        *undo_args
            Any arguments to pass to the undo factory.
        """
        ui = self.ui

        # 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)

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

        Can be overridden in a subclass for special value types.

        Parameters
        ----------
        object : HasTraits instance
            The object being modified.
        name : str
            The name of the trait that is to be changed.
        old_value : any
            The original value of the trait.
        new_value : any
            The new value of the trait.
        """
        return UndoItem(object=object,
                        name=name,
                        old_value=old_value,
                        new_value=new_value)

    # -- Trait synchronization code -----------------------------------------

    def sync_value(
        self,
        user_name,
        editor_name,
        mode="both",
        is_list=False,
        is_event=False,
    ):
        """ Synchronize an editor trait and a user object trait.

        Also sets the initial value of the editor trait from the
        user object trait (for modes 'from' and 'both'), and the initial
        value of the user object trait from the editor trait (for mode
        'to'), as long as the relevant traits are not events.

        Parameters
        ----------
        user_name : str
            The name of the trait to be used on the user object. If empty, no
            synchronization will be set up.
        editor_name : str
            The name of the relevant editor trait.
        mode : str, optional; one of 'to', 'from' or 'both'
            The direction of synchronization. 'from' means that trait changes
            in the user object should be propagated to the editor. 'to' means
            that trait changes in the editor should be propagated to the user
            object. 'both' means changes should be propagated in both
            directions. The default is 'both'.
        is_list : bool, optional
            If true, synchronization for item events will be set up in
            addition to the synchronization for the object itself.
            The default is False.
        is_event : bool, optional
            If true, this method won't attempt to initialize the user
            object or editor trait values. The default is False.
        """
        if user_name == "":
            return

        key = "%s:%s" % (user_name, editor_name)

        parts = user_name.split(".")
        if len(parts) == 1:
            user_object = self.context_object
            xuser_name = user_name
        else:
            user_object = self.ui.context[parts[0]]
            xuser_name = ".".join(parts[1:])
            user_name = parts[-1]

        if mode in {"from", "both"}:
            self._bind_from(key, user_object, xuser_name, editor_name, is_list)

            if not is_event:
                # initialize editor value from user value
                with self.raise_to_debug():
                    user_value = xgetattr(user_object, xuser_name)
                    setattr(self, editor_name, user_value)

        if mode in {"to", "both"}:
            self._bind_to(key, user_object, xuser_name, editor_name, is_list)

            if mode == "to" and not is_event:
                # initialize user value from editor value
                with self.raise_to_debug():
                    editor_value = xgetattr(self, editor_name)
                    xsetattr(user_object, xuser_name, editor_value)

    # -- Utility methods -----------------------------------------------------

    def parse_extended_name(self, name):
        """ Extract the object, name and a getter from an extended name

        Parameters
        ----------
        name : str
            The extended name to parse.

        Returns
        -------
        object, name, getter : any, str, callable
            The object from the context, the (extended) name of the
            attributes holding the value, and a callable which gets the
            current value from the context.
        """
        base_name, __, name = name.partition(".")
        if name:
            object = self.ui.context[base_name]
        else:
            name = base_name
            object = self.context_object

        return (object, name, partial(xgetattr, object, name))

    # -- Utility context managers --------------------------------------------

    @contextmanager
    def no_trait_update(self, name):
        """ Context manager that blocks updates from the named trait. """
        if name in self._no_trait_update:
            yield
            return

        self._no_trait_update.add(name)
        try:
            yield
        finally:
            self._no_trait_update.remove(name)

    @contextmanager
    def raise_to_debug(self):
        """ Context manager that uses raise to debug to raise exceptions. """
        try:
            yield
        except Exception:
            from traitsui.api import raise_to_debug

            raise_to_debug()

    @contextmanager
    def updating_value(self):
        """ Context manager to handle updating value. """
        if self.updating:
            yield
            return

        self.updating = True
        try:
            yield
        finally:
            self.updating = False

    # ------------------------------------------------------------------------
    # object interface
    # ------------------------------------------------------------------------

    def __init__(self, parent, **traits):
        """ Initializes the editor object.
        """
        super(HasPrivateTraits, self).__init__(**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")

    # ------------------------------------------------------------------------
    # private methods
    # ------------------------------------------------------------------------

    def _update_editor(self, object, name, old_value, new_value):
        """ Performs updates when the object trait changes.

        This is designed to be used as a trait listener.
        """
        # 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 = self.ui.get_extended_value(self.object_name)

        # 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 the Item is not readonly
        # or it is not for an event):
        if (self.item.style != "readonly"
                and object.base_trait(name).type != "event"):
            # Indicate that the contents of the UI have been changed:
            self.ui.modified = True

            if self.updating:
                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.updating:
            # Update the editor control to reflect the current object state:
            self.update_editor()

    def _sync_values(self):
        """ Initialize and synchronize editor and factory traits

        Initializes and synchronizes (as needed) editor traits with the
        value of corresponding factory traits.  The name of the factory
        trait and the editor trait must match and the factory trait needs
        to have ``sync_value`` metadata set.  The strategy followed is:

        - for each factory trait with ``sync_value`` metadata:

          1.  if the value is a :class:`ContextValue` instance then
              call :meth:`sync_value` with the ``name`` from the
              context value.
          2.  if the trait has ``sync_name`` metadata, look at the
              referenced trait value and if it is a non-empty string
              then use this value as the name of the value in the
              context.
          3.  otherwise initialize the current value of the factory
              trait to the corresponding value of the editor.

        - synchronization mode in cases 1 and 2 is taken from the
          ``sync_value`` metadata of the editor trait first and then
          the ``sync_value`` metadata of the factory trait if that is
          empty.

        - if the value is a container type, then the `is_list` metadata
          is set to
        """
        factory = self.factory
        for name, trait in factory.traits(sync_value=not_none).items():
            value = getattr(factory, name)
            self_trait = self.trait(name)
            if self_trait.sync_value:
                mode = self_trait.sync_value
            else:
                mode = trait.sync_value
            if isinstance(value, ContextValue):
                self.sync_value(
                    value.name,
                    name,
                    mode,
                    bool(self_trait.is_list),
                    self_trait.type == "event",
                )
            elif (trait.sync_name is not None
                  and getattr(factory, trait.sync_name, "") != ""):
                # Note: this is implemented as a stepping stone from things
                # like ``low_name`` and ``high_name`` to using context values.
                sync_name = getattr(factory, trait.sync_name)
                self.sync_value(
                    sync_name,
                    name,
                    mode,
                    bool(self_trait.is_list),
                    self_trait.type == "event",
                )
            elif value is not Undefined:
                setattr(self, name, value)

    def _bind_from(self, key, user_object, xuser_name, editor_name, is_list):
        """ Bind trait change handlers from a user object to the editor.

        Parameters
        ----------
        key : str
            The key to use to guard against recursive updates.
        user_object : object
            The object in the TraitsUI context that is being bound.
        xuser_name: : str
            The extended name of the trait to be used on the user object.
        editor_name : str
            The name of the relevant editor trait.
        is_list : bool, optional
            If true, synchronization for item events will be set up in
            addition to the synchronization for the object itself.
            The default is False.
        """
        def user_trait_modified(new):
            if key not in self._no_trait_update:
                with self.no_trait_update(key), self.raise_to_debug():
                    xsetattr(self, editor_name, new)

        user_object.on_trait_change(user_trait_modified, xuser_name)
        self._user_to.append((user_object, xuser_name, user_trait_modified))

        if is_list:

            def user_list_modified(event):
                if (isinstance(event, TraitListEvent)
                        and key not in self._no_trait_update):
                    with self.no_trait_update(key), self.raise_to_debug():
                        n = event.index
                        getattr(self,
                                editor_name)[n:n +
                                             len(event.removed)] = event.added

            items = xuser_name + "_items"
            user_object.on_trait_change(user_list_modified, items)
            self._user_to.append((user_object, items, user_list_modified))

    def _bind_to(self, key, user_object, xuser_name, editor_name, is_list):
        """ Bind trait change handlers from a user object to the editor.

        Parameters
        ----------
        key : str
            The key to use to guard against recursive updates.
        user_object : object
            The object in the TraitsUI context that is being bound.
        xuser_name: : str
            The extended name of the trait to be used on the user object.
        editor_name : str
            The name of the relevant editor trait.
        is_list : bool, optional
            If true, synchronization for item events will be set up in
            addition to the synchronization for the object itself.
            The default is False.
        """
        def editor_trait_modified(new):
            if key not in self._no_trait_update:
                with self.no_trait_update(key), self.raise_to_debug():
                    xsetattr(user_object, xuser_name, new)

        self.on_trait_change(editor_trait_modified, editor_name)

        self._user_from.append((editor_name, editor_trait_modified))

        if is_list:

            def editor_list_modified(event):
                if key not in self._no_trait_update:
                    with self.no_trait_update(key), self.raise_to_debug():
                        n = event.index
                        value = xgetattr(user_object, xuser_name)
                        value[n:n + len(event.removed)] = event.added

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

    def __set_value(self, value):
        """ Set the value of the trait the editor is editing.

        This calls the appropriate setattr method on the handler to perform
        the actual change.
        """
        with self.updating_value():
            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

    # -- Traits property getters and setters --------------------------------

    @cached_property
    def _get_context_object(self):
        """ Returns the context object the editor is using

        In some cases a proxy object is edited rather than an object directly
        in the context, in which case we return ``self.object``.
        """
        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

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

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

    def _get_value(self):
        """ Returns the value of the trait the editor is editing.
        """
        return getattr(self.object, self.name, Undefined)

    def _set_value(self, value):
        """ Set the value of the trait the editor is editing.

        Dispatches via the TraitsUI Undo/Redo mechanisms to make change
        reversible, if desired.
        """
        if self.ui and self.name != "None":
            self.ui.do_undoable(self.__set_value, value)

    def _get_str_value(self):
        """ Returns the text representation of the object trait.
        """
        return self.string_value(getattr(self.object, self.name, Undefined))
Esempio n. 7
0
class Package(HasTraits):

    name = Str
    version = Str
    dependencies = Set(This)
    preinstalled = Dict(Str, This)
    buildInputs = Dict(Str, List(Str))
    available = Dict(Str, This)  # :: name -> Package
    frozen = Bool(False)

    def __eq__(self, other):
        return \
            self.name == other.name and\
            self.version == other.version

    def __str__(self):
        return '{}=={}'.format(self.name, self.version)

    @property
    def frozen_name(self):
        return str(self)

    def apply_version_transformations(self, transformations=None):
        tx = transformations or VERSION_TRANSFORMS
        logger.debug('%s applying version transformations %s', self, tx)

        if self.frozen:
            logger.debug('Using stored %s', self)
            return

        ver = self.version
        for old, new in tx.items():
            ver = ver.replace(old, new)

        logger.debug('%s updating version to %s', self.name, ver)
        self.version = ver

        for dep in self.dependencies:
            dep.apply_version_transformations(transformations=tx)

    def find_requirements(self):
        logger.debug('Finding requirements for %s', self)

        if self.frozen:
            logger.debug('Using stored %s', self)
            return

        if is_cached(self):
            logger.debug('Using cached %s', self)
            return

        frozen = freeze([self.frozen_name],
                        preinstalled=self.preinstalled.keys(),
                        buildInputs=self.buildInputs.get(self.name))

        for entry in frozen:
            if entry.name == self.name:
                continue

            pkg = self.available[entry.name]
            self.dependencies.add(pkg)

        logger.debug('\t%s', map(str, sorted(list(self.dependencies))))

    def prune_dependencies(self):
        logger.debug('Prunning dependencies for %s', self)

        if self.frozen:
            logger.debug('Using stored %s', self)
            return

        dep_names = set([dep.name for dep in self.dependencies])  # :: {str}
        to_prune = set()  # :: {str}

        logger.debug('%s children: %s', self.name, ' '.join(dep_names))

        for child in self.dependencies:

            if not is_cached(child):

                child.find_requirements()
                logger.debug(
                    '%s grandchildren: %s', self.name,
                    ' '.join(map(lambda p: p.name, child.dependencies)))

                cache(child)

            else:

                logger.debug('Using cached %s', child)

            for grandchild in child.dependencies:
                if grandchild.name in dep_names:
                    to_prune.add(grandchild.name)

        logger.debug('Prunning for %s: %s', self, ' '.join(to_prune))
        pruned = filter(lambda dep: dep.name not in to_prune,
                        self.dependencies)
        self.dependencies = set(pruned)

    def freeze(self, store):
        self.frozen = True
        store.set(self.name, self.version, self)
Esempio n. 8
0
class StyleSheet(HasStrictTraits):
    """ An object representing a style sheet.

    The style sheet is constructed with a sequence of 'style' objects
    which are then parsed into an internal dictionary of selectors.
    The value for a style property which matches style criteria can be
    retrieved through the 'get_property' method.

    Attributes
    ----------
    updated : Event
        An event which is fired when the style sheet has changed. The
        payload of the event will be a set of property names that have
        been updated. This event is not fired when the sheet is first
        instantiated.

    Methods
    -------
    update(*styles)
        Update the style sheet with the given style objects. This
        will trigger an 'updated' event.

    replace(*styles)
        Replace the style sheet with the give style objects. This
        will trigger an 'updated' event.

    """
    # Maps the style selector string to the created Selector object.
    _selectors = Dict(Str, Instance(Selector))

    # Maps a property name to the set of Selectors
    # which contain a property with that name.
    _property_selectors = Dict(Str, Set(Instance(Selector)))

    # A counter that is used
    _counter = Instance(itertools.count, ())

    updated = Event

    def __init__(self, *styles):
        """ Construct a style sheet object.

        Parameters
        ----------
        styles : Sequence of 'style' objects.
            The style objects with which to populate the sheet.

        """
        super(StyleSheet, self).__init__()
        self._selectors = {}
        self._property_selectors = {}
        self._parse_styles(*styles)

    def _parse_styles(self, *styles):
        """ A private method which parses the styles in the appropriate
        selector objects.

        """
        selector_map = self._selectors
        counter = self._counter
        updated_selectors = set()

        for sty in styles:

            selector_strings = sty.selectors
            properties = sty.properties

            for selector_str in selector_strings:

                match = TYPE_SELECTOR.match(selector_str)
                if match:
                    match_str = match.group(1)
                    if match_str in selector_map:
                        selector = selector_map[match_str]
                    else:
                        selector = TypeSelector(node_type=match_str)
                        selector_map[match_str] = selector
                    selector.order = counter.next()
                    selector.properties.update(properties)
                    updated_selectors.add(selector)
                    continue

                match = CLASS_SELECTOR.match(selector_str)
                if match:
                    match_str = match.group(1)
                    if match_str in selector_map:
                        selector = selector_map[match_str]
                    else:
                        node_classes = set(filter(None, match_str.split('.')))
                        selector = ClassSelector(node_classes=node_classes)
                        selector_map[match_str] = selector
                    selector.order = counter.next()
                    selector.properties.update(properties)
                    updated_selectors.add(selector)
                    continue

                match = QUAL_SELECTOR.match(selector_str)
                if match:
                    match_str = selector_str
                    if match_str in selector_map:
                        selector = selector_map[match_str]
                    else:
                        node_type, rest = match_str.split('.', 1)
                        node_classes = set(rest.split('.'))
                        selector = QualSelector(node_type=node_type,
                                                node_classes=node_classes)
                        selector_map[match_str] = selector
                    selector.order = counter.next()
                    selector.properties.update(properties)
                    updated_selectors.add(selector)
                    continue

                match = ID_SELECTOR.match(selector_str)
                if match:
                    match_str = match.group(1)
                    if match_str in selector_map:
                        selector = selector_map[match_str]
                    else:
                        selector = IDSelector(node_id=match_str[1:])
                        selector_map[match_str] = selector
                    selector.order = counter.next()
                    selector.properties.update(properties)
                    updated_selectors.add(selector)
                    continue

                match = DEFAULT_SELECTOR.match(selector_str)
                if match:
                    match_str = match.group(1)
                    if match_str in selector_map:
                        selector = selector_map[match_str]
                    else:
                        selector = Selector()
                        selector_map[match_str] = selector
                    selector.order = counter.next()
                    selector.properties.update(properties)
                    updated_selectors.add(selector)
                    continue

        property_selectors = self._property_selectors
        updated_properties = set()
        for selector in updated_selectors:
            for key in selector.properties:
                property_selectors.setdefault(key, set()).add(selector)
                updated_properties.add(key)

        return updated_properties

    def update(self, *styles):
        """ Update the style sheet with the given style objects.

        This will trigger a 'sheet_updated' event.

        Parameters
        ----------
        styles : Sequence of 'style' objects.
            The style objects with which to update the sheet.

        """
        self.updated = self._parse_styles(*styles)

    def replace(self, *styles):
        """ Replace the style sheet with the give style objects.

        Parameters
        ----------
        styles : Sequence of 'style' objects.
            The style objects with which to populate the sheet.

        """
        self._selectors = {}
        self._property_selectors = {}
        self._counter = itertools.count()
        self.updated = self._parse_styles(*styles)

    def get_property(self, tag, node_data):
        """ Returns the style property for the given tag.

        This method is used to query the style sheet for the value of a
        property for a particular node as described by the given style
        node data object.

        Arguments
        ---------
        node_data : IStyleNodeData
            An object for the node being styled which implements the
            IStyleNodeData interface.

        Returns
        -------
        result : style property or NO_STYLE
            Returns the style property for the most specific match of
            the sheet, or NO_STYLE if no match is found.

        """
        selectors = self._property_selectors.get(tag)
        if not selectors:
            return NO_STYLE

        matches = (selector.match(node_data) for selector in selectors)
        specs = (spec for match, spec in matches if match)

        # If there are no matches, max will bail on the empty sequence.
        try:
            res = StyleValue(max(specs)[2][tag])
        except ValueError:
            res = NO_STYLE

        return res

    def get_tags(self):
        """ Returns an iterable of all the tags in the style_sheet.

        """
        return self._property_selectors.iterkeys()

    def has_tag(self, tag):
        """ Returns True or False depending on whether the tag is in the
        style sheet.

        """
        return tag in self._property_selectors
Esempio n. 9
0
class ValueDragTool(DragTool):
    """ Abstract tool for modifying a value as the mouse is dragged

    The tool allows the use of an x_mapper and y_mapper to map between
    screen coordinates and more abstract data coordinates.  These mappers must
    be objects with a map_data() method that maps a component-space coordinate
    to a data-space coordinate.  Chaco mappers satisfy the required API, and
    the tool will look for 'x_mapper' and 'y_mapper' attributes on the
    component to use as the defaults, facilitating interoperability with Chaco
    plots. Failing that, a simple identity mapper is provided which does
    nothing. Coordinates are given relative to the component.

    Subclasses of this tool need to supply get_value() and set_delta() methods.
    The get_value method returns the current underlying value, while the
    set_delta method takes the current mapped x and y deltas from the original
    position, and sets the underlying value appropriately.  The object stores
    the original value at the start of the operation as the original_value
    attribute.
    """

    #: set of modifier keys that must be down to invoke the tool
    modifier_keys = Set(Enum(*keys))

    #: mapper that maps from horizontal screen coordinate to data coordinate
    x_mapper = Any

    #: mapper that maps from vertical screen coordinate to data coordinate
    y_mapper = Any

    #: start point of the drag in component coordinates
    original_screen_point = Tuple(Float, Float)

    #: start point of the drag in data coordinates
    original_data_point = Tuple(Any, Any)

    #: initial underlying value
    original_value = Any

    #: new_value event for inspector overlay
    new_value = Event(Dict)

    #: visibility for inspector overlay
    visible = Bool(False)

    def get_value(self):
        """ Return the current value that is being modified
        """
        pass

    def set_delta(self, value, delta_x, delta_y):
        """ Set the value that is being modified

        This function should modify the underlying value based on the provided
        delta_x and delta_y in data coordinates.  These deltas are total
        displacement from the original location, not incremental.  The value
        parameter is the original value at the point where the drag started.
        """
        pass

    # Drag tool API

    def drag_start(self, event):
        self.original_screen_point = (event.x, event.y)
        data_x = self.x_mapper.map_data(event.x)
        data_y = self.y_mapper.map_data(event.y)
        self.original_data_point = (data_x, data_y)
        self.original_value = self.get_value()
        self.visible = True
        return True

    def dragging(self, event):
        position = event.current_pointer_position()
        delta_x = (self.x_mapper.map_data(position[0]) -
                   self.original_data_point[0])
        delta_y = (self.y_mapper.map_data(position[1]) -
                   self.original_data_point[1])
        self.set_delta(self.original_value, delta_x, delta_y)
        return True

    def drag_end(self, event):
        event.window.set_pointer("arrow")
        self.visible = False
        return True

    def _drag_button_down(self, event):
        # override button down to handle modifier keys correctly
        if not event.handled and self._drag_state == "nondrag":
            key_states = dict((key, key in self.modifier_keys) for key in keys)
            if (not all(
                    getattr(event, key + "_down") == state
                    for key, state in key_states.items())):
                return False
            self.mouse_down_position = (event.x, event.y)
            if not self.is_draggable(*self.mouse_down_position):
                self._mouse_down_recieved = False
                return False
            self._mouse_down_received = True
            return True
        return False

    # traits default handlers

    def _x_mapper_default(self):
        # if the component has an x_mapper, try to use it by default
        return getattr(self.component, "x_mapper", identity_mapper)

    def _y_mapper_default(self):
        # if the component has an x_mapper, try to use it by default
        return getattr(self.component, "y_mapper", identity_mapper)
Esempio n. 10
0
class CustomEditor(Editor):
    """ Custom Traits UI date editor that wraps QCalendarWidget.
    """

    #: Style used for when a date is unselected.
    #: Mapping from datetime.date to CellFormat
    _unselected_styles = Dict(Date, Instance(CellFormat))

    #: Selected dates (used when multi_select is true)
    _selected = Set(Date)

    def init(self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        self.control = QtGui.QCalendarWidget()

        if not self.factory.allow_future:
            self.control.setMaximumDate(QtCore.QDate.currentDate())

        self.control.clicked.connect(self.update_object)

    def update_editor(self):
        """ Updates the editor when the object trait changes externally to the
            editor.
        """
        value = self.value
        if value:
            if not self.factory.multi_select:
                q_date = QtCore.QDate(value.year, value.month, value.day)
                self.control.setSelectedDate(q_date)
            else:
                self.apply_unselected_style_to_all()
                for date in value:
                    self.apply_style(self.factory.selected_style, date)
                self._selected = set(value)

    def update_object(self, q_date):
        """ Handles the user entering input data in the edit control.
        """
        value = datetime.date(q_date.year(), q_date.month(), q_date.day())
        if self.factory.multi_select:
            if value in self.value:
                self.unselect_date(value)
            else:
                self.select_date(value)

            self.value = sorted(self._selected)

        else:
            self.value = value

    def unselect_date(self, date):
        self._selected.remove(date)
        self.apply_unselected_style(date)

    def select_date(self, date):
        self._selected.add(date)
        self.apply_style(self.factory.selected_style, date)

    def set_unselected_style(self, style, date):
        """ Set the style used for a date when it is not selected."""
        self._unselected_styles[date] = style
        if self.factory.multi_select:
            if date not in self.value:
                self.apply_style(style, date)
        else:
            self.apply_style(style, date)

    def apply_style(self, style, date):
        """ Apply a given style to a given date."""
        qdt = QtCore.QDate(date)
        textformat = self.control.dateTextFormat(qdt)
        _apply_cellformat(style, textformat)
        self.control.setDateTextFormat(qdt, textformat)

    def apply_unselected_style(self, date):
        """ Apply the style for when a date is unselected."""
        # Resets the text format on the given dates
        textformat = QtGui.QTextCharFormat()
        if date in self._unselected_styles:
            _apply_cellformat(self._unselected_styles[date], textformat)
        qdt = QtCore.QDate(date)
        self.control.setDateTextFormat(qdt, textformat)

    def apply_unselected_style_to_all(self):
        """ Make all the selected dates appear unselected.
        """
        for date in self._selected:
            self.apply_unselected_style(date)
Esempio n. 11
0
class IOController(HasStrictTraits):

    ### Current Sensor Values  ################################################

    acc_x = Float(plot_data=True, comparison_mode=NO_COMPARE)

    acc_y = Float(plot_data=True, comparison_mode=NO_COMPARE)

    acc_z = Float(plot_data=True, comparison_mode=NO_COMPARE)

    switch = Float(plot_data=True, comparison_mode=NO_COMPARE)

    distance = Float(plot_data=True, comparison_mode=NO_COMPARE)

    potentiometer = Float(plot_data=True, comparison_mode=NO_COMPARE)

    ### Plots  ################################################################

    logo_plot = Instance(Figure)

    acc_x_plot = Instance(Plot)

    acc_y_plot = Instance(Plot)

    acc_z_plot = Instance(Plot)

    switch_plot = Instance(Plot)

    distance_plot = Instance(Plot)

    pot_plot = Instance(Plot)

    link_plot = Instance(Component)

    plot_data = Instance(ArrayPlotData)

    line = Any()
    ax = Any()

    ### Outputs  ##############################################################

    led = Int(output=True)

    servo = Int(output=True)

    motor = Int(output=True)

    ### IOController Interface  ###############################################

    added_links = List()

    removed_links = List()

    outputs = Dict()

    ### Private Traits  #######################################################

    _current_links = Set()

    ### Trait Defaults  #######################################################

    def _logo_plot_default(self):
        fig = Figure()
        ax = Axes3D(fig)
        line, = ax.plot((1, 2), (1, 2), (1, 2))
        self.line = line
        self.ax = ax
        self.ax.set_xlim(0, 1, auto=False)
        self.ax.set_ylim(0, 1, auto=False)
        self.ax.set_zlim(0, 1, auto=False)
        return fig

    def _acc_x_plot_default(self):
        plot = Plot(self.plot_data)
        plot.plot(('acc_x', ))
        plot.padding = (0, 0, 0, 0)
        plot.value_mapper.range.low_setting = 0
        plot.value_mapper.range.high_setting = 1
        return plot

    def _acc_y_plot_default(self):
        plot = Plot(self.plot_data)
        plot.plot(('acc_y', ))
        plot.padding = (0, 0, 0, 0)
        plot.value_mapper.range.low_setting = 0
        plot.value_mapper.range.high_setting = 1
        return plot

    def _acc_z_plot_default(self):
        plot = Plot(self.plot_data)
        plot.plot(('acc_z', ))
        plot.padding = (0, 0, 0, 0)
        plot.value_mapper.range.low_setting = 0
        plot.value_mapper.range.high_setting = 1
        return plot

    def _switch_plot_default(self):
        plot = Plot(self.plot_data)
        plot.plot(('switch', ))
        plot.padding = (0, 0, 0, 0)
        plot.value_mapper.range.low_setting = 0
        plot.value_mapper.range.high_setting = 1
        return plot

    def _distance_plot_default(self):
        plot = Plot(self.plot_data)
        plot.plot(('distance', ))
        plot.padding = (0, 0, 0, 0)
        plot.value_mapper.range.low_setting = 0
        plot.value_mapper.range.high_setting = 1
        return plot

    def _pot_plot_default(self):
        plot = Plot(self.plot_data)
        plot.plot(('potentiometer', ))
        plot.padding = (0, 0, 0, 0)
        plot.value_mapper.range.low_setting = 0
        plot.value_mapper.range.high_setting = 1
        return plot

    def _link_plot_default(self):
        return LinksComponent()

    def _plot_data_default(self):
        plot_data = ArrayPlotData()
        plot_data.set_data('distance', np.zeros(50))
        plot_data.set_data('potentiometer', np.zeros(50))
        plot_data.set_data('switch', np.zeros(50))
        plot_data.set_data('acc_x', np.zeros(50))
        plot_data.set_data('acc_y', np.zeros(50))
        plot_data.set_data('acc_z', np.zeros(50))
        return plot_data

    def clicked(self, win):
        import ipdb
        ipdb.set_trace()  # XXX BREAKPOINT

    ### Trait Change Handlers  ################################################

    @on_trait_change('acc_x, acc_y, acc_z')
    def _update_3d_plot(self):
        if self.line and self.ax and self.ax.figure.canvas:
            x, y, z = self.acc_x, self.acc_y, self.acc_z
            #self.line.set_data(np.array([[0, 0, 0], [x, y, z]]).T)
            data = np.array([[.5, .5, .5], [x, y, z]]).T
            self.line.set_data(data[0:2, :])
            self.line.set_3d_properties(data[2, :])
            self.ax.figure.canvas.draw()
            #print x, y, z
            #self.ax.clear()
            #self.ax.plot((0, x), (0, y), (0, z))
            #self.ax.set_xlim(0, 1, auto=False)
            #self.ax.set_ylim(0, 1, auto=False)
            #self.ax.set_zlim(0, 1, auto=False)
            #self.ax.figure.canvas.draw()

    @on_trait_change('+plot_data')
    def _push_to_plot_data(self, name, new):
        # XXX This is causing NSConcreteMapTable to leak
        ary = self.plot_data[name]
        if ary is not None:
            ary = np.append(ary, new)
            ary = ary[-50:]
            self.plot_data.set_data(name, ary)

    @on_trait_change('+output')
    def _push_to_server(self, name, new):
        self.outputs[name] = new
        print self.outputs

    @on_trait_change('link_plot.links[]')
    def _links_changed(self, new):
        new = set(new)
        old = self._current_links
        added = new - old
        added_links = []
        for i, out in added:
            added_links.append((INPUT_MAP[i], OUTPUT_MAP[out]))
        removed = old - new
        removed_links = []
        for i, out in removed:
            removed_links.append((INPUT_MAP[i], OUTPUT_MAP[out]))
        self._current_links = new
        self.added_links.extend(added_links)
        self.removed_links.extend(removed_links)
        print added, removed
Esempio n. 12
0
class Test(HasTraits):
    var = Set(trait=Int())
 class Foo(HasTraits):
     values = Set(items=False)
 class TestSet(HasTraits):
     letters = Set(Str())
class Foo(HasTraits):
    values = Set()
Esempio n. 16
0
class OfType(AbstractQuery):
    """
    Gives all objects of given type that are found in System

    Usage & example::

        OfType(type, **kwargs)
        OfType(AbstractActuator, exclude=['actuator1', 'actuator2'])
        # returns all actuators in system, except those named 'actuator1' and 'actuator2'.

    :param list exclude: list of instances to be excluded from the returned list.

    """

    system_objects_changed = Event
    triggers = Property(
        trait=Set(trait=StatusObject),
        depends_on=
        'on_setup_callable, _kwargs_items, _args_items, system_objects_changed'
    )

    targets = Property(
        trait=Set(trait=StatusObject),
        depends_on=
        'on_setup_callable, _kwargs, _kwargs_items, _args, _args_items, '
        'system_objects_changed')

    setup_complete = CBool(transient=True)

    @on_trait_change('system')
    def set_setup_complete(self, name, new):
        if name == 'system':
            self.system.on_trait_change(self.set_setup_complete,
                                        'post_init_trigger')
        if name == 'post_init_trigger':
            self.setup_complete = True

    @on_trait_change('system.objects, system.objects_items, setup_complete')
    def trigger_system_objects_changed(self):
        if self.setup_complete:
            self.system_objects_changed = 1

    def _both(self):
        if not self.system:
            return set()
        exclude = set(self._kwargs.get('exclude',
                                       []))  # exclude must be list type
        return {
            i
            for i in self.system.objects if isinstance(i, tuple(self.objects))
        } - exclude

    @cached_property
    def _get_triggers(self):
        if self._kwargs.get('type', 'both') in ['triggers', 'both']:
            return self._both()
        return set()

    @cached_property
    def _get_targets(self):
        if self._kwargs.get('type', 'both') in ['targets', 'both']:
            return self._both()
        return set()

    def call(self, caller=None, **kwargs):
        return self.targets

    def give_str(self):
        args = [ReprObject(i.__name__) for i in self.objects]
        return self._give_str(args, self._kwargs)

    def give_str_indented(self, tags=False):
        args = [ReprObject(i.__name__) for i in self.objects]
        return self._give_str_indented(args, self._kwargs, tags)
Esempio n. 17
0
class AbstractCallable(SystemObject, CompareMixin):
    """

    A base class for subclassing Callables that are used in Program conditions and action attributes.

    Callables are configured by giving them arguments and keyword arguments.They must always define :meth:`.call`
    method which defines their functionality.
    """

    #: Arguments given for callable are stored here
    _args = CList

    #: Keyword arguments given for callable are stored here
    _kwargs = Dict

    #: Lock must be used when accessing :attr:`.state`
    _lock = Instance(Lock, transient=True)

    #: Property that gives set of all *triggers* of this callable and it's children callables.
    #: Triggers are all those StatusObjects that alter the status (return value of :meth:`.call`) of
    #: Callable.
    triggers = Property(
        trait=Set(trait=AbstractStatusObject),
        depends_on=
        '_args, _args_items, _args.triggers, _kwargs, _kwargs_items, _kwargs.triggers'
    )

    #: Property that gives set of all *targets* of this callable and it's children callables. Targets are
    #: all those StatusObjects of which status the callable might alter in :meth:`.call`.
    targets = Property(
        trait=Set(trait=AbstractStatusObject),
        depends_on=
        '_args, _args_items, _args.targets, _kwargs, _kwargs_items, _kwargs.targets'
    )

    # Status property not yet used anywhere. In the future, Program.active could be changed to Property
    # which uses this status.
    # Remark: isn't it used in web?

    #: Read-only status property of the callable. Usefull only when callable is used as a condition.
    #: This automatically depends on all the StatusObjects below the Callable tree.
    status = Property(depends_on='_args.status, _kwargs.status')

    #: State dictionary that is used by :meth:`.call` and :meth:`.cancel` if some state variables are needed to be saved
    #: Remember to clean data in subclasses when it is no longer needed.
    state = Dict(transient=True)

    def get_state(self, caller):
        """
            Get per-program state.
        """

        if caller in self.state:
            return self.state[caller]
        else:
            rv = self.state[caller] = DictObject()
            return rv

    def del_state(self, caller):
        """
            Delete per-program state.
        """
        if caller in self.state:
            del self.state[caller]

    @cached_property
    def _get_status(self):
        if not self.system:
            # Raising exception prevents invalid value from being cached when system is in pre-mature state
            raise SystemNotReady(
                'System not ready yet -- this is normal when loading dump.')
        return self.call(None)

    #: Event that can be used to execute code right after callable setup. See :class:`.OfType`.
    #: Something that needs to be done manually this way, because Traits does not allow
    #: defining the order of subscribed function calls.
    on_setup_callable = Event

    def setup_callables(self):
        super().setup_callables()
        self.setup_callable_system(self.system, init=True)

    @on_trait_change('_args, _args_items', post_init=True)
    def objects_changed(self, name, old, new):
        if not self.system:
            return
        for o in new.added:
            if isinstance(o, AbstractCallable):
                o.setup_callable_system(self.system)

    @on_trait_change('_kwargs, _kwargs_items', post_init=True)
    def kwargs_changed(self, name, old, new):
        if not self.system:
            return
        for o in list(new.added.values()):
            if isinstance(o, AbstractCallable):
                o.setup_callable_system(self.system)

    @cached_property
    def _get_triggers(self):
        if not self.system:
            # Raising exception prevents invalid value from being cached when system is in pre-mature state
            raise SystemNotReady(
                'System not ready yet -- this is normal when loading dump.')
        triggers = self.collect('triggers')
        self.logger.debug('Collected triggers for %s (%s): %s', self, id(self),
                          triggers)
        return triggers

    @cached_property
    def _get_targets(self):
        if not self.system:
            # Raising exception prevents invalid value from being cached when system is in pre-mature state
            raise SystemNotReady(
                'System not ready yet -- this is normal when loading dump.')
        targets = self.collect('targets')
        self.logger.debug('Collected targets for %s (%s): %s', self, id(self),
                          targets)
        return targets

    def __getitem__(self, item):
        return self._args[item]

    def __setitem__(self, item, value):
        self._args[item] = value

    def __init__(self, *args, **kwargs):
        self._lock = Lock("Lock for callable " + self.__class__.__name__)
        self._kwargs = kwargs
        super().__init__()
        super(SystemObject, self).__init__()
        if not self.traits_inited():
            self.logger.error('Traits not inited!!!')
        if args:
            self._args = args

    def __setstate__(self, state, trait_change_notify=True):
        self._lock = Lock("Lock for callable ")
        self._passed_arguments = None, state.copy()
        self.logger = logging.getLogger('automate.%s' %
                                        self.__class__.__name__)
        state.pop('name', '')
        super(SystemObject, self).__setstate__(state, trait_change_notify)

    def call_eval(self, value, caller, return_value=True, **kwargs):
        """
            Value might be either name registered in System namespace, or object, either
            StatusObject or Callable. If Callable, evaluate :meth:`.call` method. If StatusObject,
            return status.
        """
        value = self.name_to_system_object(value)
        if return_value and isinstance(value, AbstractStatusObject):
            return value.status
        if hasattr(value, 'call'):
            return self.call_eval(value.call(caller, **kwargs), caller,
                                  return_value, **kwargs)
        else:
            return value

    def _fix_list(self, lst):
        if isinstance(lst, dict):
            lst2 = list(lst.items())
        elif isinstance(lst, list):
            lst2 = enumerate(lst)
        else:
            raise RuntimeError('Error in _fix_list, type %s', type(lst))
        for idx, obj in lst2:
            if isinstance(obj, str):
                lst[idx] = self.name_to_system_object(obj)
            if isinstance(obj, list):
                self._fix_list(obj)

    def setup_callable_system(self, system, init=False):
        """
            This function basically sets up :attr:`.system`, if it is not yet set up. After that,
            other Callable initialization actions are performed.

            :param init: value ``True`` is given when running this at the initialization phase. Then system
                         attribute is set already, but callable needs to be initialized otherwise.

        """
        if not self.system or init:
            self.system = system
            self._fix_list(self._args)
            self._fix_list(self._kwargs)
            for i in self.children:
                if isinstance(i, AbstractCallable):
                    i.setup_callable_system(system, init=init)
                elif isinstance(i, SystemObject):
                    i.system = system
            self.on_setup_callable = 1
            self.logger.debug('setup_callable_system for %s (%s) ready.', self,
                              id(self))

    def call(self, *args, **kwargs):
        """
            The basic functionality of the Callable is implemented in this function.
            Needs to be defined in derived subclasses.

            If callable is used as a Program condition, this must return the value of the condition
            (see for example conditions :class:`.And`, :class:`.Sum` etc.), otherwise return value is optional.
        """
        raise NotImplementedError

    @property
    def objects(self):
        """
            Shortcut to :attr:`._args`.
        """
        return self._args

    @property
    def obj(self):
        """
            Shortcut property to the first stored object.
        """
        try:
            return self._args[0]
        except IndexError:
            return None

    @property
    def value(self):
        """
            Shortcut property to the second stored object.
        """
        try:
            return self._args[1]
        except IndexError:
            return None

    def name_to_system_object(self, value):
        """
        Return object for given name registered in System namespace.
        """
        if not self.system:
            raise SystemNotReady

        if isinstance(value, (str, Object)):
            rv = self.system.name_to_system_object(value)
            return rv if rv else value
        else:
            return value

    def collect(self, target):
        """Recursively collect all potential triggers/targets in this node and its children.
        Define targets and triggers of this particular callable in :meth:`_give_triggers`
        and :meth:`_give_targets`.

        :param str target: valid values: ``'targets'`` and ``'triggers'``
        """
        statusobjects = set()
        callables = set()
        objs_from_this_obj = getattr(self, '_give_%s' % target)()

        if not is_iterable(objs_from_this_obj):
            objs_from_this_obj = [objs_from_this_obj]

        if is_iterable(objs_from_this_obj):
            for i in (self.name_to_system_object(j)
                      for j in objs_from_this_obj):
                if isinstance(i, AbstractStatusObject):
                    statusobjects.add(i)
                elif isinstance(i, AbstractCallable):
                    callables.add(i)

        for i in (self.name_to_system_object(j)
                  for j in deep_iterate(callables)):
            if isinstance(i, AbstractCallable):
                statusobjects.update(getattr(i, target))

        return statusobjects

    def collect_triggers(self):
        return self.collect('triggers')

    def collect_targets(self):
        return self.collect('targets')

    @property
    def children(self):
        """
            A property giving a generator that goes through all the children of this Callable (not recursive)
        """
        return deep_iterate(self._args + list(self._kwargs.values()))

    def _give_triggers(self):
        """Give all triggers of this object (non-recursive)"""
        return self.children

    def _give_targets(self):
        """Give all targets of this object (non-recursive)"""
        return self.children

    def cancel(self, caller):
        """
            Recursively cancel all threaded background processes of this Callable.
            This is called automatically for actions if program deactivates.
        """
        for o in {i for i in self.children if isinstance(i, AbstractCallable)}:
            o.cancel(caller)

    def __repr__(self):
        return self.give_str()

    def __str__(self):
        return self.give_str()

    def _give_str(self, args, kwargs):
        if self in self.system.namespace.reverse:
            return repr(self.name)
        kwstr = u', '.join(k + u'=' + repr(v) for k, v in list(kwargs.items()))
        if kwstr and args:
            kwstr = ', ' + kwstr
        return str(self.__class__.__name__) + u"(" + u", ".join(
            [repr(i) for i in args]) + kwstr + u")"

    def give_str(self):
        """
            Give string representation of the callable.
        """
        args = self._args[:]
        kwargs = self._kwargs
        return self._give_str(args, kwargs)

    @staticmethod
    def strip_color_tags(_str):
        return re.sub('__\w*__', "", _str)

    def _give_str_indented(self, args, kwargs, tags):
        if self in self.system.namespace.reverse:
            rv = repr(self.name)

        def indent(o_str):
            n_strs = []
            for o in o_str.split('\n'):
                n_strs.append(u'  ' + o)
            return '\n'.join(n_strs)

        def indented_str(obj, no_repr=False, no_color=False):
            if hasattr(obj, 'give_str_indented'
                       ) and not obj in self.system.namespace.reverse:
                rv = obj.give_str_indented(tags)
            else:
                rv = str(obj if no_repr else repr(obj))
                if not no_color:
                    rv = ('__ACT__'
                          if getattr(obj, 'status', obj) else '__INACT__') + rv
            return indent(rv)

        def in_one_line(obj):
            rv = repr(obj)
            if not isinstance(obj, AbstractCallable):
                rv = ('__ACT__'
                      if getattr(obj, 'status', obj) else '__INACT__') + rv
            return rv

        kwstrs = [k + u'=' + in_one_line(v) for k, v in list(kwargs.items())]

        argstr = u"(\n" + indent(u", \n".join(
            [indented_str(i) for i in args] +
            [indented_str(i, no_repr=True, no_color=True)
             for i in kwstrs]) + u"\n)")
        if len(self.strip_color_tags(argstr)) < 35:
            argstr = u"(" + u", ".join([in_one_line(i)
                                        for i in args] + kwstrs) + u")"

        rv = str(self.__class__.__name__) + argstr
        if tags:
            rv = ('__ACT__' if self.status else '__INACT__') + rv
        return rv

    def give_str_indented(self, tags=False):
        """
            Give indented string representation of the callable.
            This is used in :ref:`automate-webui`.
        """
        args = self._args[:]
        kwargs = self._kwargs
        rv = self._give_str_indented(args, kwargs, tags)
        if not tags:
            rv = self.strip_color_tags(rv)
        return rv

    def __eq__(self, other):
        if isinstance(other, self.__class__):
            return (self._args, self._kwargs) == (other._args, other._kwargs)
        return False

    def __hash__(self):
        return id(self)
Esempio n. 18
0
class ClassWithSetOfInstance(HasTraits):

    instances = Set(Instance(SingleValue))

    instances_compat = Set(Instance(SingleValue))
Esempio n. 19
0
        class HasEnumInList(HasTraits):
            #: Valid digits
            digits = Set(Int)

            #: Sequence of those digits
            digit_sequence = List(Enum(values="digits"))