Ejemplo n.º 1
0
class CirclePainter(AbstractOverlay):
    """用半径为r的圆绘制鼠标轨迹,并调用InPaintDemo的fill_circle()
    在其mask图像上绘制选区。当鼠标松开时,调用InPaintDemo的inpaint()
    进行inpaint图像处理。
    """
    x = Float(-1)  # 当前圆心X轴坐标
    y = Float(-1)  # 当前圆心Y轴坐标
    r = Range(2.0, 20.0, 10.0)  # 圆形半径(数据坐标系)
    event_state = Enum("normal", "drawing")
    track = List()  # 储存鼠标轨迹
    updated = Event()

    def clear_track(self):
        self.track = []
        self.request_redraw()

    def fill_circle(self, event):
        self.x = event.x
        self.y = event.y
        plot = self.component
        x = plot.x_mapper.map_data(event.x)
        y = plot.y_mapper.map_data(event.y)
        self.track.append((self.x, self.y, x, y))
        self.request_redraw()

    def normal_mouse_move(self, event):
        self.x = event.x
        self.y = event.y
        self.request_redraw()

    def normal_left_down(self, event):
        self.event_state = "drawing"
        self.fill_circle(event)

    def drawing_mouse_move(self, event):
        self.fill_circle(event)

    def drawing_left_up(self, event):
        self.event_state = "normal"
        self.updated = True

    def normal_mouse_leave(self, event):
        self.x, self.y = -1, -1
        self.request_redraw()

    def overlay(self, component, gc, view_bounds=None, mode="normal"):
        plot = self.component
        r = plot.x_mapper.map_screen(self.r)

        gc.save_state()
        if self.x > 0 and self.y > 0:
            gc.set_stroke_color((1, 1, 1))
            gc.arc(self.x, self.y, r, 0.0, 2 * np.pi)
            gc.stroke_path()

        gc.set_fill_color((1, 1, 1))
        for x, y, _, _ in self.track:
            gc.arc(x, y, r, 0.0, 2 * np.pi)
        gc.fill_path()
        gc.restore_state()
Ejemplo n.º 2
0
class IUserManager(Interface):
    """The interface implemented by a user manager to manage users."""

    # The list of PyFace management actions (ie. actions related to all users)
    # implemented by this user manager.
    management_actions = List(Instance(Action))

    # The current user.
    user = Instance(IUser)

    # The list of PyFace user actions (ie. actions related to the current user)
    # implemented by this user manager.
    user_actions = List(Instance(Action))

    # This is fired whenever the currently authenticated user changes.  It will
    # be None if the current user isn't authenticated.
    user_authenticated = Event(IUser)

    def bootstrapping(self):
        """Return True if the user manager is bootstrapping.  Typically this is
        when no users have been defined."""

    def authenticate_user(self):
        """Authenticate (ie. login) the user.  If successfully authenticated
        all secured objects are re-enabled according to the user's permissions.
        """

    def unauthenticate_user(self):
        """Unauthenticate (ie. logout) the user.  All secured objects are
        disabled."""

    def select_user(self, name):
        """Return an object that implements IUser for the user selected based
Ejemplo n.º 3
0
class IPythonShell(IWidget):
    """ The interface for an interactive Python shell. """

    #### 'IPythonShell' interface #############################################

    # A command has been executed.
    command_executed = Event

    # A key has been pressed.
    key_pressed = Event(KeyPressedEvent)

    ###########################################################################
    # 'IPythonShell' interface.
    ###########################################################################

    def interpreter(self):
        """ Returns the code.InteractiveInterpreter instance. """

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

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

        If 'hidden' is True then nothing is shown in the shell - not even
        a blank line.
        """

    def execute_file(self, path, hidden=True):
        """ Execute a file in the interpeter.
Ejemplo n.º 4
0
class EventComp(Component):
    doit = Event()

    def __init__(self):
        super(EventComp, self).__init__()
        self.num_doits = 0

    def _doit_fired(self):
        self.num_doits += 1

    def execute(self):
        pass
Ejemplo n.º 5
0
class StressCriterionBond(HasTraits):
    '''Response of an elastic brittle filament pulled out
    from the matrix with a bond represented by a simple
    constitutive law.'''
    def __init__(self, material, **kw):
        super(StressCriterionBond, self).__init__(**kw)
        self.material = material
        self._redraw()
        self.on_trait_change(self._redraw, 'qf,qy,k')

    material = Instance(Material)

    k = DelegatesTo('material', modified=True)
    qf = DelegatesTo('material', modified=True)
    qy = DelegatesTo('material', modified=True)

    figure = Instance(Figure)

    def _figure_default(self):
        figure = Figure(facecolor='white')
        figure.add_axes([0.17, 0.2, 0.8, 0.68])
        return figure

    data_changed = Event(True)

    def _redraw(self):
        slip_qy = self.qy / self.k
        xdata = array([0, slip_qy, slip_qy, 3 * slip_qy])
        ydata = [0, self.qy, self.qf, self.qf]
        figure = self.figure

        axes = figure.axes[0]
        axes.clear()

        axes.plot(xdata, ydata, color='black', linewidth=2, linestyle='solid')
        axes.set_xlabel('slip [m]', weight='semibold')
        axes.set_ylabel('shear stress [N/m]', weight='semibold')
        axes.set_title( 'bond law', \
                        size = 'large', color = 'black', \
                        weight = 'bold', position = ( .5, 1.03 ) )
        axes.set_axis_bgcolor(color='white')
        axes.ticklabel_format(scilimits=(-3., 4.))
        self.data_changed = True

    traits_view = View(
        Item('figure',
             editor=BondLawEditor(),
             resizable=True,
             show_label=False))
Ejemplo n.º 6
0
class ITVTKActorModel(HasTraits):
    """ An interface for view models that can control a TVTK scene's contents.
    """

    # This maintains a dictionary mapping objects (by identity) to lists (or
    # single items) of TVTK Actors or 3D Widgets that represent them in the
    # scene. Adding and removing objects from this dictionary adds and removes
    # them from the scene. This is the trait that will be edited by a
    # ActorEditor.
    actor_map = Dict()

    # Turn off rendering such that multiple adds/removes can be refreshed at
    # once.
    disable_render = Bool(False)

    # Send this event in order to force a rendering of the scene.
    do_render = Event()
Ejemplo n.º 7
0
class IUndoManager(Interface):
    """ The undo manager interface.  An undo manager is responsible for one or
    more command stacks.  Typically an application would have a single undo
    manager.
    """

    #### 'IUndoManager' interface #############################################

    # This is the currently active command stack and may be None.  Typically it
    # is set when some sort of editor becomes active.
    active_stack = Instance('enthought.undo.api.ICommandStack')

    # This reflects the clean state of the currently active command stack.  It
    # is intended to support a "document modified" indicator in the GUI.  It is
    # maintained by the undo manager.
    active_stack_clean = Bool

    # This is the name of the command that can be redone.  It will be empty if
    # there is no command that can be redone.  It is maintained by the undo
    # manager.
    redo_name = Unicode

    # This is the sequence number of the next command to be performed.  It is
    # incremented immediately before a command is invoked (by its 'do()'
    # method).
    sequence_nr = Int

    # This event is fired when the index of a command stack changes.  Note that
    # it may not be the active stack.
    stack_updated = Event(Instance('enthought.undo.api.ICommandStack'))

    # This is the name of the command that can be undone.  It will be empty if
    # there is no command that can be undone.  It is maintained by the undo
    # manager.
    undo_name = Unicode

    ###########################################################################
    # 'IUndoManager' interface.
    ###########################################################################

    def redo(self):
        """ Redo the last undone command of the active command stack. """

    def undo(self):
        """ Undo the last command of the active command stack. """
Ejemplo n.º 8
0
class IPythonEditor(Interface):
    """ A widget for editing Python code. """

    #### 'IPythonEditor' interface ############################################
    
    # Has the file in the editor been modified?
    dirty = Bool(False)

    # The pathname of the file being edited.
    path = Unicode

    # Should line numbers be shown in the margin?
    show_line_numbers = Bool(True)

    #### Events ####

    # The contents of the editor has changed.
    changed = Event

    # A key has been pressed.
    key_pressed = Event(KeyPressedEvent)
    
    ###########################################################################
    # 'IPythonEditor' interface.
    ###########################################################################
    
    def load(self, path=None):
        """ Loads the contents of the editor. """

    def save(self, path=None):
        """ Saves the contents of the editor. """

    # FIXME v3: This is very dependent on the underlying implementation.
    def set_style(self, n, fore, back):
        """ Set the foreground and background colors for a particular style and
        set the font and size to default values.
        """

    def select_line(self, lineno):
        """ Selects the specified line. """
class WireLoop(HasTraits):
    nodes = Array(shape=(None, 3), dtype=np.double)
    radius = Float(0.1, desc="radius of the wire")
    source = Instance(tvtk.ProgrammableSource, ())
    tube = Instance(tvtk.TubeFilter, ())
    pipeline = Any
    actor = Instance(tvtk.Actor, ())
    update = Event()

    def _radius_changed(self):
        self.tube.radius = self.radius
        self.update = True

    def _nodes_changed(self):
        self.source.modified()
        self.update = True

    def _pipeline_default(self):
        src = self.source

        def execute():
            output = src.poly_data_output
            output.points = self.nodes
            a = range(self.nodes.shape[0]) + [0]
            lines = [a]
            output.lines = lines

        src.set_execute_method(execute)

        tube = self.tube
        tube.number_of_sides = 18
        tube.input_connection = src.output_port
        tube.radius = self.radius

        return tube

    def _actor_default(self):
        map = tvtk.PolyDataMapper(input_connection=self.pipeline.output_port)
        act = tvtk.Actor(mapper=map)
        return act
Ejemplo n.º 10
0
class TreeModel(HasTraits):
    """ Model for tree views. """

    #### 'TreeModel' interface ################################################

    # The root of the model.
    root = Any

    # Fired when nodes in the tree have changed in some way that affects their
    # appearance but NOT their structure or position in the tree.
    nodes_changed = Event(NodeEvent)

    # Fired when nodes have been inserted into the tree.
    nodes_inserted = Event(NodeEvent)

    # Fired when nodes have been removed from the tree.
    nodes_removed = Event(NodeEvent)

    # Fired when nodes have been replaced in the tree.
    nodes_replaced = Event(NodeEvent)

    # Fire when the structure of the tree has changed DRASTICALLY from a given
    # node down.
    structure_changed = Event(NodeEvent)

    #########################################################################
    # 'TreeModel' interface.
    #########################################################################

    def has_children(self, node):
        """ Returns True if a node has children, otherwise False.

        This method is provided in case the model has an efficient way to
        determine whether or not a node has any children without having to
        actually get the children themselves.

        """

        raise NotImplementedError

    def get_children(self, node):
        """ Returns the children of a node. """

        raise NotImplementedError

    def get_drag_value(self, node):
        """ Get the value that is dragged for a node.

        By default the drag value is the node itself.

        """

        return node

    def can_drop(self, node, obj):
        """ Returns True if a node allows an object to be dropped onto it. """

        return False

    def drop(self, node, obj):
        """ Drops an object onto a node. """

        raise NotImplementedError

    def get_image(self, node, selected, expanded):
        """ Returns the label image for a node.

        Return None (the default) if no image is required.

        """

        return None

    def get_key(self, node):
        """ Generate a unique key for a node. """

        try:
            key = hash(node)

        except:
            key = id(node)

        return key

    def get_selection_value(self, node):
        """ Get the value that is used when a node is selected.

        By default the selection value is the node itself.

        """

        return node

    def get_text(self, node):
        """ Returns the label text for a node.

        Return None if no text is required.  By default we return 'str(node)'.

        """

        return str(node)

    def can_set_text(self, node, text):
        """ Returns True if the node's label can be set. """

        return len(text.strip()) > 0

    def set_text(self, node, text):
        """ Sets the label text for a node. """

        pass

    def is_collapsible(self, node):
        """ Returns True if the node is collapsible, otherwise False. """

        return True

    def is_draggable(self, node):
        """ Returns True if the node is draggable, otherwise False. """

        return True

    def is_editable(self, node):
        """ Returns True if the node is editable, otherwise False.

        If the node is editable, its text can be set via the UI.

        """

        return False

    def is_expandable(self, node):
        """ Returns True if the node is expandanble, otherwise False. """

        return True

    def add_listener(self, node):
        """ Adds a listener for changes to a node. """

        pass

    def remove_listener(self, node):
        """ Removes a listener for changes to a node. """

        pass

    def fire_nodes_changed(self, node, children):
        """ Fires the nodes changed event. """

        self.nodes_changed = NodeEvent(node=node, children=children)

        return

    def fire_nodes_inserted(self, node, children):
        """ Fires the nodes inserted event. """

        self.nodes_inserted = NodeEvent(node=node, children=children)

        return

    def fire_nodes_removed(self, parent, children):
        """ Fires the nodes removed event. """

        self.nodes_removed = NodeEvent(node=node, children=children)

        return

    def fire_nodes_replaced(self, node, old_children, new_children):
        """ Fires the nodes removed event. """

        self.nodes_replaced = NodeEvent(node=node,
                                        old_children=old_children,
                                        children=new_children)

        return

    def fire_structure_changed(self, node):
        """ Fires the structure changed event. """

        self.structure_changed = NodeEvent(node=node)

        return
