class DataSourceWizard(HasTraits):

    data_sources = Dict

    _data_sources_names = Property(depends_on='data_sources')

    def _get__data_sources_names(self):
        names = []
        for name in self.data_sources:
            try:
                self.data_sources[name] + 1
                names.append(name)
            except TypeError:
                pass
        names.sort()
        return names

    # Dictionnary mapping the views
    data_type = Trait(
        'point', {
            'A surface': 'surface',
            'A set of points, that can be connected by lines': 'point',
            'A set of vectors': 'vector',
            'Volumetric data': 'volumetric',
        })

    position_type = Trait(
        'image data', {
            'Specified explicitly': 'explicit',
            'Implicitely positioned on a regular grid': 'image data',
            'On an orthogonal grid with varying spacing': 'orthogonal grid',
        })

    # The array that is used for finding out the shape of the grid,
    # when creating an ImageData
    grid_shape_source = Property(depends_on='grid_shape_source_')

    def _get_grid_shape_source(self):
        if self.grid_shape_source_ == '':
            # catter for improperly initialized view
            keys = self._data_sources_names
            if not self.grid_shape.any():
                self.grid_shape = \
                        self.data_sources[keys[0]].shape
            return keys[0]
        elif self.grid_shape_source_[:16] == 'Shape of array: ':
            return self.grid_shape_source_[17:-1]
        else:
            return ""

    # Shadow traits for grid_shape_source
    grid_shape_source_ = Str

    def _grid_shape_source_changed(self):
        if not self.grid_shape_source == '':
            array_shape = \
                    atleast_3d(self.data_sources[self.grid_shape_source]).shape
            grid_shape = ones((3, ))
            grid_shape[:len(array_shape)] = array_shape
            self.grid_shape = grid_shape

    _grid_shape_source_labels = Property(depends_on='_data_sources_names')

    def _get__grid_shape_source_labels(self):
        values = [
            'Shape of array: "%s"' % name for name in self._data_sources_names
        ]
        values.sort
        values.append('Specified explicitly')
        return values

    # The shape of the grid array. Used when position is implicit
    grid_shape = CArray(shape=(3, ), dtype='i')

    # Whether or not the data points should be connected.
    lines = false

    # The scalar data selection
    scalar_data = Str('',
                      help="Select the array that gives the value of the "
                      "scalars plotted.")

    position_x = Str(help="Select the array that gives the x "
                     "position of the data points")

    position_y = Str(help="Select the array that gives the y "
                     "position of the data points")

    position_z = Str(help="Select the array that gives the z "
                     "position of the data points")

    connectivity_triangles = Str

    has_vector_data = false(help="""Do you want to plot vector components?""")

    # A boolean to ask the user if he wants to load scalar data
    has_scalar_data = false

    vector_u = Str

    vector_v = Str

    vector_w = Str

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

    def init_arrays(self):
        # Force all the array names to be properly initialized
        array_names = set(self.data_sources.keys())
        if len(array_names) == 0:
            # We should probably bail out here.
            return False
        for attr in (
                'position_x',
                'position_y',
                'position_z',
                'scalar_data',
                'vector_u',
                'vector_v',
                'vector_w',
                'connectivity_triangles',
        ):
            if len(array_names) > 0:
                array_name = array_names.pop()
            setattr(self, attr, array_name)

    def guess_arrays(self):
        """ Do some guess work on the arrays to find sensible default.
        """
        array_names = set(self._data_sources_names)
        found_some = False
        if set(('x', 'y', 'z')).issubset(array_names):
            self.position_x = 'x'
            self.position_y = 'y'
            self.position_z = 'z'
            array_names = array_names.difference(('x', 'y', 'z'))
            found_some = True
        elif set(('X', 'Y', 'Z')).issubset(array_names):
            self.position_x = 'X'
            self.position_y = 'Y'
            self.position_z = 'Z'
            array_names = array_names.difference(('X', 'Y', 'Z'))
            found_some = True

        if set(('u', 'v', 'w')).issubset(array_names):
            self.vector_u = 'u'
            self.vector_v = 'v'
            self.vector_w = 'w'
            array_names = array_names.difference(('u', 'v', 'w'))
            found_some = True
        elif set(('U', 'V', 'W')).issubset(array_names):
            self.vector_u = 'U'
            self.vector_v = 'V'
            self.vector_w = 'W'
            array_names = array_names.difference(('U', 'V', 'W'))
            found_some = True

        if found_some:
            # Need to re-attribute the guessed names.
            for attr in ('scalar_data', 'vector_u', 'vector_v', 'vector_w',
                         'connectivity_triangles'):
                if len(array_names) > 0:
                    setattr(self, attr, array_names.pop())
                else:
                    break

    def build_data_source(self):
        """ This is where we apply the selections made by the user in
            in the wizard to build the data source.
        """
        factory = DataSourceFactory()
        # Keep a reference to the factory to be able to replay it, say
        # on other data.
        self._factory = factory
        if self.data_type_ == 'point':
            # The user wants to explicitly position vector,
            # thus only sensible data structures for points is with
            # explicit positioning.
            self.position_type_ == 'explicit'
            # In addition, this view does not allow for
            # connectivity.
            factory.unstructured = True
            factory.connected = False
        else:
            factory.connected = True

        if (self.position_type_ == "image data"
                and not self.data_type_ == "point"):
            if not self.has_scalar_data and not self.vector_u == '':
                # With image data we need a scalar array always:
                factory.scalar_data = ones(self.grid_shape)
            factory.position_implicit = True
        else:
            factory.position_x = self.get_sdata(self.position_x)
            factory.position_y = self.get_sdata(self.position_y)
            factory.position_z = self.get_sdata(self.position_z)
        if self.position_type_ == "orthogonal grid":
            factory.orthogonal_grid = True
        if self.position_type_ == "explicit" and self.data_type_ == "surface":
            factory.connectivity_triangles = self.get_data(
                self.connectivity_triangles)
        if self.lines and self.data_type_ == "point":
            factory.lines = True

        if self.has_vector_data or self.data_type_ == 'vector':
            # In the vector view, the user is not explicitly asked to
            # Enable vectors.
            factory.has_vector_data = True
            factory.vector_u = self.get_sdata(self.vector_u)
            factory.vector_v = self.get_sdata(self.vector_v)
            factory.vector_w = self.get_sdata(self.vector_w)

        if self.has_scalar_data or self.data_type_ == 'volumetric':
            # In the volumetric view, the user is not explicitly asked to
            # Enable scalars.
            factory.scalar_data = self.get_sdata(self.scalar_data)

        if self.connectivity_triangles == '':
            factory.connectivity_triangles = None

        self.data_source = factory.build_data_source()

        if self.has_scalar_data:
            if hasattr(self.data_source, 'scalar_name'):
                self.data_source.scalar_name = self.scalar_data
            elif hasattr(self.data_source, 'point_scalar_name'):
                self.data_source.point_scalar_name = self.scalars

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

    def get_data(self, name):
        return self.data_sources[name]

    def get_sdata(self, name):
        ary = self.data_sources[name]
        if not self.data_type_ == 'point':
            ary = resize(ary, self.grid_shape)
        return ary

    def active_arrays(self):
        """ Return the list of the active array-selection drop-downs.
        """
        arrays = []
        if self.data_type_ == 'point' or self.position_type_ == 'explicit':
            arrays.extend([
                'position_x',
                'position_y',
                'position_z',
            ])
        if self.data_type_ == 'vector' or self.has_vector_data:
            arrays.extend(['vector_u', 'vector_v', 'vector_w'])
        if self.has_scalar_data or self.data_type_ == 'volumetric':
            arrays.extend(['scalar_data'])
        return arrays

    def check_arrays(self):
        """ Checks that all the array have the right size.
        """
        arrays_to_check = self.active_arrays()
        if len(arrays_to_check) == 0:
            return True
        size = self.get_data(getattr(self, arrays_to_check.pop())).size
        for attr in arrays_to_check:
            if not self.get_data(getattr(self, attr)).size == size:
                return False
        if (self.data_type_ == 'surface'
                and self.position_type_ == "explicit"):
            if not self.connectivity_triangles.size / 3 == size:
                return False
        return True
