Пример #1
0
class BazNoModify(HasTraits):
    foo = Instance(Foo, ())
    sd = Delegate('foo', prefix='s')
    t = Delegate('foo')

    def _s_changed(self, name, old, new):
        print 'BazNoModify._s_changed( %s, %s, %s, %s)' % (self, name, old,
                                                           new)
        global baz_s_handler_self
        baz_s_handler_self = self
        return

    def _sd_changed(self, name, old, new):
        print 'BazNoModify._sd_changed( %s, %s, %s, %s)' % (self, name, old,
                                                            new)
        global baz_sd_handler_self
        baz_sd_handler_self = self
        return

    def _t_changed(self, name, old, new):
        print 'BazNoModify._t_changed( %s, %s, %s, %s)' % (self, name, old,
                                                           new)
        global baz_t_handler_self
        baz_t_handler_self = self
        return
Пример #2
0
class SetActivePerspectiveAction(WorkbenchAction):
    """ An action that sets the active perspective. """

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

    # Is the action enabled?
    enabled = Delegate('perspective')

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

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

    # The action's style.
    style = 'radio'

    #### 'SetActivePerspectiveAction' interface ###############################

    # The perspective that we set the active perspective to.
    perspective = Instance(IPerspective)

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

    def destroy(self):
        """ Destroy the action. """

        self.window = None

        return

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

        self.window.active_perspective = self.perspective

        return

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

    @on_trait_change('perspective,window.active_perspective')
    def _refresh_checked(self):
        """ Refresh the checked state of the action. """

        self.checked = self.perspective is not None \
          and self.window is not None \
          and self.window.active_perspective is not None \
          and self.perspective.id is self.window.active_perspective.id

        return
Пример #3
0
class Child(HasTraits):
    age = Int

    # 验证: father属性的值必须是Parent类的实例
    father = Instance(Parent)

    # 代理: Child的实例的last_name属性代理给其father属性的last_name
    last_name = Delegate('father')

    # 监听: 当age属性的值被修改时,下面的函数将被运行
    def _age_changed(self, old, new):
        print 'Age changed from %s to %s ' % (old, new)
Пример #4
0
class Child(HasTraits):

    age = Int

    # VALIDATION: 'father' must be a Parent instance:
    father = Instance(Parent)

    # DELEGATION: 'last_name' is delegated to father's 'last_name':
    last_name = Delegate('father')

    # NOTIFICATION: This method is called when 'age' changes:
    def _age_changed(self, old, new):
        print 'Age changed from %s to %s ' % (old, new)
Пример #5
0
class BazModify(HasTraits):
    foo = Instance(Foo, ())
    sd = Delegate('foo', prefix='s', modify=True)
    t = Delegate('foo', modify=True)

    def _s_changed(self, name, old, new):
        # should never be called
        print 'BazModify._s_changed( %s, %s, %s, %s)' % (self, name, old, new)
        global baz_s_handler_self
        baz_s_handler_self = self
        return

    def _sd_changed(self, name, old, new):
        print 'BazModify._sd_changed( %s, %s, %s, %s)' % (self, name, old, new)
        global baz_sd_handler_self
        baz_sd_handler_self = self
        return

    def _t_changed(self, name, old, new):
        print 'BazModify._t_changed( %s, %s, %s, %s)' % (self, name, old, new)
        global baz_t_handler_self
        baz_t_handler_self = self
        return
Пример #6
0
class VirtualDataName(HasPrivateTraits):

    # The TemplateDataName this is a virtual copy of:
    data_name = Instance(TemplateDataName)

    # The data name description:
    description = Delegate('data_name', modify=True)

    # The 'virtual' traits of this object:
    value0 = VirtualValue(index=0)
    value1 = VirtualValue(index=1)
    value2 = VirtualValue(index=2)
    value3 = VirtualValue(index=3)
    value4 = VirtualValue(index=4)
    value5 = VirtualValue(index=5)
class Foo(HasTraits):
    a = Any
    b = Bool
    s = Str
    i = Instance(HasTraits)
    e = Event
    d = Delegate('i')

    p = Property

    def _get_p(self):
        return self._p

    def _set_p(self, p):
        self._p = p

    # Read Only Property
    p_ro = Property

    def _get_p_ro(self):
        return id(self)
Пример #8
0
# The default dock style to use
dock_style_trait = Enum('fixed',
                        'horizontal',
                        'vertical',
                        'tab',
                        desc="the default docking style to use")

# The default notebook tab image to use
image_trait = Instance('enthought.pyface.image_resource.ImageResource',
                       desc='the image to be displayed on notebook tabs')

# The category of elements dragged out of the view
export_trait = Str(desc='the category of elements dragged out of the view')

# Delegate a trait value to the object's **container** trait
container_delegate = Delegate('container')

# An identifier for the external help context
help_id_trait = Str(desc="the external help context identifier")

# A button to add to a view
a_button = Trait('', Str, Instance('enthought.traits.ui.menu.Action'))
# The set of buttons to add to the view
buttons_trait = List(a_button,
                     desc='the action buttons to add to the bottom of '
                     'the view')

# View trait specified by name or instance:
AView = Any
#AView = Trait( '', Str, Instance( 'enthought.traits.ui.View' ) )
Пример #9
0
class EngineView(HasTraits):
    """ A view displaying the engine's object tree. """

    # The MayaVi engine we are a view of.
    engine = Instance(Engine, allow_none=True)

    # Path used to search for images
    _image_path = [
        join(resource_path(), 'images'),
    ]

    # The icon of the dialog
    icon = ImageResource('mv2.ico', search_path=_image_path)

    # Nodes on the tree.
    nodes = Any

    # TreeEditor
    tree_editor = Instance(TreeEditor)

    # Toolbar
    toolbar = Instance(ToolBar)

    # Toolbar actions.
    actions = List(Either(Action, Separator))

    # Some delegates, for the toolbar to update
    scenes = Delegate('engine')
    current_selection = Delegate('engine')

    ###########################################################################
    # `object` interface.
    ###########################################################################
    def __init__(self, **traits):
        super(EngineView, self).__init__(**traits)

    ###########################################################################
    # `HasTraits` interface.
    ###########################################################################
    def default_traits_view(self):
        """The default traits view of the Engine View.
        """

        view = View(Item(name='engine',
                         id='engine',
                         editor=self.tree_editor,
                         resizable=True,
                         show_label=False),
                    id='enthought.mayavi.engine',
                    help=False,
                    resizable=True,
                    scrollable=True,
                    undo=False,
                    revert=False,
                    ok=False,
                    cancel=False,
                    icon=self.icon,
                    title='Mayavi pipeline',
                    toolbar=self.toolbar,
                    handler=EngineViewHandler)
        return view

    def _nodes_default(self):
        """ The default value of the cached nodes list.
        """
        # Now setup the view.
        nodes = [
            TreeNode(
                node_for=[Engine],
                children='children_ui_list',
                label='=Mayavi',
                auto_open=False,
                copy=False,
                delete=False,
                rename=True,
            ),
            ObjectTreeNode(
                node_for=[Base],
                children='children_ui_list',
                label='name',
                auto_open=True,
                copy=True,
                delete=True,
                rename=True,
                tooltip='=Right click for more options',
            ),
            AdderTreeNode(
                node_for=[SceneAdderNode],
                icon_item='add_scene.png',
            ),
            AdderTreeNode(
                node_for=[SourceAdderNode],
                icon_item='add_source.png',
            ),
            AdderTreeNode(
                node_for=[ModuleFilterAdderNode],
                icon_item='add_module.png',
            ),
        ]
        return nodes

    def _tree_editor_default(self):
        return TreeEditor(editable=False,
                          hide_root=True,
                          on_dclick='handler._on_dclick',
                          on_select='handler._on_select',
                          orientation='vertical',
                          selected='object.engine.current_selection',
                          nodes=self.nodes)

    def _toolbar_default(self):
        toolbar = ToolBar(*self.actions)
        toolbar.image_size = (16, 16)
        toolbar.show_tool_names = False
        toolbar.show_divider = False
        return toolbar

    def _actions_default(self):
        add_scene = \
            Action(
                image=ImageResource('add_scene.png',
                                            search_path=self._image_path),
                tooltip="Create a new scene",
                defined_when='True',
                enabled_when='True',
                perform=self._perform_new_scene,
            )

        add_source = \
            Action(
                image=ImageResource('add_source.png',
                                            search_path=self._image_path),
                tooltip="Add a data source",
                defined_when='True',
                enabled_when='len(scenes) > 0',
                perform=self._perform_add_source,
            )

        add_module = \
            Action(
                image=ImageResource('add_module.png',
                                            search_path=self._image_path),
                tooltip="Add a visualization module",
                defined_when='True',
                # isinstance doesn't work in enabled_when
                enabled_when=\
                    'current_selection is not None and'
                    '( hasattr(current_selection, "output_info")'
                    'or current_selection.__class__.__name__ =='
                    '"ModuleFilterAdderNode")',
                perform=self._perform_add_module,
            )

        add_filter = \
            Action(
                image=ImageResource('add_filter.png',
                                            search_path=self._image_path),
                tooltip="Add a processing filter",
                defined_when='True',
                enabled_when=\
                    'current_selection is not None and'
                    '( ( hasattr(current_selection, "output_info")'
                    ' and not current_selection.type in (" module", '
                    ' " module manager"))'
                    'or current_selection.__class__.__name__ =='
                    '"ModuleFilterAdderNode")',
                perform=self._perform_add_filter,
             )

        help = \
            Action(
                image=ImageResource('help-action.png',
                                            search_path=self._image_path),
                tooltip="Help on the Mayavi pipeline",
                defined_when='True',
                enabled_when='True',
                perform=open_help_index,
            )

        tvtk_docs = \
            Action(
                image=ImageResource('reader.png',
                                            search_path=self._image_path),
                tooltip="Search the VTK class browser",
                defined_when='True',
                enabled_when='True',
                perform=open_tvtk_docs,
            )

        record = \
            Action(
                image=ImageResource('record.png',
                                     search_path=self._image_path),
                tooltip="Start/Stop script recording",
                style='toggle',
                checked=False,
                defined_when='True',
                enabled_when='engine is not None',
                perform=self._perform_record,
            )

        # Check the record icon if the engine already has a recorder
        # set.
        if self.engine is not None and self.engine.recorder is not None:
            record.checked = True

        return [
            tvtk_docs,
            Separator(), add_scene, add_source, add_module, add_filter,
            Separator(), help, record
        ]

    ###########################################################################
    # Private interface.
    ###########################################################################
    def _perform_new_scene(self):
        self.engine.new_scene()
        self.engine.current_selection = self.engine.current_scene

    def _perform_add_source(self):
        adder = SourceAdderNode(object=self.engine.current_scene)
        adder.edit_traits(view=adder.dialog_view())

    def _perform_add_module(self):
        object = self.engine.current_selection
        if isinstance(object, AdderNode):
            object = object.object
        adder = ModuleAdderNode(object=object)
        adder.edit_traits(view=adder.dialog_view())

    def _perform_add_filter(self):
        object = self.engine.current_selection
        if isinstance(object, AdderNode):
            object = object.object
        adder = FilterAdderNode(object=object)
        adder.edit_traits(view=adder.dialog_view())

    def _perform_record(self):
        e = self.engine
        if e.recorder is None:
            start_recording(e, known=True, script_id='engine')
        else:
            stop_recording(e, save=False)

    def _recorder_changed_for_engine(self, recorder):
        """Called when the recorder trait on the engine trait of this
        object changes.

        This basically toggles the recording action when someone
        attaches a recorder to the engine.
        """
        record_action = None
        for action in self.actions:
            if hasattr(action, 'tooltip') and \
               action.tooltip.endswith('recording'):
                record_action = action
                break

        if record_action is not None:
            if recorder is not None:
                record_action.checked = True
            else:
                record_action.checked = False
