Ejemplo n.º 1
0
class BaseDataSource(HasStrictTraits):

    has_data = Bool(False)  # set True when data is loaded

    description = Str
    kind = data_source_kinds
    voxel_sizes = Tuple(Float(1.0), Float(1.0), Float(1.0))
    pixel_sizes = Tuple(Float(1.0), Float(1.0))

    tables = Dict

    is_image_stack = Bool(True)
    is_image_timeseries = Bool(False)
    is_image_stack_timeseries = Bool(False)

    @property
    def data(self):
        return _data[0]

    @data.setter
    def data(self, data):
        _data[0] = data
        self.has_data = data is not None
        if data is None:
            print '%s.data.setter: removing data' % (self.__class__.__name__)
        else:
            print '%s.data.setter: setting data' % (self.__class__.__name__)

    def _kind_changed(self):
        self.is_image_stack = self.kind == 'image_stack'
        self.is_image_timeseries = self.kind == 'image_timeseries'
        self.is_image_stack_timeseries = self.kind == 'image_stack_timeseries'
Ejemplo n.º 2
0
class MayaviMlabPreferencesPage(PreferencesPage):
    """ Preferences page for Mayavi. """

    #### 'PreferencesPage' interface ##########################################

    # The page's category (e.g. 'General/Appearance'). The empty string means
    # that this is a top-level page.
    category = 'Mayavi'

    # 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 = ''

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

    # The path to the preferences node that contains the preferences.
    preferences_path = 'enthought.mayavi.mlab'

    #### Preferences ##########################################################

    # The mlab backend to use.
    backend = Enum('auto',
                   'envisage',
                   'simple',
                   'test',
                   desc='the mlab backend to use')

    # The background color of the renderer.
    background_color = Tuple(Range(0., 1.),
                             Range(0., 1.),
                             Range(0., 1.),
                             editor=RGBColorEditor,
                             desc='the background color of the scene')

    # The foreground color of the renderer.
    foreground_color = Tuple(Range(0., 1.),
                             Range(0., 1.),
                             Range(0., 1.),
                             editor=RGBColorEditor,
                             desc='the foreground color of the scene')

    # Offscreen rendering.
    offscreen = Bool(desc='if mlab should use offscreen rendering'
                     ' (no window will show up in this case)')

    ######################################################################
    # Traits UI view.

    traits_view = View(Group(
        Item('backend'),
        Item('background_color'),
        Item('foreground_color'),
        Item('offscreen'),
    ),
                       resizable=True)
Ejemplo n.º 3
0
class ISplashScreen(IWindow):
    """ The interface for a splash screen. """

    #### 'ISplashScreen' interface ############################################

    # The image to display on the splash screen.
    image = Instance(ImageResource, ImageResource('splash'))

    # If log messages are to be displayed then this is the logging level. See
    # the Python documentation for the 'logging' module for more details.
    log_level = Int(logging.DEBUG)

    # Should the splash screen display log messages in the splash text?
    show_log_messages = Bool(True)

    # Optional text to display on top of the splash image.
    text = Unicode

    # The text color.
    # FIXME v3: When TraitsUI supports PyQt then change this to 'Color',
    # (unless that needs the toolkit to be selected too soon, in which case it
    # may need to stay as Any - or Str?)
    #text_color = WxColor('black')
    text_color = Any

    # The text font.
    # FIXME v3: When TraitsUI supports PyQt then change this back to
    # 'Font(None)' with the actual default being toolkit specific.
    #text_font = Font(None)
    text_font = Any

    # The x, y location where the text will be drawn.
    # FIXME v3: Remove this.
    text_location = Tuple(5, 5)
Ejemplo n.º 4
0
class RegularPolygon(Polygon):
    center = Tuple(Float, Float)
    sides = Int(6)
    radius = Float(1.0)
    theta = Float(0.)  # orientation in radians
    sequence = 'rectangles'

    def __init__(self):
        self._update_vertices()

    def _sides_changed(self, old, new):
        self._update_verts()

    def _theta_changed(self, old, new):
        self._update_verts()

    def _radius_changed(self, old, new):
        self._update_verts()

    def _update_verts(self):

        theta = 2 * npy.pi / self.sides * npy.arange(self.sides) + self.theta
        x, y = self.center

        xy = npy.zeros((self.sides, 2))

        xy[:, 0] = x + self.radius * npy.cos(theta)
        xy[:, 1] = y + self.radius * npy.sin(theta)

        self.vertices = xy
Ejemplo n.º 5
0
class ImageSilhouette(HasTraits):
    """Class representing a silhouette image of segmented cells."""
    label_image = Array()
    object_slices = List(Tuple(slice, slice), ())

    def __init__(self, *args, **kwargs):
        """
        Construct an ImageSilhouette object from a PyTables node containing
        a binary mask array.
        """
        super(ImageSilhouette, self).__init__(*args, **kwargs)

        # Label the binary array from the HDF5 file
        self.label_image, number = ndimage.label(self.node.read())

        if DEBUG:
            print "%d objects segmented." % number

        # Get slices that index the array
        self.object_slices = ndimage.find_objects(self.label_image)

    def __len__(self):
        return len(self.object_slices)

    def __getitem__(self, key):
        if type(key) is slice:
            indices = islice(xrange(len(self)), *key.indices(len(self)))
            return [self[nkey] for nkey in indices]
        else:
            image = self.label_image[self.object_slices[key]] == (key + 1)
            return ObjectSilhouette(image=image)

    def __contains__(self):
        raise TypeError("Containment checking not supported: %s" % str(self))
Ejemplo n.º 6
0
class DataPrinter(AbstractController):
    point0 = Tuple(Float,Float)
    point1 = Tuple(Float,Float)
    process = Function
    
    def dispatch(self, event, suffix):
            if suffix in ('left_up', 'left_down') and event.handled == False:
                x = self.component.x_mapper.map_data(event.x)
                y = self.component.y_mapper.map_data(event.y)
                if suffix == 'left_down':
                    self.point0 = x,y
                else:
                    self.point1 = x,y
                    self.process(self.point0, self.point1)
                    
    def _process_default(self):
        def process(point0, point1):
            print('selection', point0, point1)
        return process
Ejemplo n.º 7
0
class ToolBarManager(ActionManager):
    """ A tool bar manager realizes itself in errr, a tool bar control. """

    #### 'ToolBarManager' interface ###########################################

    # The size of tool images (width, height).
    image_size = Tuple((16, 16))

    # The orientation of the toolbar.
    orientation = Enum('horizontal', 'vertical')

    # Should we display the name of each tool bar tool under its image?
    show_tool_names = Bool(True)

    # Should we display the horizontal divider?
    show_divider = Bool(True)

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

    # Cache of tool images (scaled to the appropriate size).
    _image_cache = Instance(ImageCache)

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

    def __init__(self, *args, **traits):
        """ Creates a new tool bar manager. """

        # Base class contructor.
        super(ToolBarManager, self).__init__(*args, **traits)

        # An image cache to make sure that we only load each image used in the
        # tool bar exactly once.
        self._image_cache = ImageCache(self.image_size[0], self.image_size[1])

        return

    ###########################################################################
    # 'ToolBarManager' interface.
    ###########################################################################

    def create_tool_bar(self, parent, controller=None):
        """ Creates a tool bar. """

        # If a controller is required it can either be set as a trait on the
        # tool bar manager (the trait is part of the 'ActionManager' API), or
        # passed in here (if one is passed in here it takes precedence over the
        # trait).
        if controller is None:
            controller = self.controller

        return None
Ejemplo n.º 8
0
class FigText(HasStrictTraits):
    """Defines figure settable text attributes
    """
    #: text that is displayed
    text = Str('')
    #: position of the text, shoud be a tuple (0.2,0.2) by default
    position = Tuple((0.2, 0.2))

    view = View(Item('text', style='custom'),
                'position',
                resizable=True,
                width=200)
Ejemplo n.º 9
0
class MlabPreferencesHelper(PreferencesHelper):

    # The preferences path for which we use.
    preferences_path = 'enthought.mayavi.mlab'

    ######################################################################
    # Our preferences.

    # The mlab backend to use.
    backend = Enum('auto',
                   'envisage',
                   'simple',
                   'test',
                   desc='the mlab backend to use')

    # The background color of the renderer.
    background_color = Tuple(Range(0., 1.),
                             Range(0., 1.),
                             Range(0., 1.),
                             editor=RGBColorEditor,
                             desc='the background color of the scene')

    # The foreground color of the renderer.
    foreground_color = Tuple(Range(0., 1.),
                             Range(0., 1.),
                             Range(0., 1.),
                             editor=RGBColorEditor,
                             desc='the foreground color of the scene')

    # Offscreen rendering.
    offscreen = Bool(desc='if mlab should use offscreen rendering'
                     ' (no window will show up in this case)')

    ######################################################################
    # Traits UI view.

    traits_view = View(Group(Item('backend'), Item('background_color'),
                             Item('foreground_color'), Item('offscreen')),
                       resizable=True)