Ejemplo n.º 11
0
class ScriptManager(HasTraits):
    """ The ScriptManager class is the default implementation of
    IScriptManager.
    """

    implements(IScriptManager)

    #### 'IScriptManager' interface ###########################################

    # This event is fired whenever a scriptable object is bound or unbound.  It
    # is intended to be used by an interactive Python shell to give the
    # advanced user access to the scriptable objects.  If an object is created
    # via a factory then the event is fired when the factory is called, and not
    # when the factory is bound.
    bind_event = Event(IBindEvent)

    # This is set if user actions are being recorded as a script.  It is
    # maintained by the script manager.
    recording = Bool(False)

    # This is the text of the script currently being recorded (or the last
    # recorded script if none is currently being recorded).  It is updated
    # automatically as the user performs actions.
    script = Property(Unicode)

    # This event is fired when the recorded script changes.  The value of the
    # event will be the ScriptManager instance.
    script_updated = Event(IScriptManager)

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

    # The list of calls to scriptable calls.
    _calls = List(Instance(_ScriptCall))

    # The dictionary of bound names.  The value is the next numerical suffix
    # to use when the binding policy is 'auto'.
    _names = Dict

    # The dictionary of _BoundObject instances keyed by the name the object is
    # bound to.
    _namespace = Dict

    # The next sequential result number.
    _next_result_nr = Int

    # The results returned by previous scriptable calls.  The key is the id()
    # of the result object.  The value is a two element tuple of the sequential
    # result number (easier for the user to use than the id()) and the result
    # object itself.
    _results = Dict

    # The dictionary of _ScriptObject instances keyed by the object's id().
    _so_by_id = Dict

    # The dictionary of _ScriptObject instances keyed by the a weak reference
    # to the object.
    _so_by_ref = Dict

    # The date and time when the script was recorded.
    _when_started = Any

    ###########################################################################
    # 'IScriptManager' interface.
    ###########################################################################

    def bind(self, obj, name=None, bind_policy='unique', api=None,
            includes=None, excludes=None):
        """ Bind obj to name and make (by default) its public methods and
        traits (ie. those not beginning with an underscore) scriptable.  The
        default value of name is the type of obj with the first character
        forced to lower case.  name may be a dotted name (eg. 'abc.def.xyz').

        bind_policy determines what happens if the name is already bound.  If
        the policy is 'auto' then a numerical suffix will be added to the name,
        if necessary, to make it unique.  If the policy is 'unique' then an
        exception is raised.  If the policy is 'rebind' then the previous
        binding is discarded.  The default is 'unique'

        If api is given then it is a class, or a list of classes, that define
        the attributes that will be made scriptable.

        Otherwise if includes is given it is a list of names of attributes that
        will be made scriptable.

        Otherwise all the public attributes of scripted_type will be made
        scriptable except those in the excludes list.
        """

        # Register the object.
        self.new_object(obj, obj.__class__, name=name, bind_policy=bind_policy)

        # Make it scriptable.
        make_object_scriptable(obj, api=api, includes=includes,
                excludes=excludes)

    def bind_factory(self, factory, name, bind_policy='unique', api=None,
            includes=None, excludes=None):
        """ Bind factory to name.  This does the same as the bind() method
        except that it uses a factory that will be called later on to create
        the object only if the object is needed.

        See the documentation for bind() for a description of the remaining
        arguments.
        """

        name = self._unique_name(name, bind_policy)
        self._namespace[name] = _FactoryObject(name=name, factory=factory,
                api=api, includes=includes, excludes=excludes)

    def run(self, script):
        """ Run the given script, either a string or a file-like object.
        """

        # Initialise the namespace with all explicitly bound objects.
        nspace = LazyNamespace()
        for name, bo in self._namespace.iteritems():
            if bo.explicitly_bound:
                add_to_namespace(bo.obj, name, nspace)

        exec script in nspace

    def run_file(self, file_name):
        """ Run the given script file.
        """

        f = open(file_name)
        self.run(f)
        f.close()

    def start_recording(self):
        """ Start the recording of user actions.  The 'script' trait is cleared
        and all subsequent actions are added to 'script'.  The 'recording'
        trait is updated appropriately.
        """

        self._calls = []
        self._next_result_nr = 0
        self._results = {}

        self.recording = True
        self.script_updated = self

    def stop_recording(self):
        """ Stop the recording of user actions.  The 'recording' trait is
        updated appropriately.
        """

        self.recording = False

    ###########################################################################
    # 'ScriptManager' interface.
    ###########################################################################

    def record_method(self, func, args, kwargs):
        """ Record the call of a method of a ScriptableObject instance and
        return the result.  This is intended to be used only by the scriptable
        decorator.
        """
        if self.recording:
            # Record the arguments before the function has a chance to modify
            # them.
            srec = self._new_method(func, args, kwargs)
            result = func(*args, **kwargs)
            self._add_method(srec, result)

            self.script_updated = self
        else:
            result = func(*args, **kwargs)

        return result

    def record_trait_get(self, obj, name, result):
        """ Record the get of a trait of a scriptable object.  This is intended
        to be used only by the Scriptable trait getter.
        """

        if self.recording:
            side_effects = self._add_trait_get(obj, name, result)

            # Don't needlessly fire the event if there are no side effects.
            if side_effects:
                self.script_updated = self

    def record_trait_set(self, obj, name, value):
        """ Record the set of a trait of a scriptable object.  This is intended
        to be used only by the Scriptable trait getter.
        """

        if self.recording:
            self._add_trait_set(obj, name, value)

            self.script_updated = self

    def new_object(self, obj, scripted_type, args=None, kwargs=None, name=None,
            bind_policy='auto'):
        """ Register a scriptable object and the arguments used to create it.
        If no arguments were provided then assume the object is being
        explicitly bound.
        """

        # The name defaults to the type name.
        if not name:
            name = scripted_type.__name__
            name = name[0].lower() + name[1:]

        name = self._unique_name(name, bind_policy)

        obj_id = id(obj)
        obj_ref = weakref.ref(obj, self._gc_script_obj)

        so = _ScriptObject(name=name, obj_id=obj_id, obj_ref=obj_ref,
                scripted_type=scripted_type)

        # If we are told how to create the object then it must be implicitly
        # bound.
        if args is not None:
            # Convert each argument to its string representation if possible.
            # Doing this now avoids problems with mutable arguments.
            so.args = [self._scriptable_object_as_string(a) for a in args]

            for n, value in kwargs.iteritems():
                so.kwargs[n] = self._scriptable_object_as_string(value)

            so.explicitly_bound = False

        # Remember the scriptable object via the different access methods.
        self._so_by_id[obj_id] = so
        self._so_by_ref[obj_ref] = so
        self._namespace[name] = so

        # Note that if anything listening to this event doesn't use weak
        # references then the object will be kept alive.
        self.bind_event = BindEvent(name=name, obj=obj)

    @staticmethod
    def args_as_string_list(args, kwargs, so_needed=None):
        """ Return a complete argument list from sets of positional and keyword
        arguments.  Update the optional so_needed list for those arguments that
        refer to a scriptable object.
        """

        if so_needed is None:
            so_needed = []

        all_args = []

        for arg in args:
            s = ScriptManager.arg_as_string(arg, so_needed)
            all_args.append(s)

        for name, value in kwargs.iteritems():
            s = ScriptManager.arg_as_string(value, so_needed)
            all_args.append('%s=%s' % (name, s))

        return all_args

    @staticmethod
    def arg_as_string(arg, so_needed):
        """ Return the string representation of an argument.  Update the
        so_needed list if the argument refers to a scriptable object.  Any
        delayed conversion exception is handled here.
        """

        if isinstance(arg, Exception):
            raise arg

        if isinstance(arg, _ScriptObject):
            # Check it hasn't been unbound.
            if not arg.name:
                raise NameError("%s has been unbound but is needed by the script" % arg.obj_ref())

            # Add it to the needed list if it isn't already there.
            if arg not in so_needed:
                so_needed.append(arg)

            arg = arg.name

        return arg

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

    def _new_method(self, func, args, kwargs):
        """ Return an object that encapsulates a call to a scriptable method.
        _add_method() must be called to add it to the current script.
        """

        # Convert each argument to its string representation if possible.
        # Doing this now avoids problems with mutable arguments.
        nargs = [self._object_as_string(arg) for arg in args]

        if type(func) is types.FunctionType:
            so = None
        else:
            so = nargs[0]
            nargs = nargs[1:]

        nkwargs = {}
        for name, value in kwargs.iteritems():
            nkwargs[name] = self._object_as_string(value)

        return _ScriptMethod(name=func.func_name, so=so, args=nargs,
                kwargs=nkwargs)

    def _add_method(self, entry, result):
        """ Add a method call (returned by _new_method()), with it's associated
        result and ID, to the current script.
        """

        self._start_script()

        if result is not None:
            # Assume that a tuple represents multiple returned values - not
            # necessarily a valid assumption unless we make it a rule for
            # scriptable functions.
            if type(result) is type(()):
                for r in result:
                    self._save_result(r)
            else:
                self._save_result(result)

            entry.result = result

        self._calls.append(entry)

    def _add_trait_get(self, obj, name, result):
        """ Add a call to a trait getter, with it's associated result and ID,
        to the current script.  Return True if the get had side effects.
        """

        self._start_script()

        side_effects = obj.trait(name).has_side_effects

        if side_effects is None:
            side_effects = False

        so = self._object_as_string(obj)

        if result is not None:
            self._save_result(result)

        self._calls.append(_ScriptTraitGet(so=so, name=name, result=result,
                has_side_effects=side_effects))

        return side_effects

    def _add_trait_set(self, obj, name, value):
        """ Add a call to a trait setter, with it's associated value and ID,
        to the current script.
        """

        self._start_script()

        so = self._object_as_string(obj)
        value = self._object_as_string(value)

        self._calls.append(_ScriptTraitSet(so=so, name=name, value=value))

    def _unique_name(self, name, bind_policy):
        """ Return a name that is guaranteed to be unique according to the bind
        policy.
        """

        # See if the name is already is use.
        bo = self._namespace.get(name)

        if bo is None:
            self._names[name] = 1
        elif bind_policy == 'auto':
            suff = self._names[name]
            self._names[name] = suff + 1

            name = '%s%d' % (name, suff)
        elif bind_policy == 'rebind':
            self._unbind(bo)
        else:
            raise NameError("\"%s\" is already bound to a scriptable object" % name)

        return name

    def _unbind(self, bo):
        """Unbind the given bound object."""

        # Tell everybody it is no longer bound.  Don't bother if it is a
        # factory because the corresponding bound event wouldn't have been
        # fired.
        if not isinstance(bo, _FactoryObject):
            self.bind_event = BindEvent(name=bo.name, obj=None)

        # Forget about it.
        del self._namespace[bo.name]
        bo.name = ''

    @staticmethod
    def _gc_script_obj(obj_ref):
        """ The callback invoked when a scriptable object is garbage collected.
        """

        # Avoid recursive imports.
        from package_globals import get_script_manager

        sm = get_script_manager()
        so = sm._so_by_ref[obj_ref]

        if so.name:
            sm._unbind(so)

        del sm._so_by_id[so.obj_id]
        del sm._so_by_ref[so.obj_ref]

    def _start_script(self):
        """ Save when a script recording is started. """

        if len(self._calls) == 0:
            self._when_started = datetime.datetime.now().strftime('%c')

    def _object_as_string(self, obj):
        """ Convert an object to a string as it will appear in a script.  An
        exception may be returned (not raised) if there was an error in the
        conversion.
        """

        obj_id = id(obj)

        # See if the argument is the result of a previous call.
        nr, _ = self._results.get(obj_id, (None, None))

        if nr is not None:
            if nr < 0:
                nr = self._next_result_nr
                self._next_result_nr += 1

                # Key on the ID of the argument (which is hashable) rather than
                # the argument itself (which might not be).
                self._results[obj_id] = (nr, obj)

            return "r%d" % nr

        return self._scriptable_object_as_string(obj)

    def _scriptable_object_as_string(self, obj):
        """ Convert an object to a string as it will appear in a script.  An
        exception may be returned (not raised) if there was an error in the
        conversion.
        """

        obj_id = id(obj)

        # If it is a scriptable object we return the object and convert it to a
        # string later when we know it is really needed.
        so = self._so_by_id.get(obj_id)

        if so is not None:
            return so

        # Use the repr result if it doesn't appear to be the generic response,
        # ie. it doesn't contain its own address as a hex string.
        s = repr(obj)

        if hex(obj_id) not in s:
            return s

        # We don't know how to represent the argument as a string.  This is
        # most likely because an appropriate __init__ hasn't been made
        # scriptable.  We don't raise an exception until the user decides to
        # convert the calls to a script.
        return ValueError("unable to create a script representation of %s" % obj)

    def _save_result(self, result):
        """ Save the result of a call to a scriptable method so that it can be
        recognised later.
        """

        if id(result) not in self._results:
            self._results[id(result)] = (-1, result)

    def _get_script(self):
        """ Convert the current list of calls to a script. """

        # Handle the trivial case.
        if len(self._calls) == 0:
            return ""

        # Generate the header.
        header = "# Script generated %s" % self._when_started

        # Generate the calls.
        so_needed = []
        calls = []

        for call in self._calls:
            s = call.as_str(self, so_needed)

            if s:
                calls.append(s)

        calls = "\n".join(calls)

        # Generate the scriptable object constructors.
        types_needed = []
        ctors = []

        for so in so_needed:
            if so.explicitly_bound:
                continue

            so_type = so.scripted_type
            args = self.args_as_string_list(so.args, so.kwargs)

            ctors.append("%s = %s(%s)" % (so.name, so_type.__name__, ", ".join(args)))

            # See if a new import is needed.
            if so_type not in types_needed:
                types_needed.append(so_type)

        ctors = "\n".join(ctors)

        # Generate the import statements.
        imports = []

        for so_type in types_needed:
            imports.append("from %s import %s" % (so_type.__module__, so_type.__name__))

        imports = "\n".join(imports)

        return "\n\n".join([header, imports, ctors, calls]) + "\n"
Ejemplo n.º 12
0
class TraitsTest(HasTraits):

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

    integer_text = Int(1)
    enumeration = Enum('one', 'two', 'three', 'four', 'five', 'six', cols=3)
    float_range = Range(0.0, 10.0, 10.0)
    int_range = Range(1, 6)
    int_range2 = Range(1, 50)
    compound = Trait(1, Range(1, 6), 'one', 'two', 'three', 'four', 'five',
                     'six')
    boolean = Bool(True)
    instance = Trait(Instance())
    color = Color
    font = Font
    check_list = List(
        editor=CheckListEditor(values=['one', 'two', 'three', 'four'], cols=4))
    list = List(Str,
                ['East of Eden', 'The Grapes of Wrath', 'Of Mice and Men'])
    button = Event(0, editor=ButtonEditor(label='Click'))
    file = File
    directory = Directory
    image_enum = Trait(editor=ImageEnumEditor(values=origin_values,
                                              suffix='_origin',
                                              cols=4,
                                              klass=Instance),
                       *origin_values)

    #---------------------------------------------------------------------------
    #  View definitions:
    #---------------------------------------------------------------------------

    view = View(
        ('|{Enum}', ('|<[Enumeration]', 'enumeration[Simple]', '_',
                     'enumeration[Custom]@', '_', 'enumeration[Text]*', '_',
                     'enumeration[Readonly]~'),
         ('|<[Check List]', 'check_list[Simple]', '_', 'check_list[Custom]@',
          '_', 'check_list[Text]*', '_', 'check_list[Readonly]~')),
        ('|{Range}', ('|<[Float Range]', 'float_range[Simple]', '_',
                      'float_range[Custom]@', '_', 'float_range[Text]*', '_',
                      'float_range[Readonly]~'),
         ('|<[Int Range]', 'int_range[Simple]', '_', 'int_range[Custom]@', '_',
          'int_range[Text]*', '_', 'int_range[Readonly]~'),
         ('|<[Int Range 2]', 'int_range2[Simple]', '_', 'int_range2[Custom]@',
          '_', 'int_range2[Text]*', '_', 'int_range2[Readonly]~')),
        ('|{Misc}', ('|<[Integer Text]', 'integer_text[Simple]', '_',
                     'integer_text[Custom]@', '_', 'integer_text[Text]*', '_',
                     'integer_text[Readonly]~'),
         ('|<[Compound]', 'compound[Simple]', '_', 'compound[Custom]@', '_',
          'compound[Text]*', '_', 'compound[Readonly]~'),
         ('|<[Boolean]', 'boolean[Simple]', '_', 'boolean[Custom]@', '_',
          'boolean[Text]*', '_', 'boolean[Readonly]~')),
        ('|{Color/Font}', ('|<[Color]', 'color[Simple]', '_', 'color[Custom]@',
                           '_', 'color[Text]*', '_', 'color[Readonly]~'),
         ('|<[Font]', 'font[Simple]', '_', 'font[Custom]@', '_', 'font[Text]*',
          '_', 'font[Readonly]~')),
        ('|{List}', ('|<[List]', 'list[Simple]', '_', 'list[Custom]@', '_',
                     'list[Text]*', '_', 'list[Readonly]~')),
        (
            '|{Button}',
            ('|<[Button]', 'button[Simple]', '_', 'button[Custom]@'),
            #                                        'button[Text]*',
            #                                        'button[Readonly]~' ),
            ('|<[Image Enum]', 'image_enum[Simple]', '_',
             'image_enum[Custom]@', '_', 'image_enum[Text]*', '_',
             'image_enum[Readonly]~'),
            ('|<[Instance]', 'instance[Simple]', '_', 'instance[Custom]@', '_',
             'instance[Text]*', '_', 'instance[Readonly]~'),
        ),
        ('|{File}', (
            '|<[File]',
            'file[Simple]',
            '_',
            'file[Custom]@',
            '_',
            'file[Text]*',
            '_',
            'file[Readonly]~',
        ), ('|<[Directory]', 'directory[Simple]', '_', 'directory[Custom]@',
            '_', 'directory[Text]*', '_', 'directory[Readonly]~')),
        buttons=['Apply', 'Revert', 'Undo', 'OK'])
Ejemplo n.º 13
0
class Window(MWindow, Widget):
    """ The toolkit specific implementation of a Window.  See the IWindow
    interface for the API documentation.
    """

    implements(IWindow)

    #### 'IWindow' interface ##################################################

    position = Property(Tuple)

    size = Property(Tuple)

    title = Unicode

    #### Events #####

    activated = Event

    closed =  Event

    closing =  Event

    deactivated = Event

    key_pressed = Event(KeyPressedEvent)

    opened = Event

    opening = Event

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

    # Shadow trait for position.
    _position = Tuple((-1, -1))

    # Shadow trait for size.
    _size = Tuple((-1, -1))

    ###########################################################################
    # 'IWindow' interface.
    ###########################################################################

    def show(self, visible):
        pass

    ###########################################################################
    # Protected 'IWindow' interface.
    ###########################################################################

    def _add_event_listeners(self):
        pass

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

    def _get_position(self):
        """ Property getter for position. """

        return self._position

    def _set_position(self, position):
        """ Property setter for position. """

        old = self._position
        self._position = position

        self.trait_property_changed('position', old, position)

    def _get_size(self):
        """ Property getter for size. """

        return self._size

    def _set_size(self, size):
        """ Property setter for size. """

        old = self._size
        self._size = size

        self.trait_property_changed('size', old, size)
Ejemplo n.º 14
0
class MultiSelections(HasTraits):
    """ Object used to store analysis objects in List of points
    """
    selections = List(RectangleSelection)
    selection = Instance(RectangleSelection, transient=True)

    #: default filename for points load, save
    filename = File()

    add_point = Button()
    load = Button()
    save = Button()

    index_high = Property(Int, depends_on='selections', transient=True)
    index = Property(Int, depends_on='selection,index_high', transient=True)

    updated = Event()

    _update = Bool(True)

    view = View(Item('index',
                     editor=RangeEditor(low=0,
                                        high_name='index_high',
                                        is_float=False,
                                        mode='spinner')),
                HGroup(
                    Item('add_point', show_label=False, springy=True),
                    Item('load', show_label=False, springy=True),
                    Item('save', show_label=False, springy=True),
                ),
                '_',
                VGroup(
                    Item('selections@',
                         id='notebook',
                         show_label=False,
                         editor=ListEditor(use_notebook=True,
                                           deletable=True,
                                           selected='selection',
                                           export='DockWindowShell',
                                           page_name='.name')), ),
                id='enthought.traits.ui.demo.Traits UI Demo.Advanced.'
                'List_editor_notebook_selection_demo',
                dock='horizontal',
                resizable=True,
                height=-0.5)

    def _load_fired(self):
        f = FileDialog(action='open',
                       title='Load points',
                       default_path=self.filename,
                       wildcard='*.points')
        if f.open() == OK:
            self.filename = f.path

            self.from_file()

    def _save_fired(self):
        f = FileDialog(action='save as',
                       title='Save points',
                       default_path=self.filename,
                       wildcard='*.points')
        if f.open() == OK:
            self.filename = f.path
            self.to_file()

    def _selection_default(self):
        return self.selections[0]

    def _get_index_high(self):
        return len(self.selections) - 1

    def _add_point_fired(self):
        self._update = False
        point = (self.selections[self.index]).clone_traits()
        point.name = 'point ' + str(len(self.selections))
        self.selections.append(point)
        self.selection = self.selections[-1]
        self._update = True
        self.update()

    def _selections_default(self):
        return POINTS

    @on_trait_change('selections[]')
    def _update_names(self):
        #update point names
        for i, point in enumerate(self.selections):
            point.name = 'point ' + str(i)

    def _get_index(self):
        try:
            return self.selections.index(self.selection)
        except:
            return self.index_high
        self.update()

    def _set_index(self, value):
        self.selection = self.selections[value]

    def from_file(self):
        """Reads selections from file
        """
        with open(self.filename, 'rb') as f:
            self._update = False
            self.selections = pickle.load(f)
            self.index = 0
            self._update = True
            self.update()

    def to_file(self):
        """Stores selections to file
        """
        with open(self.filename, 'wb') as f:
            pickle.dump(self.selections, f)

    @on_trait_change('selection.updated')
    def update(self):
        """
        Notify update
        """
        if self._update:
            self.updated = True
Ejemplo n.º 15
0
class SourceEditor(Editor):
    """ Editor for source code which uses the QScintilla widget.
    """

    #---------------------------------------------------------------------------
    #  PyFace PythonEditor interface:
    #---------------------------------------------------------------------------

    # Event that is fired on keypresses:
    key_pressed = Event(KeyPressedEvent)

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

    # The code editor is scrollable. This value overrides the default.
    scrollable = True

    #---------------------------------------------------------------------------
    #  SoureEditor interface:
    #---------------------------------------------------------------------------

    # Is the editor read only?
    readonly = Bool(False)

    # The currently selected line
    selected_line = Int

    # The currently selected text
    selected_text = Unicode

    # The list of line numbers to mark
    mark_lines = List(Int)

    # The current line number
    line = Event

    # The current column
    column = Event

    # The Scintilla lexer to use
    lexer = Int

    # The lines to be dimmed
    dim_lines = List(Int)
    dim_color = Str
    dim_style_number = Int(16)  # 0-15 are reserved for the python lexer

    # The lines to have squiggles drawn under them
    squiggle_lines = List(Int)
    squiggle_color = Str

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

    def init(self, parent):
        """ Finishes initializing the editor by creating the underlying toolkit
            widget.
        """
        self.control = QtGui.QWidget()
        layout = QtGui.QVBoxLayout(self.control)
        layout.setMargin(0)

        # Create the QScintilla widget
        factory = self.factory
        self._scintilla = control = _Scintilla(self, None)
        layout.addWidget(control)

        # Connect to the QScintilla signals that we care about
        if not self.readonly:
            QtCore.QObject.connect(control, QtCore.SIGNAL('lostFocus'),
                                   self.update_object)
            if factory.auto_set:
                control.connect(control, QtCore.SIGNAL('textChanged()'),
                                self.update_object)
        if (factory.line != '') or (factory.column != ''):
            # We need to monitor the line or column position being changed
            control.connect(control,
                            QtCore.SIGNAL('cursorPositionChanged(int, int)'),
                            self._position_changed)

        # Create the find bar
        self._find_widget = FindWidget(self.find, self.control)
        self._find_widget.hide()
        layout.addWidget(self._find_widget)

        # Make sure that the find bar will fit in the editor
        min_width = self._find_widget.minimumSizeHint().width()
        self.control.setMinimumWidth(min_width)

        # Grab keyboard focus whenever the find bar is closed
        QtCore.QObject.connect(self._find_widget, QtCore.SIGNAL('hidden()'),
                               self._scintilla, QtCore.SLOT('setFocus()'))

        # Set up the lexer. Before we set our custom lexer, we call setLexer
        # with the QSciLexer that will set the keywords and styles for the
        # basic syntax lexing. We save and then restore these keywords/styles
        # because they get nuked when we call setLexer again.
        self.lexer = getattr(QsciBase, 'SCLEX_' + self.factory.lexer.upper(),
                             QsciBase.SCLEX_NULL)
        base_lexer_class = LEXER_MAP.get(self.lexer)
        if base_lexer_class:
            lexer = base_lexer_class(control)
            control.setLexer(lexer)
            keywords = lexer.keywords(1)
            styles = []
            attr_names = [
                'FORE', 'BACK', 'BOLD', 'ITALIC', 'SIZE', 'UNDERLINE'
            ]
            for style in xrange(128):
                attrs = [
                    control.SendScintilla(
                        getattr(QsciBase, 'SCI_STYLEGET' + a), style)
                    for a in attr_names
                ]
                styles.append(attrs)
        lexer = SourceLexer(self)
        control.setLexer(lexer)
        if base_lexer_class:
            if keywords:
                control.SendScintilla(QsciBase.SCI_SETKEYWORDS, 0, keywords)
            for style, attrs in enumerate(styles):
                for attr_num, attr in enumerate(attrs):
                    msg = getattr(QsciBase,
                                  'SCI_STYLESET' + attr_names[attr_num])
                    control.SendScintilla(msg, style, attr)

        # Set a monspaced font. Use the (supposedly) same font and size as the
        # wx version.
        for style in xrange(128):
            f = lexer.font(style)
            f.setFamily('courier new')
            f.setPointSize(10)
            lexer.setFont(f, style)

        # Mark the maximum line size.
        control.setEdgeMode(Qsci.QsciScintilla.EdgeLine)
        control.setEdgeColumn(79)

        # Display line numbers in the margin.
        if factory.show_line_numbers:
            control.setMarginLineNumbers(1, True)
            control.setMarginWidth(1, 45)
        else:
            control.setMarginWidth(1, 4)
            control.setMarginsBackgroundColor(QtCore.Qt.white)

        # Configure indentation and tabs.
        control.setIndentationsUseTabs(False)
        control.setTabWidth(4)

        # Configure miscellaneous control settings:
        control.setEolMode(Qsci.QsciScintilla.EolUnix)

        if self.readonly:
            control.setReadOnly(True)

        # Define the markers we use:
        control.markerDefine(Qsci.QsciScintilla.Background, MARK_MARKER)
        control.setMarkerBackgroundColor(factory.mark_color_, MARK_MARKER)

        control.markerDefine(Qsci.QsciScintilla.Background, SEARCH_MARKER)
        control.setMarkerBackgroundColor(factory.search_color_, SEARCH_MARKER)

        control.markerDefine(Qsci.QsciScintilla.Background, SELECTED_MARKER)
        control.setMarkerBackgroundColor(factory.selected_color_,
                                         SELECTED_MARKER)

        # Make sure the editor has been initialized:
        self.update_editor()

        # Set up any event listeners:
        self.sync_value(factory.mark_lines, 'mark_lines', 'from', is_list=True)
        self.sync_value(factory.selected_line, 'selected_line', 'from')
        self.sync_value(factory.selected_text, 'selected_text', 'to')
        self.sync_value(factory.line, 'line')
        self.sync_value(factory.column, 'column')

        self.sync_value(factory.dim_lines, 'dim_lines', 'from', is_list=True)
        if self.factory.dim_color == '':
            self.dim_color = 'grey'
        else:
            self.sync_value(factory.dim_color, 'dim_color', 'from')

        self.sync_value(factory.squiggle_lines,
                        'squiggle_lines',
                        'from',
                        is_list=True)
        if factory.squiggle_color == '':
            self.squiggle_color = 'red'
        else:
            self.sync_value(factory.squiggle_color, 'squiggle_color', 'from')

        # Set the control tooltip:
        self.set_tooltip()

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

    def dispose(self):
        """ Disposes of the contents of an editor.
        """
        # Make sure that the editor does not try to update as the control is
        # being destroyed:
        QtCore.QObject.disconnect(self._scintilla, QtCore.SIGNAL('lostFocus'),
                                  self.update_object)

        super(SourceEditor, self).dispose()

    #---------------------------------------------------------------------------
    #  Handles the user entering input data in the edit control:
    #---------------------------------------------------------------------------

    def update_object(self):
        """ Handles the user entering input data in the edit control.
        """
        if not self._locked:
            try:
                value = unicode(self._scintilla.text())
                if isinstance(self.value, SequenceTypes):
                    value = value.split()
                self.value = value
                self._scintilla.lexer().setPaper(OKColor)
            except TraitError, excp:
                pass