Beispiel #2
0
class DataModuleFactory(ModuleFactory):
    """ Base class for all the module factories operating on data (ie not
        text and outline) """

    reset_zoom = true(help="""Reset the zoom to accomodate the data newly
                        added to the scene. Defaults to True.""")

    extent = CArray(shape=(6,),
                    help="""[xmin, xmax, ymin, ymax, zmin, zmax]
                            Default is the x, y, z arrays extent. Use
                            this to change the extent of the object
                            created.""", )

    def _extent_changed(self):
        tools.set_extent(self._target, self.extent)

    transparent = false(help="""make the opacity of the actor depend on the
                               scalar.""")

    def _transparent_changed(self):
        if self.transparent:
            data_range = \
                self._target.module_manager.scalar_lut_manager.data_range
            self._target.module_manager.scalar_lut_manager.lut.alpha_range = \
                                                                (0.2, 0.8)
            data_range = ( numpy.mean(data_range)
                            + 0.4 * ( data_range.max() - data_range.min())
                                * numpy.array([-1, 1]))
            self._target.module_manager.scalar_lut_manager.data_range = \
                data_range

    colormap = Trait('blue-red', lut_mode_list(),
                        help="""type of colormap to use.""")

    def _colormap_changed(self):
        colormap = self.colormap
        if colormap[-2:] == "_r":
            colormap = colormap[:-2]
            self._target.module_manager.scalar_lut_manager.reverse_lut = True
            self._target.module_manager.vector_lut_manager.reverse_lut = True
        self._target.module_manager.scalar_lut_manager.lut_mode = colormap
        self._target.module_manager.vector_lut_manager.lut_mode = colormap


    vmin = Trait(None, None, CFloat,
                    help="""vmin is used to scale the colormap.
                            If None, the min of the data will be used""")

    vmax = Trait(None, None, CFloat,
                    help="""vmax is used to scale the colormap.
                            If None, the max of the data will be used""")

    def _vmin_changed(self):
        if self.vmin == None and self.vmax == None:
            self._target.module_manager.scalar_lut_manager.use_default_range\
                    = True
            return

        self._target.module_manager.scalar_lut_manager.use_default_range \
                    = False
        vmin, vmax = \
                self._target.module_manager.scalar_lut_manager.data_range
        if self.vmin is not None:
            vmin = self.vmin
        if self.vmax is not None:
            vmax = self.vmax
        self._target.module_manager.scalar_lut_manager.data_range = \
                        (vmin, vmax)

    _vmax_changed = _vmin_changed

    def __init__(self, *args, **kwargs):
        super(DataModuleFactory, self).__init__(*args, **kwargs)
        # We are adding data to the scene, reset the zoom:
        scene = self._scene.scene
        if scene is not None and self.reset_zoom:
            scene.reset_zoom()