Ejemplo n.º 10
0
class DoublePendulumComponent(Component):
    gui = Instance("DoublePendulumGUI")
    m1 = DelegatesTo("gui")
    m2 = DelegatesTo("gui")
    l1 = DelegatesTo("gui")
    l2 = DelegatesTo("gui")
    p = Tuple((0.0, 0.0, 0.0, 0.0))
    event_state = Enum('normal', 'adjusting')

    def _draw_overlay(self, gc, view_bounds=None, mode="normal"):
        scale = (self.height - 100) / (self.l1 + self.l2) / 1.5
        self.cx = cx = self.width / 2
        self.cy = cy = self.height - 100
        x0, y0, x1, y1 = self.p

        gc.save_state()
        gc.translate_ctm(cx, cy)
        gc.scale_ctm(scale, scale)
        gc.set_line_width(3)
        gc.move_to(0, 0)
        gc.line_to(x0, y0)
        gc.line_to(x1, y1)
        gc.stroke_path()
        gc.arc(x0, y0, 3 * np.sqrt(self.m1) / scale, 0.0, 2 * np.pi)
        gc.arc(x1, y1, 3 * np.sqrt(self.m2) / scale, 0.0, 2 * np.pi)
        gc.draw_path()
        gc.restore_state()

    def normal_left_down(self, event):
        dx = event.x - self.cx
        dy = event.y - self.cy
        self.x0 = event.x
        self.y0 = event.y
        self.a1 = np.arctan2(dx, -dy)
        self.a2 = 0
        self.event_state = "adjusting"
        self.gui.animation = False
        self.gui.pendulum.init_status[:] = self.a1, 0, 0, 0

    def adjusting_mouse_move(self, event):
        dx = event.x - self.x0
        dy = event.y - self.y0
        self.a2 = np.arctan2(dx, -dy)
        self.gui.pendulum.init_status[:] = self.a1, self.a2, 0, 0

    def adjusting_left_up(self, event):
        self.gui.animation = True
        self.event_state = "normal"
Ejemplo n.º 11
0
class TupleEditorDemo(HasTraits):
    """ This class specifies the details of the TupleEditor demo.
    """

    # To demonstrate any given Trait editor, an appropriate Trait is required.
    tuple = Tuple(Color, Range(1, 4), Str)

    # Display specification (one Item per editor style)
    tuple_group = Group(Item('tuple', style='simple', label='Simple'),
                        Item('_'), Item('tuple',
                                        style='custom',
                                        label='Custom'), Item('_'),
                        Item('tuple', style='text', label='Text'), Item('_'),
                        Item('tuple', style='readonly', label='ReadOnly'))

    # Demo view
    view1 = View(tuple_group, title='TupleEditor', buttons=['OK'])
Ejemplo n.º 12
0
class PathPrimitive(HasTraits):
    """
    The path is an object that talks to the backends, and is an
    intermediary between the high level path artists like Line and
    Polygon, and the backend renderer
    """

    color = mtraits.Color('black')
    facecolor = mtraits.Color('blue')
    alpha = mtraits.Alpha(1.0)
    linewidth = mtraits.LineWidth(1.0)
    antialiased = mtraits.AntiAliased
    pathdata = Tuple(Array('b'), VertexArray)
    affine = Instance(Affine, ())

    def _pathdata_default(self):
        return (npy.array([0, 0], dtype=npy.uint8),
                npy.array([[0, 0], [0, 0]], npy.float_))
class FloodFillDemo(HasTraits):
    lo_diff = Array(np.float, (1, 4))
    hi_diff = Array(np.float, (1, 4))
    plot = Instance(Plot)
    point = Tuple((0, 0))
    option = Trait(u"以邻点为标准-4联通", Options)

    view = View(VGroup(
        VGroup(Item("lo_diff", label=u"负方向范围"), Item("hi_diff",
                                                     label=u"正方向范围"),
               Item("option", label=u"算法标志")),
        Item("plot", editor=ComponentEditor(), show_label=False),
    ),
                title=u"FloodFill Demo控制面板",
                width=500,
                height=450,
                resizable=True)

    def __init__(self, *args, **kwargs):
        self.lo_diff.fill(5)
        self.hi_diff.fill(5)
        self.img = cv.imread("lena.jpg")
        self.data = ArrayPlotData(img=self.img[:, :, ::-1])
        w = self.img.size().width
        h = self.img.size().height
        self.plot = Plot(self.data, padding=10, aspect_ratio=float(w) / h)
        self.plot.x_axis.visible = False
        self.plot.y_axis.visible = False
        self.imgplot = self.plot.img_plot("img", origin="top left")[0]
        self.imgplot.interpolation = "nearest"
        self.imgplot.overlays.append(
            PointPicker(application=self, component=self.imgplot))

        self.on_trait_change(self.redraw, "point,lo_diff,hi_diff,option")

    def redraw(self):
        img = self.img.clone()
        cv.floodFill(img,
                     cv.Point(*self.point),
                     cv.Scalar(255, 0, 0, 255),
                     loDiff=cv.asScalar(self.lo_diff[0]),
                     upDiff=cv.asScalar(self.hi_diff[0]),
                     flags=self.option_)
        self.data["img"] = img[:, :, ::-1]
Ejemplo n.º 14
0
class BodyViewer(HasTraits):
    body = Instance(Body)
    # x,dia
    diameters = Property(Array(numpy.float), depends_on='_cp,num_pts')
    num_pts = Int(20)
    # numpoints, xyx radius
    bodydata = Property(Array(dtype=numpy.float, shape=(None, 4)), depends_on='diameters')
    # inflexion_pt, min, max of the x coordinates
    data_props = Property(Tuple(Int, Float, Float), depends_on='body.data')
    @cached_property
    def _get_data_props(self):
        x = self.body.data[:, 0]
        if x[1] > x[0]:
            return numpy.argmax(x), numpy.min(x), numpy.max(x)
        else:
            return numpy.argmin(x), numpy.min(x), numpy.max(x)
    @cached_property
    def _get_diameters(self):
        x = numpy.linspace(numpy.min(self.data_props[2]), numpy.min(self.data_props[1]), self.num_pts)
        # datax, datay
        dxu, dyu = self.body.data[:self.data_props[0], 0], self.body.data[:self.data_props[0], 1]
        dxl, dyl = self.body.data[self.data_props[0] - 1:, 0], self.body.data[self.data_props[0] - 1:, 1]
        #iu, il = numpy.searchsorted(dxu, x), numpy.searchsorted(dxl, x)
        #yu = dyu[iu] - (dyu[iu] - dyu[iu - 1]) * (dxu[iu]-x) / (dxu[iu]-dx[iu-1])
        #yl = dyl[il] - (dyl[il] - dyl[il - 1]) * (dxl[il]-x) / (dxl[il]-dx[il-1])
        yu = numpy.interp(x, dxu, dyu)
        yl = numpy.interp(x, dxl, dyl)
        return numpy.array([x, abs(yu - yl)]).T
    # superior will take care of yduplicate
    @cached_property
    def _get_bodydata(self):
        ret = numpy.empty((self.num_pts, 4))
        # y,z coords are 0
        ret[:, 1:3] = 0.0
        # scale : radii (by sqrt(y*z)) and x
        # set the radii
        ret[:, 3] = self.diameters[:, 1] / 2 * (self.body.scale[1] * self.body.scale[2]) ** 0.5
        # set the radii centers
        ret[:, 0] = self.diameters[:, 0] * self.body.scale[0]
        # translate
        ret[:, :3] += self.body.translate
        return ret
Ejemplo n.º 15
0
class TupleEditorDemo(HasTraits):
    """ Defines the TupleEditor demo class.
    """

    # Define a trait to view:
    tuple = Tuple(Color, Range(1, 4), Str)

    # Display specification (one Item per editor style):
    tuple_group = Group(Item('tuple', style='simple', label='Simple'),
                        Item('_'), Item('tuple',
                                        style='custom',
                                        label='Custom'), Item('_'),
                        Item('tuple', style='text', label='Text'), Item('_'),
                        Item('tuple', style='readonly', label='ReadOnly'))

    # Demo view
    view = View(tuple_group,
                title='TupleEditor',
                buttons=['OK'],
                resizable=True)
Ejemplo n.º 16
0
class Point(HasStrictTraits):

    coordinates = Tuple(Int, Int, Int)
    x = Property(depends_on='coordinates')
    y = Property(depends_on='coordinates')
    z = Property(depends_on='coordinates')

    selected = Bool(False)
    value = Any

    @cached_property
    def _get_z(self):
        return self.coordinates[0]

    @cached_property
    def _get_y(self):
        return self.coordinates[1]

    @cached_property
    def _get_x(self):
        return self.coordinates[2]