Пример #10
0
class WorkbenchWindow(ApplicationWindow):
    """ A workbench window. """

    #### 'IWorkbenchWindow' interface #########################################

    # The view or editor that currently has the focus.
    active_part = Instance(IWorkbenchPart)
    
    # The editor manager is used to create/restore editors.
    editor_manager = Instance(IEditorManager)

    # The current selection within the window.
    selection = List

    # The workbench that the window belongs to.
    workbench = Instance('enthought.pyface.workbench.api.IWorkbench')
    
    #### Editors #######################

    # The active editor.
    active_editor = Instance(IEditor)

    # The visible (open) editors.
    editors = List(IEditor)

    # The Id of the editor area.
    editor_area_id = Constant('enthought.pyface.workbench.editors')

    # The (initial) size of the editor area (the user is free to resize it of
    # course).
    editor_area_size = Tuple((100, 100))

    # Fired when an editor is about to be opened (or restored).
    editor_opening = Delegate('layout') # Event(IEditor)
    
    # Fired when an editor has been opened (or restored).
    editor_opened = Delegate('layout')  # Event(IEditor)

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

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

    #### Views #########################

    # The active view.
    active_view = Instance(IView)

    # The available views (note that this is *all* of the views, not just those
    # currently visible).
    #
    # Views *cannot* be shared between windows as each view has a reference to
    # its toolkit-specific control etc.
    views = List(IView)

    #### Perspectives ##################

    # The active perspective.
    active_perspective = Instance(IPerspective)

    # The available perspectives. If no perspectives are specified then the
    # a single instance of the 'Perspective' class is created.
    perspectives = List(IPerspective)

    # The Id of the default perspective.
    #
    # There are two situations in which this is used:
    #
    # 1. When the window is being created from scratch (i.e., not restored).
    #
    #    If this is the empty string, then the first perspective in the list of
    #    perspectives is shown (if there are no perspectives then an instance
    #    of the default 'Perspective' class is used). If this is *not* the
    #    empty string then the perspective with this Id is shown.
    #
    # 2. When the window is being restored.
    #
    #    If this is the empty string, then the last perspective that was
    #    visible when the window last closed is shown. If this is not the empty
    #    string then the perspective with this Id is shown.
    #
    default_perspective_id = Str

    #### 'WorkbenchWindow' interface ##########################################

    # The window layout is responsible for creating and managing the internal
    # structure of the window (i.e., it knows how to add and remove views and
    # editors etc).
    layout = Instance(WorkbenchWindowLayout)

    #### 'Private' interface ##################################################

    # The state of the window suitable for pickling etc.
    _memento = Instance(WorkbenchWindowMemento)

    ###########################################################################
    # 'Window' interface.
    ###########################################################################

    def open(self):
        """ Open the window.

        Overridden to make the 'opening' event vetoable.

        Return True if the window opened successfully; False if the open event
        was vetoed.

        """

        logger.debug('window %s opening', self)

        # Trait notification.
        self.opening = event = Vetoable()
        if not event.veto:
            if self.control is None:
                self._create()

            self.show(True)

            # Trait notification.
            self.opened = self

            logger.debug('window %s opened', self)

        else:
            logger.debug('window %s open was vetoed', self)

        # fixme: This is not actually part of the Pyface 'Window' API (but
        # maybe it should be). We return this to indicate whether the window
        # actually opened.
        return self.control is not None

    def close(self):
        """ Closes the window.

        Overridden to make the 'closing' event vetoable.

        Return True if the window closed successfully (or was not even open!),
        False if the close event was vetoed.

        """

        logger.debug('window %s closing', self)

        if self.control is not None:
            # Trait notification.
            self.closing = event = Vetoable()

            # fixme: Hack to mimic vetoable events!
            if not event.veto:
                # Give views and editors a chance to cleanup after themselves.
                self.destroy_views(self.views)
                self.destroy_editors(self.editors)

                # Cleanup the window layout (event handlers, etc.)
                self.layout.close()
                
                # Cleanup the toolkit-specific control.
                self.destroy()

                # Cleanup our reference to the control so that we can (at least
                # in theory!) be opened again.
                self.control = None

                # Trait notification.
                self.closed = self

                logger.debug('window %s closed', self)

            else:
                logger.debug('window %s close was vetoed', self)

        else:
            logger.debug('window %s is not open', self)

        # FIXME v3: This is not actually part of the Pyface 'Window' API (but
        # maybe it should be). We return this to indicate whether the window
        # actually closed.
        return self.control is None
        
    ###########################################################################
    # Protected 'Window' interface.
    ###########################################################################

    def _create_contents(self, parent):
        """ Create and return the window contents. """

        # Create the initial window layout.
        contents = self.layout.create_initial_layout(parent)

        # Save the initial window layout so that we can reset it when changing
        # to a perspective that has not been seen yet.
        self._initial_layout = self.layout.get_view_memento()

        # Are we creating the window from scratch or restoring it from a
        # memento?
        if self._memento is None:
            self._memento = WorkbenchWindowMemento()

        else:
            self._restore_contents()
            
        # Set the initial perspective.
        self.active_perspective = self._get_initial_perspective()
        
        return contents

    ###########################################################################
    # 'WorkbenchWindow' interface.
    ###########################################################################

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

    def _editor_manager_default(self):
        """ Trait initializer. """

        from editor_manager import EditorManager
        
        return EditorManager(window=self)

    def _layout_default(self):
        """ Trait initializer. """

        return WorkbenchWindowLayout(window=self)
    
    #### Methods ##############################################################

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

        self.layout.activate_editor(editor)

        return

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

        self.layout.activate_view(view)
        
        return

    def add_editor(self, editor, title=None):
        """ Adds an editor.

        If no title is specified, the editor's name is used.

        """

        if title is None:
            title = editor.name

        self.layout.add_editor(editor, title)
        self.editors.append(editor)

        return

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

        self.layout.add_view(view, position, relative_to, size)

        # This case allows for views that are created and added dynamically
        # (i.e. they were not even known about when the window was created).
        if not view in self.views:
            self.views.append(view)
        
        return

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

        self.layout.close_editor(editor)

        return

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

        fixme: Currently views are never 'closed' in the same sense as an
        editor is closed. Views are merely hidden.

        """

        self.hide_view(view)

        return

    def create_editor(self, obj, kind=None):
        """ Create an editor for an object.

        Return None if no editor can be created for the object.

        """

        return self.editor_manager.create_editor(self, obj, kind)

    def destroy_editors(self, editors):
        """ Destroy a list of editors. """

        for editor in editors:
            if editor.control is not None:
                editor.destroy_control()

        return
    
    def destroy_views(self, views):
        """ Destroy a list of views. """

        for view in views:
            if view.control is not None:
                view.destroy_control()

        return
    
    def edit(self, obj, kind=None, use_existing=True):
        """ Edit an object.

        'kind' is simply passed through to the window's editor manager to
        allow it to create a particular kind of editor depending on context
        etc.
        
        If 'use_existing' is True and the object is already being edited in
        the window then the existing editor will be activated (i.e., given
        focus, brought to the front, etc.).

        If 'use_existing' is False, then a new editor will be created even if
        one already exists.

        """

        if use_existing:
            # Is the object already being edited in the window?
            editor = self.get_editor(obj, kind)

            if editor is not None:
                # If so, activate the existing editor (i.e., bring it to the
                # front, give it the focus etc).
                self.activate_editor(editor)
                return editor

        # Otherwise, create an editor for it.
        editor = self.create_editor(obj, kind)

        if editor is None:
            logger.warn('no editor for object %s', obj)

        self.add_editor(editor)
        self.activate_editor(editor)

        return editor

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

        Return None if no such editor exists.

        """

        return self.editor_manager.get_editor(self, obj, kind)

    def get_editor_by_id(self, id):
        """ Return the editor with the specified Id.

        Return None if no such editor exists.

        """

        for editor in self.editors:
            if editor.id == id:
                break

        else:
            editor = None

        return editor

    def get_part_by_id(self, id):
        """ Return the workbench part with the specified Id.

        Return None if no such part exists.

        """

        return self.get_view_by_id(id) or self.get_editor_by_id(id)
    
    def get_perspective_by_id(self, id):
        """ Return the perspective with the specified Id.

        Return None if no such perspective exists.

        """

        for perspective in self.perspectives:
            if perspective.id == id:
                break

        else:
            if id == Perspective.DEFAULT_ID:
                perspective = Perspective()

            else:
                perspective = None

        return perspective

    def get_perspective_by_name(self, name):
        """ Return the perspective with the specified name.

        Return None if no such perspective exists.

        """

        for perspective in self.perspectives:
            if perspective.name == name:
                break

        else:
            perspective = None

        return perspective

    def get_view_by_id(self, id):
        """ Return the view with the specified Id.

        Return None if no such view exists.

        """

        for view in self.views:
            if view.id == id:
                break

        else:
            view = None

        return view

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

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

        self.layout.hide_view(view)
            
        return

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

        self.layout.refresh()

        return

    def reset_active_perspective(self):
        """ Reset the active perspective back to its original contents. """

        perspective = self.active_perspective
        
        # If the perspective has been seen before then delete its memento.
        if perspective.id in self._memento.perspective_mementos:
            # Remove the perspective's memento.
            del self._memento.perspective_mementos[perspective.id]

        # Re-display the perspective (because a memento no longer exists for
        # the perspective, its 'create_contents' method will be called again).
        self._show_perspective(perspective, perspective)

        return

    def reset_all_perspectives(self):
        """ Reset all perspectives back to their original contents. """

        # Remove all perspective mementos (except user perspectives).
        for id in self._memento.perspective_mementos.keys():
            if not id.startswith('__user_perspective'):
                del self._memento.perspective_mementos[id]

        # Re-display the active perspective.
        self._show_perspective(self.active_perspective,self.active_perspective)

        return

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

        self.layout.reset_editors()

        return

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

        self.layout.reset_views()

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

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

        # If the view is already in the window layout, but hidden, then just
        # show it.
        #
        # fixme: This is a little gorpy, reaching into the window layout here,
        # but currently this is the only thing that knows whether or not the
        # view exists but is hidden.
        if self.layout.contains_view(view):
            self.layout.show_view(view)
            
        # Otherwise, we have to add the view to the layout.
        else:
            self._add_view_in_default_position(view)
            self.refresh()

        return

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

    def get_memento(self):
        """ Return the state of the window suitable for pickling etc. """

        # The size and position of the window.
        self._memento.size = self.size
        self._memento.position = self.position

        # The Id of the active perspective.
        self._memento.active_perspective_id = self.active_perspective.id

        # The layout of the active perspective.
        self._memento.perspective_mementos[self.active_perspective.id] = (
            self.layout.get_view_memento(),
            self.active_view and self.active_view.id or None
        )

        # The layout of the editor area.
        self._memento.editor_area_memento = self.layout.get_editor_memento()
        
        return self._memento

    def set_memento(self, memento):
        """ Restore the state of the window from a memento. """

        # All we do here is save a reference to the memento - we don't actually
        # do anything with it until the window is opened.
        #
        # This obviously means that you can't set the memento of a window
        # that is already open, but I can't see a use case for that anyway!
        self._memento = memento

        return

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

    def _add_view_in_default_position(self, view):
        """ Adds a view in its 'default' position. """

        # Is the view in the current perspectives contents list? If it is then
        # we use the positioning information in the perspective item. Otherwise
        # we will use the default positioning specified in the view itself.
        item = self._get_perspective_item(self.active_perspective, view)
        if item is None:
            item = view

        # fixme: This only works because 'PerspectiveItem' and 'View' have the
        # identical 'position', 'relative_to', 'width' and 'height' traits! We
        # need to unify these somehow!
        relative_to = self.get_view_by_id(item.relative_to)
        size = (item.width, item.height)

        self.add_view(view, item.position, relative_to, size)

        return
            
    def _get_initial_perspective(self, *methods):
        """ Return the initial perspective. """

        methods = [
            # If a default perspective was specified then we prefer that over
            # any other perspective.
            self._get_default_perspective,
            
            # If there was no default perspective then try the perspective that
            # was active the last time the application was run.
            self._get_previous_perspective,

            # If there was no previous perspective, then try the first one that
            # we know about.
            self._get_first_perspective
        ]

        for method in methods:
            perspective = method()
            if perspective is not None:
                break

        # If we have no known perspectives, make a new blank one up.
        else:
            logger.warn('no known perspectives - creating a new one')
            perspective = Perspective()

        return perspective
    
    def _get_default_perspective(self):
        """ Return the default perspective.

        Return None if no default perspective was specified or it no longer
        exists.

        """

        id = self.default_perspective_id

        if len(id) > 0:
            perspective = self.get_perspective_by_id(id)
            if perspective is None:
                logger.warn('default perspective %s no longer available', id)

        else:
            perspective = None
            
        return perspective

    def _get_previous_perspective(self):
        """ Return the previous perspective.

        Return None if there has been no previous perspective or it no longer
        exists.

        """

        id = self._memento.active_perspective_id
            
        if len(id) > 0:
            perspective = self.get_perspective_by_id(id)
            if perspective is None:
                logger.warn('previous perspective %s no longer available', id)

        else:
            perspective = None

        return perspective

    def _get_first_perspective(self):
        """ Return the first perspective in our list of perspectives.

        Return None if no perspectives have been defined.

        """

        if len(self.perspectives) > 0:
            perspective = self.perspectives[0]

        else:
            perspective = None

        return perspective

    def _get_perspective_item(self, perspective, view):
        """ Return the perspective item for a view.

        Return None if the view is not mentioned in the perspectives contents.

        """

        # fixme: Errrr, shouldn't this be a method on the window?!?
        for item in perspective.contents:
            if item.id == view.id:
                break

        else:
            item = None

        return item

    def _hide_perspective(self, perspective):
        """ Hide a perspective. """

        # fixme: This is a bit ugly but... when we restore the layout we ignore
        # the default view visibility.
        for view in self.views:
            view.visible = False

        # Save the current layout of the perspective.
        self._memento.perspective_mementos[perspective.id] = (
            self.layout.get_view_memento(),
            self.active_view and self.active_view.id or None
        )

        return

    def _show_perspective(self, old, new):
        """ Show a perspective. """

        # If the perspective has been seen before then restore it.
        memento = self._memento.perspective_mementos.get(new.id)
        if memento is not None:
            view_memento, active_view_id = memento
            self.layout.set_view_memento(view_memento)

            # Make sure the active part, view and editor reflect the new
            # perspective.
            view = self.get_view_by_id(active_view_id)
            if view is not None:
                self.active_view = view
        
        # Otherwise, this is the first time the perspective has been seen
        # so create it.
        else:
            if old is not None:
                # Reset the window layout to its initial state.
                self.layout.set_view_memento(self._initial_layout)

            # Create the perspective in the window.
            new.create(self)

            # Make sure the active part, view and editor reflect the new
            # perspective.
            self.active_view = None

        # Show the editor area?
        if new.show_editor_area:
            self.show_editor_area()
        else:
            self.hide_editor_area()
            self.active_editor = None

        # Inform the perspective that it has been shown.
        new.show(self)
            
        # This forces the dock window to update its layout.
        if old is not None:
            self.refresh()

        return

    def _restore_contents(self):
        """ Restore the contents of the window. """

        self.layout.set_editor_memento(self._memento.editor_area_memento)

        self.size = self._memento.size
        self.position = self._memento.position

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

    #### Static ####

    def _active_perspective_changed(self, old, new):
        """ Static trait change handler. """

        logger.debug('active perspective changed from <%s> to <%s>', old, new)

        # Hide the old perspective...
        if old is not None:
            self._hide_perspective(old)
                
        # ... and show the new one.
        if new is not None:
            self._show_perspective(old, new)

        return

    def _active_editor_changed(self, old, new):
        """ Static trait change handler. """

        logger.debug('active editor changed from <%s> to <%s>', old, new)
        self.active_part = new
        
        return

    def _active_part_changed(self, old, new):
        """ Static trait change handler. """

        if new is None:
            self.selection = []

        else:
            self.selection = new.selection

        logger.debug('active part changed from <%s> to <%s>', old, new)

        return

    def _active_view_changed(self, old, new):
        """ Static trait change handler. """

        logger.debug('active view changed from <%s> to <%s>', old, new)
        self.active_part = new

        return
        
    def _views_changed(self, old, new):
        """ Static trait change handler. """

        # Cleanup any old views.
        for view in old:
            view.window = None
            
        # Initialize any new views.
        for view in new:
            view.window = self

        return

    def _views_items_changed(self, event):
        """ Static trait change handler. """

        # Cleanup any old views.
        for view in event.removed:
            view.window = None

        # Initialize any new views.
        for view in event.added:
            view.window = self

        return

    #### Dynamic ####

    @on_trait_change('layout.editor_closed')
    def _on_editor_closed(self, editor):
        """ Dynamic trait change handler. """

        index = self.editors.index(editor)
        del self.editors[index]
        if editor is self.active_editor:
            if len(self.editors) > 0:
                index = min(index, len(self.editors) - 1)
                # If the user closed the editor manually then this method is
                # being called from a toolkit-specific event handler. Because
                # of that we have to make sure that we don't change the focus
                # from within this method directly hence we activate the editor
                # later in the GUI thread.
                GUI.invoke_later(self.activate_editor, self.editors[index])

            else:
                self.active_editor = None

        return

    @on_trait_change('editors.has_focus')
    def _on_editor_has_focus_changed(self, obj, trait_name, old, new):
        """ Dynamic trait change handler. """

        if trait_name == 'has_focus' and new:
            self.active_editor = obj
                
        return

    @on_trait_change('views.has_focus')
    def _has_focus_changed_for_view(self, obj, trait_name, old, new):
        """ Dynamic trait change handler. """

        if trait_name == 'has_focus' and new:
            self.active_view = obj

        return

    @on_trait_change('views.visible')
    def _visible_changed_for_view(self, obj, trait_name, old, new):
        """ Dynamic trait change handler. """

        if trait_name == 'visible':
            if not new:
                if obj is self.active_view:
                    self.active_view = None

        return