Beispiel #3
0
class Picker(HasTraits):
    """This module creates a 'Picker' that can interactively select a
    point and/or a cell in the data.  It also can use a world point
    picker (i.e. a generic point in space) and will probe for the data
    at that point.

    The Picker is usually called via a callback from the GUI
    interactor window.  After performing a pick on the VTK scene, a
    Picker object creates a `PickedData` object and passes it on to
    the pick_handler trait for further handling.
    """

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

    # Speficifies the pick type.  The 'point_picker' and 'cell_picker'
    # options are self-explanatory.  The 'world_picker' picks a point
    # using a WorldPointPicker and additionally uses a ProbeFilter to
    # probe the data at the picked point.
    pick_type = RevPrefixMap({'point_picker': 1,
                              'cell_picker': 2,
                              'world_picker': 3},
                             default_value='point_picker',
                             desc='specifies the picker type to use')

    # The pick_handler.  Set this to your own subclass if you want do
    # do something different from the default.
    pick_handler = Trait(DefaultPickHandler(), Instance(PickHandler))

    # Picking tolerance.
    tolerance = Range(0.0, 0.1, 0.025)

    # Raise the GUI on pick ?
    auto_raise = false(desc="whether to raise the picker GUI on pick")

    default_view = View(Group(Group(Item(name='pick_type'),
                                    Item(name='tolerance'), show_border=True),
                              Group(Item(name='pick_handler', style='custom'),
                                    show_border=True, show_labels=False),
                              ),
                        resizable=True,
                        buttons=['OK'])

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

        self.pointpicker = tvtk.PointPicker()
        self.cellpicker = tvtk.CellPicker()
        self.worldpicker = tvtk.WorldPointPicker()
        self.probe_data = tvtk.PolyData()
        # Use a set of axis to show the picked point.
        self.p_source = tvtk.Axes()
        self.p_mapper = tvtk.PolyDataMapper()
        self.p_actor = tvtk.Actor()
        self.p_source.symmetric = 1
        self.p_actor.pickable = 0
        self.p_actor.visibility = 0
        prop = self.p_actor.property
        prop.line_width = 2
        prop.ambient = 1.0
        prop.diffuse = 0.0
        configure_input(self.p_mapper, self.p_source)
        self.p_actor.mapper = self.p_mapper

        self.probe_data.points = [[0.0, 0.0, 0.0]]

        self.text_rep = tvtk.TextRepresentation()
        self.text_widget = tvtk.TextWidget()
        self.data = PickedData(renwin=renwin)
        self.data.text_actor = tvtk.TextActor()
        self.text_setup()
        self.widgets = False

    def __get_pure_state__(self):
        d = self.__dict__.copy()
        for x in ['renwin', 'ui', 'pick_handler', '__sync_trait__',
                  '__traits_listener__']:
            d.pop(x, None)
        return d

    def __getstate__(self):
        return state_pickler.dumps(self)

    def __setstate__(self, str_state):
        # This method is unnecessary since this object will almost
        # never be pickled by itself and only via the scene, therefore
        # __init__ will be called when the scene is constructed.
        # However, setstate is defined just for completeness.
        state_pickler.set_state(self, state_pickler.loads_state(str_state))

    #################################################################
    # `Picker` interface.
    #################################################################
    def pick(self, x, y):
        """Calls one of the current pickers and then passes the
        obtained data to the `self.pick_handler` object's
        `handle_pick` method.

        Parameters
        ----------

        - x : X position of the mouse in the window.

        - y : Y position of the mouse in the window.

        Note that the origin of x, y must be at the left bottom
        corner of the window.  Thus, for most GUI toolkits, y must
        be flipped appropriately such that y=0 is the bottom of the
        window.
        """
        if self.pick_type_ == 1:
            self.data = self.pick_point(x, y)
        elif self.pick_type_ == 2:
            self.data = self.pick_cell(x, y)
        elif self.pick_type_ == 3:
            self.data = self.pick_world(x, y)

        if self.widgets is False:
            self.setup_widgets()

        if self.data.valid_:
            self.text_widget.enabled = 1
            self.pick_handler.handle_pick(self.data)
            self.data.text_actor._get_text_property().trait_set(
                justification="left"
            )
        else:
            self.close_picker()

        text_color = self.data.text_actor._get_text_property().color
        if not self.data.renwin.background == text_color:
            pass
        else:
            self.set_text_color()

    def pick_point(self, x, y):
        """ Picks the nearest point. Returns a `PickedData` instance."""
        self.pointpicker.pick((float(x), float(y), 0.0),
                              self.data.renwin.renderer)

        pp = self.pointpicker
        id = pp.point_id
        picked_data = PickedData(renwin=self.data.renwin,
                                 text_actor=self.data.text_actor)
        coord = pp.pick_position
        picked_data.coordinate = coord
        if id > -1:
            data = pp.mapper.input.point_data
            bounds = pp.mapper.input.bounds

            picked_data.valid = 1
            picked_data.point_id = id
            picked_data.data = data

            self._update_actor(coord, bounds)
        else:
            self.p_actor.visibility = 0

        self.data.renwin.render()

        return picked_data

    def pick_cell(self, x, y):
        """ Picks the nearest cell. Returns a `PickedData` instance."""
        try:
            self.cellpicker.pick(float(x), float(y), 0.0,
                                 self.data.renwin.renderer)
        except TypeError:
            # On old versions of VTK, the signature used to be different
            self.cellpicker.pick((float(x), float(y), 0.0),
                                 self.data.renwin.renderer)

        cp = self.cellpicker
        id = cp.cell_id
        picked_data = PickedData(renwin=self.data.renwin,
                                 text_actor=self.data.text_actor)
        coord = cp.pick_position
        picked_data.coordinate = coord

        if id > -1:
            data = cp.mapper.input.cell_data
            bounds = cp.mapper.input.bounds

            picked_data.valid = 1
            picked_data.cell_id = id
            picked_data.data = data

            self._update_actor(coord, bounds)
        else:
            self.p_actor.visibility = 0

        self.data.renwin.render()
        return picked_data

    def pick_world(self, x, y):
        """ Picks a world point and probes for data there. Returns a
        `PickedData` instance."""
        self.worldpicker.pick((float(x), float(y), 0.0),
                              self.data.renwin.renderer)

        # Use the cell picker to get the data that needs to be probed.
        try:
            self.cellpicker.pick((float(x), float(y), 0.0),
                                 self.data.renwin.renderer)
        except TypeError:
            self.cellpicker.pick(float(x), float(y), 0.0,
                                 self.data.renwin.renderer)

        wp = self.worldpicker
        cp = self.cellpicker
        coord = wp.pick_position
        self.probe_data.points = [list(coord)]
        picked_data = PickedData(renwin=self.data.renwin,
                                 text_actor=self.data.text_actor)
        picked_data.coordinate = coord

        if cp.mapper:
            data = get_last_input(cp.mapper.input)
            # Need to create the probe each time because otherwise it
            # does not seem to work properly.
            probe = tvtk.ProbeFilter()
            probe.set_source_data(data)
            probe.set_input_data(self.probe_data)
            probe.update()
            data = probe.output.point_data
            bounds = cp.mapper.input.bounds

            picked_data.valid = 1
            picked_data.world_pick = 1
            picked_data.point_id = 0
            picked_data.data = data

            self._update_actor(coord, bounds)
        else:
            self.p_actor.visibility = 0

        self.data.renwin.render()
        return picked_data

    def close_picker(self):
        """This method makes the picker actor invisible when a non
        data point is selected"""
        if self.widgets:
            self.p_actor.visibility = 0
            self.data.renwin.renderer.remove_actor(self.p_actor)
            self.data.text_actor.visibility = 0
            self.data.renwin.renderer.remove_actor(self.data.text_actor)
            self.text_widget.enabled = 0
            self.widgets = False
            self.data.renwin.render()

    #################################################################
    # Non-public interface.
    #################################################################
    def text_setup(self):
        """Sets the properties of the text widget"""
        self.data.text_actor._get_text_property().font_size = 100
        self.text_rep._get_position_coordinate().trait_set(value=(.15, .15, 0))
        self.text_rep._get_position2_coordinate().trait_set(value=(.3, .2, 0))
        self.text_widget.trait_set(representation=self.text_rep)
        self.text_widget.trait_set(text_actor=self.data.text_actor)
        self.text_widget.selectable = 0

    def _update_actor(self, coordinate, bounds):
        """Updates the actor by setting its position and scale."""
        dx = 0.3*(bounds[1]-bounds[0])
        dy = 0.3*(bounds[3]-bounds[2])
        dz = 0.3*(bounds[5]-bounds[4])
        scale = max(dx, dy, dz)
        self.p_source.origin = coordinate
        self.p_source.scale_factor = scale
        self.p_actor.visibility = 1
        self.data.text_actor.visibility = 1

    def setup_widgets(self):
        """Sets up the picker actor and text actor"""

        self.text_widget.trait_set(interactor=self.data.renwin.interactor)
        self.data.renwin.renderer.add_actor(self.p_actor)
        self.data.renwin.renderer.add_actor(self.data.text_actor)
        self.p_actor.visibility = 1
        self.data.text_actor.visibility = 1
        self.widgets = True

    def set_picker_props(self, pick_type, tolerance, text_color):
        """Lets you set the picker properties"""
        self.pick_type = pick_type
        self.tolerance = tolerance
        self.set_text_color(text_color)

    def set_text_color(self, color=None):
        if color is None:
            bgcolor = self.data.renwin.background
            text_color = [0, 0, 0]
            for i in range(3):
                text_color[i] = abs(bgcolor[i]-1)
            self.data.text_actor._get_text_property().color = tuple(text_color)
        else:
            self.data.text_actor._get_text_property().color = color