Ejemplo n.º 17
0
class ToolPaletteManager(ActionManager):
    """ A tool bar manager realizes itself in a tool palette bar control. """

    #### 'ToolPaletteManager' interface #######################################
    
    # The size of tool images (width, height).
    image_size = Tuple((16, 16))

    # Should we display the name of each tool bar tool under its image?
    show_tool_names = Bool(True)

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

    # Cache of tool images (scaled to the appropriate size).
    _image_cache = Instance(ImageCache)

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

    def __init__(self, *args, **traits):
        """ Creates a new tool bar manager. """

        # Base class contructor.
        super(ToolPaletteManager, self).__init__(*args, **traits)

        # An image cache to make sure that we only load each image used in the
        # tool bar exactly once.
        self._image_cache = ImageCache(self.image_size[0], self.image_size[1])
        
        return

    ###########################################################################
    # 'ToolPaletteManager' interface.
    ###########################################################################

    def create_tool_palette(self, parent, controller=None):
        """ Creates a tool bar. """
        return None
Ejemplo n.º 18
0
class MDIApplicationWindow(ApplicationWindow):
    """ An MDI top-level application window.

    The application window has support for a menu bar, tool bar and a status
    bar (all of which are optional).

    Usage: Create a sub-class of this class and override the protected
    '_create_contents' method.

    """

    #### 'MDIApplicationWindow' interface #####################################

    # The workarea background image.
    background_image = Instance(ImageResource, ImageResource('background'))

    # Should we tile the workarea  background image?  The alternative is to
    # scale it.  Be warned that scaling the image allows for 'pretty' images,
    # but is MUCH slower than tiling.
    tile_background_image = Bool(True)

    # WX HACK FIXME
    # UPDATE: wx 2.6.1 does NOT fix this issue.
    _wx_offset = Tuple(Int, Int)

    ###########################################################################
    # 'MDIApplicationWindow' interface.
    ###########################################################################

    def create_child_window(self, title=None, is_mdi=True, float=True):
        """ Create a child window. """
        if title is None:
            title = self.title

        if is_mdi:
            return wx.MDIChildFrame(self.control, -1, title)
        else:
            if float:
                style = wx.DEFAULT_FRAME_STYLE | wx.FRAME_FLOAT_ON_PARENT
            else:
                style = wx.DEFAULT_FRAME_STYLE
            return wx.Frame(self.control, -1, title, style=style)

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

    def _create_contents(self, parent):
        """ Create the contents of the MDI window. """

        # Create the 'trim' widgets (menu, tool and status bars etc).
        self._create_trim_widgets(self.control)

        # The work-area background image (it can be tiled or scaled).
        self._image = self.background_image.create_image()
        self._bmp = self._image.ConvertToBitmap()

        # Frame events.
        #
        # We respond to size events to layout windows around the MDI frame.
        wx.EVT_SIZE(self.control, self._on_size)

        # Client window events.
        client_window = self.control.GetClientWindow()
        wx.EVT_ERASE_BACKGROUND(client_window, self._on_erase_background)

        self._wx_offset = client_window.GetPositionTuple()

        if AUI:
            # Let the AUI manager look after the frame.
            self._aui_manager.SetManagedWindow(self.control)

        contents = super(MDIApplicationWindow, self)._create_contents(parent)

        return contents

    def _create_control(self, parent):
        """ Create the toolkit-specific control that represents the window. """

        control = wx.MDIParentFrame(parent,
                                    -1,
                                    self.title,
                                    style=wx.DEFAULT_FRAME_STYLE,
                                    size=self.size,
                                    pos=self.position)

        return control

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

    def _tile_background_image(self, dc, width, height):
        """ Tiles the background image. """

        w = self._bmp.GetWidth()
        h = self._bmp.GetHeight()

        x = 0
        while x < width:
            y = 0
            while y < height:
                dc.DrawBitmap(self._bmp, x, y)
                y = y + h

            x = x + w

        return

    def _scale_background_image(self, dc, width, height):
        """ Scales the background image. """

        # Scale the image (if necessary).
        image = self._image
        if image.GetWidth() != width or image.GetHeight() != height:
            image = self._image.Copy()
            image.Rescale(width, height)

        # Convert it to a bitmap and draw it.
        dc.DrawBitmap(image.ConvertToBitmap(), 0, 0)

        return

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

    def _on_size(self, event):
        """ Called when the frame is resized. """

        wx.LayoutAlgorithm().LayoutMDIFrame(self.control)

        return

    def _on_erase_background(self, event):
        """ Called when the background of the MDI client window is erased. """

        # fixme: Close order...
        if self.control is None:
            return

        frame = self.control

        dc = event.GetDC()
        if not dc:
            dc = wx.ClientDC(frame.GetClientWindow())

        size = frame.GetClientSize()

        # Currently you have two choices, tile the image or scale it.  Be
        # warned that scaling is MUCH slower than tiling.
        if self.tile_background_image:
            self._tile_background_image(dc, size.width, size.height)

        else:
            self._scale_background_image(dc, size.width, size.height)

        return
Ejemplo n.º 19
0
class BSpline(Component):
    """ Defines a B-spline component. """

    # Pen used to draw the curve.
    pen = Instance(Pen, desc="Pen instance with which to draw the component")

    # Points defining the path of the polygon
    points = List(Tuple(Float, Float, labels=["x", "y"], cols=2, minlen=4),
                  desc="points defining the path of the curve")

    #--------------------------------------------------------------------------
    #  "Component" interface:
    #--------------------------------------------------------------------------

    # Background colour of the component
    bgcolor = "transparent"  #(1.0, 0.5, 0.5, 1.0)

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

    traits_view = View(
        Group(Item("pen", style="custom", show_label=False),
              label="Pen",
              show_border=True), Item("points", height=250, show_label=False))

    #--------------------------------------------------------------------------
    #  Draw component on the graphics context:
    #--------------------------------------------------------------------------

    def _draw_mainlayer(self, gc, view_bounds=None, mode="default"):
        """ Draws the Bezier component """

        if not self.points: return
        gc.save_state()
        try:
            gc.set_fill_color(self.pen.fill_color_)

            gc.set_line_width(self.pen.line_width)
            gc.set_stroke_color(self.pen.color_)

            gc.begin_path()
            start_x, start_y = self.points[0]
            gc.move_to(start_x, start_y)
            for triple in nsplit(self.points[1:], 3):
                x1, y1 = triple[0]
                x2, y2 = triple[1]
                end_x, end_y = triple[2]
                gc.curve_to(x1, y1, x2, y2, end_x, end_y)
                # One point overlap
                gc.move_to(end_x, end_y)
            gc.stroke_path()
        finally:
            gc.restore_state()

    def normal_left_down(self, event):
        """ Handles left mouse button clicks in 'normal' mode """

        print "Bezier selected at (%d, %d)" % (event.x, event.y)

    #--------------------------------------------------------------------------
    #  CoordinateBox interface
    #--------------------------------------------------------------------------

#    def is_in(self, x, y):
#        """ Tests if a point is within a certain tolerance of the line """
#
#        if not self.points: return False
#
#        # First-pass bounding box check
#        minx, miny = self.position
#        maxx = minx + self.bounds[0]
#        maxy = miny + self.bounds[1]
#        if (minx <= x <= maxx) and (miny <= y <= maxy):
#            print "IS IN?"
#            # P(t) = (1-t)^3 P0 + 3t(1-t)^2 P1 + 3(1-t)t^2 P2 + t^3 P3
#            x0, y0 = self.points[0]
#            for triple in nsplit(self.points[1:], 3):
#                x1, y1 = triple[0]
#                x2, y2 = triple[1]
#                x3, y3 = triple[2]
#
#                a = -x0 + 3*x1 - 3*x2 + x3
#                b = 3*x0 - 6*x1 + 3*x2
#                c = -3*x0 + 3*x1
#                d = x0 - x
#                t, r2, r3 = cubic(a, b, c, d)
#
#                ans = ((1-t)**3)*x0 + 3*t*((1-t)**2)*x1 + 3*(1-t)*(t**2)*x2 + (t**3)*x3
#
#                print "ANS:", ans
#
#                ay = -y0 + 3*y1 - 3*y2 + y3
#                by = 3*y0 - 6*y1 + 3*y2
#                cy = -3*y0 + 3*y1
#                dy = y0 - y
#                ry1, ry2, ry3 = cubic(ay, by, cy, dy)
#
#                print "Root 1:", t, ry1
#                print "Root 2:", r2, ry2
#                print "Root 3:", r3, ry3
#
##                x0, y0 = triple[2]
#            result = True
#        else:
#            result = False
#
#        return result

#--------------------------------------------------------------------------
#  "BSpline" interface:
#--------------------------------------------------------------------------

#    def _position_changed(self, old, new):
#        """ Handles the position of the component changing.
#        """
#        dx = new[0] - old[0]
#        dy = new[1] - old[1]
#        self.points = [(t[0] + dx, t[1] + dy) for t in self.points]

    @on_trait_change("pen.+,points")
    def _update(self):
        """ Updates the position and bounds of the component.
        """
        if not self.points:
            return

        x_points = [x for x, y in self.points]
        y_points = [y for x, y in self.points]

        x = min(x_points)
        x2 = max(x_points)
        y = min(y_points)
        y2 = max(y_points)

        self.position = [x, y]

        # If bounds are set to 0, horizontal/vertical lines will not render
        self.bounds = [max(x2 - x, 5), max(y2 - y, 5)]

        self.request_redraw()