Пример #11
0
class PreferencesNode(TreeItem):
    """ Abstract base class for a node in a preferences dialog.

    A preferences node has a name and an image which are used to represent the
    node in a preferences dialog (usually in the form of a tree).

    """

    #### 'PreferenceNode' interface ###########################################

    # The page's help identifier (optional). If a help Id *is* provided then
    # there will be a 'Help' button shown on the preference page.
    help_id = Delegate('page')

    # The page name (this is what is shown in the preferences dialog.
    name = Delegate('page')

    # The page that we are a node for.
    page = Instance(IPreferencesPage)

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

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

        if self.page is None:
            s = 'root'

        else:
            s = self.page.name

        return s

    __repr__ = __str__

    ###########################################################################
    # 'PreferencesNode' interface.
    ###########################################################################

    def create_page(self, parent):
        """ Creates the preference page for this node. """

        return self.page.create_control(parent)

    def lookup(self, name):
        """ Returns the child of this node with the specified Id.

        Returns None if no such child exists.

        """

        for node in self.children:
            if node.name == name:
                break

        else:
            node = None

        return node

    ###########################################################################
    # Debugging interface.
    ###########################################################################

    def dump(self, indent=''):
        """ Pretty-print the node to stdout. """

        print indent, 'Node', str(self)

        for child in self.children:
            child.dump(indent + '  ')

        return