Ejemplo n.º 16
0
class Context(HasTraits):
    """ The base class for all naming contexts. """

    # Keys for environment properties.
    INITIAL_CONTEXT_FACTORY = INITIAL_CONTEXT_FACTORY
    OBJECT_FACTORIES = OBJECT_FACTORIES
    STATE_FACTORIES = STATE_FACTORIES

    # Non-JNDI.
    TYPE_MANAGER = TYPE_MANAGER

    #### 'Context' interface ##################################################

    # The naming environment in effect for this context.
    environment = Dict(ENVIRONMENT)

    # The name of the context within its own namespace.
    namespace_name = Property(Str)

    # The type manager in the context's environment (used to create context
    # adapters etc.).
    #
    # fixme: This is an experimental 'convenience' trait, since it is common
    # to get hold of the context's type manager to see if some object has a
    # context adapter.
    type_manager = Property(Instance(TypeManager))

    #### Events ####

    # Fired when an object has been added to the context (either via 'bind' or
    # 'create_subcontext').
    object_added = Event(NamingEvent)

    # Fired when an object has been changed (via 'rebind').
    object_changed = Event(NamingEvent)

    # Fired when an object has been removed from the context (either via
    # 'unbind' or 'destroy_subcontext').
    object_removed = Event(NamingEvent)

    # Fired when an object in the context has been renamed (via 'rename').
    object_renamed = Event(NamingEvent)

    # Fired when the contents of the context have changed dramatically.
    context_changed = Event(NamingEvent)

    #### Protected 'Context' interface #######################################

    # The bindings in the context.
    _bindings = Dict(Str, Any)

    ###########################################################################
    # 'Context' interface.
    ###########################################################################

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

    def _get_namespace_name(self):
        """
        Return the name of the context within its own namespace.

        That is the full-path, through the namespace this context participates
        in, to get to this context.  For example, if the root context of the
        namespace was called 'Foo', and there was a subcontext of that called
        'Bar', and we were within that and called 'Baz', then this should
        return 'Foo/Bar/Baz'.

        """

        # FIXME: We'd like to raise an exception and force implementors to
        # decide what to do.  However, it appears to be pretty common that
        # most Context implementations do not override this method -- possibly
        # because the comments aren't clear on what this is supposed to be?
        #
        # Anyway, if we raise an exception then it is impossible to use any
        # evaluations when building a Traits UI for a Context.  That is, the
        # Traits UI can't include items that have a 'visible_when' or
        # 'enabled_when' evaluation.  This is because the Traits evaluation
        # code calls the 'get()' method on the Context which attempts to
        # retrieve the current namespace_name value.
        #raise OperationNotSupportedError()
        return ''

    def _get_type_manager(self):
        """ Returns the type manager in the context's environment.

        This will return None if no type manager was used to create the initial
        context.

        """

        return self.environment.get(self.TYPE_MANAGER)

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

    def bind(self, name, obj, make_contexts=False):
        """ Binds a name to an object.

        If 'make_contexts' is True then any missing intermediate contexts are
        created automatically.

        """

        if len(name) == 0:
            raise InvalidNameError('empty name')

        # Parse the name.
        components = self._parse_name(name)

        # If there is exactly one component in the name then the operation
        # takes place in this context.
        if len(components) == 1:
            atom = components[0]

            # Is the name already bound?
            if self._is_bound(atom):
                raise NameAlreadyBoundError(name)

            # Do the actual bind.
            self._bind(atom, obj)

            # Trait event notification.
            self.object_added = NamingEvent(
                new_binding=Binding(name=name, obj=obj, context=self))

        # Otherwise, attempt to continue resolution into the next context.
        else:
            if not self._is_bound(components[0]):
                if make_contexts:
                    self._create_subcontext(components[0])

                else:
                    raise NameNotFoundError(components[0])

            next_context = self._get_next_context(components[0])
            next_context.bind('/'.join(components[1:]), obj, make_contexts)

        return

    def rebind(self, name, obj, make_contexts=False):
        """ Binds an object to a name that may already be bound.

        If 'make_contexts' is True then any missing intermediate contexts are
        created automatically.

        The object may be a different object but may also be the same object
        that is already bound to the specified name. The name may or may not be
        already used. Think of this as a safer version of 'bind' since this
        one will never raise an exception regarding a name being used.
        
        """

        if len(name) == 0:
            raise InvalidNameError('empty name')

        # Parse the name.
        components = self._parse_name(name)

        # If there is exactly one component in the name then the operation
        # takes place in this context.
        if len(components) == 1:
            # Do the actual rebind.
            self._rebind(components[0], obj)

            # Trait event notification.
            self.object_changed = NamingEvent(
                new_binding=Binding(name=name, obj=obj, context=self))

        # Otherwise, attempt to continue resolution into the next context.
        else:
            if not self._is_bound(components[0]):
                if make_contexts:
                    self._create_subcontext(components[0])

                else:
                    raise NameNotFoundError(components[0])

            next_context = self._get_next_context(components[0])
            next_context.rebind('/'.join(components[1:]), obj, make_contexts)

        return

    def unbind(self, name):
        """ Unbinds a name. """

        if len(name) == 0:
            raise InvalidNameError('empty name')

        # Parse the name.
        components = self._parse_name(name)

        # If there is exactly one component in the name then the operation
        # takes place in this context.
        if len(components) == 1:
            atom = components[0]

            if not self._is_bound(atom):
                raise NameNotFoundError(name)

            # Lookup the object that we are unbinding to use in the event
            # notification.
            obj = self._lookup(atom)

            # Do the actual unbind.
            self._unbind(atom)

            # Trait event notification.
            self.object_removed = NamingEvent(
                old_binding=Binding(name=name, obj=obj, context=self))

        # Otherwise, attempt to continue resolution into the next context.
        else:
            if not self._is_bound(components[0]):
                raise NameNotFoundError(components[0])

            next_context = self._get_next_context(components[0])
            next_context.unbind('/'.join(components[1:]))

        return

    def rename(self, old_name, new_name):
        """ Binds a new name to an object. """

        if len(old_name) == 0 or len(new_name) == 0:
            raise InvalidNameError('empty name')

        # Parse the names.
        old_components = self._parse_name(old_name)
        new_components = self._parse_name(new_name)

        # If there is axactly one component in BOTH names then the operation
        # takes place ENTIRELY in this context.
        if len(old_components) == 1 and len(new_components) == 1:
            # Is the old name actually bound?
            if not self._is_bound(old_name):
                raise NameNotFoundError(old_name)

            # Is the new name already bound?
            if self._is_bound(new_name):
                raise NameAlreadyBoundError(new_name)

            # Do the actual rename.
            self._rename(old_name, new_name)

            # Lookup the object that we are renaming to use in the event
            # notification.
            obj = self._lookup(new_name)

            # Trait event notification.
            self.object_renamed = NamingEvent(
                old_binding=Binding(name=old_name, obj=obj, context=self),
                new_binding=Binding(name=new_name, obj=obj, context=self))

        else:
            # fixme: This really needs to be transactional in case the bind
            # succeeds but the unbind fails.  To be safe should we just not
            # support cross-context renaming for now?!?!
            #
            # Lookup the object.
            obj = self.lookup(old_name)

            # Bind the new name.
            self.bind(new_name, obj)

            # Unbind the old one.
            self.unbind(old_name)

        return

    def lookup(self, name):
        """ Resolves a name relative to this context. """

        # If the name is empty we return the context itself.
        if len(name) == 0:
            # fixme: The JNDI spec. says that this should return a COPY of
            # the context.
            return self

        # Parse the name.
        components = self._parse_name(name)

        # If there is exactly one component in the name then the operation
        # takes place in this context.
        if len(components) == 1:
            atom = components[0]

            if not self._is_bound(atom):
                raise NameNotFoundError(name)

            # Do the actual lookup.
            obj = self._lookup(atom)

        # Otherwise, attempt to continue resolution into the next context.
        else:
            if not self._is_bound(components[0]):
                raise NameNotFoundError(components[0])

            next_context = self._get_next_context(components[0])
            obj = next_context.lookup('/'.join(components[1:]))

        return obj

    # fixme: Non-JNDI
    def lookup_binding(self, name):
        """ Looks up the binding for a name relative to this context. """

        if len(name) == 0:
            raise InvalidNameError('empty name')

        # Parse the name.
        components = self._parse_name(name)

        # If there is exactly one component in the name then the operation
        # takes place in this context.
        if len(components) == 1:
            atom = components[0]

            if not self._is_bound(atom):
                raise NameNotFoundError(name)

            # Do the actual lookup.
            binding = self._lookup_binding(atom)

        # Otherwise, attempt to continue resolution into the next context.
        else:
            if not self._is_bound(components[0]):
                raise NameNotFoundError(components[0])

            next_context = self._get_next_context(components[0])
            binding = next_context.lookup_binding('/'.join(components[1:]))

        return binding

    # fixme: Non-JNDI
    def lookup_context(self, name):
        """ Resolves a name relative to this context.

        The name MUST resolve to a context. This method is useful to return
        context adapters.

        """

        # If the name is empty we return the context itself.
        if len(name) == 0:
            # fixme: The JNDI spec. says that this should return a COPY of
            # the context.
            return self

        # Parse the name.
        components = self._parse_name(name)

        # If there is exactly one component in the name then the operation
        # takes place in this context.
        if len(components) == 1:
            atom = components[0]

            if not self._is_bound(atom):
                raise NameNotFoundError(name)

            # Do the actual lookup.
            obj = self._get_next_context(atom)

        # Otherwise, attempt to continue resolution into the next context.
        else:
            if not self._is_bound(components[0]):
                raise NameNotFoundError(components[0])

            next_context = self._get_next_context(components[0])
            obj = next_context.lookup('/'.join(components[1:]))

        return obj

    def create_subcontext(self, name):
        """ Creates a sub-context. """

        if len(name) == 0:
            raise InvalidNameError('empty name')

        # Parse the name.
        components = self._parse_name(name)

        # If there is exactly one component in the name then the operation
        # takes place in this context.
        if len(components) == 1:
            atom = components[0]

            # Is the name already bound?
            if self._is_bound(atom):
                raise NameAlreadyBoundError(name)

            # Do the actual creation of the sub-context.
            sub = self._create_subcontext(atom)

            # Trait event notification.
            self.object_added = NamingEvent(
                new_binding=Binding(name=name, obj=sub, context=self))

        # Otherwise, attempt to continue resolution into the next context.
        else:
            if not self._is_bound(components[0]):
                raise NameNotFoundError(components[0])

            next_context = self._get_next_context(components[0])
            sub = next_context.create_subcontext('/'.join(components[1:]))

        return sub

    def destroy_subcontext(self, name):
        """ Destroys a sub-context. """

        if len(name) == 0:
            raise InvalidNameError('empty name')

        # Parse the name.
        components = self._parse_name(name)

        # If there is exactly one component in the name then the operation
        # takes place in this context.
        if len(components) == 1:
            atom = components[0]

            if not self._is_bound(atom):
                raise NameNotFoundError(name)

            obj = self._lookup(atom)
            if not self._is_context(atom):
                raise NotContextError(name)

            # Do the actual destruction of the sub-context.
            self._destroy_subcontext(atom)

            # Trait event notification.
            self.object_removed = NamingEvent(
                old_binding=Binding(name=name, obj=obj, context=self))

        # Otherwise, attempt to continue resolution into the next context.
        else:
            if not self._is_bound(components[0]):
                raise NameNotFoundError(components[0])

            next_context = self._get_next_context(components[0])
            next_context.destroy_subcontext('/'.join(components[1:]))

        return

    # fixme: Non-JNDI
    def get_unique_name(self, prefix):
        """ Returns a name that is unique within the context.

        The name returned will start with the specified prefix.

        """

        return make_unique_name(prefix,
                                existing=self.list_names(''),
                                format='%s (%d)')

    def list_names(self, name=''):
        """ Lists the names bound in a context. """

        # If the name is empty then the operation takes place in this context.
        if len(name) == 0:
            names = self._list_names()

        # Otherwise, attempt to continue resolution into the next context.
        else:
            # Parse the name.
            components = self._parse_name(name)

            if not self._is_bound(components[0]):
                raise NameNotFoundError(components[0])

            next_context = self._get_next_context(components[0])
            names = next_context.list_names('/'.join(components[1:]))

        return names

    def list_bindings(self, name=''):
        """ Lists the bindings in a context. """

        # If the name is empty then the operation takes place in this context.
        if len(name) == 0:
            bindings = self._list_bindings()

        # Otherwise, attempt to continue resolution into the next context.
        else:
            # Parse the name.
            components = self._parse_name(name)

            if not self._is_bound(components[0]):
                raise NameNotFoundError(components[0])

            next_context = self._get_next_context(components[0])
            bindings = next_context.list_bindings('/'.join(components[1:]))

        return bindings

    # fixme: Non-JNDI
    def is_context(self, name):
        """ Returns True if the name is bound to a context. """

        # If the name is empty then it refers to this context.
        if len(name) == 0:
            is_context = True

        else:
            # Parse the name.
            components = self._parse_name(name)

            # If there is exactly one component in the name then the operation
            # takes place in this context.
            if len(components) == 1:
                atom = components[0]

                if not self._is_bound(atom):
                    raise NameNotFoundError(name)

                # Do the actual check.
                is_context = self._is_context(atom)

            # Otherwise, attempt to continue resolution into the next context.
            else:
                if not self._is_bound(components[0]):
                    raise NameNotFoundError(components[0])

                next_context = self._get_next_context(components[0])
                is_context = next_context.is_context('/'.join(components[1:]))

        return is_context

    # fixme: Non-JNDI
    def search(self, obj):
        """ Returns a list of namespace names that are bound to obj. """

        # don't look for None
        if obj is None:
            return []

        # Obj is bound to these names relative to this context
        names = []

        # path contain the name components down to the current context
        path = []

        self._search(obj, names, path, {})

        return names

    ###########################################################################
    # Protected 'Context' interface.
    ###########################################################################

    def _parse_name(self, name):
        """ Parse a name into a list of components.

        e.g. 'foo/bar/baz' -> ['foo', 'bar', 'baz']

        """

        return name.split('/')

    def _is_bound(self, name):
        """ Is a name bound in this context? """

        return name in self._bindings

    def _lookup(self, name):
        """ Looks up a name in this context. """

        obj = self._bindings[name]

        return naming_manager.get_object_instance(obj, name, self)

    def _lookup_binding(self, name):
        """ Looks up the binding for a name in this context. """

        return Binding(name=name, obj=self._lookup(name), context=self)

    def _bind(self, name, obj):
        """ Binds a name to an object in this context. """

        state = naming_manager.get_state_to_bind(obj, name, self)
        self._bindings[name] = state

        return

    def _rebind(self, name, obj):
        """ Rebinds a name to an object in this context. """

        self._bind(name, obj)

        return

    def _unbind(self, name):
        """ Unbinds a name from this context. """

        del self._bindings[name]

        return

    def _rename(self, old_name, new_name):
        """ Renames an object in this context. """

        # Bind the new name.
        self._bindings[new_name] = self._bindings[old_name]

        # Unbind the old one.
        del self._bindings[old_name]

        return

    def _create_subcontext(self, name):
        """ Creates a sub-context of this context. """

        sub = self.__class__(environment=self.environment)
        self._bindings[name] = sub

        return sub

    def _destroy_subcontext(self, name):
        """ Destroys a sub-context of this context. """

        del self._bindings[name]

        return

    def _list_bindings(self):
        """ Lists the bindings in this context. """

        bindings = []
        for name in self._list_names():
            bindings.append(
                Binding(name=name, obj=self._lookup(name), context=self))

        return bindings

    def _list_names(self):
        """ Lists the names bound in this context. """

        return self._bindings.keys()

    def _is_context(self, name):
        """ Returns True if a name is bound to a context. """

        return self._get_next_context(name) is not None

    def _get_next_context(self, name):
        """ Returns the next context. """

        obj = self._lookup(name)

        # If the object is a context then everything is just dandy.
        if isinstance(obj, Context):
            next_context = obj

        # Otherwise, instead of just giving up, see if the context has a type
        # manager that knows how to adapt the object to make it quack like a
        # context.
        else:
            next_context = self._get_context_adapter(obj)

            # If no adapter was found then we cannot continue name resolution.
            if next_context is None:
                raise NotContextError(name)

        return next_context

    def _search(self, obj, names, path, searched):
        """ Append to names any name bound to obj.
            Join path and name with '/' to for a complete name from the
            top context.
        """

        # Check the bindings recursively.
        for binding in self.list_bindings():
            if binding.obj is obj:
                path.append(binding.name)
                names.append('/'.join(path))
                path.pop()

            if isinstance( binding.obj, Context ) \
                and not binding.obj in searched:
                path.append(binding.name)
                searched[binding.obj] = True
                binding.obj._search(obj, names, path, searched)
                path.pop()

        return

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

    def _get_context_adapter(self, obj):
        """ Returns a context adapter for an object.

        Returns None if no such adapter is available.

        """

        if self.type_manager is not None:
            adapter = self.type_manager.object_as(obj,
                                                  Context,
                                                  environment=self.environment,
                                                  context=self)

        else:
            adapter = None

        return adapter