Ejemplo n.º 20
0
class Perspective(HasTraits):
    """ The default perspective. """

    implements(IPerspective)

    # The ID of the default perspective.
    DEFAULT_ID = 'enthought.pyface.workbench.default'

    # The name of the default perspective.
    DEFAULT_NAME = 'Default'

    #### 'IPerspective' interface #############################################

    # The perspective's unique identifier (unique within a workbench window).
    id = Str(DEFAULT_ID)

    # The perspective's name.
    name = Str(DEFAULT_NAME)

    # The contents of the perspective.
    contents = List(PerspectiveItem)

    # The size of the editor area in this perspective. A value of (-1, -1)
    # indicates that the workbench window should choose an appropriate size
    # based on the sizes of the views in the perspective.
    editor_area_size = Tuple((-1, -1))

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

    # Should the editor area be shown in this perspective?
    show_editor_area = Bool(True)

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

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

        return 'Perspective(%s)' % self.id

    ###########################################################################
    # 'Perspective' interface.
    ###########################################################################

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

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

        # If no Id is specified then use the name.
        return self.name

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

    def create(self, window):
        """ Create the perspective in a workbench window.

        For most cases you should just be able to set the 'contents' trait to
        lay out views as required. However, you can override this method if
        you want to have complete control over how the perspective is created.
        
        """

        # Set the size of the editor area.
        if self.editor_area_size != (-1, -1):
            window.editor_area_size = self.editor_area_size

        # If the perspective has specific contents then add just those.
        if len(self.contents) > 0:
            self._add_contents(window, self.contents)

        # Otherwise, add all of the views defined in the window at their
        # default positions realtive to the editor area.
        else:
            self._add_all(window)

        # Activate the first view in every region.
        window.reset_views()

        return

    def show(self, window):
        """ Called when the perspective is shown in a workbench window. 

        The default implementation does nothing, but you can override this
        method if you want to do something whenever the perspective is
        activated.

        """

        return

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

    def _add_contents(self, window, contents):
        """ Adds the specified contents. """

        # If we are adding specific contents then we ignore any default view
        # visibility.
        #
        # fixme: This is a bit ugly! Why don't we pass the visibility in to
        # 'window.add_view'?
        for view in window.views:
            view.visible = False

        for item in contents:
            self._add_perspective_item(window, item)

        return

    def _add_perspective_item(self, window, item):
        """ Adds a perspective item to a window. """

        # If no 'relative_to' is specified then the view is positioned
        # relative to the editor area.
        if len(item.relative_to) > 0:
            relative_to = window.get_view_by_id(item.relative_to)

        else:
            relative_to = None

        # fixme: This seems a bit ugly, having to reach back up to the
        # window to get the view. Maybe its not that bad?
        view = window.get_view_by_id(item.id)
        if view is not None:
            # fixme: This is probably not the ideal way to sync view traits
            # and perspective_item traits.
            view.style_hint = item.style_hint
            # Add the view to the window.
            window.add_view(view, item.position, relative_to,
                            (item.width, item.height))

        else:
            # The reason that we don't just barf here is that a perspective
            # might use views from multiple plugins, and we probably want to
            # continue even if one or two of them aren't present.
            #
            # fixme: This is worth keeping an eye on though. If we end up with
            # a strict mode that throws exceptions early and often for
            # developers, then this might be a good place to throw one ;^)
            logger.error('missing view for perspective item <%s>' % item.id)

        return

    def _add_all(self, window):
        """ Adds *all* of the window's views defined in the window. """

        for view in window.views:
            if view.visible:
                self._add_view(window, view)

        return

    def _add_view(self, window, view):
        """ Adds a view to a window. """

        # If no 'relative_to' is specified then the view is positioned
        # relative to the editor area.
        if len(view.relative_to) > 0:
            relative_to = window.get_view_by_id(view.relative_to)

        else:
            relative_to = None

        # Add the view to the window.
        window.add_view(view, view.position, relative_to,
                        (view.width, view.height))

        return
Ejemplo n.º 21
0
class Polygon(Component):
    """ Component with Polygon traits """

    #--------------------------------------------------------------------------
    #  "Polygon" interface:
    #--------------------------------------------------------------------------

    # Pen used to draw the polygon
    pen = Instance(Pen, desc="the pen with which to draw the polygon")

    # Points defining the vertices of the polygon
    points = List(Tuple(Float, Float, labels=["x", "y"], cols=2),
                  desc="points defining the vertices of the polygon")

    # Is the polygon filled?
    filled = Bool(False, desc="Should the component be filled")

    # Rule to use to determine the inside of the polygon
    inside_rule = Trait(
        "winding", {
            "winding": FILL_STROKE,
            "oddeven": EOF_FILL_STROKE
        },
        desc="the rule to use to determine the inside of the polygon")

    # Background colour of the component
    bgcolor = "transparent"  #(1.0, 0.5, 0.5, 0.33)

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

    traits_view = View(
        Group(Item("pen", style="custom", show_label=False),
              label="Pen",
              show_border=True),
        Group(Item("points", height=250, show_label=False),
              label="Points",
              show_border=True), Item("filled"), Item("inside_rule"))

    #--------------------------------------------------------------------------
    #  Draw component on the graphics context:
    #--------------------------------------------------------------------------

    def _draw_mainlayer(self, gc, view_bounds=None, mode="default"):
        """ Draws a closed polygon """

        gc.save_state()
        try:
            #            self._draw_bounds(gc)
            if len(self.points) >= 2:
                # Set the drawing parameters.
                gc.set_fill_color(self.pen.fill_color_)
                gc.set_stroke_color(self.pen.color_)
                gc.set_line_width(self.pen.line_width)

                # Draw the path.
                gc.begin_path()
                #                x0 = self.points[0][0] - self.x
                #                y0 = self.points[0][1] + self.y
                #                gc.move_to(x0, y0)
                #                offset_points = [(x-self.x, y+self.y) for x, y in self.points]
                gc.lines(self.points)

                gc.close_path()
                if self.filled:
                    gc.draw_path(self.inside_rule_)
                else:
                    gc.stroke_path()
        finally:
            gc.restore_state()

    def is_in(self, point_x, point_y):
        """ Test if a point is within this polygonal region """

        point_array = array(((point_x, point_y), ))
        vertices = array(self.points)
        winding = self.inside_rule == "winding"
        result = points_in_polygon(point_array, vertices, winding)
        return result[0]

    def _draw_bounds(self, gc):
        """ Draws the component bounds for testing purposes """

        dx, dy = self.bounds
        x, y = self.position
        gc.rect(x, y, dx, dy)
        gc.stroke_path()

    def normal_left_down(self, event):
        """ Handles left mouse button clicks in 'normal' mode """

        print "Polygon selected at (%d, %d)" % (event.x, event.y)

    @on_trait_change("pen.+,points,filled")
    def _update(self):
        if not self.points: return
        x_points = [x for x, y in self.points]
        y_points = [y for x, y in self.points]
        x = min(x_points)
        x2 = max(x_points)
        y = min(y_points)
        y2 = max(y_points)
        self.position = [x, y]
        # Don't let bounds be set to 0, otherwise, horizontal and vertical
        # lines will not render because enable skips rendering items with
        # bounds=[0,0]
        self.bounds = [max(x2 - x, 1), max(y2 - y, 1)]

        self.request_redraw()