Пример #12
0
class AbstractAdapterFactory(HasTraits):
    """ Abstract base class for all adapter factories.

    Adapter factories define behavioural extensions for classes.

    """

    #### 'AbstractAdapterFactory' interface ###################################

    # The adapter manager that the factory is registered with (this will be
    # None iff the factory is not registered with a manager).
    adapter_manager = Instance(AdapterManager)

    # The type system used by the factory (it determines 'is_a' relationships
    # and type MROs etc). By default we use standard Python semantics.
    type_system = Delegate('adapter_manager')

    ###########################################################################
    # 'AbstractAdapterFactory' interface.
    ###########################################################################

    def adapt(self, adaptee, target_class, *args, **kw):
        """ Returns an adapter that adapts an object to the target class.

        Returns None if the factory cannot produce such an adapter.

        """

        if self._can_adapt(adaptee, target_class, *args, **kw):
            adapter = self._adapt(adaptee, target_class, *args, **kw)
            if adapter is None:
                logger.warn(self._get_warning_message(adaptee, target_class))

        else:
            adapter = None

        return adapter

    ###########################################################################
    # Protected 'AbstractAdapterFactory' interface.
    ###########################################################################

    def _can_adapt(self, adaptee, target_class, *args, **kw):
        """ Returns True if the factory can produce an appropriate adapter. """

        raise NotImplementedError

    def _adapt(self, adaptee, target_class, *args, **kw):
        """ Returns an adapter that adapts an object to the target class. """

        raise NotImplementedError

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

    def _get_warning_message(self, adaptee, target_class):
        """ Returns a warning message.

        The warning message is used when a factory fails to adapt something
        that it said it could!

        """

        message = '%s failed to adapt %s to %s' % (
            self.__class__.__name__, str(adaptee), target_class.__name__)

        return message
Пример #13
0
class DataSetClipper(Filter):

    # The version of this class.  Used for persistence.
    __version__ = 0

    # The widgets to be used for the Clipping Filter.
    widget = Instance(ImplicitWidgets, allow_none=False, record=True)

    # The clipping filter.
    filter = Instance(tvtk.Object, allow_none=False, record=True)

    # The update mode of the widget-- this is delegated to the
    # ImplicitWidgets.
    update_mode = Delegate('widget', modify=True)

    input_info = PipelineInfo(datasets=['any'],
                              attribute_types=['any'],
                              attributes=['any'])

    output_info = PipelineInfo(datasets=['any'], attributes=['any'])

    ########################################
    # View related traits.

    # Button to reset the boundaries of the implicit_widget.
    reset_button = Button('Reset Boundaries')

    view = View(Group(Group(Item('update_mode'), ),
                      Group(Item('reset_button'),
                            Item(name='widget', style='custom',
                                 resizable=True),
                            show_labels=False),
                      label='ImplicitWidget'),
                Group(Group(Item('filter', style='custom'), show_labels=False),
                      label='Clipper'),
                resizable=True)

    ########################################
    # Private traits.
    _transform = Instance(tvtk.Transform, allow_none=False)

    ######################################################################
    # `object` interface.
    ######################################################################
    def __get_pure_state__(self):
        d = super(DataSetClipper, self).__get_pure_state__()
        for name in ('_first', '_observer_id'):
            d.pop(name, None)
        d['matrix'] = cPickle.dumps(self._transform.matrix)
        return d

    def __set_pure_state__(self, state):
        mat = state.pop('matrix')
        super(DataSetClipper, self).__set_pure_state__(state)
        state_pickler.set_state(self, state)
        self._transform.set_matrix(cPickle.loads(mat))
        self.widget.set_transform(self._transform)

    ######################################################################
    # `Filter` interface
    ######################################################################
    def setup_pipeline(self):
        self.widget = ImplicitWidgets()
        self._transform = tvtk.Transform()
        self.filter = tvtk.ClipDataSet()
        self.widget.on_trait_change(self._handle_widget, 'widget')
        super(DataSetClipper, self).setup_pipeline()

    def update_pipeline(self):
        inputs = self.inputs
        if len(inputs) == 0:
            return

        widget = self.widget
        widget.inputs = inputs
        widget.update_pipeline()

        filter = self.filter
        filter.input = inputs[0].outputs[0]
        widget.update_implicit_function()
        filter.clip_function = widget.implicit_function
        filter.update()
        self._set_outputs([filter.output])

        self.pipeline_changed = True

    def update_data(self):
        # Do nothing if there is no input.
        if len(self.inputs) == 0:
            return

        self.filter.update()
        # Propagate the data_changed event.
        self.data_changed = True

    ######################################################################
    # Non-public methods.
    ######################################################################
    def _on_interaction_event(self, obj, event):
        tfm = self._transform
        self.widget.widget.get_transform(tfm)
        recorder = self.recorder
        if recorder is not None:
            state = {}
            state['elements'] = tfm.matrix.__getstate__()['elements']
            name = recorder.get_script_id(self)
            recorder.record('%s._transform.matrix.__setstate__(%s)'\
                            %(name, state))
            recorder.record('%s.widget.widget.set_transform(%s._transform)'\
                            %(name, name))
            recorder.record('%s.widget.update_implicit_function()' % name)
            recorder.record('%s.render()' % name)

    def _widget_changed(self, old, new):
        self.widgets = self.widget.widgets

        if len(self.inputs) > 0:
            new.inputs = self.inputs
            new.update_pipeline()
        self._observer_id = new.widget.add_observer(self.update_mode_,
                                                    self._on_interaction_event)

    def _filter_changed(self, old, new):
        if old is not None:
            old.on_trait_change(self.render, remove=True)
        new.on_trait_change(self.render)
        if len(self.inputs) > 0:
            inp = self.inputs[0].outputs[0]
            new.input = inp
            self.outputs = [new.output]

    def _reset_button_fired(self):
        self.widget.widget.place_widget()
        self.widget.update_implicit_function()
        self.filter.update()
        self.render()

    def _handle_widget(self, value):
        self.widgets = self.widget.widgets
        f = self.filter
        f.clip_function = self.widget.implicit_function
        f.update()
        self.update_pipeline()