Ejemplo n.º 17
0
class IWorkbench(Interface):
    """ The workbench interface. """

    #### 'IWorkbench' interface ###############################################

    # The active workbench window (the last one to get focus).
    active_window = Instance(WorkbenchWindow)

    # The optional application scripting manager.
    script_manager = Instance('enthought.appscripting.api.IScriptManager')

    # A directory on the local file system that we can read and write to at
    # will. This is used to persist window layout information, etc.
    state_location = Str

    # The optional undo manager.
    undo_manager = Instance('enthought.undo.api.IUndoManager')

    # The user defined perspectives manager.
    user_perspective_manager = Instance(UserPerspectiveManager)

    # All of the workbench windows created by the workbench.
    windows = List(WorkbenchWindow)

    #### Workbench lifecycle events ####

    # Fired when the workbench is about to exit.
    #
    # This can be caused by either:-
    #
    # a) The 'exit' method being called.
    # b) The last open window being closed.
    exiting = VetoableEvent

    # Fired when the workbench has exited.
    #
    # This is fired after the last open window has been closed.
    exited = Event

    #### Window lifecycle events ####

    # Fired when a workbench window has been created.
    window_created = Event(WindowEvent)

    # Fired when a workbench window is opening.
    window_opening = Event(VetoableWindowEvent)

    # Fired when a workbench window has been opened.
    window_opened = Event(WindowEvent)

    # Fired when a workbench window is closing.
    window_closing = Event(VetoableWindowEvent)

    # Fired when a workbench window has been closed.
    window_closed = Event(WindowEvent)

    ###########################################################################
    # 'IWorkbench' interface.
    ###########################################################################

    def create_window(self, **kw):
        """ Factory method that creates a new workbench window. """

    def edit(self, obj, kind=None, use_existing=True):
        """ Edit an object in the active workbench window. """

    def exit(self):
        """ Exit the workbench.

        This closes all open workbench windows.

        This method is not called when the user clicks the close icon. Nor when
        they do an Alt+F4 in Windows. It is only called when the application
        menu File->Exit item is selected.

        """

    def get_editor(self, obj, kind=None):
        """ Return the editor that is editing an object.

        Returns None if no such editor exists.

        """

    def get_editor_by_id(self, id):
        """ Return the editor with the specified Id.
Ejemplo n.º 18
0
class Tree(Widget):
    """ A tree control with a model/ui architecture. """

    # The default tree style.
    STYLE = wx.TR_EDIT_LABELS | wx.TR_HAS_BUTTONS | wx.CLIP_CHILDREN

    #### 'Tree' interface #####################################################

    # The tree's filters (empty if no filtering is required).
    filters = List(Filter)

    # Mode for lines connecting tree nodes which emphasize hierarchy:
    # 'appearance' - only on when lines look good,
    # 'on' - always on, 'off' - always off
    # NOTE: on and off are ignored in favor of show_lines for now
    lines_mode = Enum('appearance', 'on', 'off')

    # The model that provides the data for the tree.
    model = Instance(TreeModel, ())

    # The root of the tree (this is for convenience, it just delegates to
    # the tree's model).
    root = Property(Any)

    # The objects currently selected in the tree.
    selection = List

    # Selection mode.
    selection_mode = Enum('single', 'extended')

    # Should an image be shown for each node?
    show_images = Bool(True)

    # Should lines be drawn between levels in the tree.
    show_lines = Bool(True)

    # Should the root of the tree be shown?
    show_root = Bool(True)

    # The tree's sorter (None if no sorting is required).
    sorter = Instance(Sorter)

    #### Events ####

    # A right-click occurred on the control (not a node!).
    control_right_clicked = Event  #(Point)

    # A key was pressed while the tree has focus.
    key_pressed = Event(KeyPressedEvent)

    # A node has been activated (ie. double-clicked).
    node_activated = Event  #(Any)

    # A drag operation was started on a node.
    node_begin_drag = Event  #(Any)

    # A (non-leaf) node has been collapsed.
    node_collapsed = Event  #(Any)

    # A (non-leaf) node has been expanded.
    node_expanded = Event  #(Any)

    # A left-click occurred on a node.
    #
    # Tuple(node, point).
    node_left_clicked = Event  #(Tuple)

    # A right-click occurred on a node.
    #
    # Tuple(node, point)
    node_right_clicked = Event  #(Tuple)

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

    # A name to distinguish the tree for debugging!
    #
    # fixme: This turns out to be kinda useful... Should 'Widget' have a name
    # trait?
    _name = Str('Anonymous tree')

    # An optional callback to detect the end of a label edit.  This is
    # useful because the callback will be invoked even if the node label was
    # not actually changed.
    _label_edit_callback = Trait(None, Callable, None)

    # Flag for allowing selection events to be ignored
    _ignore_selection_events = Bool(False)

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

    def __init__(self, parent, image_size=(16, 16), **traits):
        """ Creates a new tree.

        'parent' is the toolkit-specific control that is the tree's parent.

        'image_size' is a tuple in the form (int width, int height) that
        specifies the size of the images (if required) displayed in the tree.

        """

        # Base class constructors.
        super(Tree, self).__init__(**traits)

        # Get our wx Id.
        wxid = wx.NewId()

        # Create the toolkit-specific control.
        self.control = tree = _Tree(self,
                                    parent,
                                    wxid,
                                    style=self._get_style())

        # Wire up the wx tree events.
        wx.EVT_CHAR(tree, self._on_char)
        wx.EVT_LEFT_DOWN(tree, self._on_left_down)
        # fixme: This is not technically correct as context menus etc should
        # appear on a right up (or right click).  Unfortunately,  if we
        # change this to 'EVT_RIGHT_UP' wx does not fire the event unless the
        # right mouse button is double clicked 8^()  Sad,  but true!
        wx.EVT_RIGHT_DOWN(tree, self._on_right_down)
        # fixme: This is not technically correct as we would really like to use
        # 'EVT_TREE_ITEM_ACTIVATED'. Unfortunately, (in 2.6 at least), it
        # throws an exception when the 'Enter' key is pressed as the wx tree
        # item Id in the event seems to be invalid. It also seems to cause
        # any child frames that my be created in response to the event to
        # appear *behind* the parent window, which is, errrr, not great ;^)
        wx.EVT_LEFT_DCLICK(tree, self._on_tree_item_activated)
        #wx.EVT_TREE_ITEM_ACTIVATED(tree, wxid, self._on_tree_item_activated)
        wx.EVT_TREE_ITEM_COLLAPSING(tree, wxid, self._on_tree_item_collapsing)
        wx.EVT_TREE_ITEM_COLLAPSED(tree, wxid, self._on_tree_item_collapsed)
        wx.EVT_TREE_ITEM_EXPANDING(tree, wxid, self._on_tree_item_expanding)
        wx.EVT_TREE_ITEM_EXPANDED(tree, wxid, self._on_tree_item_expanded)
        wx.EVT_TREE_BEGIN_LABEL_EDIT(tree, wxid,
                                     self._on_tree_begin_label_edit)
        wx.EVT_TREE_END_LABEL_EDIT(tree, wxid, self._on_tree_end_label_edit)
        wx.EVT_TREE_BEGIN_DRAG(tree, wxid, self._on_tree_begin_drag)
        wx.EVT_TREE_SEL_CHANGED(tree, wxid, self._on_tree_sel_changed)
        wx.EVT_TREE_DELETE_ITEM(tree, wxid, self._on_tree_delete_item)

        # Enable the tree as a drag and drop target.
        self.control.SetDropTarget(PythonDropTarget(self))

        # The image list is a wxPython-ism that caches all images used in the
        # control.
        self._image_list = ImageList(image_size[0], image_size[1])
        if self.show_images:
            tree.AssignImageList(self._image_list)

        # Mapping from node to wx tree item Ids.
        self._node_to_id_map = {}

        # Add the root node.
        if self.root is not None:
            self._add_root_node(self.root)

        # Listen for changes to the model.
        self._add_model_listeners(self.model)

        return

    ###########################################################################
    # 'Tree' interface.
    ###########################################################################

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

    def _get_root(self):
        """ Returns the root node of the tree. """

        return self.model.root

    def _set_root(self, root):
        """ Sets the root node of the tree. """

        self.model.root = root

        return

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

    def collapse(self, node):
        """ Collapses the specified node. """

        wxid = self._get_wxid(node)
        if wxid is not None:
            self.control.Collapse(wxid)

        return

    def edit_label(self, node, callback=None):
        """ Edits the label of the specified node.

        If a callback is specified it will be called when the label edit
        completes WHETHER OR NOT the label was actually changed.

        The callback must take exactly 3 arguments:- (tree, node, label)

        """

        wxid = self._get_wxid(node)
        if wxid is not None:
            self._label_edit_callback = callback
            self.control.EditLabel(wxid)

        return

    def expand(self, node):
        """ Expands the specified node. """

        wxid = self._get_wxid(node)
        if wxid is not None:
            self.control.Expand(wxid)

        return

    def expand_all(self):
        """ Expands every node in the tree. """

        if self.show_root:
            self._expand_item(self._get_wxid(self.root))

        else:
            for child in self._get_children(self.root):
                self._expand_item(self._get_wxid(child))

        return

    def get_parent(self, node):
        """ Returns the parent of a node.

        This will only work iff the node has been displayed in the tree.  If it
        hasn't then None is returned.

        """

        # Has the node actually appeared in the tree yet?
        wxid = self._get_wxid(node)
        if wxid is not None:
            pid = self.control.GetItemParent(wxid)

            # The item data is a tuple.  The first element indicates whether or
            # not we have already populated the item with its children.  The
            # second element is the actual item data.
            populated, parent = self.control.GetPyData(pid)

        else:
            parent = None

        return parent

    def is_expanded(self, node):
        """ Returns True if the node is expanded, otherwise False. """

        wxid = self._get_wxid(node)
        if wxid is not None:
            # If the root node is hidden then it is always expanded!
            if node is self.root and not self.show_root:
                is_expanded = True

            else:
                is_expanded = self.control.IsExpanded(wxid)

        else:
            is_expanded = False

        return is_expanded

    def is_selected(self, node):
        """ Returns True if the node is selected, otherwise False. """

        wxid = self._get_wxid(node)
        if wxid is not None:
            is_selected = self.control.IsSelected(wxid)

        else:
            is_selected = False

        return is_selected

    def refresh(self, node):
        """ Refresh the tree starting from the specified node.

        Call this when the structure of the content has changed DRAMATICALLY.

        """

        # Has the node actually appeared in the tree yet?
        pid = self._get_wxid(node)
        if pid is not None:
            # Delete all of the node's children and re-add them.
            self.control.DeleteChildren(pid)
            self.control.SetPyData(pid, (False, node))

            # Does the node have any children?
            has_children = self._has_children(node)
            self.control.SetItemHasChildren(pid, has_children)

            # fixme: At least on Windows, wx does not fire an expanding
            # event for a hidden root node, so we have to populate the node
            # manually.
            if node is self.root and not self.show_root:
                # Add the child nodes.
                for child in self._get_children(node):
                    self._add_node(pid, child)

            else:
                # Expand it.
                if self.control.IsExpanded(pid):
                    self.control.Collapse(pid)

                self.control.Expand(pid)

        return

    def select(self, node):
        """ Selects the specified node. """

        wxid = self._get_wxid(node)
        if wxid is not None:
            self.control.SelectItem(wxid)

        return

    def set_selection(self, list):
        """ Selects the specified list of nodes. """
        logger.debug('Setting selection to [%s] within Tree [%s]', list, self)

        # Update the control to reflect the target list by unselecting
        # everything and then selecting each item in the list.  During this
        # process, we want to avoid changing our own selection.
        self._ignore_selection_events = True
        self.control.UnselectAll()
        for node in list:
            try:
                self.select(node)
            except:
                logger.exception('Unable to select node [%s]', node)

        self._ignore_selection_events = False

        # Update our selection to reflect the final selection state.
        self.selection = self._get_selection()

    ###########################################################################
    # 'PythonDropTarget' interface.
    ###########################################################################

    def on_drag_over(self, x, y, obj, default_drag_result):
        """ Called when a node is dragged over the tree. """

        result = wx.DragNone

        # Find the node that we are dragging over...
        node = self._get_drag_drop_node(x, y)
        if node is not None:
            # Ask the model if the node allows the object to be dropped onto
            # it.
            if self.model.can_drop(node, obj):
                result = default_drag_result

        return result

    def on_drop(self, x, y, obj, default_drag_result):
        """ Called when a node is dropped on the tree. """

        # Find the node that we are dragging over...
        node = self._get_drag_drop_node(x, y)
        if node is not None:
            self.model.drop(node, obj)

        return default_drag_result

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

    def _get_wxid(self, node):
        """ Returns the wxid for the specified node.

        Returns None if the node has not yet appeared in the tree.

        """

        # The model must generate a unique key for each node (unique within the
        # model).
        key = self.model.get_key(node)

        return self._node_to_id_map.get(key, None)

    def _set_wxid(self, node, wxid):
        """ Sets the wxid for the specified node. """

        # The model must generate a unique key for each node (unique within the
        # model).
        key = self.model.get_key(node)

        self._node_to_id_map[key] = wxid

        return

    def _remove_wxid(self, node):
        """ Removes the wxid for the specified node. """

        # The model must generate a unique key for each node (unique within the
        # model).
        key = self.model.get_key(node)

        try:
            del self._node_to_id_map[key]

        except KeyError:
            # fixme: No, really, this is a serious one... How do we get in this
            # situation.  It came up when using the canvas stuff...
            logger.warn('removing node: %s' % str(node))

        return

    def _get_style(self):
        """ Returns the wx style flags for creating the tree control. """

        # Start with the default flags.
        style = self.STYLE

        # Turn lines off for appearance on *nix.
        # ...for now, show_lines determines if lines are on or off, but
        # eventually lines_mode may eliminate the need for show_lines
        if self.lines_mode == 'appearance' and os.name == 'posix':
            self.show_lines = False

        if not self.show_lines:
            style = style | wx.TR_NO_LINES

        if not self.show_root:
            # fixme: It looks a little weird, but it we don't have the
            # 'lines at root' style then wx won't draw the expand/collapse
            # image on non-leaf nodes at the root level 8^()
            style = style | wx.TR_HIDE_ROOT | wx.TR_LINES_AT_ROOT

        if self.selection_mode != 'single':
            style = style | wx.TR_MULTIPLE | wx.TR_EXTENDED

        return style

    def _add_model_listeners(self, model):
        """ Adds listeners for model changes. """

        # Listen for changes to the model.
        model.on_trait_change(self._on_root_changed, 'root')
        model.on_trait_change(self._on_nodes_changed, 'nodes_changed')
        model.on_trait_change(self._on_nodes_inserted, 'nodes_inserted')
        model.on_trait_change(self._on_nodes_removed, 'nodes_removed')
        model.on_trait_change(self._on_nodes_replaced, 'nodes_replaced')
        model.on_trait_change(self._on_structure_changed, 'structure_changed')

        return

    def _remove_model_listeners(self, model):
        """ Removes listeners for model changes. """

        # Unhook the model event listeners.
        model.on_trait_change(self._on_root_changed, 'root', remove=True)

        model.on_trait_change(self._on_nodes_changed,
                              'nodes_changed',
                              remove=True)

        model.on_trait_change(self._on_nodes_inserted,
                              'nodes_inserted',
                              remove=True)

        model.on_trait_change(self._on_nodes_removed,
                              'nodes_removed',
                              remove=True)

        model.on_trait_change(self._on_nodes_replaced,
                              'nodes_replaced',
                              remove=True)

        model.on_trait_change(self._on_structure_changed,
                              'structure_changed',
                              remove=True)

        return

    def _add_root_node(self, node):
        """ Adds the root node. """

        # Get the tree item image index and the label text.
        image_index = self._get_image_index(node)
        text = self._get_text(node)

        # Add the node.
        wxid = self.control.AddRoot(text, image_index, image_index)

        # This gives the model a chance to wire up trait handlers etc.
        self.model.add_listener(node)

        # If the root node is hidden, get its children.
        if not self.show_root:
            # Add the child nodes.
            for child in self._get_children(node):
                self._add_node(wxid, child)

        # Does the node have any children?
        has_children = self._has_children(node)
        self.control.SetItemHasChildren(wxid, has_children)

        # The item data is a tuple.  The first element indicates whether or not
        # we have already populated the item with its children.  The second
        # element is the actual item data (which in our case is an arbitrary
        # Python object provided by the tree model).
        if self.show_root:
            self.control.SetPyData(wxid, (not self.show_root, node))

        # Make sure that we can find the node's Id.
        self._set_wxid(node, wxid)

        # Automatically expand the root.
        if self.show_root:
            self.control.Expand(wxid)

        return

    def _add_node(self, pid, node):
        """ Adds 'node' as a child of the node identified by 'pid'.

        If 'pid' is None then we are adding the root node.

        """

        # Get the tree item image index and the label text.
        image_index = self._get_image_index(node)
        text = self._get_text(node)

        # Add the node.
        wxid = self.control.AppendItem(pid, text, image_index, image_index)

        # This gives the model a chance to wire up trait handlers etc.
        self.model.add_listener(node)

        # Does the node have any children?
        has_children = self._has_children(node)
        self.control.SetItemHasChildren(wxid, has_children)

        # The item data is a tuple.  The first element indicates whether or not
        # we have already populated the item with its children.  The second
        # element is the actual item data (which in our case is an arbitrary
        # Python object provided by the tree model).
        self.control.SetPyData(wxid, (False, node))

        # Make sure that we can find the node's Id.
        self._set_wxid(node, wxid)

        return

    def _insert_node(self, pid, node, index):
        """ Inserts 'node' as a child of the node identified by 'pid'.

        If 'pid' is None then we are adding the root node.

        """

        # Get the tree item image index and the label text.
        image_index = self._get_image_index(node)
        text = self._get_text(node)

        # Add the node.
        wxid = self.control.InsertItemBefore(pid, index, text, image_index,
                                             image_index)

        # This gives the model a chance to wire up trait handlers etc.
        self.model.add_listener(node)

        # Does the node have any children?
        has_children = self._has_children(node)
        self.control.SetItemHasChildren(wxid, has_children)

        # The item data is a tuple.  The first element indicates whether or not
        # we have already populated the item with its children.  The second
        # element is the actual item data (which in our case is an arbitrary
        # Python object provided by the tree model).
        self.control.SetPyData(wxid, (False, node))

        # Make sure that we can find the node's Id.
        self._set_wxid(node, wxid)

        return

    def _remove_node(self, wxid, node):
        """ Removes a node from the tree. """

        # This gives the model a chance to remove trait handlers etc.
        self.model.remove_listener(node)

        # Remove the reference to the item's data.
        self._remove_wxid(node)
        self.control.SetPyData(wxid, None)

        return

    def _update_node(self, wxid, node):
        """ Updates the image and text of the specified node. """

        # Get the tree item image index.
        image_index = self._get_image_index(node)
        self.control.SetItemImage(wxid, image_index, wx.TreeItemIcon_Normal)
        self.control.SetItemImage(wxid, image_index, wx.TreeItemIcon_Selected)

        # Get the tree item text.
        text = self._get_text(node)
        self.control.SetItemText(wxid, text)

        return

    def _has_children(self, node):
        """ Returns True if a node has children. """

        # fixme: To be correct we *should* apply filtering here, but that
        # seems to blow a hole throught models that have some efficient
        # mechanism for determining whether or not they have children.  There
        # is also a precedent for doing it this way in Windoze, where a node
        # gets marked as though it can be expanded, even thought when the
        # expansion occurs, no children are present!
        return self.model.has_children(node)

    def _get_children(self, node):
        """ Get the children of a node. """

        children = self.model.get_children(node)

        # Filtering....
        filtered_children = []
        for child in children:
            for filter in self.filters:
                if not filter.select(self, node, child):
                    break

            else:
                filtered_children.append(child)

        # Sorting...
        if self.sorter is not None:
            self.sorter.sort(self, node, filtered_children)

        return filtered_children

    def _get_image_index(self, node):
        """ Returns the tree item image index for a node. """

        expanded = self.is_expanded(node)
        selected = self.is_selected(node)

        # Get the image used to represent the node.
        image = self.model.get_image(node, selected, expanded)
        if image is not None:
            image_index = self._image_list.GetIndex(image)

        else:
            image_index = -1

        return image_index

    def _get_drag_drop_node(self, x, y):
        """ Returns the node that is being dragged/dropped on.

        Returns None if the cursor is not over the icon or label of a node.

        """

        data, wxid, flags, point = self._hit_test((x, y))
        if data is not None:
            populated, node = data

        else:
            node = None

        return node

    def _get_text(self, node):
        """ Returns the tree item text for a node. """

        text = self.model.get_text(node)
        if text is None:
            text = ''

        return text

    def _unpack_event(self, event, wxid=None):
        """ Unpacks the event to see whether a tree item was involved. """

        try:
            point = event.GetPosition()

        except:
            point = event.GetPoint()

        return self._hit_test(point, wxid)

    def _hit_test(self, point, wxid=None):
        """ Determines whether a point is within a node's label or icon. """

        flags = wx.TREE_HITTEST_ONITEMLABEL
        if (wxid is None) or (not wxid.IsOk()):
            wxid, flags = self.control.HitTest(point)

        # Warning: On GTK we have to check the flags before we call 'GetPyData'
        # because if we call it when the hit test returns 'nowhere' it will
        # barf (on Windows it simply returns 'None' 8^()
        if flags & wx.TREE_HITTEST_NOWHERE:
            data = None

        elif flags & wx.TREE_HITTEST_ONITEMICON \
             or flags & wx.TREE_HITTEST_ONITEMLABEL:

            data = self.control.GetPyData(wxid)

        # fixme: Not sure why 'TREE_HITTEST_NOWHERE' doesn't catch everything!
        else:
            data = None

        return data, wxid, flags, point

    def _get_selection(self):
        """ Returns a list of the selected nodes """

        selection = []
        for wxid in self.control.GetSelections():
            data = self.control.GetPyData(wxid)
            if data is not None:
                populated, node = data
                selection.append(self.model.get_selection_value(node))

        return selection

    def _expand_item(self, wxid):
        """ Recursively expand a tree item. """

        self.control.Expand(wxid)

        cid, cookie = self.control.GetFirstChild(wxid)
        while cid.IsOk():
            self._expand_item(cid)
            cid, cookie = self.control.GetNextChild(wxid, cookie)

        return

    #### Trait event handlers #################################################

    def _on_root_changed(self, root):
        """ Called when the root of the model has changed. """

        # Delete everything...
        if self.control is not None:
            self.control.DeleteAllItems()

            self._node_to_id_map = {}

            # ... and then add the root item back in.
            if root is not None:
                self._add_root_node(root)

        return

    def _on_nodes_changed(self, event):
        """ Called when nodes have been changed. """

        self._update_node(self._get_wxid(event.node), event.node)

        for child in event.children:
            cid = self._get_wxid(child)
            if cid is not None:
                self._update_node(cid, child)

        return

    def _on_nodes_inserted(self, event):
        """ Called when nodes have been inserted. """

        parent = event.node
        children = event.children
        index = event.index

        # Has the node actually appeared in the tree yet?
        pid = self._get_wxid(parent)
        if pid is not None:
            # The item data is a tuple.  The first element indicates whether or
            # not we have already populated the item with its children.  The
            # second element is the actual item data.
            if self.show_root or parent is not self.root:
                populated, node = self.control.GetPyData(pid)

            else:
                populated = True

            # If the node is not yet populated then just get the children and
            # add them.
            if not populated:
                for child in self._get_children(parent):
                    self._add_node(pid, child)

            # Otherwise, insert them.
            else:
                # An index of -1 means append!
                if index == -1:
                    index = self.control.GetChildrenCount(pid, False)

                for child in children:
                    self._insert_node(pid, child, index)
                    index += 1

            # The element is now populated!
            if self.show_root or parent is not self.root:
                self.control.SetPyData(pid, (True, parent))

            # Does the node have any children now?
            has_children = self.control.GetChildrenCount(pid) > 0
            self.control.SetItemHasChildren(pid, has_children)

            # If the node is not expanded then expand it.
            if not self.is_expanded(parent):
                self.expand(parent)

        return

    def _on_nodes_removed(self, event):
        """ Called when nodes have been removed. """

        parent = event.node
        children = event.children

        # Has the node actually appeared in the tree yet?
        pid = self._get_wxid(parent)
        if pid is not None:
            for child in event.children:
                cid = self._get_wxid(child)
                if cid is not None:
                    self.control.Delete(cid)

            # Does the node have any children left?
            has_children = self.control.GetChildrenCount(pid) > 0
            self.control.SetItemHasChildren(pid, has_children)

        return

    def _on_nodes_replaced(self, event):
        """ Called when nodes have been replaced. """

        for old_child, new_child in zip(event.old_children, event.children):
            cid = self._get_wxid(old_child)
            if cid is not None:
                # Remove listeners from the old node.
                self.model.remove_listener(old_child)

                # Delete all of the node's children.
                self.control.DeleteChildren(cid)

                # Update the visual appearance of the node.
                self._update_node(cid, new_child)

                # Update the node data.
                #
                # The item data is a tuple.  The first element indicates
                # whether or not we have already populated the item with its
                # children. The second element is the actual item data (which
                # in our case is an arbitrary Python object provided by the
                # tree model).
                self.control.SetPyData(cid, (False, new_child))

                # Remove the old node from the node to Id map.
                self._remove_wxid(old_child)

                # Add the new node to the node to Id map.
                self._set_wxid(new_child, cid)

                # Add listeners to the new node.
                self.model.add_listener(new_child)

                # Does the new node have any children?
                has_children = self._has_children(new_child)
                self.control.SetItemHasChildren(cid, has_children)

        # Update the tree's selection (in case the old node that was replaced
        # was selected, the selection should now include the new node).
        self.selection = self._get_selection()
        return

    def _on_structure_changed(self, event):
        """ Called when the structure of a node has changed drastically. """

        self.refresh(event.node)

        return

    #### wx event handlers ####################################################

    def _on_char(self, event):
        """ Called when a key is pressed when the tree has focus. """

        self.key_pressed = KeyPressedEvent(
            alt_down=event.m_altDown == 1,
            control_down=event.m_controlDown == 1,
            shift_down=event.m_shiftDown == 1,
            key_code=event.m_keyCode)

        event.Skip()

        return

    def _on_left_down(self, event):
        """ Called when the left mouse button is clicked on the tree. """

        data, id, flags, point = self._unpack_event(event)

        # Save point for tree_begin_drag method to workaround a bug in ?? when
        # wx.TreeEvent.GetPoint returns only (0,0).  This happens under linux
        # when using wx-2.4.2.4, for instance.
        self._point_left_clicked = point

        # Did the left click occur on a tree item?
        if data is not None:
            populated, node = data

            # Trait event notification.
            self.node_left_clicked = node, point

        # Give other event handlers a chance.
        event.Skip()

        return

    def _on_right_down(self, event):
        """ Called when the right mouse button is clicked on the tree. """

        data, id, flags, point = self._unpack_event(event)

        # Did the right click occur on a tree item?
        if data is not None:
            populated, node = data

            # Trait event notification.
            self.node_right_clicked = node, point

        # Otherwise notify that the control itself was clicked
        else:
            self.control_right_clicked = point

        # Give other event handlers a chance.
        event.Skip()

        return

    def _on_tree_item_activated(self, event):
        """ Called when a tree item is activated (i.e., double clicked). """

        # fixme: See the comment where the events are wired up for more
        # information.

        ##         # Which item was activated?
        ##         wxid = event.GetItem()

        # Which item was activated.
        point = event.GetPosition()
        wxid, flags = self.control.HitTest(point)

        # The item data is a tuple.  The first element indicates whether or not
        # we have already populated the item with its children.  The second
        # element is the actual item data.
        populated, node = self.control.GetPyData(wxid)

        # Trait event notiification.
        self.node_activated = node

        return

    def _on_tree_item_collapsing(self, event):
        """ Called when a tree item is about to collapse. """

        # Which item is collapsing?
        wxid = event.GetItem()

        # The item data is a tuple.  The first element indicates whether or not
        # we have already populated the item with its children.  The second
        # element is the actual item data.
        populated, node = self.control.GetPyData(wxid)

        # Give the model a chance to veto the collapse.
        if not self.model.is_collapsible(node):
            event.Veto()

        return

    def _on_tree_item_collapsed(self, event):
        """ Called when a tree item has been collapsed. """

        # Which item was collapsed?
        wxid = event.GetItem()

        # The item data is a tuple.  The first element indicates whether or not
        # we have already populated the item with its children.  The second
        # element is the actual item data.
        populated, node = self.control.GetPyData(wxid)

        # Make sure that the item's 'closed' icon is displayed etc.
        self._update_node(wxid, node)

        # Trait event notification.
        self.node_collapsed = node

        return

    def _on_tree_item_expanding(self, event):
        """ Called when a tree item is about to expand. """

        # Which item is expanding?
        wxid = event.GetItem()

        # The item data is a tuple.  The first element indicates whether or not
        # we have already populated the item with its children.  The second
        # element is the actual item data.
        populated, node = self.control.GetPyData(wxid)

        # Give the model a chance to veto the expansion.
        if self.model.is_expandable(node):
            # Lazily populate the item's children.
            if not populated:
                # Add the child nodes.
                for child in self._get_children(node):
                    self._add_node(wxid, child)

                # The element is now populated!
                self.control.SetPyData(wxid, (True, node))

        else:
            event.Veto()

        return

    def _on_tree_item_expanded(self, event):
        """ Called when a tree item has been expanded. """

        # Which item was expanded?
        wxid = event.GetItem()

        # The item data is a tuple.  The first element indicates whether or not
        # we have already populated the item with its children.  The second
        # element is the actual item data.
        populated, node = self.control.GetPyData(wxid)

        # Make sure that the node's 'open' icon is displayed etc.
        self._update_node(wxid, node)

        # Trait event notification.
        self.node_expanded = node

        return

    def _on_tree_begin_label_edit(self, event):
        """ Called when the user has started editing an item's label. """

        wxid = event.GetItem()

        # The item data is a tuple.  The first element indicates whether or not
        # we have already populated the item with its children.  The second
        # element is the actual item data.
        populated, node = self.control.GetPyData(wxid)

        # Give the model a chance to veto the edit.
        if not self.model.is_editable(node):
            event.Veto()

        return

    def _on_tree_end_label_edit(self, event):
        """ Called when the user has finished editing am item's label. """

        wxid = event.GetItem()

        # The item data is a tuple.  The first element indicates whether or not
        # we have already populated the item with its children.  The second
        # element is the actual item data.
        populated, node = self.control.GetPyData(wxid)

        # Give the model a chance to veto the edit.
        label = event.GetLabel()

        # Making sure the new label is not an empty string

        if label is not None and len(label) > 0 and \
            self.model.can_set_text(node, label):

            def end_label_edit():
                """ Called to complete the label edit. """

                # Set the node's text.
                self.model.set_text(node, label)

                # If a label edit callback was specified (in the call to
                # 'edit_label'), then call it).
                if self._label_edit_callback is not None:
                    self._label_edit_callback(self, node, label)

                return

            # We use a deffered call here, because a name change can trigger
            # the structure of a node to change, and hence the actual tree
            # nodes might get moved/deleted before the label edit operation has
            # completed.  When this happens wx gets very confused!  By using
            # 'invoke_later' we allow the label edit to complete.
            GUI.invoke_later(end_label_edit)

        else:
            event.Veto()

            # If a label edit callback was specified (in the call to
            # 'edit_label'), then call it).
            if self._label_edit_callback is not None:
                self._label_edit_callback(self, node, label)

        return

    def _on_tree_begin_drag(self, event):
        """ Called when a drag operation is starting on a tree item. """

        # Get the node, its id and the point where the event occurred.
        data, wxid, flags, point = self._unpack_event(event, event.GetItem())

        if point == (0, 0):
            # Apply workaround for GTK.
            point = self.point_left_clicked
            wxid, flags = self.HitTest(point)
            data = self.control.GetPyData(wxid)

        if data is not None:
            populated, node = data

            # Give the model a chance to veto the drag.
            if self.model.is_draggable(node):
                # We ask the model for the actual value to drag.
                drag_value = self.model.get_drag_value(node)

                # fixme: This is a terrible hack to get the binding x passed
                # during a drag operation.  Bindings should probably *always*
                # be dragged and our drag and drop mechanism should allow
                # extendable ways to extract the actual data.
                from enthought.util.wx.drag_and_drop import clipboard
                clipboard.node = [node]

                # Make sure that the tree selection is updated before we start
                # the drag. If we don't do this then if the first thing a
                # user does is drag a tree item (i.e., without a separate click
                # to select it first) then the selection appears empty.
                self.selection = self._get_selection()

                # Start the drag.
                PythonDropSource(self.control, drag_value, self)

                # Trait event notification.
                self.node_begin_drag = node

            else:
                event.Veto()

        return

    # fixme: This is part of the drag and drop hack...
    def on_dropped(self):
        """ Callback invoked when a drag/drop operation has completed. """

        from enthought.util.wx.drag_and_drop import clipboard
        clipboard.node = None

        return

    def _on_tree_sel_changed(self, event):
        """ Called when the selection is changed. """

        # Update our record of the selection to whatever was selected in the
        # tree UNLESS we are ignoring selection events.
        if not self._ignore_selection_events:

            # Trait notification.
            self.selection = self._get_selection()

        return

    def _on_tree_delete_item(self, event):
        """ Called when a tree item is being been deleted. """

        # Which item is being deleted?
        wxid = event.GetItem()

        # Check if GetPyData() returned a valid to tuple to unpack
        # ...if so, remove the node from the tree, otherwise just return
        #
        # fixme: Whoever addeed this code (and the comment above) didn't say
        # when this was occurring. This is method is called in response to a wx
        # event to delete an item and hence the item data should never be None
        # surely?!? Was it happening just on one platform?!?
        data = self.control.GetPyData(wxid)
        if data is not None:
            # The item data is a tuple.  The first element indicates whether or
            # not we have already populated the item with its children.  The
            # second element is the actual item data.
            populated, node = data

            # Remove the node.
            self._remove_node(wxid, node)

        return