Ejemplo n.º 22
0
class FieldOperations(Filter):
    """
    Performs standard field operations. Use this filter in conjunction with SetActiveAttribute filter
    to view new/modified fields.
    """

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

    grid = Instance(tvtk.UnstructuredGrid, allow_none=False)

    operation = Enum('Sum', 'Multiply', 'Grad', 'Div', 'Curl', 'd/dx', 'd/dy',
                     'd/dz', 'Normalize', '2-norm', 'Reciprocal',
                     'Dot product', 'Cross product', 'Step', 'Natural log',
                     'Exponential', 'Scale/Offset', 'x-component',
                     'y-component', 'z-component', 'Python function')
    custom_function = Code(
        '\n# Define a single-line operation in terms of\n# the variable \'inputs\' which is an array of\n# input arrays. math and numpy are imported.\n# The operation must return a single array.\n'
    )

    # Input and output fields
    scale_factor = Float(1.0)
    offset = Float(0.0)
    field_name = String

    input_field = Tuple(field_name, scale_factor, offset)
    input_fields = List(input_field)

    output_field = Tuple(field_name, scale_factor, offset)

    # Buttons
    apply = Button
    clear = Button

    # Saving
    output_file = File
    save = Button

    # Renaming
    rename_field_old = String
    rename_field_new = String
    rename = Button

    # Deleting
    remove_field = String
    remove = Button

    ######################################################################
    # The view.
    ######################################################################

    field_editor = TupleEditor(labels=['Name', 'Scale factor', 'Offset'])
    field_list_editor = ListEditor(style='custom', rows=3, editor=field_editor)

    traits_view = \
        View(
            Group(
                Item(name='operation'),
                Group(
                    Item(name='custom_function'),
                    visible_when='operation==\'Python function\'',
                    show_labels=False
                ),
                Group(
                    Item(name='input_fields', style='custom', editor=field_list_editor),
                    show_labels=False,
                    show_border=True,
                    label='Input fields'
                ),
                Group(
                    Item(name='output_field', style='custom', editor=field_editor),
                    show_labels=False,
                    show_border=True,
                    label='Output field'
                ),
                Group(
                    Item(name='apply', label='Apply operation'),
                    Item(name='clear', label='Clear'),
                    show_labels=False
                ),
                label='Operation'
            ),
            Group(
                Group(
                    Item(name='output_file'),
                    Item(name='save', label='Save'),
                    show_labels=False,
                    show_border=True,
                    label='Save changes to file'
                ),
                Group(
                    Group(
                        Item(name='rename_field_old', label='Field to rename'),
                        Item(name='rename_field_new', label='New name')
                    ),
                    Item(name='rename', label='Rename'),
                    show_labels=False,
                    show_border=True,
                    label='Rename field'
                ),
                Group(
                    Group(
                        Item(name='remove_field', label='Field to remove')
                    ),
                    Item(name='remove', label='Remove'),
                    show_labels=False,
                    show_border=True,
                    label='Remove field'
                ),
                label='Misc'
            ),
            height=600,
            width=550
        )

    dialog_msg = String

    ######################################################################
    # `Filter` interface.
    ######################################################################
    def setup_pipeline(self):
        print 'setup_pipeline'

    def update_pipeline(self):
        print 'update_pipeline'

        if len(self.inputs) == 0 or len(self.inputs[0].outputs) == 0:
            return

        self.grid = tvtk.UnstructuredGrid()
        ## Way of doing this without a deep_copy (idea: copy input to output, with additional arrays tagged on)?
        self.grid.deep_copy(self.inputs[0].outputs[0])

        # Add a blank input field by default
        self.input_fields.append(self.input_field)

        self._set_outputs([self.grid])

    def update_data(self):
        print 'update_data'
        self.data_changed = True

    ######################################################################
    # Non-public interface.
    ######################################################################
    def _apply_fired(self):
        try:
            self.field_operation(self.operation, self.input_fields,
                                 self.output_field)
            self._dialog_box(
                'Success',
                'Operation complete. The result has been stored in \'' +
                self.output_field[0] + '\'.')
        except Exception, inst:
            self._dialog_box('Error', inst.__str__())
Ejemplo n.º 23
0
#------------------------------------------------------------------------------

# An ID is one of the following:
#  * Any string of alphabetic ([a-zA-Z\200-\377]) characters, underscores
#    ('_') or digits ([0-9]), not beginning with a digit;
#  * a number [-]?(.[0-9]+ | [0-9]+(.[0-9]*)? );
#  * any double-quoted string ("...") possibly containing escaped
#    quotes (\")1;
#  * an HTML string (<...>).
alphanum = "[a-zA-Z]"  #\200-\377] "# | [0-9] "#| [_]"
number = "[-]?(.[0-9]+ | [0-9]+(.[0-9]*)? ) "
dquote = '\" '
html = "<...>"
id_trait = Regex(regex=alphanum + "|" + number + "|" + dquote + "|" + html)

pointf_trait = Tuple(Float, Float, desc="the point (x,y)", graphviz=True)

point_trait = pointf_trait  #Either(
#    pointf_trait, Tuple(Float, Float, Float, desc="the point (x,y,z)")
#)

color_schemes = [
    "X11", "Accent", "Blues", "BRBG", "BUGN", "BUPU", "Dark", "GUBU", "Greens",
    "Greys", "Oranges", "OORD", "Paired", "Pastel", "PIYG", "PRGN", "PUBU",
    "PUBUGN", "PUOR", "PURD", "Purples", "RDBU", "RDGY", "RDPU", "RDYLBU",
    "RDYLGN", "Reds", "Set", "Spectral", "YLGN", "YLGNBU", "YLORBR", "YLORRD"
]