Пример #14
0
class Streamline(Module):

    # The version of this class.  Used for persistence.
    __version__ = 0

    # The streamline generator.
    stream_tracer = Instance(tvtk.StreamTracer, allow_none=False, record=True)

    # The seed for the streamlines.
    seed = Instance(SourceWidget, allow_none=False, record=True)

    # The update mode of the seed -- this is delegated to the
    # SourceWidget.
    update_mode = Delegate('seed', modify=True)

    # Determines if the streamlines are shown as lines or ribbons or
    # tubes.
    streamline_type = Trait('line',
                            TraitPrefixList(['line', 'ribbon', 'tube']),
                            desc='draw streamlines as lines/ribbons/tubes')

    # The ribbon filter.
    ribbon_filter = Instance(tvtk.RibbonFilter, allow_none=False, record=True)

    # The tube filter.
    tube_filter = Instance(tvtk.TubeFilter, allow_none=False, record=True)

    # The actor component that represents the visualization.
    actor = Instance(Actor, allow_none=False, record=True)

    input_info = PipelineInfo(datasets=['any'],
                              attribute_types=['any'],
                              attributes=['vectors'])

    ########################################
    # Private traits.

    _first = Bool(True)

    ########################################
    # View related code.

    # A button to update the streamlines.
    update_streamlines = Button('Update Streamlines')

    _tube_group = Group(Item(name='capping'),
                        Item(name='sides_share_vertices'),
                        Item(name='vary_radius'), Item(name='number_of_sides'),
                        Item(name='radius'), Item(name='radius_factor'),
                        Item(name='offset'), Item(name='on_ratio'))

    _ribbon_group = Group(Item(name='vary_width'), Item(name='width'),
                          Item(name='width_factor'), Item(name='angle'))

    view = View(Group(
        Group(Item(name='update_mode'), ),
        Group(
            Item(name='update_streamlines'),
            show_labels=False,
        ),
        Group(Item(name='streamline_type'),
              Item(name='ribbon_filter',
                   style='custom',
                   visible_when='object.streamline_type == "ribbon"',
                   editor=InstanceEditor(view=View(_ribbon_group))),
              Item(name='tube_filter',
                   style='custom',
                   visible_when='object.streamline_type == "tube"',
                   editor=InstanceEditor(view=View(_tube_group))),
              show_labels=False,
              label='Streamline'),
        label='Streamline'),
                Group(Item(name='seed', style='custom', resizable=True),
                      label='Seed',
                      show_labels=False),
                Group(Item(name='stream_tracer',
                           style='custom',
                           resizable=True),
                      label='StreamTracer',
                      show_labels=False),
                Group(Item(name='actor', style='custom'),
                      label='Actor',
                      show_labels=False),
                resizable=True)

    ######################################################################
    # `Module` interface
    ######################################################################
    def setup_pipeline(self):
        """Override this method so that it *creates* the tvtk
        pipeline.

        This method is invoked when the object is initialized via
        `__init__`.  Note that at the time this method is called, the
        tvtk data pipeline will *not* yet be setup.  So upstream data
        will not be available.  The idea is that you simply create the
        basic objects and setup those parts of the pipeline not
        dependent on upstream sources and filters.  You should also
        set the `actors` attribute up at this point.
        """
        # Create and setup the default objects.
        self.seed = SourceWidget()
        self.stream_tracer = tvtk.StreamTracer(
            maximum_propagation=50,
            integration_direction='forward',
            compute_vorticity=True,
            integrator_type='runge_kutta4',
        )
        self.ribbon_filter = tvtk.RibbonFilter()
        self.tube_filter = tvtk.TubeFilter()

        self.actor = Actor()
        # Setup the actor suitably for this module.
        self.actor.property.line_width = 2.0

    def update_pipeline(self):
        """Override this method so that it *updates* the tvtk pipeline
        when data upstream is known to have changed.

        This method is invoked (automatically) when any of the inputs
        sends a `pipeline_changed` event.
        """
        mm = self.module_manager
        if mm is None:
            return

        src = mm.source
        self.stream_tracer.input = src.outputs[0]
        self.seed.inputs = [src]

        # Setup the radius/width of the tube/ribbon filters based on
        # given input.
        if self._first:
            b = src.outputs[0].bounds
            l = [(b[1] - b[0]), (b[3] - b[2]), (b[5] - b[4])]
            length = sqrt(l[0] * l[0] + l[1] * l[1] + l[2] * l[2])
            self.ribbon_filter.width = length * 0.0075
            self.tube_filter.radius = length * 0.0075
            self._first = False

        self._streamline_type_changed(self.streamline_type)
        # Set the LUT for the mapper.
        self.actor.set_lut(mm.scalar_lut_manager.lut)

        self.pipeline_changed = True

    def update_data(self):
        """Override this method so that it flushes the vtk pipeline if
        that is necessary.

        This method is invoked (automatically) when any of the inputs
        sends a `data_changed` event.
        """
        # Just set data_changed, the components should do the rest if
        # they are connected.
        self.data_changed = True

    ######################################################################
    # Non-public methods.
    ######################################################################
    def _streamline_type_changed(self, value):
        if self.module_manager is None:
            return
        st = self.stream_tracer
        rf = self.ribbon_filter
        tf = self.tube_filter
        if value == 'line':
            self.outputs = [st.output]
        elif value == 'ribbon':
            rf.input = st.output
            self.outputs = [rf.output]
        elif value == 'tube':
            tf.input = st.output
            self.outputs = [tf.output]
        self.render()

    def _update_streamlines_fired(self):
        self.seed.update_poly_data()
        self.render()

    def _stream_tracer_changed(self, old, new):
        if old is not None:
            old.on_trait_change(self.render, remove=True)
        seed = self.seed
        if seed is not None:
            new.source = seed.poly_data
        new.on_trait_change(self.render)
        mm = self.module_manager
        if mm is not None:
            new.input = mm.source.outputs[0]

        # A default output so there are no pipeline errors.  The
        # update_pipeline call corrects this if needed.
        self.outputs = [new.output]

        self.update_pipeline()

    def _seed_changed(self, old, new):
        st = self.stream_tracer
        if st is not None:
            st.source = new.poly_data
        self._change_components(old, new)

    def _ribbon_filter_changed(self, old, new):
        if old is not None:
            old.on_trait_change(self.render, remove=True)
        new.on_trait_change(self.render)
        self._streamline_type_changed(self.streamline_type)

    def _tube_filter_changed(self, old, new):
        if old is not None:
            old.on_trait_change(self.render, remove=True)
        new.on_trait_change(self.render)
        self._streamline_type_changed(self.streamline_type)

    def _actor_changed(self, old, new):
        new.scene = self.scene
        new.inputs = [self]
        self._change_components(old, new)
Пример #15
0
class ToggleViewVisibilityAction(WorkbenchAction):
    """ An action that toggles a view's visibility (ie. hides/shows it). """

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

    # The action's unique identifier (may be None).
    id = Delegate('view', modify=True)

    # The action's name (displayed on menus/tool bar tools etc).
    name = Delegate('view', modify=True)

    # The action's style.
    style = 'toggle'

    #### 'ViewAction' interface ###############################################

    # The view that we toggle the visibility for.
    view = Instance(IView)

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

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

        if self.view is not None:
            self._remove_view_listeners(self.view)

        return

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

        self._toggle_view_visibility(self.view)

        return

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

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

    def _view_changed(self, old, new):
        """ Static trait change handler. """

        if old is not None:
            self._remove_view_listeners(old)

        if new is not None:
            self._add_view_listeners(new)

        self._refresh_checked()

        return

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

    def _add_view_listeners(self, view):
        """ Add listeners for trait events on a view. """

        view.on_trait_change(self._refresh_checked, 'visible')
        view.on_trait_change(self._refresh_checked, 'window')

        return

    def _remove_view_listeners(self, view):
        """ Add listeners for trait events on a view. """

        view.on_trait_change(self._refresh_checked, 'visible', remove=True)
        view.on_trait_change(self._refresh_checked, 'window', remove=True)

        return

    def _refresh_checked(self):
        """ Refresh the checked state of the action. """

        self.checked = self.view is not None \
          and self.view.window is not None \
          and self.view.visible

        return

    def _toggle_view_visibility(self, view):
        """ Toggle the visibility of a view. """

        if view.visible:
            view.hide()

        else:
            view.show()

        return