Ejemplo n.º 19
0
class IWorkbenchWindowLayout(Interface):
    """ The workbench window layout interface.

    Window layouts are responsible for creating and managing the internal
    structure of a workbench window (it knows how to add and remove views and
    editors etc).

    """

    # The Id of the editor area.
    # FIXME v3: This is toolkit specific.
    editor_area_id = Str

    # The workbench window that this is the layout for.
    window = Instance('enthought.pyface.workbench.api.WorkbenchWindow')

    #### Events ####

    # Fired when an editor is about to be opened (or restored).
    editor_opening = Event(IEditor)

    # Fired when an editor has been opened (or restored).
    editor_opened = Event(IEditor)

    # Fired when an editor is about to be closed.
    editor_closing = Event(IEditor)

    # Fired when an editor has been closed.
    editor_closed = Event(IEditor)

    # Fired when a view is about to be opened (or restored).
    view_opening = Event(IView)

    # Fired when a view has been opened (or restored).
    view_opened = Event(IView)

    # Fired when a view is about to be closed (*not* hidden!).
    view_closing = Event(IView)

    # Fired when a view has been closed (*not* hidden!).
    view_closed = Event(IView)

    # FIXME v3: The "just for convenience" returns are a really bad idea.
    #
    # Why? They allow the call to be used on the LHS of an expression...
    # Because they have nothing to do with what the call is supposed to be
    # doing, they are unlikely to be used (because they are so unexpected and
    # inconsistently implemented), and only serve to replace two shorter lines
    # of code with one long one, arguably making code more difficult to read.
    def activate_editor(self, editor):
        """ Activate an editor.

        Returns the editor (just for convenience).

        """

    def activate_view(self, view):
        """ Activate a view.

        Returns the view (just for convenience).

        """

    def add_editor(self, editor, title):
        """ Add an editor.

        Returns the editor (just for convenience).

        """

    def add_view(self, view, position=None, relative_to=None, size=(-1, -1)):
        """ Add a view.

        Returns the view (just for convenience).

        """

    def close_editor(self, editor):
        """ Close an editor.

        Returns the editor (just for convenience).

        """

    def close_view(self, view):
        """ Close a view.

        FIXME v3: Currently views are never 'closed' in the same sense as an
        editor is closed. When we close an editor, we destroy its control.
        When we close a view, we merely hide its control. I'm not sure if this
        is a good idea or not. It came about after discussion with Dave P. and
        he mentioned that some views might find it hard to persist enough state
        that they can be re-created exactly as they were when they are shown
        again.

        Returns the view (just for convenience).

        """

    def close(self):
        """ Close the entire window layout.

        FIXME v3: Should this be called 'destroy'?

        """

    def create_initial_layout(self, parent):
        """ Create the initial window layout.

        Returns the layout.

        """

    def contains_view(self, view):
        """ Return True if the view exists in the window layout.

        Note that this returns True even if the view is hidden.

        """

    def hide_editor_area(self):
        """ Hide the editor area.

        """

    def hide_view(self, view):
        """ Hide a view.

        Returns the view (just for convenience).

        """

    def refresh(self):
        """ Refresh the window layout to reflect any changes.

        """

    def reset_editors(self):
        """ Activate the first editor in every group.

        """

    def reset_views(self):
        """ Activate the first view in every region.

        """

    def show_editor_area(self):
        """ Show the editor area.

        """

    def show_view(self, view):
        """ Show a view.

        """

    #### Methods for saving and restoring the layout ##########################

    def get_view_memento(self):
        """ Returns the state of the views.

        """

    def set_view_memento(self, memento):
        """ Restores the state of the views.

        """

    def get_editor_memento(self):
        """ Returns the state of the editors.

        """

    def set_editor_memento(self, memento):
        """ Restores the state of the editors.
Ejemplo n.º 20
0
class MWorkbenchWindowLayout(HasTraits):
    """ Mixin containing common code for toolkit-specific implementations. """

    implements(IWorkbenchWindowLayout)

    #### 'IWorkbenchWindowLayout' interface ###################################

    # The Id of the editor area.
    # FIXME v3: This is toolkit specific.
    editor_area_id = Str

    # The workbench window that this is the layout for.
    window = Instance('enthought.pyface.workbench.api.WorkbenchWindow')

    #### Events ####

    # Fired when an editor is about to be opened (or restored).
    editor_opening = Event(IEditor)

    # Fired when an editor has been opened (or restored).
    editor_opened = Event(IEditor)

    # Fired when an editor is about to be closed.
    editor_closing = Event(IEditor)

    # Fired when an editor has been closed.
    editor_closed = Event(IEditor)

    # Fired when a view is about to be opened (or restored).
    view_opening = Event(IView)

    # Fired when a view has been opened (or restored).
    view_opened = Event(IView)

    # Fired when a view is about to be closed (*not* hidden!).
    view_closing = Event(IView)

    # Fired when a view has been closed (*not* hidden!).
    view_closed = Event(IView)

    ###########################################################################
    # 'IWorkbenchWindowLayout' interface.
    ###########################################################################

    def activate_editor(self, editor):
        """ Activate an editor. """

        raise NotImplementedError

    def activate_view(self, view):
        """ Activate a view. """

        raise NotImplementedError

    def add_editor(self, editor, title):
        """ Add an editor. """

        raise NotImplementedError

    def add_view(self, view, position=None, relative_to=None, size=(-1, -1)):
        """ Add a view. """

        raise NotImplementedError

    def close_editor(self, editor):
        """ Close an editor. """

        raise NotImplementedError

    def close_view(self, view):
        """ Close a view. """

        raise NotImplementedError

    def close(self):
        """ Close the entire window layout. """

        raise NotImplementedError

    def create_initial_layout(self, parent):
        """ Create the initial window layout. """

        raise NotImplementedError

    def contains_view(self, view):
        """ Return True if the view exists in the window layout. """

        raise NotImplementedError

    def hide_editor_area(self):
        """ Hide the editor area. """

        raise NotImplementedError

    def hide_view(self, view):
        """ Hide a view. """

        raise NotImplementedError

    def refresh(self):
        """ Refresh the window layout to reflect any changes. """

        raise NotImplementedError

    def reset_editors(self):
        """ Activate the first editor in every group. """

        raise NotImplementedError

    def reset_views(self):
        """ Activate the first view in every region. """

        raise NotImplementedError

    def show_editor_area(self):
        """ Show the editor area. """

        raise NotImplementedError

    def show_view(self, view):
        """ Show a view. """

        raise NotImplementedError

    #### Methods for saving and restoring the layout ##########################

    def get_view_memento(self):
        """ Returns the state of the views. """

        raise NotImplementedError

    def set_view_memento(self, memento):
        """ Restores the state of the views. """

        raise NotImplementedError

    def get_editor_memento(self):
        """ Returns the state of the editors. """

        raise NotImplementedError

    def set_editor_memento(self, memento):
        """ Restores the state of the editors. """

        raise NotImplementedError

    ###########################################################################
    # Protected 'MWorkbenchWindowLayout' interface.
    ###########################################################################

    def _get_editor_references(self):
        """ Returns a reference to every editor. """

        editor_manager = self.window.editor_manager

        editor_references = {}
        for editor in self.window.editors:
            # Create the editor reference.
            #
            # If the editor manager returns 'None' instead of a resource
            # reference then this editor will not appear the next time the
            # workbench starts up. This is useful for things like text files
            # that have an editor but have NEVER been saved.
            editor_reference = editor_manager.get_editor_memento(editor)
            if editor_reference is not None:
                editor_references[editor.id] = editor_reference

        return editor_references
Ejemplo n.º 21
0
class KeyBindings(HasStrictTraits):
    """ A set of key bindings.
    """
    #---------------------------------------------------------------------------
    #  Trait definitions:
    #---------------------------------------------------------------------------

    # Set of defined key bindings (added dynamically)
    #bindings = List( KeyBinding )

    # Optional prefix to add to each method name
    prefix = Str

    # Optional suffix to add to each method name
    suffix = Str

    # Event fired when one of the contained KeyBinding objects is changed
    binding_modified = Event(KeyBinding)

    # Control that currently has the focus (if any)
    focus_owner = Any

    #---------------------------------------------------------------------------
    #  Traits view definitions:
    #---------------------------------------------------------------------------

    traits_view = View([
        Item('bindings@#', editor=ListEditor(style='custom')),
        '|{Click on a first or second column entry, then '
        'press the key to assign to the corresponding '
        'function}<>'
    ],
                       title='Update Key Bindings',
                       kind='livemodal',
                       resizable=True,
                       width=0.4,
                       height=0.4,
                       help=False)

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

    def __init__(self, *bindings, **traits):
        super(KeyBindings, self).__init__(**traits)
        n = len(bindings)
        self.add_trait('bindings',
                       List(KeyBinding, minlen=n, maxlen=n, mode='list'))
        self.bindings = [binding.set(owner=self) for binding in bindings]

    #---------------------------------------------------------------------------
    #  Processes a keyboard event:
    #---------------------------------------------------------------------------

    def do(self, event, controller, *args):
        """ Processes a keyboard event.
        """
        key_name = toolkit().key_event_to_name(event)
        for binding in self.bindings:
            if (key_name == binding.binding1) or (key_name
                                                  == binding.binding2):
                method_name = '%s%s%s' % (self.prefix, binding.method_name,
                                          self.suffix)
                return (getattr(controller, method_name)(*args) != False)
        return False

    #---------------------------------------------------------------------------
    #  Merges another set of key bindings into this set:
    #---------------------------------------------------------------------------

    def merge(self, key_bindings):
        """ Merges another set of key bindings into this set.
        """
        binding_dic = {}
        for binding in self.bindings:
            binding_dic[binding.method_name] = binding

        for binding in key_bindings.bindings:
            binding2 = binding_dic.get(binding.method_name)
            if binding2 is not None:
                binding2.binding1 = binding.binding1
                binding2.binding2 = binding.binding2

    #---------------------------------------------------------------------------
    #  Returns the current binding for a specified key (if any):
    #---------------------------------------------------------------------------

    def key_binding_for(self, binding, key_name):
        """ Returns the current binding for a specified key (if any).
        """
        if key_name != '':
            for a_binding in self.bindings:
                if ((a_binding is not binding)
                        and ((key_name == a_binding.binding1) or
                             (key_name == a_binding.binding2))):
                    return a_binding
        return None

    #---------------------------------------------------------------------------
    #  Handles a binding being changed:
    #---------------------------------------------------------------------------

    def _binding_modified_changed(self, binding):
        binding1 = binding.binding1
        binding2 = binding.binding2
        for a_binding in self.bindings:
            if binding is not a_binding:
                if binding1 == a_binding.binding1:
                    a_binding.binding1 = ''
                if binding1 == a_binding.binding2:
                    a_binding.binding2 = ''
                if binding2 == a_binding.binding1:
                    a_binding.binding1 = ''
                if binding2 == a_binding.binding2:
                    a_binding.binding2 = ''

    #---------------------------------------------------------------------------
    #  Handles the focus owner being changed:
    #---------------------------------------------------------------------------

    def _focus_owner_changed(self, old, new):
        if old is not None:
            old.border_size = 0

#-- object overrides -----------------------------------------------------------

#---------------------------------------------------------------------------
#  Restores the state of a previously pickled object:
#---------------------------------------------------------------------------

    def __setstate__(self, state):
        """ Restores the state of a previously pickled object.
        """
        n = len(state['bindings'])
        self.add_trait('bindings', List(KeyBinding, minlen=n, maxlen=n))
        self.__dict__.update(state)
        self.bindings = self.bindings[:]
Ejemplo n.º 22
0
class TraitsTest(HasTraits):

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

    enabled = true
    integer_text = Int(1)
    enumeration = Trait('one', 'two', 'three', 'four', 'five', 'six', cols=3)
    float_range = Range(0.0, 10.0, 10.0)
    int_range = Range(1, 6)
    int_range2 = Range(1, 50)
    compound = Trait(1, Range(1, 6), 'one', 'two', 'three', 'four', 'five',
                     'six')
    boolean = true
    instance = Trait(Instance())
    color = Color('cyan')
    font = Font()
    check_list = List(
        editor=CheckListEditor(values=['one', 'two', 'three', 'four'], cols=4))
    list = List(Str,
                ['East of Eden', 'The Grapes of Wrath', 'Of Mice and Men'])
    button = Event(0, editor=ButtonEditor(label='Click'))
    file = File()
    directory = Directory()
    image_enum = Trait(editor=ImageEnumEditor(values=origin_values,
                                              suffix='_origin',
                                              cols=4,
                                              klass=Instance),
                       *origin_values)

    #---------------------------------------------------------------------------
    #  View definitions:
    #---------------------------------------------------------------------------

    view = View(
        ('|{Enum}', ('enabled', ),
         ('|<[Enumeration]', 'f1:enumeration[Simple]', '_',
          'f2:enumeration[Custom]@', '_', 'f3:enumeration[Text]*', '_',
          'f4:enumeration[Readonly]~'),
         ('|<[Check List]', 'f5:check_list[Simple]', '_',
          'f6:check_list[Custom]@', '_', 'f7:check_list[Text]*', '_',
          'f8:check_list[Readonly]~')),
        ('|{Range}',
         ('|<[Float Range]', 'f9:float_range[Simple]', '_',
          'f10:float_range[Custom]@', '_', 'f11:float_range[Text]*', '_',
          'f12:float_range[Readonly]~'),
         ('|<[Int Range]', 'f13:int_range[Simple]', '_',
          'f14:int_range[Custom]@', '_', 'f15:int_range[Text]*', '_',
          'f16:int_range[Readonly]~'),
         ('|<[Int Range 2]', 'f17:int_range2[Simple]', '_',
          'f18:int_range2[Custom]@', '_', 'f19:int_range2[Text]*', '_',
          'f20:int_range2[Readonly]~')),
        ('|{Misc}',
         ('|<[Integer Text]', 'f21:integer_text[Simple]', '_',
          'f22:integer_text[Custom]@', '_', 'f23:integer_text[Text]*', '_',
          'f24:integer_text[Readonly]~'),
         ('|<[Compound]', 'f25:compound[Simple]', '_', 'f26:compound[Custom]@',
          '_', 'f27:compound[Text]*', '_', 'f28:compound[Readonly]~'),
         ('|<[Boolean]', 'f29:boolean[Simple]', '_', 'f30:boolean[Custom]@',
          '_', 'f31:boolean[Text]*', '_', 'f32:boolean[Readonly]~')),
        ('|{Color/Font}',
         ('|<[Color]', 'f33:color[Simple]', '_', 'f34:color[Custom]@', '_',
          'f35:color[Text]*', '_', 'f36:color[Readonly]~'),
         ('|<[Font]', 'f37:font[Simple]', '_', 'f38:font[Custom]@', '_',
          'f39:font[Text]*', '_', 'f40:font[Readonly]~')),
        ('|{List}', ('|<[List]', 'f41:list[Simple]', '_', 'f42:list[Custom]@',
                     '_', 'f43:list[Text]*', '_', 'f44:list[Readonly]~')),
        (
            '|{Button}',
            ('|<[Button]', 'f45:button[Simple]', '_', 'f46:button[Custom]@'),
            #                                        'button[Text]*',
            #                                        'button[Readonly]~' ),
            ('|<[Image Enum]', 'f47:image_enum[Simple]', '_',
             'f48:image_enum[Custom]@', '_', 'f49:image_enum[Text]*', '_',
             'f50:image_enum[Readonly]~'),
            ('|<[Instance]', 'f51:instance[Simple]', '_',
             'f52:instance[Custom]@', '_', 'f53:instance[Text]*', '_',
             'f54:instance[Readonly]~'),
        ),
        ('|{File}', (
            '|<[File]',
            'f55:file[Simple]',
            '_',
            'f56:file[Custom]@',
            '_',
            'f57:file[Text]*',
            '_',
            'f58:file[Readonly]~',
        ), ('|<[Directory]', 'f59:directory[Simple]', '_',
            'f60:directory[Custom]@', '_', 'f61:directory[Text]*', '_',
            'f62:directory[Readonly]~')),
        apply=True,
        revert=True,
        undo=True,
        ok=True,
        handler=TraitsTestHandler())
Ejemplo n.º 23
0
class Rectangle(HasTraits):
    """Rectangle class with rectangle center position in float(x,y) 
    and rectangle widh and height specified as int.
    
    >>> r = Rectangle()
    
    You can set coordinates from a given set of points (one or two)    
    
    >>> r.set_from_points((5.,6.))
    >>> r.center == (5.0, 6.0)
    True
    >>> r.set_from_points((1.,2.4),(4.,5.2))
    >>> r.width == 3 and r.height == 2 #height and width of the rectangle
    True
    >>> r.center == (2.5, 3.8)
    True
    >>> r.top_left == (2,3) and r.bottom_right == (4,4) #index coordinates of the corner
    True
    >>> r.bottom_left == (2,4) and  r.top_right == (4,3)
    True
    
    If width and height is 1 than all four corners have the same value    
    
    >>> r.width = 1
    >>> r.height = 1
    >>> r.top_left == (3,4) and r.bottom_right == (3,4)
    True
    
    Consider this, rounding is performed when center does not fit to index coordinates    
    
    >>> r.width = 3
    >>> r.height = 3
    >>> r.center = (1,1.)
    >>> r.top_left == (0,0) 
    True
    >>> r.center = (0.5,1.5)
    >>> r.top_left == (0,1) 
    True
    
    """
    #: rectangle center position
    center = Tuple((0., 0.))
    #: rectangle width must be an int
    width = Int(1)
    #: rectangle height must be an int
    height = Int(1)
    #: (width, height) tuple
    size = Property(Tuple(Int, Int), depends_on='width,height')

    #: top left coordinate tuple property
    top_left = Property(Tuple(Int, Int), depends_on='center,width,height')
    #: bottom right coordinate tuple property
    bottom_right = Property(Tuple(Int, Int),
                            depends_on='top_left,width,height')
    #: top right  coordinate tuple property
    top_right = Property(Tuple(Int, Int), depends_on='top_left,width')
    #: bottom left  coordinate tuple property
    bottom_left = Property(Tuple(Int, Int), depends_on='top_left,height')

    #: whenever x,y,width or height are changed this event is called
    updated = Event()

    _update = Bool(True)

    @on_trait_change('center,width,height')
    def update(self):
        """
        Notify update of the Rectangle
        """
        if self._update:
            self.updated = True

    def _get_top_left(self):
        return int(1. + self.center[0] + self.width / 2.) - self.width, int(
            1. + self.center[1] + self.height / 2.) - self.height

    def _set_top_left(self, coordinate):
        self.center = tuple()

    def _get_bottom_right(self):
        return tuple(
            map(lambda x, y: x + y, self.top_left,
                (self.width - 1, self.height - 1)))

    def _get_bottom_left(self):
        return tuple(
            map(lambda x, y: x + y, self.top_left, (0, self.height - 1)))

    def _get_top_right(self):
        return tuple(
            map(lambda x, y: x + y, self.top_left, (self.width - 1, 0)))

    def _get_size(self):
        return self.width, self.height

    def set_from_points(self, *points):
        """
        Sets x,y and possibly width and height given by points.
        One or two points must be given. If one is given, it is a center position.
        if two are given, calculates center and shape
        """
        if len(points) == 1:
            self._update = False
            self.center = tuple(points[0])
            self._update = True
            self.update()
        elif len(points) == 2:
            self._update = False
            top_left = list(map(lambda x, y: min(x, y), *points))
            bottom_right = list(map(lambda x, y: max(x, y), *points))
            self.center = tuple(
                map(lambda x, y: (y + x) / 2., top_left, bottom_right))
            self.width, self.height = list(
                map(lambda x, y: int(y - x), top_left, bottom_right))
            self._update = True
            self.update()

    def set_from_corners(self, *corners):
        warnings.warn('Use set_from_points instead', DeprecationWarning)
        self.set_from_points(*corners)

    def slice_image(self, image):
        size = image.shape[0:2]
        xmin, ymin = list(map(lambda x, y: max(x, y), self.top_left, (0, 0)))
        xmax, ymax = list(
            map(lambda x, y: 1 + min(x, y - 1), self.bottom_right,
                (size[1], size[0])))
        im = image[ymin:ymax, xmin:xmax]
        return im

    def slice_indices(self, indices):
        size = indices.shape[-2:]
        xmin, ymin = list(map(lambda x, y: max(x, y), self.top_left, (0, 0)))
        xmax, ymax = list(
            map(lambda x, y: 1 + min(x, y - 1), self.bottom_right,
                (size[1], size[0])))
        indices = indices[:, ymin:ymax, xmin:xmax]
        return indices

    view = View(
        'center',
        'width',
        'height',
        Item('top_left', style='readonly'),
    )
Ejemplo n.º 24
0
class RespFuncView(ModelView):
    def __init__(self, **kw):
        super(RespFuncView, self).__init__(**kw)
        self._redraw()
        self.on_trait_change(self._redraw, 'model.values')

    model = Instance(RespFunc)

    def _model_default(self):
        return RespFunc()

    figure = Instance(Figure)

    def _figure_default(self):
        figure = Figure(facecolor='white')
        figure.add_axes([0.08, 0.13, 0.85, 0.74])
        return figure

    data_changed = Event(True)

    count = Int

    def _redraw(self):
        self.count += 1
        print('redraw', self.count)

        # data post-processing
        #
        xdata, ydata = self.model.values
        if self.model.approach.plot.yvalues == 'forces':
            ylabel = 'pull-out force [N]'
        else:
            ydata = ydata / self.model.approach.Af
            ylabel = 'pull-out stress [N/m2]'

        title = self.model.boundary.type.BC
        if title[0] == 'd':
            xlabel = 'crack opening [m]'
        else:
            xlabel = 'displacement [m]'
        figure = self.figure
        axes = figure.axes[0]
        axes.clear()

        axes.plot(xdata, ydata, color='blue', linewidth=2, linestyle='solid')
        axes.set_xlabel(xlabel, weight='semibold')
        axes.set_ylabel(ylabel, weight='semibold')
        axes.set_title( title, \
                        size='large', color='black', \
                        weight='bold', position=( .5, 1.03 ) )
        axes.set_axis_bgcolor(color='white')
        axes.ticklabel_format(scilimits=(-3., 4.))
        axes.grid(color='gray', linestyle='--', linewidth=0.1, alpha=0.4)

        self.data_changed = True

    plot_button = Button('Plot pull-out')

    def _plot_button_fired(self):
        self._redraw()

    traits_view = View(
        HSplit(
            VGroup(
                Item('@model.boundary', show_label=False),
                Item('@model.approach', show_label=False),
                id='rf.model',
                dock='tab',
                label='pull-out model',
            ),
            VGroup(
                Item('figure',
                     editor=MPLFigureEditor(),
                     resizable=True,
                     show_label=False),
                #Item('plot_button', show_label = False),
                label='plot sheet',
                id='rf.figure_window',
                dock='tab',
            ),
            id='rf.viewmodel.hsplit',
        ),
        title='Response Function',
        id='rf.viewmodel',
        dock='tab',
        kind='live',
        resizable=True,
        height=0.8,
        width=0.8,
        buttons=[OKButton])

    def open(self, uiinfo):
        file_name = open_file(filter=['*.pst'],
                              extensions=[FileInfo(), TextInfo()])
        if file_name == '':
            return

        file = open(file_name, 'r')
        self.model = pickle.load(file)
        file.close()

    def save(self, uiinfo):
        file_name = save_file(filter=['*.pst'],
                              extensions=[FileInfo(), TextInfo()])
        if file_name == '':
            return

        print('writing into', file_name)
        file = open(file_name, 'w')
        pickle.dump(self.model, file)
        file.close()
Ejemplo n.º 25
0
class Engine(HasStrictTraits):
    """ The Mayavi engine base class.
    """

    # The version of this class.  Used for persistence.
    __version__ = 0

    # The scenes associated with this project.
    scenes = List(Scene, record=True)

    # The list to provide to a TreeEditor.  Always add on a AdderNode.
    # TODO: It makes more sense to put the modification of the list
    # in some other UI module, and not here.
    children_ui_list = Property(record=False)

    # Our name.
    name = Str('Mayavi Engine')

    # Current scene.
    current_scene = Property(Instance(Scene), record=False)

    # Current object.
    current_object = Property(record=False)

    # Current selection -- the currently selected object on the tree.
    current_selection = Property(record=False)

    # Has the Engine started?  Use this event to do something after
    # the engine has been started.
    started = Event(record=False)

    # An optional callable that will generate a usable new viewer
    # containing a `enthought.tvtk.pyface.TVTKScene` instance. Ideally
    # the viewer should have an interface like
    # `enthought.tvtk.pyface.TVTKWindow` -- basically it must
    # implement the `closing` and `activated` events, however, this is
    # not necessary.  The created viewer is used by the `new_scene`
    # method to create a new Viewer.  This is a mechanism to use a
    # user specified scene with the Engine and have the ability to
    # load saved visualizations using the new scene.  Handy for things
    # like off-screen rendering.
    scene_factory = Callable(viewer_factory)

    # Are we running?
    running = Bool(False, record=False)

    # The recorder for script recording.
    recorder = Instance(Recorder, record=False)

    ########################################
    # Private traits.

    _current_scene = Instance(Scene)
    _current_object = Instance(HasTraits)
    _current_selection = Instance(HasTraits)
    _viewer_ref = Dict

    # View related traits.
    current_selection_view = View(Item(
        name='_current_selection',
        enabled_when='_current_selection is not None',
        style='custom',
        springy=True,
        show_label=False,
    ),
                                  resizable=True,
                                  scrollable=True)

    ######################################################################
    # `object` interface
    ######################################################################
    def __init__(self, **traits):
        super(Engine, self).__init__(**traits)

        # FIXME: This is tied to preferences.  It really should not be
        # we need to use bind_preferences here.
        cbk = lambda: self.trait_property_changed('children_ui_list', [], self.
                                                  children_ui_list)
        preference_manager.root.on_trait_change(cbk, 'show_helper_nodes')

    def __get_pure_state__(self):
        d = self.__dict__.copy()
        for x in [
                '_current_scene', '_current_object', '__sync_trait__',
                '_viewer_ref', '__traits_listener__'
        ]:
            d.pop(x, None)
        return d

    def __set_pure_state__(self, state):
        # Current number of scenes.
        n_scene = len(self.scenes)
        # Number of scenes in saved state.
        n_saved_scene = len(state.scenes)
        # Remove extra ones.
        for i in range(n_scene - n_saved_scene):
            self.close_scene(self.scenes[-1])
        # Add new ones.
        for i in range(n_saved_scene - n_scene):
            self.new_scene()
        # Set the state.
        state_pickler.set_state(self, state)

    def __getstate__(self):
        return state_pickler.dumps(self)

    def __setstate__(self, str_state):
        self.__init__()
        state = state_pickler.loads_state(str_state)
        state_pickler.update_state(state)
        self.__set_pure_state__(state)

    ######################################################################
    # `Engine` interface
    ######################################################################
    def start(self):
        """This is called by the plugin when the plugin actually
        starts."""
        registry.register_engine(self)
        # Notify any listeners that the engine is started.
        self.started = self
        self.running = True

    def stop(self):
        registry.unregister_engine(self)
        self.running = False

    @recordable
    def add_source(self, src, scene=None):
        """Adds a source to the pipeline. Uses the current scene unless a
        scene is given in the scene keyword argument."""
        passed_scene = scene
        if scene is not None:
            tvtk_scene = scene.scene
            for sc in self.scenes:
                if sc.scene == tvtk_scene:
                    scene = sc
                    break
            else:
                error('This scene is not managed by mayavi')
                return
        else:
            scene = self.current_scene

        # Create a new scene if none is available.
        if scene is None:
            self.new_scene()
            scene = self.current_scene
        scene.add_child(src)
        self.current_object = src

    @recordable
    def add_filter(self, fil, obj=None):
        """Adds a filter to the pipeline at an appropriate point. Adds it 
        to the selected object, or to an object passed as the 
        kwarg `obj`.
        """
        passed_obj = obj
        if obj is None:
            obj = self.current_object
        if not isinstance(obj, Base):
            msg = 'No valid current object, '\
                  'please select an active object.'
            error(msg)
            return
        if (obj is not None) and (not isinstance(obj, Scene)):
            if obj.running:
                obj.add_child(fil)
                self.current_object = fil
            else:
                msg = 'Current object is not active, '\
                      'please select an active object.'
                error(msg)
        else:
            if obj is None:
                error('Please create a VTK scene and open some data first.')
            else:
                error('No data: cannot use a Filter/Module/ModuleManager.')

    @recordable
    def add_module(self, mod, obj=None):
        """Adds a module to the pipeline at an appropriate point. Adds it 
        to the selected object, or to an object passed through the 
        kwarg `obj`.
        """
        self.add_filter(mod, obj=obj)

    @recordable
    def save_visualization(self, file_or_fname):
        """Given a file or a file name, this saves the current
        visualization to the file.
        """
        # Save the state of VTK's global warning display.
        o = vtk.vtkObject
        w = o.GetGlobalWarningDisplay()
        o.SetGlobalWarningDisplay(0)  # Turn it off.
        try:
            state_pickler.dump(self, file_or_fname)
        finally:
            # Reset the warning state.
            o.SetGlobalWarningDisplay(w)

    @recordable
    def load_visualization(self, file_or_fname):
        """Given a file/file name this loads the visualization."""
        # Save the state of VTK's global warning display.
        o = vtk.vtkObject
        w = o.GetGlobalWarningDisplay()
        o.SetGlobalWarningDisplay(0)  # Turn it off.
        try:
            # Get the state from the file.
            state = state_pickler.load_state(file_or_fname)
            state_pickler.update_state(state)
            # Add the new scenes.
            for scene_state in state.scenes:
                self.new_scene()
                scene = self.scenes[-1]
                # Disable rendering initially.
                if scene.scene is not None:
                    scene.scene.disable_render = True
                # Update the state.
                state_pickler.update_state(scene_state)
                scene.__set_pure_state__(scene_state)
                # Setting the state will automatically reset the
                # disable_render.
                scene.render()
        finally:
            # Reset the warning state.
            o.SetGlobalWarningDisplay(w)

    @recordable
    def open(self, filename, scene=None):
        """Open a file given a filename if possible in either the
        current scene or the passed `scene`.
        """
        passed_scene = scene
        reader = registry.get_file_reader(filename)
        if reader is None:
            msg = 'No suitable reader found for the file %s' % filename
            error(msg)
        else:
            src = None
            if scene is None:
                scene = self.current_scene
            if scene is None:
                scene = self.new_scene()
            try:
                sc = scene.scene
                if sc is not None:
                    sc.busy = True
                callable = reader.get_callable()
                if reader.factory is None:
                    src = callable()
                    src.initialize(filename)
                else:
                    # Factory functions are passed the filename and a
                    # reference to the engine.
                    src = callable(filename, self)
                if src is not None:
                    self.add_source(src, passed_scene)
            finally:
                if sc is not None:
                    sc.busy = False
            if src is not None:
                return src

    def record(self, msg):
        """This is merely a convenience method to record messages to the
        script recorder.
        """
        r = self.recorder
        if r is not None:
            r.record(msg)

    ######################################################################
    # Scene creation/deletion related methods.
    ######################################################################
    def add_scene(self, scene, name=None):
        """Add given `scene` (a `pyface.tvtk.scene.Scene` instance) to
        the mayavi engine so that mayavi can manage the scene.  This
        is used when the user creates a scene.  Note that for the
        `EnvisageEngine` this is automatically taken care of when you
        create a new scene using the TVTK scene plugin.

        Parameters:
        -----------

         scene - `pyface.tvtk.scene.Scene`

          The scene that needs to be managed from mayavi.

         name - `str` 
          The name assigned to the scene.  It tries to determine the
          name of the scene from the passed scene instance.  If this
          is not possible it defaults to 'Mayavi Scene'.
          
        """
        if name is None:
            if hasattr(scene, 'name'):
                name = scene.name
            else:
                name = 'Mayavi Scene %d' % scene_id_generator.next()

        s = Scene(scene=scene, name=name, parent=self)
        s.start()
        # We don't want the startup setup to be recorded.
        recorder = self.recorder
        self.scenes.append(s)
        self.current_scene = s
        if recorder is not None:
            recorder.register(s)

    @recordable
    def remove_scene(self, scene, **kwargs):
        """Remove a given `scene` (a `pyface.tvtk.scene.Scene`
        instance) from the mayavi engine if it is already being
        managed by mayavi.  Note that for the `EnvisageEngine` this is
        automatically taken care of when you close a scene started
        using the TVTK scene plugin.

        Parameters:
        -----------

         scene - `pyface.tvtk.scene.Scene`

          The scene that needs to be removed from mayavi.
        """
        s = None
        for index, x in enumerate(self.scenes):
            if x.scene is scene:
                s = x
                break
        if s is not None:
            s.stop()
            self.scenes.remove(s)
            # Don't record it shutting down.  To do this we must
            # unregister it here so we don't record unnecessary calls.
            recorder = self.recorder
            if recorder is not None:
                recorder.unregister(s)

        # Remove the reference to the viewer if any.
        if scene in self._viewer_ref:
            del self._viewer_ref[scene]

        # Clear the current scene if it has been removed.
        if scene is self._current_scene:
            self._current_scene = None

    @recordable
    def new_scene(self, viewer=None, name=None, **kwargs):
        """Create or manage a new VTK scene window.  If no `viewer`
        argument is provided, the method creates a new viewer using
        `self.scene_factory`.  If `self.scene_factory` is `None` then
        it creates an `ivtk` viewer.  This code requires that the
        `viewer` has a `scene` attribute/trait that is a
        `pyface.tvtk.scene.Scene`.  It also works best if the viewer
        supports `closing` and `activated` events.

        The method returns the created viewer.
         
        Parameters:
        -----------

         viewer - The viewer object, if None, one is created for you.

         name - The name attribute of the viewer

         ``**kwargs`` - The extra keyword arguments are passed along to
         the scene factory.

        """
        if viewer is None:
            factory_kwargs = {}
            factory_kwargs_names = get_args(self.scene_factory)
            for arg, value in kwargs.iteritems():
                if arg in factory_kwargs_names:
                    factory_kwargs[arg] = value

            viewer = self.scene_factory(**factory_kwargs)
            process_ui_events()

        if name is not None:
            viewer.name = name
        # Hang on to a reference to this viewer, if not done this will cause a
        # crash with Qt4.  This because the viewer will be closed and gc'd if
        # there isn't a reference to it.  When the viewer is gc'd the scene is
        # also closed and the engine will have a dead scene causing a crash.
        self._viewer_ref[viewer.scene] = viewer

        self.add_scene(viewer.scene)
        if hasattr(viewer, 'on_trait_change'):
            viewer.on_trait_change(self._on_scene_closed, 'closing')
            viewer.on_trait_change(self._on_scene_activated, 'activated')
            if hasattr(viewer, 'title'):
                self.current_scene.sync_trait('name', viewer, 'title')
        return viewer

    @recordable
    def close_scene(self, scene):
        """Given a scene created from new_scene, this method closes it
        and removes the scene from the list of scenes we manage.

        Parameters:
        -----------

         scene - `pyface.tvtk.scene.Scene` or an object that holds a
         reference to a `pyface.tvtk.scene.Scene` in a `scene`
         attribute.
        """
        viewer = self.get_viewer(scene)
        self.remove_scene(scene.scene)
        if hasattr(scene, 'close'):
            scene.close()
        elif scene.scene is not None:
            scene.scene.close()
        if viewer is not None and hasattr(viewer, 'close'):
            viewer.close()

    def get_viewer(self, scene):
        """Return the viewer associated with a given scene.

        Parameters:
        -----------
         scene - An `enthought.mayavi.core.scene.Scene` instance.
        """
        return self._viewer_ref.get(scene.scene)

    def dialog_view(self):
        """ Default dialog view for Engine objects.
        """
        return None

    ######################################################################
    # Non-public interface
    ######################################################################
    def _on_select(self, object):
        """Called by the EngineTree when an object on the view is
        selected.  This basically sets the current object and current
        scene."""
        self.current_selection = object
        self._current_object = object
        try:
            scene = object.scene
            for s in self.scenes:
                if s.scene == scene:
                    self._current_scene = s
                    break
        except AttributeError:
            pass

    def _get_current_scene(self):
        n_scene = len(self.scenes)
        if n_scene == 0:
            return None
        elif n_scene == 1:
            return self.scenes[0]
        elif self._current_scene is not None:
            return self._current_scene
        elif n_scene > 1:
            return self.scenes[-1]
        else:
            return None

    def _set_current_scene(self, scene):
        old = self._current_scene
        self._current_scene = scene
        self.trait_property_changed('current_scene', old, scene)

    def _get_current_object(self):
        if self._current_object is not None:
            return self._current_object
        elif self.current_scene is not None:
            return self.current_scene
        else:
            return None

    def _set_current_object(self, object):
        old = self._current_object
        self._current_object = object
        self.trait_property_changed('current_object', old, object)

    def _get_current_selection(self):
        return self._current_selection

    def _set_current_selection(self, object):
        old = self._current_selection
        if not isinstance(object, (Base, AdderNode)):
            object = None
        self._current_selection = object
        self.trait_property_changed('current_selection', old, object)

    def _on_scene_closed(self, obj, name, old, new):
        self.remove_scene(obj.scene)

    def _on_scene_activated(self, obj, name, old, new):
        for scene in self.scenes:
            if scene.scene is obj.scene:
                self.current_scene = scene
                break

    def _get_children_ui_list(self):
        """ Trait getter for children_ui_list Property.
        """
        if preference_manager.root.show_helper_nodes \
                    and len(self.scenes) == 0:
            return [SceneAdderNode(object=self)]
        else:
            return self.scenes

    @on_trait_change('scenes[]')
    def _trigger_children_ui_list(self, old, new):
        """ Trigger a children_ui_list change when scenes changed.
        """
        self.trait_property_changed('children_ui_list', old, new)

    def _recorder_changed(self, old, new):
        if new is not None:
            new.record('# Recorded script from Mayavi2')
            new.record('from numpy import array')
            new.record('try:')
            new.record('    engine = mayavi.engine')
            new.record('except NameError:')
            new.record('    from enthought.mayavi.api import Engine')
            new.record('    engine = Engine()')
            new.record('    engine.start()')
            new.record('if len(engine.scenes) == 0:')
            new.record('    engine.new_scene()')
            new.record('# ------------------------------------------- ')
        elif old is not None:
            old.record('# ------------------------------------------- ')
            old.record('from enthought.mayavi.tools.show import show')
            old.record('show()')
Ejemplo n.º 26
0
class UndoHistory(HasStrictTraits):
    """ Manages a list of undoable changes.
    """
    #---------------------------------------------------------------------------
    #  Trait definitions:
    #---------------------------------------------------------------------------

    # List of accumulated undo changes
    history = List
    # The current position in the list
    now = Int
    # Fired when state changes to undoable
    undoable = Event(False)
    # Fired when state changes to redoable
    redoable = Event(False)
    # Can an action be undone?
    can_undo = Property
    # Can an action be redone?
    can_redo = Property

    #---------------------------------------------------------------------------
    #  Adds an UndoItem to the history:
    #---------------------------------------------------------------------------

    def add(self, undo_item, extend=False):
        """ Adds an UndoItem to the history.
        """
        if extend:
            self.extend(undo_item)
            return

        # Try to merge the new undo item with the previous item if allowed:
        now = self.now
        if now > 0:
            previous = self.history[now - 1]
            if (len(previous) == 1) and previous[0].merge_undo(undo_item):
                self.history[now:] = []
                return

        old_len = len(self.history)
        self.history[now:] = [[undo_item]]
        self.now += 1
        if self.now == 1:
            self.undoable = True
        if self.now <= old_len:
            self.redoable = False

    #---------------------------------------------------------------------------
    #  Extends the most recent 'undo' item:
    #---------------------------------------------------------------------------

    def extend(self, undo_item):
        """ Extends the undo history. 
        
        If possible the method merges the new UndoItem with the last item in 
        the history; otherwise, it appends the new item.
        """
        if self.now > 0:
            undo_list = self.history[self.now - 1]
            if not undo_list[-1].merge_undo(undo_item):
                undo_list.append(undo_item)

    #---------------------------------------------------------------------------
    #  Undo an operation:
    #---------------------------------------------------------------------------

    def undo(self):
        """ Undoes an operation.
        """
        if self.can_undo:
            self.now -= 1
            items = self.history[self.now]
            for i in range(len(items) - 1, -1, -1):
                items[i].undo()
            if self.now == 0:
                self.undoable = False
            if self.now == (len(self.history) - 1):
                self.redoable = True

    #---------------------------------------------------------------------------
    #  Redo an operation:
    #---------------------------------------------------------------------------

    def redo(self):
        """ Redoes an operation.
        """
        if self.can_redo:
            self.now += 1
            for item in self.history[self.now - 1]:
                item.redo()
            if self.now == 1:
                self.undoable = True
            if self.now == len(self.history):
                self.redoable = False

    #---------------------------------------------------------------------------
    #  Reverts all changes made so far and clears the history:
    #---------------------------------------------------------------------------

    def revert(self):
        """ Reverts all changes made so far and clears the history.
        """
        history = self.history[:self.now]
        self.clear()
        for i in range(len(history) - 1, -1, -1):
            items = history[i]
            for j in range(len(items) - 1, -1, -1):
                items[j].undo()

    #---------------------------------------------------------------------------
    #  Clears the undo history
    #---------------------------------------------------------------------------

    def clear(self):
        """ Clears the undo history.
        """
        old_len = len(self.history)
        old_now = self.now
        self.now = 0
        del self.history[:]
        if old_now > 0:
            self.undoable = False
        if old_now < old_len:
            self.redoable = False

    #---------------------------------------------------------------------------
    #  Are there any undoable operations?
    #---------------------------------------------------------------------------

    def _get_can_undo(self):
        """ Are there any undoable operations?
        """
        return self.now > 0

    #---------------------------------------------------------------------------
    #  Are there any redoable operations?
    #---------------------------------------------------------------------------

    def _get_can_redo(self):
        """ Are there any redoable operations?
        """
        return self.now < len(self.history)
Ejemplo n.º 27
0
class UserManager(HasTraits):
    """The default user manager implementation."""

    implements(IUserManager)

    #### 'IUserManager' interface #############################################

    management_actions = List(Instance(Action))

    user = Instance(IUser)

    user_actions = List(Instance(Action))

    user_authenticated = Event(IUser)

    #### 'UserManager' interface ##############################################

    # The user database.
    user_db = Instance(IUserDatabase)

    ###########################################################################
    # 'IUserManager' interface.
    ###########################################################################

    def bootstrapping(self):
        """Return True if we are bootstrapping, ie. no users have been defined.
        """

        return self.user_db.bootstrapping()

    def authenticate_user(self):
        """Authenticate the user."""

        if self.user_db.authenticate_user(self.user):
            self.user.authenticated = True

            # Tell the policy manager before everybody else.
            get_permissions_manager().policy_manager.load_policy(self.user)

            self.user_authenticated = self.user

    def unauthenticate_user(self):
        """Unauthenticate the user."""

        if self.user.authenticated and self.user_db.unauthenticate_user(
                self.user):
            self.user.authenticated = False

            # Tell the policy manager before everybody else.
            get_permissions_manager().policy_manager.load_policy(None)

            self.user_authenticated = None

    def matching_user(self, name):
        """Select a user."""

        return self.user_db.matching_user(name)

    ###########################################################################
    # Trait handlers.
    ###########################################################################

    def _management_actions_default(self):
        """Return the list of management actions."""

        from enthought.permissions.secure_proxy import SecureProxy

        user_db = self.user_db
        actions = []
        perm = ManageUsersPermission()

        if user_db.can_add_user:
            act = Action(name="&Add a User...", on_perform=user_db.add_user)
            actions.append(SecureProxy(act, permissions=[perm], show=False))

        if user_db.can_modify_user:
            act = Action(name="&Modify a User...",
                         on_perform=user_db.modify_user)
            actions.append(SecureProxy(act, permissions=[perm], show=False))

        if user_db.can_delete_user:
            act = Action(name="&Delete a User...",
                         on_perform=user_db.delete_user)
            actions.append(SecureProxy(act, permissions=[perm], show=False))

        return actions

    def _user_actions_default(self):
        """Return the list of user actions."""

        actions = []

        if self.user_db.can_change_password:
            actions.append(_ChangePasswordAction())

        return actions

    def _user_default(self):
        """Return the default current user."""

        return self.user_db.user_factory()

    def _user_db_default(self):
        """Return the default user database."""

        # Defer to an external user database if there is one.
        try:
            from enthought.permissions.external.user_database import UserDatabase
        except ImportError:
            from enthought.permissions.default.user_database import UserDatabase

        return UserDatabase()
Ejemplo n.º 28
0
class MEditor(MWorkbenchPart):
    """ Mixin containing common code for toolkit-specific implementations. """

    implements(IEditor)
    
    #### 'IEditor' interface ##################################################
    
    # The optional command stack.
    command_stack = Instance('enthought.undo.api.ICommandStack')

    # Is the object that the editor is editing 'dirty' i.e., has it been
    # modified but not saved?
    dirty = Bool(False)

    # The object that the editor is editing.
    #
    # The framework sets this when the editor is created.
    obj = Any

    #### Editor Lifecycle Events ##############################################

    # Fired when the editor is opening.
    opening = VetoableEvent
    
    # Fired when the editor has been opened.
    open = Event
    
    # Fired when the editor is closing.
    closing = Event(VetoableEvent)
    
    # Fired when the editor is closed.
    closed = Event

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

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

        return 'Editor(%s)' % self.id

    ###########################################################################
    # 'IWorkbenchPart' interface.
    ###########################################################################

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

        # If no Id is specified then use a random uuid
        # this gaurantees (barring *really* unusual cases) that there are no
        # collisions between the ids of editors.
        return uuid.uuid4().hex

    ###########################################################################
    # 'IEditor' interface.
    ###########################################################################
    
    def close(self):
        """ Close the editor. """

        if self.control is not None:
            self.closing = event = Vetoable()
            if not event.veto:
                self.window.close_editor(self)
                
                self.closed = True

        return

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

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

        # We make sure the undo package is entirely optional.
        try:
            from enthought.undo.api import CommandStack
        except ImportError:
            return None
            
        return CommandStack(undo_manager=self.window.workbench.undo_manager)
Ejemplo n.º 29
0
class SceneModel(TVTKScene):

    ########################################
    # TVTKScene traits.

    light_manager = Property

    picker = Property

    ########################################
    # SceneModel traits.

    # A convenient dictionary based interface to add/remove actors and widgets.
    # This is similar to the interface provided for the ActorEditor.
    actor_map = Dict()

    # This is used primarily to implement the add_actor/remove_actor methods.
    actor_list = List()

    # The actual scene being edited.
    scene_editor = Instance(TVTKScene)

    do_render = Event()

    # Fired when this is activated.
    activated = Event()

    # Fired when this widget is closed.
    closing = Event()

    # This exists just to mirror the TVTKWindow api.
    scene = Property

    ###################################
    # View related traits.

    # Render_window's view.
    _stereo_view = Group(
        Item(name='stereo_render'),
        Item(name='stereo_type'),
        show_border=True,
        label='Stereo rendering',
    )

    # The default view of this object.
    default_view = View(
        Group(Group(
            Item(name='background'),
            Item(name='foreground'),
            Item(name='parallel_projection'),
            Item(name='disable_render'),
            Item(name='off_screen_rendering'),
            Item(name='jpeg_quality'),
            Item(name='jpeg_progressive'),
            Item(name='magnification'),
            Item(name='anti_aliasing_frames'),
        ),
              Group(
                  Item(name='render_window',
                       style='custom',
                       visible_when='object.stereo',
                       editor=InstanceEditor(view=View(_stereo_view)),
                       show_label=False), ),
              label='Scene'),
        Group(Item(name='light_manager',
                   style='custom',
                   editor=InstanceEditor(),
                   show_label=False),
              label='Lights'))

    ###################################
    # Private traits.

    # Used by the editor to determine if the widget was enabled or not.
    enabled_info = Dict()

    def __init__(self, parent=None, **traits):
        """ Initializes the object. """
        # Base class constructor.  We call TVTKScene's super here on purpose.
        # Calling TVTKScene's init will create a new window which we do not
        # want.
        super(TVTKScene, self).__init__(**traits)
        self.control = None

    ######################################################################
    # TVTKScene API.
    ######################################################################
    def render(self):
        """ Force the scene to be rendered. Nothing is done if the
        `disable_render` trait is set to True."""

        self.do_render = True

    def add_actors(self, actors):
        """ Adds a single actor or a tuple or list of actors to the
        renderer."""
        if hasattr(actors, '__iter__'):
            self.actor_list.extend(actors)
        else:
            self.actor_list.append(actors)

    def remove_actors(self, actors):
        """ Removes a single actor or a tuple or list of actors from
        the renderer."""
        my_actors = self.actor_list
        if hasattr(actors, '__iter__'):
            for actor in actors:
                my_actors.remove(actor)
        else:
            my_actors.remove(actors)

    # Conevenience methods.
    add_actor = add_actors
    remove_actor = remove_actors

    def add_widgets(self, widgets, enabled=True):
        """Adds widgets to the renderer.
        """
        if not hasattr(widgets, '__iter__'):
            widgets = [widgets]
        for widget in widgets:
            self.enabled_info[widget] = enabled
        self.add_actors(widgets)

    def remove_widgets(self, widgets):
        """Removes widgets from the renderer."""
        if not hasattr(widgets, '__iter__'):
            widgets = [widgets]
        self.remove_actors(widgets)
        for widget in widgets:
            del self.enabled_info[widget]

    def reset_zoom(self):
        """Reset the camera so everything in the scene fits."""
        if self.scene_editor is not None:
            self.scene_editor.reset_zoom()

    def save(self, file_name, size=None, **kw_args):
        """Saves rendered scene to one of several image formats
        depending on the specified extension of the filename.

        If an additional size (2-tuple) argument is passed the window
        is resized to the specified size in order to produce a
        suitably sized output image.  Please note that when the window
        is resized, the window may be obscured by other widgets and
        the camera zoom is not reset which is likely to produce an
        image that does not reflect what is seen on screen.

        Any extra keyword arguments are passed along to the respective
        image format's save method.
        """
        self._check_scene_editor()
        self.scene_editor.save(file_name, size, **kw_args)

    def save_ps(self, file_name):
        """Saves the rendered scene to a rasterized PostScript image.
        For vector graphics use the save_gl2ps method."""
        self._check_scene_editor()
        self.scene_editor.save_ps(file_name)

    def save_bmp(self, file_name):
        """Save to a BMP image file."""
        self._check_scene_editor()
        self.scene_editor.save_bmp(file_name)

    def save_tiff(self, file_name):
        """Save to a TIFF image file."""
        self._check_scene_editor()
        self.scene_editor.save_tiff(file_name)

    def save_png(self, file_name):
        """Save to a PNG image file."""
        self._check_scene_editor()
        self.scene_editor.save_png(file_name)

    def save_jpg(self, file_name, quality=None, progressive=None):
        """Arguments: file_name if passed will be used, quality is the
        quality of the JPEG(10-100) are valid, the progressive
        arguments toggles progressive jpegs."""
        self._check_scene_editor()
        self.scene_editor.save_jpg(file_name, quality, progressive)

    def save_iv(self, file_name):
        """Save to an OpenInventor file."""
        self._check_scene_editor()
        self.scene_editor.save_iv(file_name)

    def save_vrml(self, file_name):
        """Save to a VRML file."""
        self._check_scene_editor()
        self.scene_editor.save_vrml(file_name)

    def save_oogl(self, file_name):
        """Saves the scene to a Geomview OOGL file. Requires VTK 4 to
        work."""
        self._check_scene_editor()
        self.scene_editor.save_oogl(file_name)

    def save_rib(self, file_name, bg=0, resolution=None, resfactor=1.0):
        """Save scene to a RenderMan RIB file.

        Keyword Arguments:

        file_name -- File name to save to.

        bg -- Optional background option.  If 0 then no background is
        saved.  If non-None then a background is saved.  If left alone
        (defaults to None) it will result in a pop-up window asking
        for yes/no.

        resolution -- Specify the resolution of the generated image in
        the form of a tuple (nx, ny).

        resfactor -- The resolution factor which scales the resolution.
        """
        self._check_scene_editor()
        self.scene_editor.save_rib(file_name, bg, resolution, resfactor)

    def save_wavefront(self, file_name):
        """Save scene to a Wavefront OBJ file.  Two files are
        generated.  One with a .obj extension and another with a .mtl
        extension which contains the material proerties.

        Keyword Arguments:

        file_name -- File name to save to
        """
        self._check_scene_editor()
        self.scene_editor.save_wavefront(file_name)

    def save_gl2ps(self, file_name, exp=None):
        """Save scene to a vector PostScript/EPS/PDF/TeX file using
        GL2PS.  If you choose to use a TeX file then note that only
        the text output is saved to the file.  You will need to save
        the graphics separately.

        Keyword Arguments:

        file_name -- File name to save to.

        exp -- Optionally configured vtkGL2PSExporter object.
        Defaults to None and this will use the default settings with
        the output file type chosen based on the extention of the file
        name.
        """
        self._check_scene_editor()
        self.scene_editor.save_gl2ps(file_name, exp)

    def get_size(self):
        """Return size of the render window."""
        self._check_scene_editor()
        return self.scene_editor.get_size()

    def set_size(self, size):
        """Set the size of the window."""
        self._check_scene_editor()
        self.scene_editor.set_size(size)

    def _update_view(self, x, y, z, vx, vy, vz):
        """Used internally to set the view."""
        if self.scene_editor is not None:
            self.scene_editor._update_view(x, y, z, vx, vy, vz)

    def _check_scene_editor(self):
        if self.scene_editor is None:
            msg = """
            This method requires that there be an active scene editor.
            To do this, you will typically need to invoke::
              object.edit_traits()
            where object is the object that contains the SceneModel.
            """
            raise SceneModelError(msg)

    def _scene_editor_changed(self, old, new):
        if new is None:
            self._renderer = None
            self._renwin = None
            self._interactor = None
        else:
            self._renderer = new._renderer
            self._renwin = new._renwin
            self._interactor = new._interactor

    def _get_picker(self):
        """Getter for the picker."""
        se = self.scene_editor
        if se is not None and hasattr(se, 'picker'):
            return se.picker
        return None

    def _get_light_manager(self):
        """Getter for the light manager."""
        se = self.scene_editor
        if se is not None:
            return se.light_manager
        return None

    ######################################################################
    # SceneModel API.
    ######################################################################
    def _get_scene(self):
        """Getter for the scene property."""
        return self
Ejemplo n.º 30
0
class Workbench(HasTraits):
    """ A workbench.

    There is exactly *one* workbench per application. The workbench can create
    any number of workbench windows.

    """

    implements(IWorkbench)

    #### 'IWorkbench' interface ###############################################

    # The active workbench window (the last one to get focus).
    active_window = Instance(WorkbenchWindow)

    # The editor manager is used to create/restore editors.
    editor_manager = Instance(IEditorManager)

    # The optional application scripting manager.
    script_manager = Instance('enthought.appscripting.api.IScriptManager')

    # A directory on the local file system that we can read and write to at
    # will. This is used to persist window layout information, etc.
    state_location = Unicode

    # The optional undo manager.
    undo_manager = Instance('enthought.undo.api.IUndoManager')

    # The user-defined perspectives manager.
    user_perspective_manager = Instance(UserPerspectiveManager)

    # All of the workbench windows created by the workbench.
    windows = List(WorkbenchWindow)

    #### Workbench lifecycle events ###########################################

    # Fired when the workbench is about to exit.
    #
    # This can be caused by either:-
    #
    # a) The 'exit' method being called.
    # b) The last open window being closed.
    #
    exiting = VetoableEvent

    # Fired when the workbench has exited.
    exited = Event

    #### Window lifecycle events ##############################################

    # Fired when a workbench window has been created.
    window_created = Event(WindowEvent)

    # Fired when a workbench window is opening.
    window_opening = Event(VetoableWindowEvent)

    # Fired when a workbench window has been opened.
    window_opened = Event(WindowEvent)

    # Fired when a workbench window is closing.
    window_closing = Event(VetoableWindowEvent)

    # Fired when a workbench window has been closed.
    window_closed = Event(WindowEvent)

    #### 'Workbench' interface ################################################

    # The factory that is used to create workbench windows. This is used in
    # the default implementation of 'create_window'. If you override that
    # method then you obviously don't need to set this trait!
    window_factory = Callable

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

    # An 'explicit' exit is when the the 'exit' method is called.
    # An 'implicit' exit is when the user closes the last open window.
    _explicit_exit = Bool(False)

    ###########################################################################
    # 'IWorkbench' interface.
    ###########################################################################

    def create_window(self, **kw):
        """ Factory method that creates a new workbench window. """

        window = self.window_factory(workbench=self, **kw)

        # Add on any user-defined perspectives.
        window.perspectives.extend(self.user_perspective_manager.perspectives)

        # Restore the saved window memento (if there is one).
        self._restore_window_layout(window)

        # Listen for the window being activated/opened/closed etc. Activated in
        # this context means 'gets the focus'.
        #
        # NOTE: 'activated' is not fired on a window when the window first
        # opens and gets focus. It is only fired when the window comes from
        # lower in the stack to be the active window.
        window.on_trait_change(self._on_window_activated, 'activated')
        window.on_trait_change(self._on_window_opening, 'opening')
        window.on_trait_change(self._on_window_opened, 'opened')
        window.on_trait_change(self._on_window_closing, 'closing')
        window.on_trait_change(self._on_window_closed, 'closed')

        # Event notification.
        self.window_created = WindowEvent(window=window)

        return window

    def exit(self):
        """ Exits the workbench.

        This closes all open workbench windows.

        This method is not called when the user clicks the close icon. Nor when
        they do an Alt+F4 in Windows. It is only called when the application
        menu File->Exit item is selected.

        Returns True if the exit succeeded, False if it was vetoed.

        """

        logger.debug('**** exiting the workbench ****')

        # Event notification.
        self.exiting = event = Vetoable()
        if not event.veto:
            # This flag is checked in '_on_window_closing' to see what kind of
            # exit is being performed.
            self._explicit_exit = True

            if len(self.windows) > 0:
                exited = self._close_all_windows()

            # The degenerate case where no workbench windows have ever been
            # created!
            else:
                # Trait notification.
                self.exited = self

                exited = True

            # Whether the exit succeeded or not, we are no longer in the
            # process of exiting!
            self._explicit_exit = False

        else:
            exited = False

        if not exited:
            logger.debug('**** exit of the workbench vetoed ****')

        return exited

    #### Convenience methods on the active window #############################

    def edit(self, obj, kind=None, use_existing=True):
        """ Edit an object in the active workbench window. """

        return self.active_window.edit(obj, kind, use_existing)

    def get_editor(self, obj, kind=None):
        """ Return the editor that is editing an object.

        Returns None if no such editor exists.

        """

        if self.active_window is None:
            return None

        return self.active_window.get_editor(obj, kind)

    def get_editor_by_id(self, id):
        """ Return the editor with the specified Id.

        Returns None if no such editor exists.

        """

        return self.active_window.get_editor_by_id(id)

    #### Message dialogs ####

    def confirm(self, message, title=None, cancel=False, default=NO):
        """ Convenience method to show a confirmation dialog. """

        return self.active_window.confirm(message, title, cancel, default)

    def information(self, message, title='Information'):
        """ Convenience method to show an information message dialog. """

        return self.active_window.information(message, title)

    def warning(self, message, title='Warning'):
        """ Convenience method to show a warning message dialog. """

        return self.active_window.warning(message, title)

    def error(self, message, title='Error'):
        """ Convenience method to show an error message dialog. """

        return self.active_window.error(message, title)

    ###########################################################################
    # 'Workbench' interface.
    ###########################################################################

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

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

        # It would be preferable to base this on GUI.state_location.
        state_location = os.path.join(ETSConfig.application_home, 'pyface',
                                      'workbench', ETSConfig.toolkit)

        if not os.path.exists(state_location):
            os.makedirs(state_location)

        logger.debug('workbench state location is %s', state_location)

        return state_location

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

        # We make sure the undo package is entirely optional.
        try:
            from enthought.undo.api import UndoManager
        except ImportError:
            return None

        return UndoManager()

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

        return UserPerspectiveManager(state_location=self.state_location)

    ###########################################################################
    # Protected 'Workbench' interface.
    ###########################################################################

    def _create_window(self, **kw):
        """ Factory method that creates a new workbench window. """

        raise NotImplementedError

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

    def _close_all_windows(self):
        """ Closes all open windows.

        Returns True if all windows were closed, False if the user changed
        their mind ;^)

        """

        # We take a copy of the windows list because as windows are closed
        # they are removed from it!
        windows = self.windows[:]
        windows.reverse()

        for window in windows:
            # We give the user chance to cancel the exit as each window is
            # closed.
            if not window.close():
                all_closed = False
                break

        else:
            all_closed = True

        return all_closed

    def _restore_window_layout(self, window):
        """ Restore the window layout. """

        filename = os.path.join(self.state_location, 'window_memento')
        if os.path.exists(filename):
            try:
                # If the memento class itself has been modified then there
                # is a chance that the unpickle will fail. If so then we just
                # carry on as if there was no memento!
                f = file(filename, 'r')
                memento = cPickle.load(f)
                f.close()

                # The memento doesn't actually get used until the window is
                # opened, so there is nothing to go wrong in this step!
                window.set_memento(memento)

            # If *anything* goes wrong then simply log the error and carry on
            # with no memento!
            except:
                logger.exception('restoring window layout from %s', filename)

        return

    def _save_window_layout(self, window):
        """ Save the window layout. """

        # Save the window layout.
        f = file(os.path.join(self.state_location, 'window_memento'), 'w')
        cPickle.dump(window.get_memento(), f)
        f.close()

        return

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

    def _on_window_activated(self, window, trait_name, event):
        """ Dynamic trait change handler. """

        logger.debug('window %s activated', window)

        self.active_window = window

        return

    def _on_window_opening(self, window, trait_name, event):
        """ Dynamic trait change handler. """

        # Event notification.
        self.window_opening = window_event = VetoableWindowEvent(window=window)
        if window_event.veto:
            event.veto = True

        return

    def _on_window_opened(self, window, trait_name, event):
        """ Dynamic trait change handler. """

        # We maintain a list of all open windows so that (amongst other things)
        # we can detect when the user is attempting to close the last one.
        self.windows.append(window)

        # This is necessary because the activated event is not fired when a
        # window is first opened and gets focus. It is only fired when the
        # window comes from lower in the stack to be the active window.
        self.active_window = window

        # Event notification.
        self.window_opened = WindowEvent(window=window)

        return

    def _on_window_closing(self, window, trait_name, event):
        """ Dynamic trait change handler. """

        # Event notification.
        self.window_closing = window_event = VetoableWindowEvent(window=window)

        if window_event.veto:
            event.veto = True

        else:
            # Is this the last open window?
            if len(self.windows) == 1:
                # If this is an 'implicit exit' then make sure that we fire the
                # appropriate workbench lifecycle events.
                if not self._explicit_exit:
                    # Event notification.
                    self.exiting = window_event = Vetoable()
                    if window_event.veto:
                        event.veto = True

                if not event.veto:
                    # Save the window size, position and layout.
                    self._save_window_layout(window)

        return

    def _on_window_closed(self, window, trait_name, event):
        """ Dynamic trait change handler. """

        self.windows.remove(window)

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

        # Was this the last window?
        if len(self.windows) == 0:
            # Event notification.
            self.exited = self

        return