color_scheme_trait = Enum(color_schemes,
                          desc="a color scheme namespace",
                          label="Color scheme",
Ejemplo n.º 24
0
class Window(MWindow, Widget):
    """ The toolkit specific implementation of a Window.  See the IWindow
    interface for the API documentation.
    """

    implements(IWindow)

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

    position = Property(Tuple)

    size = Property(Tuple)

    title = Unicode

    #### Events #####

    activated = Event

    closed =  Event

    closing =  Event

    deactivated = Event

    key_pressed = Event(KeyPressedEvent)

    opened = Event

    opening = Event

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

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

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

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

    def show(self, visible):
        pass

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

    def _add_event_listeners(self):
        pass

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

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

        return self._position

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

        old = self._position
        self._position = position

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

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

        return self._size

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

        old = self._size
        self._size = size

        self.trait_property_changed('size', old, size)
Ejemplo n.º 25
0
class MousePickDispatcher(HasTraits):
    """ An event dispatcher to send pick event on mouse clicks. 
    
        This objects wires VTK observers so that picking callbacks
        can be bound to mouse click without movement.

        The object deals with adding and removing the VTK-level
        callbacks.
    """

    # The scene events are wired to.
    scene = Instance(Scene)

    # The list of callbacks, with the picker type they should be using,
    # and the mouse button that triggers them.
    callbacks = List(Tuple(
        Callable,
        Enum('cell', 'point', 'world'),
        Enum('Left', 'Middle', 'Right'),
    ),
                     help="The list of callbacks, with the picker type they "
                     "should be using, and the mouse button that "
                     "triggers them. The callback is passed "
                     "as an argument the tvtk picker.")

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

    # Whether the mouse has moved after the button press
    _mouse_no_mvt = Int

    # The button that has been pressed
    _current_button = Enum('Left', 'Middle', 'Right')

    # The various picker that are used when the mouse is pressed
    _active_pickers = Dict

    # The VTK callback numbers corresponding to our callbacks
    _picker_callback_nbs = Dict(value_trait=Int)

    # The VTK callback numbers corresponding to mouse movement
    _mouse_mvt_callback_nb = Int

    # The VTK callback numbers corresponding to mouse press
    _mouse_press_callback_nbs = Dict

    # The VTK callback numbers corresponding to mouse release
    _mouse_release_callback_nbs = Dict

    #--------------------------------------------------------------------------
    # Callbacks management
    #--------------------------------------------------------------------------

    @on_trait_change('callbacks_items')
    def dispatch_callbacks_change(self, name, trait_list_event):
        for item in trait_list_event.added:
            self.callback_added(item)
        for item in trait_list_event.removed:
            self.callback_removed(item)

    def callback_added(self, item):
        """ Wire up the different VTK callbacks. 
        """
        callback, type, button = item
        picker = getattr(self.scene.scene.picker, '%spicker' % type)
        self._active_pickers[type] = picker

        # Register the pick callback
        if not type in self._picker_callback_nbs:
            self._picker_callback_nbs[type] = \
                            picker.add_observer("EndPickEvent",
                                                self.on_pick)

        # Register the callbacks on the scene interactor
        if VTK_VERSION > 5:
            move_event = "RenderEvent"
        else:
            move_event = 'MouseMoveEvent'
        if not self._mouse_mvt_callback_nb:
            self._mouse_mvt_callback_nb = \
                self.scene.scene.interactor.add_observer(move_event,
                                                self.on_mouse_move)
        if not button in self._mouse_press_callback_nbs:
            self._mouse_press_callback_nbs[button] = \
                self.scene.scene.interactor.add_observer(
                                    '%sButtonPressEvent' % button,
                                    self.on_button_press)
        if VTK_VERSION > 5:
            release_event = "EndInteractionEvent"
        else:
            release_event = '%sButtonReleaseEvent' % button
        if not button in self._mouse_release_callback_nbs:
            self._mouse_release_callback_nbs[button] = \
                self.scene.scene.interactor.add_observer(
                                    release_event,
                                    self.on_button_release)

    def callback_removed(self, item):
        """ Clean up the unecessary VTK callbacks.
        """
        callback, type, button = item

        # If the picker is no longer needed, clean up its observers.
        if not [t for c, t, b in self.callbacks if t == type]:
            picker = self._active_pickers[type]
            picker.remove_observer(self._picker_callback_nbs[type])
            del self._active_pickers[type]

        # If there are no longer callbacks on the button, clean up
        # the corresponding observers.
        if not [b for c, t, b in self.callbacks if b == button]:
            self.scene.scene.interactor.remove_observer(
                self._mouse_press_callback_nbs[button])
            self.scene.scene.interactor.remove_observer(
                self._mouse_release_callback_nbs[button])
        if len(self.callbacks) == 0 and self._mouse_mvt_callback_nb:
            self.scene.scene.interactor.remove_observer(
                self._mouse_mvt_callback_nb)
            self._mouse_mvt_callback_nb = 0

    def clear_callbacks(self):
        while self.callbacks:
            self.callbacks.pop()

    #--------------------------------------------------------------------------
    # Mouse movement dispatch mechanism
    #--------------------------------------------------------------------------

    def on_button_press(self, vtk_picker, event):
        self._current_button = event[:-len('ButtonPressEvent')]
        self._mouse_no_mvt = 2

    def on_mouse_move(self, vtk_picker, event):
        if self._mouse_no_mvt:
            self._mouse_no_mvt -= 1

    def on_button_release(self, vtk_picker, event):
        """ If the mouse has not moved, pick with our pickers.
        """
        if self._mouse_no_mvt:
            x, y = vtk_picker.GetEventPosition()
            for picker in self._active_pickers.values():
                picker.pick((x, y, 0), self.scene.scene.renderer)
        self._mouse_no_mvt = 0

    def on_pick(self, vtk_picker, event):
        """ Dispatch the pick to the callback associated with the
            corresponding mouse button.
        """
        picker = tvtk.to_tvtk(vtk_picker)
        for event_type, event_picker in self._active_pickers.iteritems():
            if picker is event_picker:
                for callback, type, button in self.callbacks:
                    if (type == event_type and button == self._current_button):
                        callback(picker)
            break

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

    def __del__(self):
        self.clear_callbacks()
Ejemplo n.º 26
0
class GridSelector(Selector, RuntimeListener):
    """
  This selector has a grid for selecting indices from two dimensions.
  It also shows a view of the sensor output and highlights the current
  region's receptive field, if there is an ImageSensor in the network.
  """

    # Parameters
    largestSide = 160

    # Traits
    grid = Tuple((0, 0, 1, 1))  # (row, col, nRows, nCols)
    sensorLocationImage = Instance(PIL.Image.Image)
    gaborLocationImage = Instance(PIL.Image.Image)

    def __init__(self, *args, **kwargs):

        Selector.__init__(self, *args, **kwargs)
        RuntimeListener.__init__(self)

        self.outputImage = None
        self.setDimensions()
        self.update()

    def findRegions(self):
        """Look for an ImageSensor and GaborRegion."""

        self.sensor = self.gabor = None
        for e in self.root.regions.values():
            if 'ImageSensor' in e.type or 'PictureSensor' in e.type:
                self.sensor = e
            elif 'GaborRegion' in e.type:
                self.gabor = e
            if self.sensor and self.gabor:
                break

    def _getSensor(self):
        try:
            return self.sensor.getSelf()
        except:
            return self.sensor

    def createView(self):
        """Set up a view for the traits."""

        # Look for an ImageSensor/PictureSensor and a GaborRegion
        self.findRegions()

        # Calculate the height of the grid and images
        if self.sensor:
            ss = self._getSensor()
            ratio = ss.width / ss.height
            if ratio > 1:
                # Image is wider than it is tall
                self.width = self.largestSide
                self.height = int(round(self.largestSide / ratio))
            else:
                # Image is taller than it is wide
                self.height = self.largestSide
                self.width = int(round(self.largestSide * ratio))
        else:
            self.height = self.width = self.largestSide

        items = [
            Item('grid',
                 editor=GridSelectorEditor(width=self.width,
                                           height=self.height),
                 visible_when=self.runtimeElementCondition)
        ]
        if self.sensor:
            items.append(
                Item('sensorLocationImage',
                     editor=ImageEditor(width=self.width, height=self.height),
                     visible_when=self.runtimeElementCondition))
            if self.gabor:
                items.append(
                    Item('gaborLocationImage',
                         editor=ImageEditor(width=self.width,
                                            height=self.height),
                         visible_when=self.runtimeElementCondition))

        self.view = View(Group(*items, **dict(show_labels=False)),
                         buttons=NoButtons,
                         handler=SelectorHandler)

    def setDimensions(self):
        """Set the grid dimensions."""

        if self.isRegionInspector():
            dimensions = self.getDimensions()
            if dimensions:
                if len(dimensions) == 1:
                    # MD layout (1 dimension)
                    self.grid = (0, 0, dimensions[0], 1)
                elif len(dimensions) == 2:
                    # MD layout (2 dimensions)
                    self.grid = (0, 0, dimensions[0], dimensions[1])
                else:
                    # MRG layout (3 dimensions)
                    self.grid = (0, 0, dimensions[1], dimensions[2])
            else:
                self.grid = (0, 0, 1, 1)
        else:
            self.grid = (0, 0, 1, 1)

    def update(self,
               methodName=None,
               elementName=None,
               args=None,
               kwargs=None):
        """
    Called automatically in response to runtime engine activity.

    Extra arguments (optional) are passed by the wrapped methods,
    and they can be used to avoid unnecessary updating.

    methodName -- RuntimeElement class method that was called.
    elementName -- RuntimeElement name.
    args -- Positional arguments passed to the method.
    kwargs -- Keyword arguments passed to the method.
    """
        ss = self._getSensor()
        if not ss:  # If there is only a grid, no updates are necessary
            return

        ns = self.sensor.spec

        # Try to get the output image
        if 'outputImage' in ns.parameters:
            if hasattr(ss, 'outputImage'):
                # Image sensor takes this branch.
                outputImage = ss.outputImage
            else:
                # Picture sensor takes this branch.
                outputImage = ss.getParameter('outputImage')
            if outputImage:
                if isinstance(outputImage, str):
                    self.outputImage = deserializeImage(outputImage)
                if isinstance(outputImage, PIL.Image._ImageCrop):
                    self.outputImage = outputImage
                else:
                    # If the output image is multi-scale, pick the first image (largest)
                    if hasattr(outputImage, '__iter__'):
                        imgStr = ss.getParameter('outputImage')[0]
                    else:
                        imgStr = ss.getParameter('outputImage')
                    if imgStr:
                        # Valid image
                        self.outputImage = deserializeImage(imgStr)

        if self.gabor:
            # Retrieve the largest composite Gabor image
            with listenersDisabled:
                g = self.gabor.getSelf()
                s = g.getResponseImages(whichScale=0)
                self.gaborImage = deserializeImage(s)

        self.drawLocationImages()

    def drawLocationImages(self):
        """Draw the location on the sensor and gabor images."""

        ss = self._getSensor()
        scale = self.width / float(ss.width)

        if not self.outputImage:
            # Create blank images
            sensorLocationImage = PIL.Image.new('RGB',
                                                (self.width, self.height),
                                                (255, 255, 255))
            gaborLocationImage = sensorLocationImage.copy()
        else:
            # Copy the output image to the sensor location image
            sensorLocationImage = self.outputImage.convert('RGB')
            if self.gabor:
                # Copy the composite gabor image to the gabor location image
                gaborLocationImage = self.gaborImage.copy()
            if self.gabor:
                if sensorLocationImage.size != gaborLocationImage.size:
                    # Gabor image is constrained
                    # Compute offsets
                    offsetX = \
                      (sensorLocationImage.size[0] - gaborLocationImage.size[0]) / 2
                    offsetY = \
                      (sensorLocationImage.size[1] - gaborLocationImage.size[1]) / 2
                    gaborWidth = int(round(scale * gaborLocationImage.size[0]))
                    gaborHeight = int(round(scale *
                                            gaborLocationImage.size[1]))
                else:
                    gaborWidth, gaborHeight = self.width, self.height
            # Scale the images
            sensorLocationImage = sensorLocationImage.resize(
                (self.width, self.height))
            if self.gabor:
                gaborLocationImage = \
                  gaborLocationImage.resize((gaborWidth, gaborHeight))

        if self.grid != (0, 0, 1, 1):
            # ??????????? Ignore getReceptiveFields for now
            #unscaledRF = getReceptiveFields(self.getRegion(), gui=True)[0]
            unscaledRF = False
            if unscaledRF:
                rf = [int(round(scale * r)) for r in unscaledRF]
                sensorLocationImageDraw = PIL.ImageDraw.Draw(
                    sensorLocationImage)
                # Draw a 3-pixel-wide rectangle
                sensorLocationImageDraw.rectangle(
                    (rf[0] - 1, rf[1] - 1, rf[2], rf[3]), outline='blue')
                sensorLocationImageDraw.rectangle(
                    (rf[0] - 2, rf[1] - 2, rf[2] + 1, rf[3] + 1),
                    outline='blue')
                sensorLocationImageDraw.rectangle(
                    (rf[0] - 3, rf[1] - 3, rf[2] + 2, rf[3] + 2),
                    outline='blue')
                if self.gabor:
                    if sensorLocationImage.size != gaborLocationImage.size:
                        unscaledGaborRF = (unscaledRF[0] - offsetX,
                                           unscaledRF[1] - offsetY,
                                           unscaledRF[2] - offsetX,
                                           unscaledRF[3] - offsetY)
                    else:
                        unscaledGaborRF = unscaledRF
                    rf = [int(round(scale * r)) for r in unscaledGaborRF]
                    gaborLocationImageDraw = PIL.ImageDraw.Draw(
                        gaborLocationImage)
                    # Draw a 3-pixel-wide rectangle
                    gaborLocationImageDraw.rectangle(
                        (rf[0] - 1, rf[1] - 1, rf[2], rf[3]), outline='blue')
                    gaborLocationImageDraw.rectangle(
                        (rf[0] - 2, rf[1] - 2, rf[2] + 1, rf[3] + 1),
                        outline='blue')
                    gaborLocationImageDraw.rectangle(
                        (rf[0] - 3, rf[1] - 3, rf[2] + 2, rf[3] + 2),
                        outline='blue')
        self.sensorLocationImage = sensorLocationImage
        if self.gabor:
            if sensorLocationImage.size != gaborLocationImage.size:
                # Scale the offset
                offsetX = int(round(scale * offsetX))
                offsetY = int(round(scale * offsetY))
                # Pad to the full size
                newImage = PIL.Image.new('RGB', sensorLocationImage.size,
                                         (255, 255, 255))
                newImage.paste(gaborLocationImage, (offsetX, offsetY))
                gaborLocationImage = newImage
            self.gaborLocationImage = gaborLocationImage

    def switchItem(self):
        """Update the selector to reflect the newly-selected item."""

        Selector.switchItem(self)
        self.setDimensions()

    def switchFromCompanion(self, item=None, indices=None):
        """Update the selector to reflect the item in the companion selector."""

        if item:
            self.item = item
            self.setDimensions()
        if indices:
            grid = list(self.grid)
            if not self.isMRG():
                # MD layout (1 or 2 dimensions)
                for i, index in enumerate(indices):
                    if getattr(self, 'index%d' % i) != index:
                        grid[i] = index
            else:
                # MRG layout (3 dimensions)
                if indices[0] != self.index0:
                    # Scale has changed - updated the grid
                    self.index0 = indices[0]
                    dimensions = self.getDimensions()
                    grid[2:] = dimensions[1:]
                if self.index1 != indices[1]:
                    grid[0] = indices[1]
                if self.index2 != indices[2]:
                    grid[1] = indices[2]
            self.grid = tuple(grid)

    def _grid_changed(self):
        """Update the indices when the grid selection changes."""

        if not self.grid:
            return
        dimensions = self.getDimensions()
        if not self.isMRG():
            # MD layout (1 or 2 dimensions)
            if self.grid[0] != self.index0:
                self.index0 = self.grid[0]
            if self.grid[1] != self.index1:
                self.index1 = self.grid[1]
        else:
            # MRG layout (3 dimensions)
            if self.grid[0] != self.index1:
                self.index1 = self.grid[0]
            if self.grid[1] != self.index2:
                self.index2 = self.grid[1]
        if self.sensor:
            self.drawLocationImages()
Ejemplo n.º 27
0
class MultiFitGui(HasTraits):
    """
    data should be c x N where c is the number of data columns/axes and N is
    the number of points
    """
    doplot3d = Bool(False)
    show3d = Button('Show 3D Plot')
    replot3d = Button('Replot 3D')
    scalefactor3d = Float(0)
    do3dscale = Bool(False)
    nmodel3d = Int(1024)
    usecolor3d = Bool(False)
    color3d = Color((0,0,0))
    scene3d = Instance(MlabSceneModel,())
    plot3daxes = Tuple(('x','y','z'))
    data = Array(shape=(None,None))
    weights = Array(shape=(None,))
    curveaxes = List(Tuple(Int,Int))
    axisnames = Dict(Int,Str)
    invaxisnames = Property(Dict,depends_on='axisnames')

    fgs = List(Instance(FitGui))


    traits_view = View(VGroup(Item('fgs',editor=ListEditor(use_notebook=True,page_name='.plotname'),style='custom',show_label=False),
                              Item('show3d',show_label=False)),
                              resizable=True,height=900,buttons=['OK','Cancel'],title='Multiple Model Data Fitters')

    plot3d_view = View(VGroup(Item('scene3d',editor=SceneEditor(scene_class=MayaviScene),show_label=False,resizable=True),
                              Item('plot3daxes',editor=TupleEditor(cols=3,labels=['x','y','z']),label='Axes'),
                              HGroup(Item('do3dscale',label='Scale by weight?'),
                              Item('scalefactor3d',label='Point scale'),
                              Item('nmodel3d',label='Nmodel')),
                              HGroup(Item('usecolor3d',label='Use color?'),Item('color3d',label='Relation Color',enabled_when='usecolor3d')),
                              Item('replot3d',show_label=False),springy=True),
                       resizable=True,height=800,width=800,title='Multiple Model3D Plot')

    def __init__(self,data,names=None,models=None,weights=None,dofits=True,**traits):
        """
        :param data: The data arrays
        :type data: sequence of c equal-length arrays (length N)
        :param names: Names
        :type names: sequence of strings, length c
        :param models:
            The models to fit for each pair either as strings or
            :class:`astroypsics.models.ParametricModel` objects.
        :type models: sequence of models, length c-1
        :param weights: the weights for each point or None for no weights
        :type weights: array-like of size N or None
        :param dofits:
            If True, the data will be fit to the models when the object is
            created, otherwise the models will be passed in as-is (or as
            created).
        :type dofits: bool

        extra keyword arguments get passed in as new traits
        (r[finmask],m[finmask],l[finmask]),names='rh,Mh,Lh',weights=w[finmask],models=models,dofits=False)
        """
        super(MultiFitGui,self).__init__(**traits)
        self._lastcurveaxes = None

        data = np.array(data,copy=False)
        if weights is None:
            self.weights = np.ones(data.shape[1])
        else:
            self.weights = np.array(weights)

        self.data = data
        if data.shape[0] < 2:
            raise ValueError('Must have at least 2 columns')

        if isinstance(names,basestring):
            names = names.split(',')
        if names is None:
            if len(data) == 2:
                self.axisnames = {0:'x',1:'y'}
            elif len(data) == 3:
                self.axisnames = {0:'x',1:'y',2:'z'}
            else:
                self.axisnames = dict((i,str(i)) for i in data)
        elif len(names) == len(data):
            self.axisnames = dict([t for t in enumerate(names)])
        else:
            raise ValueError("names don't match data")

        #default to using 0th axis as parametric
        self.curveaxes = [(0,i) for i in range(len(data))[1:]]
        if models is not None:
            if len(models) != len(data)-1:
                raise ValueError("models don't match data")
            for i,m in enumerate(models):
                fg = self.fgs[i]
                newtmodel = TraitedModel(m)
                if dofits:
                    fg.tmodel = newtmodel
                    fg.fitmodel = True #should happen automatically, but this makes sure
                else:
                    oldpard = newtmodel.model.pardict
                    fg.tmodel = newtmodel
                    fg.tmodel .model.pardict = oldpard
                if dofits:
                    fg.fitmodel = True

    def _data_changed(self):
        self.curveaxes = [(0,i) for i in range(len(self.data))[1:]]

    def _axisnames_changed(self):
        for ax,fg in zip(self.curveaxes,self.fgs):
            fg.plot.x_axis.title = self.axisnames[ax[0]] if ax[0] in self.axisnames else ''
            fg.plot.y_axis.title = self.axisnames[ax[1]] if ax[1] in self.axisnames else ''
        self.plot3daxes = (self.axisnames[0],self.axisnames[1],self.axisnames[2] if len(self.axisnames) > 2 else self.axisnames[1])

    @on_trait_change('curveaxes[]')
    def _curveaxes_update(self,names,old,new):
        ax=[]
        for t in self.curveaxes:
            ax.append(t[0])
            ax.append(t[1])
        if set(ax) != set(range(len(self.data))):
            self.curveaxes = self._lastcurveaxes
            return #TOOD:check for recursion

        if self._lastcurveaxes is None:
            self.fgs = [FitGui(self.data[t[0]],self.data[t[1]],weights=self.weights) for t in self.curveaxes]
            for ax,fg in zip(self.curveaxes,self.fgs):
                fg.plot.x_axis.title = self.axisnames[ax[0]] if ax[0] in self.axisnames else ''
                fg.plot.y_axis.title = self.axisnames[ax[1]] if ax[1] in self.axisnames else ''
        else:
            for i,t in enumerate(self.curveaxes):
                if  self._lastcurveaxes[i] != t:
                    self.fgs[i] = fg = FitGui(self.data[t[0]],self.data[t[1]],weights=self.weights)
                    ax = self.curveaxes[i]
                    fg.plot.x_axis.title = self.axisnames[ax[0]] if ax[0] in self.axisnames else ''
                    fg.plot.y_axis.title = self.axisnames[ax[1]] if ax[1] in self.axisnames else ''

        self._lastcurveaxes = self.curveaxes

    def _show3d_fired(self):
        self.edit_traits(view='plot3d_view')
        self.doplot3d = True
        self.replot3d = True

    def _plot3daxes_changed(self):
        self.replot3d = True

    @on_trait_change('weights',post_init=True)
    def weightsChanged(self):
        for fg in self.fgs:
            if fg.weighttype != 'custom':
                fg.weighttype = 'custom'
            fg.weights = self.weights


    @on_trait_change('data','fgs','replot3d','weights')
    def _do_3d(self):
        if self.doplot3d:
            M = self.scene3d.mlab
            try:
                xi = self.invaxisnames[self.plot3daxes[0]]
                yi = self.invaxisnames[self.plot3daxes[1]]
                zi = self.invaxisnames[self.plot3daxes[2]]

                x,y,z = self.data[xi],self.data[yi],self.data[zi]
                w = self.weights

                M.clf()
                if self.scalefactor3d == 0:
                    sf = x.max()-x.min()
                    sf *= y.max()-y.min()
                    sf *= z.max()-z.min()
                    sf = sf/len(x)/5
                    self.scalefactor3d = sf
                else:
                    sf = self.scalefactor3d
                glyph = M.points3d(x,y,z,w,scale_factor=sf)
                glyph.glyph.scale_mode = 0 if self.do3dscale else 1
                M.axes(xlabel=self.plot3daxes[0],ylabel=self.plot3daxes[1],zlabel=self.plot3daxes[2])

                try:
                    xs = np.linspace(np.min(x),np.max(x),self.nmodel3d)

                    #find sequence of models to go from x to y and z
                    ymods,zmods = [],[]
                    for curri,mods in zip((yi,zi),(ymods,zmods)):
                        while curri != xi:
                            for i,(i1,i2) in enumerate(self.curveaxes):
                                if curri==i2:
                                    curri = i1
                                    mods.insert(0,self.fgs[i].tmodel.model)
                                    break
                            else:
                                raise KeyError

                    ys = xs
                    for m in ymods:
                        ys = m(ys)
                    zs = xs
                    for m in zmods:
                        zs = m(zs)

                    if self.usecolor3d:
                        c = (self.color3d[0]/255,self.color3d[1]/255,self.color3d[2]/255)
                        M.plot3d(xs,ys,zs,color=c)
                    else:
                        M.plot3d(xs,ys,zs,np.arange(len(xs)))
                except (KeyError,TypeError):
                    M.text(0.5,0.75,'Underivable relation')
            except KeyError:
                M.clf()
                M.text(0.25,0.25,'Data problem')



    @cached_property
    def _get_invaxisnames(self):
        d={}
        for k,v in self.axisnames.iteritems():
            d[v] = k
        return d
Ejemplo n.º 28
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
Ejemplo n.º 29
0
class Rectangle(HasTraits):
    """Rectangle class with rectangle center position in float(x,y) 
    and rectangle widh and height specified as int.
    
    >>> r = Rectangle()
    
    You can set coordinates from a given set of points (one or two)    
    
    >>> r.set_from_points((5.,6.))
    >>> r.center == (5.0, 6.0)
    True
    >>> r.set_from_points((1.,2.4),(4.,5.2))
    >>> r.width == 3 and r.height == 2 #height and width of the rectangle
    True
    >>> r.center == (2.5, 3.8)
    True
    >>> r.top_left == (2,3) and r.bottom_right == (4,4) #index coordinates of the corner
    True
    >>> r.bottom_left == (2,4) and  r.top_right == (4,3)
    True
    
    If width and height is 1 than all four corners have the same value    
    
    >>> r.width = 1
    >>> r.height = 1
    >>> r.top_left == (3,4) and r.bottom_right == (3,4)
    True
    
    Consider this, rounding is performed when center does not fit to index coordinates    
    
    >>> r.width = 3
    >>> r.height = 3
    >>> r.center = (1,1.)
    >>> r.top_left == (0,0) 
    True
    >>> r.center = (0.5,1.5)
    >>> r.top_left == (0,1) 
    True
    
    """
    #: rectangle center position
    center = Tuple((0., 0.))
    #: rectangle width must be an int
    width = Int(1)
    #: rectangle height must be an int
    height = Int(1)
    #: (width, height) tuple
    size = Property(Tuple(Int, Int), depends_on='width,height')

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

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

    _update = Bool(True)

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

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

    def _set_top_left(self, coordinate):
        self.center = tuple()

    def _get_bottom_right(self):
        return tuple(
            map(lambda x, y: x + y, self.top_left,
                (self.width - 1, self.height - 1)))

    def _get_bottom_left(self):
        return tuple(
            map(lambda x, y: x + y, self.top_left, (0, self.height - 1)))

    def _get_top_right(self):
        return tuple(
            map(lambda x, y: x + y, self.top_left, (self.width - 1, 0)))

    def _get_size(self):
        return self.width, self.height

    def set_from_points(self, *points):
        """
        Sets x,y and possibly width and height given by points.
        One or two points must be given. If one is given, it is a center position.
        if two are given, calculates center and shape
        """
        if len(points) == 1:
            self._update = False
            self.center = tuple(points[0])
            self._update = True
            self.update()
        elif len(points) == 2:
            self._update = False
            top_left = list(map(lambda x, y: min(x, y), *points))
            bottom_right = list(map(lambda x, y: max(x, y), *points))
            self.center = tuple(
                map(lambda x, y: (y + x) / 2., top_left, bottom_right))
            self.width, self.height = list(
                map(lambda x, y: int(y - x), top_left, bottom_right))
            self._update = True
            self.update()

    def set_from_corners(self, *corners):
        warnings.warn('Use set_from_points instead', DeprecationWarning)
        self.set_from_points(*corners)

    def slice_image(self, image):
        size = image.shape[0:2]
        xmin, ymin = list(map(lambda x, y: max(x, y), self.top_left, (0, 0)))
        xmax, ymax = list(
            map(lambda x, y: 1 + min(x, y - 1), self.bottom_right,
                (size[1], size[0])))
        im = image[ymin:ymax, xmin:xmax]
        return im

    def slice_indices(self, indices):
        size = indices.shape[-2:]
        xmin, ymin = list(map(lambda x, y: max(x, y), self.top_left, (0, 0)))
        xmax, ymax = list(
            map(lambda x, y: 1 + min(x, y - 1), self.bottom_right,
                (size[1], size[0])))
        indices = indices[:, ymin:ymax, xmin:xmax]
        return indices

    view = View(
        'center',
        'width',
        'height',
        Item('top_left', style='readonly'),
    )
Ejemplo n.º 30
0
class Polyline(Component):
    """ Defines a component with multiple line segments. """

    #--------------------------------------------------------------------------
    #  "Polyline" interface:
    #--------------------------------------------------------------------------

    # Pen used to draw the polyline.
    pen = Instance(Pen, desc="the pen with which to draw the lines")

    # Points defining the line ends.
    points = List(
        Tuple(Float, Float, labels=["x", "y"], cols=2, minlen=2),
        desc="points defining the line ends"
    )

    #--------------------------------------------------------------------------
    #  "Component" interface:
    #--------------------------------------------------------------------------

    # Background colour of the component
    bgcolor = "transparent"#(1.0, 0.5, 0.5, 0.33)

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

    traits_view = View(
        Group(
            Item("pen", style="custom", show_label=False),
            label="Pen", show_border=True
        ),
        Group(
            Item("points", height=250, show_label=False),
            label="Points", show_border=True
        )
    )

    #--------------------------------------------------------------------------
    #  Draw component on the graphics context:
    #--------------------------------------------------------------------------

    def _draw_mainlayer(self, gc, view_bounds=None, mode="default"):
        """ Draws a closed polygon. """

        gc.save_state()
        try:
#            self._draw_bounds(gc)
            if len(self.points) >= 2:
                # Set the drawing parameters.
                gc.set_fill_color(self.pen.fill_color_)
                gc.set_stroke_color(self.pen.color_)
                gc.set_line_width(self.pen.line_width)

                # Draw the path.
                gc.begin_path()
                gc.lines(self.points)

                gc.stroke_path()
        finally:
            gc.restore_state()


    def _draw_bounds(self, gc):
        """ Draws the component bounds for testing purposes. """

        dx, dy = self.bounds
        x, y = self.position
        gc.rect(x, y, dx, dy)
        gc.stroke_path()


    def normal_left_down(self, event):
        """ Handles left mouse button clicks in 'normal' mode """

        print "Polyline selected at (%d, %d)" % (event.x, event.y)


    @on_trait_change("pen.+,points")
    def _update(self):
        if not self.points: return
        x_points = [x for x, y in self.points]
        y_points = [y for x, y in self.points]
        x = min(x_points)
        x2 = max(x_points)
        y = min(y_points)
        y2 = max(y_points)
        self.position = [x, y]
        # Don't let bounds be set to 0, otherwise, horizontal and vertical
        # lines will not render because enable skips rendering items with
        # bounds=[0,0]
        self.bounds = [max(x2-x,1), max(y2-y,1)]

        self.request_redraw()