Пример #16
0
class CameraControl(HasTraits):
    # Traits
    positions = List(CameraPosition)
    yt_scene = Instance('YTScene')
    center = Delegate('yt_scene')
    scene = Delegate('yt_scene')
    camera = Instance(tvtk.OpenGLCamera)
    reset_position = Instance(CameraPosition)
    fps = Float(25.0)
    export_filename = 'frames'
    periodic = Bool

    # UI elements
    snapshot = Button()
    play = Button()
    export_frames = Button()
    reset_path = Button()
    recenter = Button()
    save_path = Button()
    load_path = Button()
    export_path = Button()

    table_def = TableEditor(columns=[
        ObjectColumn(name='position'),
        ObjectColumn(name='focal_point'),
        ObjectColumn(name='view_up'),
        ObjectColumn(name='clipping_range'),
        ObjectColumn(name='num_steps')
    ],
                            reorderable=True,
                            deletable=True,
                            sortable=True,
                            sort_model=True,
                            show_toolbar=True,
                            selection_mode='row',
                            selected='reset_position')

    default_view = View(
        VGroup(
            HGroup(Item('camera', show_label=False),
                   Item('recenter', show_label=False),
                   label='Camera'),
            HGroup(Item('snapshot', show_label=False),
                   Item('play', show_label=False),
                   Item('export_frames', show_label=False),
                   Item('reset_path', show_label=False),
                   Item('save_path', show_label=False),
                   Item('load_path', show_label=False),
                   Item('export_path', show_label=False),
                   Item('export_filename'),
                   Item('periodic'),
                   Item('fps'),
                   label='Playback'),
            VGroup(Item('positions', show_label=False, editor=table_def),
                   label='Camera Path'),
        ),
        resizable=True,
        title="Camera Path Editor",
    )

    def _reset_position_changed(self, old, new):
        if new is None: return
        cam = self.scene.camera
        cam.position = new.position
        cam.focal_point = new.focal_point
        cam.view_up = new.view_up
        cam.clipping_range = new.clipping_range
        self.scene.render()

    def __init__(self, **traits):
        HasTraits.__init__(self, **traits)

    def take_snapshot(self):
        cam = self.scene.camera
        self.positions.append(
            CameraPosition(position=cam.position,
                           focal_point=cam.focal_point,
                           view_up=cam.view_up,
                           clipping_range=cam.clipping_range,
                           distance=cam.distance,
                           orientation_wxyz=cam.orientation_wxyz))

    def _export_path_fired(self):
        dlg = pyface.FileDialog(
            action='save as',
            wildcard="*.cpath",
        )
        if dlg.open() == pyface.OK:
            print "Saving:", dlg.path
            self.export_camera_path(dlg.path)

    def export_camera_path(self, fn):
        to_dump = dict(positions=[],
                       focal_points=[],
                       view_ups=[],
                       clipping_ranges=[],
                       distances=[],
                       orientation_wxyzs=[])

        def _write(cam):
            to_dump['positions'].append(cam.position)
            to_dump['focal_points'].append(cam.focal_point)
            to_dump['view_ups'].append(cam.view_up)
            to_dump['clipping_ranges'].append(cam.clipping_range)
            to_dump['distances'].append(cam.distance)
            to_dump['orientation_wxyzs'].append(cam.orientation_wxyz)

        self.step_through(0.0, callback=_write)
        pickle.dump(to_dump, open(fn, "wb"))

    def _save_path_fired(self):
        dlg = pyface.FileDialog(
            action='save as',
            wildcard="*.cpath",
        )
        if dlg.open() == pyface.OK:
            print "Saving:", dlg.path
            self.dump_camera_path(dlg.path)

    def dump_camera_path(self, fn):
        to_dump = dict(positions=[],
                       focal_points=[],
                       view_ups=[],
                       clipping_ranges=[],
                       distances=[],
                       orientation_wxyzs=[],
                       num_stepss=[])
        for p in self.positions:
            to_dump['positions'].append(p.position)
            to_dump['focal_points'].append(p.focal_point)
            to_dump['view_ups'].append(p.view_up)
            to_dump['clipping_ranges'].append(p.clipping_range)
            to_dump['distances'].append(p.distance)
            to_dump['num_stepss'].append(p.num_steps)  # stupid s
            to_dump['orientation_wxyzs'].append(p.orientation_wxyz)
        pickle.dump(to_dump, open(fn, "wb"))

    def _load_path_fired(self):
        dlg = pyface.FileDialog(
            action='open',
            wildcard="*.cpath",
        )
        if dlg.open() == pyface.OK:
            print "Loading:", dlg.path
            self.load_camera_path(dlg.path)

    def load_camera_path(self, fn):
        to_use = pickle.load(open(fn, "rb"))
        self.positions = []
        for i in range(len(to_use['positions'])):
            dd = {}
            for kw in to_use:
                # Strip the s
                dd[kw[:-1]] = to_use[kw][i]
            self.positions.append(CameraPosition(**dd))

    def _recenter_fired(self):
        self.camera.focal_point = self.center
        self.scene.render()

    def _snapshot_fired(self):
        self.take_snapshot()

    def _play_fired(self):
        self.step_through()

    def _export_frames_fired(self):
        self.step_through(save_frames=True)

    def _reset_path_fired(self):
        self.positions = []

    def step_through(self, pause=1.0, callback=None, save_frames=False):
        cam = self.scene.camera
        frame_counter = 0
        if self.periodic:
            cyclic_pos = self.positions + [self.positions[0]]
        else:
            cyclic_pos = self.positions
        for i in range(len(cyclic_pos) - 1):
            pos1 = cyclic_pos[i]
            pos2 = cyclic_pos[i + 1]
            r = pos1.num_steps
            for p in range(pos1.num_steps):
                po = _interpolate(pos1.position, pos2.position, p, r)
                fp = _interpolate(pos1.focal_point, pos2.focal_point, p, r)
                vu = _interpolate(pos1.view_up, pos2.view_up, p, r)
                cr = _interpolate(pos1.clipping_range, pos2.clipping_range, p,
                                  r)
                _set_cpos(cam, po, fp, vu, cr)
                self.scene.render()
                if callback is not None: callback(cam)
                if save_frames:
                    self.scene.save("%s_%0.5d.png" %
                                    (self.export_filename, frame_counter))
                else:
                    time.sleep(pause * 1.0 / self.fps)
                frame_counter += 1
Пример #17
0
class YTScene(HasTraits):

    # Traits
    importer = Instance(HierarchyImporter)
    pf = Delegate("importer")
    min_grid_level = Delegate("importer")
    number_of_levels = Delegate("importer")
    field = Delegate("importer")
    center = CArray(shape=(3, ), dtype='float64')
    center_on_max = Delegate("importer")
    smoothed = Delegate("importer")
    cache = Delegate("importer")
    show_grids = Delegate("importer")

    camera_path = Instance(CameraControl)
    #window = Instance(ivtk.IVTKWithCrustAndBrowser)
    #python_shell = Delegate('window')
    #scene = Delegate('window')
    scene = Instance(HasTraits)
    operators = List(HasTraits)

    # State variables
    _grid_boundaries_actor = None

    # Views
    def _window_default(self):
        # Should experiment with passing in a pipeline browser
        # that has two root objects -- one for TVTKBases, i.e. the render
        # window, and one that accepts our objects
        return ivtk.IVTKWithCrustAndBrowser(size=(800, 600), stereo=1)

    def _camera_path_default(self):
        return CameraControl(yt_scene=self, camera=self.scene.camera)

    def __init__(self, **traits):
        HasTraits.__init__(self, **traits)
        max_level = min(self.pf.h.max_level,
                        self.min_grid_level + self.number_of_levels - 1)
        self.extracted_pf = ExtractedParameterFile(self.pf,
                                                   self.min_grid_level,
                                                   max_level,
                                                   offset=None)
        self.extracted_hierarchy = self.extracted_pf.h
        self._hdata_set = tvtk.HierarchicalBoxDataSet()
        self._ugs = []
        self._grids = []
        self._min_val = 1e60
        self._max_val = -1e60
        gid = 0
        if self.cache:
            for grid_set in self.extracted_hierarchy.get_levels():
                for grid in grid_set:
                    grid[self.field]
        for l, grid_set in enumerate(self.extracted_hierarchy.get_levels()):
            gid = self._add_level(grid_set, l, gid)
        if self.show_grids:
            self.toggle_grid_boundaries()

    def _center_default(self):
        return self.extracted_hierarchy._convert_coords([0.5, 0.5, 0.5])

    def do_center_on_max(self):
        self.center = self.extracted_hierarchy._convert_coords(
            self.pf.h.find_max("Density")[1])
        self.scene.camera.focal_point = self.center

    def _add_level(self, grid_set, level, gid):
        for grid in grid_set:
            self._hdata_set.set_refinement_ratio(level, 2)
            gid = self._add_grid(grid, gid, level)
        return gid

    def _add_grid(self, grid, gid, level=0):
        mylog.debug("Adding grid %s on level %s (%s)", grid.id, level,
                    grid.Level)
        if grid in self._grids: return
        self._grids.append(grid)

        scalars = grid.get_vertex_centered_data(self.field,
                                                smoothed=self.smoothed)

        left_index = grid.get_global_startindex()
        origin = grid.LeftEdge
        dds = grid.dds
        right_index = left_index + scalars.shape - 1
        ug = tvtk.UniformGrid(origin=origin,
                              spacing=dds,
                              dimensions=grid.ActiveDimensions + 1)
        if self.field not in self.pf.field_info or \
            self.pf.field_info[self.field].take_log:
            scalars = na.log10(scalars)
        ug.point_data.scalars = scalars.transpose().ravel()
        ug.point_data.scalars.name = self.field
        if grid.Level != self.min_grid_level + self.number_of_levels - 1:
            ug.cell_visibility_array = grid.child_mask.transpose().ravel()
        else:
            ug.cell_visibility_array = na.ones(grid.ActiveDimensions,
                                               dtype='int').ravel()
        self._ugs.append((grid, ug))
        self._hdata_set.set_data_set(level, gid, left_index, right_index, ug)

        self._min_val = min(self._min_val, scalars.min())
        self._max_val = max(self._max_val, scalars.max())

        gid += 1
        return gid

    def _add_data_to_ug(self, field):
        for g, ug in self._ugs:
            scalars_temp = grid.get_vertex_centered_data(
                field, smoothed=self.smoothed)
            ii = ug.point_data.add_array(scalars_temp.transpose().ravel())
            ug.point_data.get_array(ii).name = field

    def zoom(self, dist, unit='1'):
        vec = self.scene.camera.focal_point - \
              self.scene.camera.position
        self.scene.camera.position += \
            vec * dist/self._grids[0].pf[unit]
        self.scene.render()

    def toggle_grid_boundaries(self):
        if self._grid_boundaries_actor is None:
            # We don't need to track this stuff right now.
            ocf = tvtk.OutlineCornerFilter(
                executive=tvtk.CompositeDataPipeline(), corner_factor=0.5)
            ocf.input = self._hdata_set
            ocm = tvtk.HierarchicalPolyDataMapper(
                input_connection=ocf.output_port)
            self._grid_boundaries_actor = tvtk.Actor(mapper=ocm)
            self.scene.add_actor(self._grid_boundaries_actor)
        else:
            self._grid_boundaries_actor.visibility = \
            (not self._grid_boundaries_actor.visibility)

    def _add_sphere(self, origin=(0.0, 0.0, 0.0), normal=(0, 1, 0)):
        sphere = tvtk.Sphere(center=origin, radius=0.25)
        cutter = tvtk.Cutter(executive=tvtk.CompositeDataPipeline(),
                             cut_function=sphere)
        cutter.input = self._hdata_set
        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
        smap = tvtk.HierarchicalPolyDataMapper(
            scalar_range=(self._min_val, self._max_val),
            lookup_table=lut_manager.lut,
            input_connection=cutter.output_port)
        sactor = tvtk.Actor(mapper=smap)
        self.scene.add_actors(sactor)
        return sphere, lut_manager

    def _add_plane(self, origin=(0.0, 0.0, 0.0), normal=(0, 1, 0)):
        plane = tvtk.Plane(origin=origin, normal=normal)
        cutter = tvtk.Cutter(executive=tvtk.CompositeDataPipeline(),
                             cut_function=plane)
        cutter.input = self._hdata_set
        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
        smap = tvtk.HierarchicalPolyDataMapper(
            scalar_range=(self._min_val, self._max_val),
            lookup_table=lut_manager.lut,
            input_connection=cutter.output_port)
        sactor = tvtk.Actor(mapper=smap)
        self.scene.add_actors(sactor)
        return plane, lut_manager

    def add_plane(self, origin=(0.0, 0.0, 0.0), normal=(0, 1, 0)):
        self.operators.append(self._add_plane(origin, normal))
        return self.operators[-1]

    def _add_axis_plane(self, axis):
        normal = [0, 0, 0]
        normal[axis] = 1
        np, lut_manager = self._add_plane(self.center, normal=normal)
        LE = self.extracted_hierarchy.min_left_edge
        RE = self.extracted_hierarchy.max_right_edge
        self.operators.append(
            MappingPlane(vmin=LE[axis],
                         vmax=RE[axis],
                         vdefault=self.center[axis],
                         post_call=self.scene.render,
                         plane=np,
                         axis=axis,
                         coord=0.0,
                         lut_manager=lut_manager,
                         scene=self.scene))

    def add_x_plane(self):
        self._add_axis_plane(0)
        return self.operators[-1]

    def add_y_plane(self):
        self._add_axis_plane(1)
        return self.operators[-1]

    def add_z_plane(self):
        self._add_axis_plane(2)
        return self.operators[-1]

    def add_contour(self, val=None):
        if val is None:
            if self._min_val != self._min_val:
                self._min_val = 1.0
            val = (self._max_val + self._min_val) * 0.5
        cubes = tvtk.MarchingCubes(executive=tvtk.CompositeDataPipeline())
        cubes.input = self._hdata_set
        cubes.set_value(0, val)
        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
        cube_mapper = tvtk.HierarchicalPolyDataMapper(
            input_connection=cubes.output_port, lookup_table=lut_manager.lut)
        cube_mapper.color_mode = 'map_scalars'
        cube_mapper.scalar_range = (self._min_val, self._max_val)
        cube_actor = tvtk.Actor(mapper=cube_mapper)
        self.scene.add_actors(cube_actor)
        self.operators.append(
            MappingMarchingCubes(operator=cubes,
                                 vmin=self._min_val,
                                 vmax=self._max_val,
                                 vdefault=val,
                                 mapper=cube_mapper,
                                 post_call=self.scene.render,
                                 lut_manager=lut_manager,
                                 scene=self.scene))
        return self.operators[-1]

    def add_isocontour(self, val=None):
        if val is None: val = (self._max_val + self._min_val) * 0.5
        isocontour = tvtk.ContourFilter(executive=tvtk.CompositeDataPipeline())
        isocontour.input = self._hdata_set
        isocontour.generate_values(1, (val, val))
        lut_manager = LUTManager(data_name=self.field, scene=self.scene)
        isocontour_normals = tvtk.PolyDataNormals(
            executive=tvtk.CompositeDataPipeline())
        isocontour_normals.input_connection = isocontour.output_port
        iso_mapper = tvtk.HierarchicalPolyDataMapper(
            input_connection=isocontour_normals.output_port,
            lookup_table=lut_manager.lut)
        iso_mapper.scalar_range = (self._min_val, self._max_val)
        iso_actor = tvtk.Actor(mapper=iso_mapper)
        self.scene.add_actors(iso_actor)
        self.operators.append(
            MappingIsoContour(operator=isocontour,
                              vmin=self._min_val,
                              vmax=self._max_val,
                              vdefault=val,
                              mapper=iso_mapper,
                              post_call=self.scene.render,
                              lut_manager=lut_manager,
                              scene=self.scene))
        return self.operators[-1]
Пример #18
0
class Bar(HasTraits):
    foo = Instance(Foo, ())
    s = Delegate('foo')
Пример #19
0
    import SequenceTypes, container_delegate

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

# Group orientation trait
Orientation = Trait( 'vertical',
                     TraitPrefixList( 'vertical', 'horizontal' ) )

# Group layout trait
Layout = Trait( 'normal',
                TraitPrefixList( 'normal', 'split', 'tabbed', 'flow' ) )

# Delegate trait to the object being "shadowed"
ShadowDelegate = Delegate( 'shadow' )

# Amount of padding to add around item
Padding = Range( 0, 15, desc = 'amount of padding to add around each item' )

#-------------------------------------------------------------------------------
#  'Group' class:
#-------------------------------------------------------------------------------

class Group ( ViewSubElement ):
    """Represents a grouping of items in a user interface view.
    """
    #---------------------------------------------------------------------------
    # Trait definitions:
    #---------------------------------------------------------------------------
Пример #20
0
class GraphViewModel(ModelView):
    """ Defines a view model for Graphs.  """

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

    # File path to to use for saving.
    save_file = File

    # Is the tree view of the network displayed?
    show_tree = Bool(True, desc="that the network tree view is visible")

    # All graphs, subgraphs and clusters.
#    all_graphs = Property(List(Instance(HasTraits)))
    all_graphs = Delegate("model")

    # Select graph when adding to the graph?
    select_graph = Bool(True)

    # Working graph instance.
    selected_graph = Instance(BaseGraph, allow_none=False)

    # Exit confirmation.
    prompt_on_exit = Bool(False, desc="exit confirmation request")

    # Representation of the graph in the Dot language.
    dot_code = Code

    # Parse the dot_code and replace the existing model.
    parse_dot_code = Button("Parse", desc="dot code parsing action that "
        "replaces the existing model.")

    #--------------------------------------------------------------------------
    #  Views:
    #--------------------------------------------------------------------------

    # Default model view.
    traits_view = View(
        HGroup(
            Item(
                name="model", editor=graph_tree_editor,
                show_label=False, id=".tree_editor",
                visible_when="show_tree==True", width=.14
            ),
            Item("model", show_label=False),
        ),
        id="graph_view_model.graph_view", title="Godot", icon=frame_icon,
        resizable=True, style="custom", width=.81, height=.81, kind="live",
        buttons=NoButtons, menubar=menubar,
#        toolbar=toolbar,
        dock="vertical",
#        statusbar=[StatusItem(name="status", width=0.5),
#            StatusItem(name="versions", width=200)]
    )

    # File selection view.
#    file_view = View(
#        Item(name="file", id="file"),#, editor=FileEditor(entries=6)),
#        id="graph_view_model.file_view", title="Select a file",
#        icon=frame_icon, resizable=True, width=.3, kind="livemodal",
#        buttons=OKCancelButtons
#    )

    # Graph selection view.
    all_graphs_view = View(
        Item(name   = "selected_graph",
             editor = InstanceEditor( name     = "all_graphs",
                                      editable = False),
             label  = "Graph"),
        Item("select_graph", label="Always ask?"),
        icon = frame_icon, kind = "livemodal", title = "Select a graph",
        buttons = OKCancelButtons, close_result = False
    )

    # Model view options view.
    options_view = View(
        Item("prompt_on_exit"),
        "_",
        Item("select_graph"),
        Item("selected_graph",
             enabled_when = "not select_graph",
             editor       = InstanceEditor( name     = "all_graphs",
                                            editable = False ),
             label        = "Graph" ),
        icon = frame_icon, kind = "livemodal", title = "Options",
        buttons = OKCancelButtons, close_result = True
    )

    # Text representation of the graph viewed in a text editor
    dot_code_view = View(
        Item("dot_code", show_label=False, style="custom"),
        Item("parse_dot_code", show_label=False),
        id="godot.view_model.dot_code",
        icon = frame_icon, kind = "livemodal",
        title = "Dot Code", resizable = True,
        buttons = [], height = .3, width = .3
    )

    #--------------------------------------------------------------------------
    #  Trait intialisers:
    #--------------------------------------------------------------------------

    def _selected_graph_default(self):
        """ Trait intialiser.
        """
        return self.model


    def _parse_dot_code_fired(self):
        """ Parses the dot_code string and replaces the existing model.
        """
        parser = GodotDataParser()
        graph  = parser.parse_dot_data(self.dot_code)
        if graph is not None:
            self.model = graph

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

    def _model_changed(self, old, new):
        """ Handles the model changing.
        """
        self.selected_graph = new

    #--------------------------------------------------------------------------
    #  Action handlers:
    #--------------------------------------------------------------------------

    def new_model(self, info):
        """ Handles the new Graph action. """

        if info.initialized:
            retval = confirm(parent  = info.ui.control,
                             message = "Replace existing graph?",
                             title   = "New Graph",
                             default = YES)
            if retval == YES:
                self.model = Graph()


    def open_file(self, info):
        """ Handles the open action. """

        if not info.initialized: return # Escape.

#        retval = self.edit_traits(parent=info.ui.control, view="file_view")

        dlg = FileDialog( action = "open",
            wildcard = "Graphviz Files (*.dot, *.xdot, *.txt)|"
                "*.dot;*.xdot;*.txt|Dot Files (*.dot)|*.dot|"
                "All Files (*.*)|*.*|")

        if dlg.open() == OK:
            parser = GodotDataParser()
            model = parser.parse_dot_file(dlg.path)
            if model is not None:
                self.model = model
            else:
                print "error parsing: %s" % dlg.path

            self.save_file = dlg.path

        del dlg

#            fd = None
#            try:
#                fd = open(self.file, "rb")
#                parser = DotParser()
#                self.model = parser.parse_dot_file(self.file)
##            except:
##                error(parent=info.ui.control, title="Load Error",
##                    message="An error was encountered when loading\nfrom %s"
##                    % self.file)
#            finally:
#                if fd is not None:
#                    fd.close()


    def save(self, info):
        """ Handles saving the current model to the last file.
        """
        save_file = self.save_file

        if not isfile(save_file):
            self.save_as(info)
        else:
            fd = None
            try:
                fd = open(save_file, "wb")
                dot_code = str(self.model)
                fd.write(dot_code)
            finally:
                if fd is not None:
                    fd.close()


    def save_as(self, info):
        """ Handles saving the current model to file.
        """
        if not info.initialized:
            return

#        retval = self.edit_traits(parent=info.ui.control, view="file_view")

        dlg = FileDialog( action = "save as",
            wildcard = "Graphviz Files (*.dot, *.xdot, *.txt)|" \
                "*.dot;*.xdot;*.txt|Dot Files (*.dot)|*.dot|" \
                "All Files (*.*)|*.*|")

        if dlg.open() == OK:
            fd = None
            try:
                fd = open(dlg.path, "wb")
                dot_code = str(self.model)
                fd.write(dot_code)

                self.save_file = dlg.path

            except:
                error(parent=info.ui.control, title="Save Error",
                      message="An error was encountered when saving\nto %s"
                      % self.file)

            finally:
                if fd is not None:
                    fd.close()

        del dlg


    def configure_graph(self, info):
        """ Handles display of the graph dot traits.
        """
        if info.initialized:
            self.model.edit_traits(parent=info.ui.control,
                kind="live", view=attr_view)


    def configure_nodes(self, info):
        """ Handles display of the nodes editor.
        """
        if info.initialized:
            self.model.edit_traits(parent=info.ui.control,
                kind="live", view=nodes_view)


    def configure_edges(self, info):
        """ Handles display of the edges editor.
        """
        if info.initialized:
            self.model.edit_traits(parent=info.ui.control,
                kind="live", view=edges_view)


    def about_godot(self, info):
        """ Handles displaying a view about Godot.
        """
        if info.initialized:
            self.edit_traits(parent=info.ui.control,
                kind="livemodal", view=about_view)


    def add_node(self, info):
        """ Handles adding a Node to the graph.
        """
        if not info.initialized:
            return

        graph = self._request_graph(info.ui.control)

        if graph is None:
            return

        IDs = [v.ID for v in graph.nodes]
        node = Node(ID=make_unique_name("node", IDs))
        graph.nodes.append(node)

        retval = node.edit_traits(parent=info.ui.control, kind="livemodal")

        if not retval.result:
            graph.nodes.remove(node)


    def add_edge(self, info):
        """ Handles adding an Edge to the graph.
        """
        if not info.initialized:
            return

        graph = self._request_graph(info.ui.control)

        if graph is None:
            return

        n_nodes = len(graph.nodes)
        IDs = [v.ID for v in graph.nodes]

        if n_nodes == 0:
            tail_node = Node(ID=make_unique_name("node", IDs))
            head_name = make_unique_name("node", IDs + [tail_node.ID])
            head_node = Node(ID=head_name)
        elif n_nodes == 1:
            tail_node = graph.nodes[0]
            head_node = Node(ID=make_unique_name("node", IDs))
        else:
            tail_node = graph.nodes[0]
            head_node = graph.nodes[1]

        edge = Edge(tail_node, head_node, _nodes=graph.nodes)

        retval = edge.edit_traits(parent=info.ui.control, kind="livemodal")

        if retval.result:
            graph.edges.append(edge)


    def add_subgraph(self, info):
        """ Handles adding a Subgraph to the main graph.
        """
        if not info.initialized:
            return

        graph = self._request_graph(info.ui.control)

        if graph is not None:
            subgraph = Subgraph()#root=graph, parent=graph)
            retval = subgraph.edit_traits(parent = info.ui.control,
                                          kind   = "livemodal")
            if retval.result:
                graph.subgraphs.append(subgraph)


    def add_cluster(self, info):
        """ Handles adding a Cluster to the main graph. """

        if not info.initialized:
            return

        graph = self._request_graph(info.ui.control)

        if graph is not None:
            cluster = Cluster()#root=graph, parent=graph)
            retval = cluster.edit_traits(parent = info.ui.control,
                                         kind   = "livemodal")
            if retval.result:
                graph.clusters.append(cluster)


    def _request_graph(self, parent=None):
        """ Displays a dialog for graph selection if more than one exists.
            Returns None if the dialog is canceled.
        """

        if (len(self.all_graphs) > 1) and (self.select_graph):
            retval = self.edit_traits(parent = parent,
                                      view   = "all_graphs_view")
            if not retval.result:
                return None

        if self.selected_graph is not None:
            return self.selected_graph
        else:
            return self.model


    def toggle_tree(self, info):
        """ Handles displaying the tree view """

        if info.initialized:
            self.show_tree = not self.show_tree


    def godot_options(self, info):
        """ Handles display of the options menu. """

        if info.initialized:
            self.edit_traits( parent = info.ui.control,
                              kind   = "livemodal",
                              view   = "options_view" )


    def configure_dot_code(self, info):
        """ Handles display of the dot code in a text editor.
        """
        if not info.initialized:
            return

        self.dot_code = str(self.model)
        retval = self.edit_traits( parent = info.ui.control,
                                   kind   = "livemodal",
                                   view   = "dot_code_view" )
#        if retval.result:
#            parser = DotParser()
#            graph  = parser.parse_dot_data(self.dot_code)
#            if graph is not None:
#                self.model = graph

    #---------------------------------------------------------------------------
    #  Handle the user attempting to exit Godot:
    #---------------------------------------------------------------------------

    def on_exit(self, info):
        """ Handles the user attempting to exit Godot.
        """
        if self.prompt_on_exit:# and (not is_ok):
            retval = confirm(parent  = info.ui.control,
                             message = "Exit Godot?",
                             title   = "Confirm exit",
                             default = YES)
            if retval == YES:
                self._on_close( info )
        else:
            self._on_close( info )