class MyModel(HasTraits): select = Range(0, len(data) - 1, 0) last_select = deepcopy(select) iso_value = Range(iso_min, iso_max, iso_val, mode='logslider') opacity = Range(0, 1.0, 1.0) show_atoms = Bool(True) label = Str() available = List(Str) available = datalabels prev_button = Button('Previous') next_button = Button('Next') scene = Instance(MlabSceneModel, ()) plot_atoms = Instance(PipelineBase) plot0 = Instance(PipelineBase) # When the scene is activated, or when the parameters are changed, we # update the plot. @on_trait_change( 'select,iso_value,show_atoms,opacity,label,scene.activated') def update_plot(self): if self.plot0 is None: if not is_vectorfield: src = mlab.pipeline.scalar_field(X, Y, Z, data[self.select]) self.plot0 = self.scene.mlab.pipeline.iso_surface( src, contours=[-self.iso_value, self.iso_value], opacity=self.opacity, colormap='blue-red', vmin=-1e-8, vmax=1e-8) else: self.plot0 = self.scene.mlab.quiver3d( X, Y, Z, *data[self.select]) #flow self.plot0.scene.background = (1, 1, 1) elif self.select != self.last_select: if not is_vectorfield: self.plot0.mlab_source.set(scalars=data[self.select]) else: self.plot0.mlab_source.set( vectors=data[self.select].reshape((3, -1)).T) if not is_vectorfield: self.plot0.contour.contours = [-self.iso_value, self.iso_value] self.plot0.actor.property.opacity = self.opacity self.last_select = deepcopy(self.select) if datalabels is not None: self.label = datalabels[self.select] if geo_spec is not None: if self.plot_atoms is None: self.plot_atoms = self.scene.mlab.points3d( geo_spec[:, 0], geo_spec[:, 1], geo_spec[:, 2], scale_factor=0.75, resolution=20) self.plot_atoms.visible = self.show_atoms def _prev_button_fired(self): if self.select > 0: self.select -= 1 def _next_button_fired(self): if self.select < len(data) - 1: self.select += 1 # The layout of the dialog created items = (Item('scene', editor=SceneEditor(scene_class=MayaviScene), height=400, width=600, show_label=False), ) items0 = () if len(data) > 1: items0 += (Group( 'select', HSplit(Item('prev_button', show_label=False), Item('next_button', show_label=False))), ) items0 += (Group('iso_value', 'opacity', 'show_atoms'), ) if datalabels is not None: if len(datalabels) > 1: items1 = (Item('available', editor=ListStrEditor(title='Available Data', editable=False), show_label=False, style='readonly', width=300), ) items0 = HSplit(items0, items1) items += ( Group( Item('label', label='Selected Data', style='readonly', show_label=True), '_'), items0, ) else: items += items0 view = View(VSplit(items[0], items[1:]), resizable=True)
class MView(MWorkbenchPart, PerspectiveItem): """ Mixin containing common code for toolkit-specific implementations. """ implements(IView) #### 'IView' interface #################################################### # Is the view busy? (i.e., should the busy cursor (often an hourglass) be # displayed?). busy = Bool(False) # The category that the view belongs to (this can be used to group views # when they are displayed to the user). category = Str('General') # An image used to represent the view to the user (shown in the view tab # and in the view chooser etc). image = Instance(ImageResource) # Whether the view is visible or not. visible = Bool(False) ########################################################################### # 'IWorkbenchPart' interface. ########################################################################### def _id_default(self): """ Trait initializer. """ id = '%s.%s' % (type(self).__module__, type(self).__name__) logger.warn('view %s has no Id - using <%s>' % (self, id)) # If no Id is specified then use the name. return id def _name_default(self): """ Trait initializer. """ name = camel_case_to_words(type(self).__name__) logger.warn('view %s has no name - using <%s>' % (self, name)) return name ########################################################################### # 'IView' interface. ########################################################################### def activate(self): """ Activate the view. """ self.window.activate_view(self) return def hide(self): """ Hide the view. """ self.window.hide_view(self) return def show(self): """ Show the view. """ self.window.show_view(self) return
class TensorEigenvectorsEigenvalues(Filter): """ Computes the eigenvectors and eigenvalues of tensor fields. """ # The version of this class. Used for persistence. __version__ = 0 grid = Instance(tvtk.UnstructuredGrid, allow_none=False) processed_tensors_for_eigenvectors_eigenvalues = List() processed_tensors_for_edgelengths = List() processed_tensors_for_proportionaleigenvectors = List() active_tensor = String dimensions = Int eigenvector_eigenvalue_choice = DEnum(values_name='_dimension_list') _dimension_list = List(String) edgelength_on = Bool proprotionaleigenvectors_on = Bool ###################################################################### # The view. ###################################################################### traits_view = \ View( Group( Item(name='eigenvector_eigenvalue_choice', label='Eigenvectors/eigenvalues'), Item(name='edgelength_on', label='Edge lengths'), Item(name='proprotionaleigenvectors_on', label='Proportional eigenvectors'), ) ) ###################################################################### # `Filter` interface. ###################################################################### def update_pipeline(self): if len(self.inputs) == 0 or len(self.inputs[0].outputs) == 0: return self.grid = tvtk.UnstructuredGrid() ## This doesn't work - there must be more to copy. #self.grid.points = tvtk.Points() #self.grid.points.deep_copy(self.inputs[0].outputs[0].points) self.grid.deep_copy(self.inputs[0].outputs[0]) self.dimensions = size(self.grid.points[0]) for i in range(self.dimensions): self._dimension_list.append(i) self.active_tensor = self.inputs[0].outputs[0].point_data.tensors.name self._set_outputs([self.grid]) def update_data(self): self.active_tensor = self.inputs[0].outputs[0].point_data.tensors.name self.data_changed = True ###################################################################### # Non-public interface. ###################################################################### def _eigenvector_eigenvalue_choice_changed(self): self._update_output() def _edgelength_on_changed(self): self._update_output() def _proprotionaleigenvectors_on_changed(self): self._update_output() def _active_tensor_changed(self): self.calculate_eigenvectors_eigenvalues() self._update_output() def _update_output(self): if (self.edgelength_on): self.calculate_edgelengths() self.grid.point_data.set_active_scalars( self.active_tensor + '_edgelengths_' + self.eigenvector_eigenvalue_choice) else: self.grid.point_data.set_active_scalars( self.active_tensor + '_eigenvalues_' + self.eigenvector_eigenvalue_choice) if (self.proprotionaleigenvectors_on): self.calculate_propotionaleigenvectors() self.grid.point_data.set_active_vectors( self.active_tensor + '_proportionaleigenvectors_' + self.eigenvector_eigenvalue_choice) else: self.grid.point_data.set_active_vectors( self.active_tensor + '_eigenvectors_' + self.eigenvector_eigenvalue_choice) self.pipeline_changed = True def calculate_eigenvectors_eigenvalues(self): if not (self.active_tensor in self.processed_tensors_for_eigenvectors_eigenvalues): self.processed_tensors_for_eigenvectors_eigenvalues.append( self.active_tensor) input_grid = self.inputs[0].outputs[0] tensor_field = array( input_grid.point_data.get_array(self.active_tensor)) result = map( lambda x: linalg.eig( reshape(x, (self.dimensions, self.dimensions))), tensor_field) sorted_indices = map(lambda x: argsort(x[0]), result) for i in range(self.dimensions): eigenvalues = map(lambda x, y: x[0][y[i]], result, sorted_indices) eigenvectors = map(lambda x, y: x[1][y[i]], result, sorted_indices) eigenvalues_field = tvtk.FloatArray(name=self.active_tensor + '_eigenvalues_' + ` i `) eigenvalues_field.from_array(eigenvalues) self.grid.point_data.add_array(eigenvalues_field) eigenvectors_field = tvtk.FloatArray(name=self.active_tensor + '_eigenvectors_' + ` i `) eigenvectors_field.from_array(eigenvectors) self.grid.point_data.add_array(eigenvectors_field) def calculate_edgelengths(self): if not (self.active_tensor in self.processed_tensors_for_edgelengths): self.processed_tensors_for_edgelengths.append(self.active_tensor) input_grid = self.inputs[0].outputs[0] for i in range(self.dimensions): eigenvalues = array( self.grid.point_data.get_array(self.active_tensor + '_eigenvalues_' + ` i `)) edgelengths = map(lambda x: 1 / sqrt(x), eigenvalues) edgelengths_field = tvtk.FloatArray(name=self.active_tensor + '_edgelengths_' + ` i `) edgelengths_field.from_array(edgelengths) self.grid.point_data.add_array(edgelengths_field) def calculate_propotionaleigenvectors(self): if not (self.active_tensor in self.processed_tensors_for_proportionaleigenvectors): self.processed_tensors_for_proportionaleigenvectors.append( self.active_tensor) self.calculate_edgelengths() input_grid = self.inputs[0].outputs[0] for i in range(self.dimensions): eigenvectors = array( self.grid.point_data.get_array(self.active_tensor + '_eigenvectors_' + ` i `)) edgelengths = array( self.grid.point_data.get_array(self.active_tensor + '_edgelengths_' + ` i `)) proportionaleigenvectors = map(multiply, eigenvectors, edgelengths) proportionaleigenvectors_field = tvtk.FloatArray( name=self.active_tensor + '_proportionaleigenvectors_' + ` i `) proportionaleigenvectors_field.from_array( proportionaleigenvectors) self.grid.point_data.add_array(proportionaleigenvectors_field)
class MPLAction(Action): event = Instance(MPLEvent)
class ICommandStack(Interface): """ The command stack interface. A command stack is responsible for managing the changes to a data model and recording those changes so that they can be undone or redone. """ #### 'ICommandStack' interface ############################################ # This is the clean state of the stack. Its value changes as commands are # undone and redone. It can also be explicity set to mark the current # stack position as being clean (when the data is saved to disk for # example). clean = Bool # This is the name of the command that can be redone. It will be empty if # there is no command that can be redone. It is maintained by the undo # stack. redo_name = Unicode # This is the undo manager that manages this stack. undo_manager = Instance(IUndoManager) # This is the name of the command that can be undone. It will be empty if # there is no command that can be undone. It is maintained by the undo # stack. undo_name = Unicode ########################################################################### # 'ICommandStack' interface. ########################################################################### def begin_macro(self, name): """ This begins a macro by creating an empty command with the given 'name'. The commands passed to all subsequent calls to 'push()' will be contained in the macro until the next call to 'end_macro()'. Macros may be nested. The stack is disabled (ie. nothing can be undone or redone) while a macro is being created (ie. while there is an outstanding 'end_macro()' call). """ def clear(self): """ This clears the stack, without undoing or redoing any commands, and leaves the stack in a clean state. It is typically used when all changes to the data have been abandoned. """ def end_macro(self): """ This ends a macro. """ def push(self, command): """ This executes a command and saves it on the command stack so that it can be subsequently undone and redone. 'command' is an instance that implements the ICommand interface. Its 'do()' method is called to execute the command. If any value is returned by 'do()' then it is returned by 'push()'. The command stack will keep a reference to the result so that it can recognise it as an argument to a subsequent command (which allows a script to properly save a result needed later). """ def redo(self, sequence_nr=0): """ If 'sequence_nr' is 0 then the last command that was undone is redone and any result returned. Otherwise commands are redone up to and including the given 'sequence_nr' and any result of the last of these is returned. """ def undo(self, sequence_nr=0): """ If 'sequence_nr' is 0 then the last command is undone. Otherwise
class FeatureBar(HasPrivateTraits): #--------------------------------------------------------------------------- # Trait definitions: #--------------------------------------------------------------------------- # The wx.Window which is the parent for the FeatureBar: parent = Instance(wx.Window) # The DockControl whose features are being displayed: dock_control = Instance(DockControl) # The wx.Window being used for the FeatureBar: control = Instance(wx.Window) # Event posted when the user has completed using the FeatureBar: completed = Event # The background color for the FeatureBar: bg_color = Color(0xDBEEF7, allow_none=True) # The border color for the FeatureBar: border_color = Color(0X2583AF, allow_none=True) # Should the feature bar display horizontally (or vertically)? horizontal = Bool(True) #--------------------------------------------------------------------------- # Hides the feature bar: #--------------------------------------------------------------------------- def hide(self): """ Hides the feature bar. """ if self.control is not None: self.control.Hide() #--------------------------------------------------------------------------- # Shows the feature bar: #--------------------------------------------------------------------------- def show(self): """ Shows the feature bar. """ # Make sure all prerequisites are met: dock_control, parent = self.dock_control, self.parent if (dock_control is None) or (parent is None): return # Create the actual control (if needed): control = self.control if control is None: self.control = control = wx.Frame(None, -1, '', style=wx.BORDER_NONE) # Set up the 'erase background' event handler: wx.EVT_ERASE_BACKGROUND(control, self._erase_background) # Set up the 'paint' event handler: wx.EVT_PAINT(control, self._paint) # Set up mouse event handlers: wx.EVT_LEFT_DOWN(control, self._left_down) wx.EVT_LEFT_UP(control, self._left_up) wx.EVT_RIGHT_DOWN(control, self._right_down) wx.EVT_RIGHT_UP(control, self._right_up) wx.EVT_MOTION(control, self._mouse_move) wx.EVT_ENTER_WINDOW(control, self._mouse_enter) control.SetDropTarget(PythonDropTarget(self)) # Calculate the best size and position for the feature bar: size = wx.Size(32, 32) width = height = 0 horizontal = self.horizontal for feature in dock_control.active_features: bitmap = feature.bitmap if bitmap is not None: if horizontal: width += (bitmap.GetWidth() + 3) height = max(height, bitmap.GetHeight()) else: width = max(width, bitmap.GetWidth()) height += (bitmap.GetHeight() + 3) if width > 0: if horizontal: size = wx.Size(width + 5, height + 8) else: size = wx.Size(width + 8, height + 5) control.SetSize(size) px, py = parent.GetScreenPosition() fx, fy = dock_control.feature_popup_position control.SetPosition(wx.Point(px + fx, py + fy)) control.Show() #-- Window Event Handlers -------------------------------------------------- #--------------------------------------------------------------------------- # Handles repainting the window: #--------------------------------------------------------------------------- def _paint(self, event): """ Handles repainting the window. """ window = self.control dx, dy = window.GetSizeTuple() dc = wx.PaintDC(window) # Draw the feature container: bg_color = self.bg_color border_color = self.border_color if (bg_color is not None) or (border_color is not None): if border_color is None: dc.SetPen(wx.TRANSPARENT_PEN) else: dc.SetPen(wx.Pen(border_color, 1, wx.SOLID)) if bg_color is None: dc.SetBrush(wx.TRANSPARENT_PEN) else: dc.SetBrush(wx.Brush(bg_color, wx.SOLID)) dc.DrawRectangle(0, 0, dx, dy) # Draw the feature icons: if self.horizontal: x = 4 for feature in self.dock_control.active_features: bitmap = feature.bitmap if bitmap is not None: dc.DrawBitmap(bitmap, x, 4, True) x += (bitmap.GetWidth() + 3) else: y = 4 for feature in self.dock_control.active_features: bitmap = feature.bitmap if bitmap is not None: dc.DrawBitmap(bitmap, 4, y, True) y += (bitmap.GetHeight() + 3) #--------------------------------------------------------------------------- # Handles erasing the window background: #--------------------------------------------------------------------------- def _erase_background(self, event): """ Handles erasing the window background. """ pass #--------------------------------------------------------------------------- # Handles the left mouse button being pressed: #--------------------------------------------------------------------------- def _left_down(self, event): """ Handles the left mouse button being pressed. """ self._feature = self._feature_at(event) self._dragging = False self._xy = (event.GetX(), event.GetY()) #self.control.CaptureMouse() #--------------------------------------------------------------------------- # Handles the left mouse button being released: #--------------------------------------------------------------------------- def _left_up(self, event): """ Handles the left mouse button being released. """ #self.control.ReleaseMouse() self._dragging = None feature, self._feature = self._feature, None if feature is not None: if feature is self._feature_at(event): self.control.ReleaseMouse() self.completed = True feature._set_event(event) feature.click() #--------------------------------------------------------------------------- # Handles the right mouse button being pressed: #--------------------------------------------------------------------------- def _right_down(self, event): """ Handles the right mouse button being pressed. """ self._feature = self._feature_at(event) self._dragging = False self._xy = (event.GetX(), event.GetY()) #self.control.CaptureMouse() #--------------------------------------------------------------------------- # Handles the right mouse button being released: #--------------------------------------------------------------------------- def _right_up(self, event): """ Handles the right mouse button being released. """ #self.control.ReleaseMouse() self._dragging = None feature, self._feature = self._feature, None if feature is not None: if feature is self._feature_at(event): self.control.ReleaseMouse() self.completed = True feature._set_event(event) feature.right_click() #--------------------------------------------------------------------------- # Handles the mouse moving over the window: #--------------------------------------------------------------------------- def _mouse_move(self, event): """ Handles the mouse moving over the window. """ # Update tooltips if no mouse button is currently pressed: if self._dragging is None: feature = self._feature_at(event) if feature is not self._tooltip_feature: self._tooltip_feature = feature tooltip = '' if feature is not None: tooltip = feature.tooltip wx.ToolTip.Enable(False) wx.ToolTip.Enable(True) self.control.SetToolTip(wx.ToolTip(tooltip)) # Check to see if the mouse has left the window, and mark it # completed if it has: x, y = event.GetX(), event.GetY() dx, dy = self.control.GetSizeTuple() if (x < 0) or (y < 0) or (x >= dx) or (y >= dy): self.control.ReleaseMouse() self._tooltip_feature = None self.completed = True return # Check to see if we are in 'drag mode' yet: if not self._dragging: x, y = self._xy if (abs(x - event.GetX()) + abs(y - event.GetY())) < 3: return self._dragging = True # Check to see if user is trying to drag a 'feature': feature = self._feature if feature is not None: feature._set_event(event) prefix = button = '' if event.RightIsDown(): button = 'right_' if event.ControlDown(): prefix = 'control_' elif event.AltDown(): prefix = 'alt_' elif event.ShiftDown(): prefix = 'shift_' object = getattr(feature, '%s%sdrag' % (prefix, button))() if object is not None: self.control.ReleaseMouse() self._feature = None self.completed = True self.dock_control.pre_drag_all(object) PythonDropSource(self.control, object) self.dock_control.post_drag_all() self._dragging = None #--------------------------------------------------------------------------- # Handles the mouse entering the window: #--------------------------------------------------------------------------- def _mouse_enter(self, event): """ Handles the mouse entering the window. """ self.control.CaptureMouse() #-- Drag and drop event handlers: ---------------------------------------------- #--------------------------------------------------------------------------- # Handles a Python object being dropped on the control: #--------------------------------------------------------------------------- def wx_dropped_on(self, x, y, data, drag_result): """ Handles a Python object being dropped on the window. """ # Determine what, if any, feature the object was dropped on: feature = self._can_drop_on_feature(x, y, data) # Indicate use of the feature bar is complete: self.completed = True # Reset any drag state information: self.dock_control.post_drag(FEATURE_EXTERNAL_DRAG) # Check to see if the data was dropped on a feature or not: if feature is not None: if isinstance(data, IFeatureTool): # Handle an object implementing IFeatureTool being dropped: dock_control = feature.dock_control data.feature_dropped_on_dock_control(dock_control) data.feature_dropped_on(dock_control.object) else: # Handle a normal object being dropped: wx, wy = self.control.GetScreenPosition() feature.set(x=wx + x, y=wy + y) feature.drop(data) return drag_result return wx.DragNone #--------------------------------------------------------------------------- # Handles a Python object being dragged over the control: #--------------------------------------------------------------------------- def wx_drag_over(self, x, y, data, drag_result): """ Handles a Python object being dragged over the control. """ # Handle the case of dragging a normal object over a 'feature': if self._can_drop_on_feature(x, y, data) is not None: return drag_result return wx.DragNone #--------------------------------------------------------------------------- # Handles a dragged Python object leaving the window: #--------------------------------------------------------------------------- def wx_drag_leave(self, data): """ Handles a dragged Python object leaving the window. """ # Indicate use of the feature bar is complete: self.completed = True # Reset any drag state information: self.dock_control.post_drag(FEATURE_EXTERNAL_DRAG) #-- Private Methods -------------------------------------------------------- #--------------------------------------------------------------------------- # Returns a feature that the pointer is over and which can accept the # specified data: #--------------------------------------------------------------------------- def _can_drop_on_feature(self, x, y, data): """ Returns a feature that the pointer is over and which can accept the specified data. """ feature = self._feature_at(FakeEvent(x, y)) if (feature is not None) and feature.can_drop(data): return feature return None #--------------------------------------------------------------------------- # Returns the DockWindowFeature (if any) at a specified window position: #--------------------------------------------------------------------------- def _feature_at(self, event): """ Returns the DockWindowFeature (if any) at a specified window position. """ if self.horizontal: x = 4 for feature in self.dock_control.active_features: bitmap = feature.bitmap if bitmap is not None: bdx = bitmap.GetWidth() if self._is_in(event, x, 4, bdx, bitmap.GetHeight()): return feature x += (bdx + 3) else: y = 4 for feature in self.dock_control.active_features: bitmap = feature.bitmap if bitmap is not None: bdy = bitmap.GetHeight() if self._is_in(event, 4, y, bitmap.GetWidth(), bdy): return feature y += (bdy + 3) return None #--------------------------------------------------------------------------- # Returns whether or not an event is within a specified bounds: #--------------------------------------------------------------------------- def _is_in(self, event, x, y, dx, dy): """ Returns whether or not an event is within a specified bounds. """ return ((x <= event.GetX() < (x + dx)) and (y <= event.GetY() < (y + dy)))
class Bar(HasTraits): foo = Instance(Foo, ()) s = Delegate('foo')
class BuiltinSurface(Source): # The version of this class. Used for persistence. __version__ = 0 # Flag to set the poly data type. source = Enum('arrow', 'cone', 'cube', 'cylinder', 'disk', 'earth', 'line', 'outline', 'plane', 'point', 'polygon', 'sphere', 'superquadric', 'textured sphere', 'glyph2d', desc='which poly data source to be used') # Define the trait 'data_source' whose value must be an instance of # type PolyData data_source = Instance(tvtk.PolyDataAlgorithm, allow_none=False, record=True) # Information about what this object can produce. output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) # Create the UI for the traits. view = View(Group(Item(name='source'), Item(name='data_source', style='custom', resizable=True), label='Surface Source', show_labels=False), resizable=True) ######################################## # Private traits. # A dictionary that maps the source names to instances of the # poly data sources. _source_dict = Dict(Str, Instance(tvtk.PolyDataAlgorithm, allow_none=False)) ###################################################################### # `object` interface ###################################################################### def __init__(self, **traits): # Call parent class' init. super(BuiltinSurface, self).__init__(**traits) # Initialize the source to the default mode's instance from # the dictionary if needed. if 'source' not in traits: self._source_changed(self.source) def __set_pure_state__(self, state): self.source = state.source super(BuiltinSurface, self).__set_pure_state__(state) ###################################################################### # Non-public methods. ###################################################################### def _source_changed(self, value): """This method is invoked (automatically) when the `source` trait is changed. """ self.data_source = self._source_dict[self.source] def _data_source_changed(self, old, new): """This method is invoked (automatically) when the poly data source is changed .""" self.outputs = [self.data_source.output] if old is not None: old.on_trait_change(self.render, remove=True) new.on_trait_change(self.render) def __source_dict_default(self): """Default value for source dict.""" sd = { 'arrow': tvtk.ArrowSource(), 'cone': tvtk.ConeSource(), 'cube': tvtk.CubeSource(), 'cylinder': tvtk.CylinderSource(), 'disk': tvtk.DiskSource(), 'earth': tvtk.EarthSource(), 'line': tvtk.LineSource(), 'outline': tvtk.OutlineSource(), 'plane': tvtk.PlaneSource(), 'point': tvtk.PointSource(), 'polygon': tvtk.RegularPolygonSource(), 'sphere': tvtk.SphereSource(), 'superquadric': tvtk.SuperquadricSource(), 'textured sphere': tvtk.TexturedSphereSource(), 'glyph2d': tvtk.GlyphSource2D() } return sd
class Plot(Figure2D): r"""Plot object, for data plotting. Use this for data oplotting in a traits gui You can also use it without calling :meth:`configure_traits` and display figure with matplotlib interactively by calling :meth:`figure` >>> p = Plot() You can define attributes afterwards: >>> p.title = 'My graph' >>> p.xscale = 'log' >>> p.xlabel = 'time' >>> p.ylabel = 'speed' >>> p.figtext.text = 'This text is also displayed\nThis is the second row' >>> p.figtext.position = (0.2,0.4) Finally you can plot data and display it: >>> p.plot([1,2,3],[1,2,3]) #plots actual data >>> ok = p.configure_traits() Or you can use matplotlib by #>>> f = p.figure() #>>> f.show() """ #: defines x scale xscale = Enum('linear', 'log') #: defines y scale yscale = Enum('linear', 'log') #: title of the figure title = Str('') #: x axis label xlabel = Str('x') #: y axis label ylabel = Str('y') #: text that is displayed, '' for empty text figtext = Instance(FigText, ()) view = View(HSplit( Group('title', 'xlabel', 'ylabel', 'xscale', 'yscale', 'figtext'), Item('fig', show_label=False, editor=MPLFigureEditor())), width=500, height=400, resizable=True, handler=Figure2DHandler) def __init__(self, *args, **kw): super(Plot, self).__init__(*args, **kw) self.init_axis() self._init_figtext() self.update = True def init_axis(self): """ Clears axis and initializes label, title, etc.. """ #self.fig = Figure() #ax = self.fig.add_subplot(111) log.debug('Initializing plot') self.ax.cla() #ax = self.fig.axes[0] for name in SETTABLE_TRAITS: value = getattr(self, name) self.set_axis(name, value) def _init_figtext(self): x, y = self.figtext.position self.fig.text(x, y, self.figtext.text) def plot(self, x, y, *args, **kw): """Plots x,y :param array x: X data :param array y: X data :param args: Aditional arguments to pass to plot function :key kw: Additional keys supported by matplotlib plot and function """ #if sigma: # raise NotImplemented, 'specifying sgima does not work yet' log.debug('Plotting data') self.ax.plot(x, y, *args, **kw) self.update = True def errorbar(self, x, y, **kw): """Plots x,y, and optional sigma data :param array x: X data :param array y: X data :key kw: Additional keys supported by matplotlib errorbar function """ log.debug('Plotting data') kw.setdefault('fmt', 'o') self.ax.errorbar(x, y, **kw) self.update = True @on_trait_change(','.join(SETTABLE_TRAITS)) def set_axis(self, name, value): """Sets axis parameter :param str name: defines the name of the axis parameter to set :param value: defines parameter value """ try: setter = getattr(self.ax, 'set_' + name) setter(value) self.update = True except: pass @on_trait_change('figtext.text,figtext.position') def set_figtext(self, name, value): """Sets figure text, :param str name: defines the name of the figtext parameter to set :param value: defines parameter value """ try: setter = getattr(self.fig.texts[0], 'set_' + name) setter(value) self.update = True except: pass def figure(self, *arg, **kw): """Creates a figure object for interactive use. figure is drawn with texts and axes defined by :attr:`Figure2D.fig`. """ f = plt.figure(*arg, **kw) f.axes = self.fig.axes f.texts = self.fig.texts return f def savefig(self, fname, *args, **kw): """Saves plot to disk, see :func:`matplotlib.pyplot.savefig` :param str fname: Filename string """ f = self.figure() f.savefig(fname, *args, **kw) plt.close(f)
class Demo(HasTraits): plot = Instance(Component) fileName = "default.txt" case = List(UncertaintyValue) cases = {} defaultCase = [] # Attributes to use for the plot view. size = (400, 250) traits_view = View(Group( Group(Item('plot', editor=ComponentEditor(size=size), show_label=False), orientation="vertical", show_border=True), Group(Item('case', editor=TabularEditor(adapter=CaseAdapter(can_edit=False)), show_label=False), orientation="vertical", show_border=True), layout='split', orientation='horizontal'), title='Interactive Lines', resizable=True) def setFileName(self, newName): self.fileName = newName def _update_case(self, name): if name: self.case = self.cases.get(name) else: self.case = self.defaultCase def _plot_default(self): results = cPickle.load(open(self.fileName, 'r')) outcomes = results[0][1].keys() outcomes.pop(outcomes.index('TIME')) x = results[0][1]['TIME'] for j, aCase in enumerate(results): aCase = [ UncertaintyValue(name=key, value=value) for key, value in aCase[0][0].items() ] self.cases['y' + str(j)] = aCase uncertainties = results[0][0][0] uncertaintynames = uncertainties.keys() uncertaintyvalues = [] for key in uncertainties.keys(): uncertaintyvalues.append(uncertainties[key]) case = [] for i in range(len(uncertainties)): case.append( UncertaintyValue(name=str(uncertaintynames[i]), value="")) #haydaa self.case = case self.defaultCase = case # Create some x-y data series to plot pds = [] for i, outcome in enumerate(outcomes): pd = ArrayPlotData(index=x) for j in range(len(results)): pd.set_data("y" + str(j), results[j][1].get(outcome)) pds.append(pd) # Create a container and add our plots container = GridContainer(bgcolor="lightgray", use_backbuffer=True, shape=(1, 1)) #plot data tools = [] for j in range(len(outcomes)): pd1 = pds[j] # Create some line plots of some of the data plot = Plot(pd1, title=outcomes[j], border_visible=True, border_width=1) plot.legend.visible = False for i in range(len(results)): plotvalue = "y" + str(i) color = colors[i % len(colors)] plot.plot(("index", plotvalue), name=plotvalue, color=color) for value in plot.plots.values(): for entry in value: entry.index.sort_order = 'ascending' # Attach the selector tools to the plot selectorTool1 = LineSelectorTool(component=plot) plot.tools.append(selectorTool1) tools.append(selectorTool1) # Attach some tools to the plot plot.tools.append(PanTool(plot)) zoom = ZoomTool(component=plot, tool_mode="box", always_on=False) plot.overlays.append(zoom) container.add(plot) #make sure the selector tools know each other for tool in tools: tool._demo = self return container
class VTKXMLFileReader(FileDataSource): """A VTK XML file reader. The reader supports all the different types of data sets. This reader also supports a time series. Currently, this reader assumes that there is only one output that has configurable attributes. """ # The version of this class. Used for persistence. __version__ = 0 ######################################## # Dynamic traits: These traits are dynamic and are automatically # updated depending on the contents of the file. # The active point scalar name. An empty string indicates that # the attribute is "deactivated". This is useful when you have # both point and cell attributes and want to use cell data by # default. point_scalars_name = DEnum(values_name='_point_scalars_list', desc='scalar point data attribute to use') # The active point vector name. point_vectors_name = DEnum(values_name='_point_vectors_list', desc='vectors point data attribute to use') # The active point tensor name. point_tensors_name = DEnum(values_name='_point_tensors_list', desc='tensor point data attribute to use') # The active cell scalar name. cell_scalars_name = DEnum(values_name='_cell_scalars_list', desc='scalar cell data attribute to use') # The active cell vector name. cell_vectors_name = DEnum(values_name='_cell_vectors_list', desc='vectors cell data attribute to use') # The active cell tensor name. cell_tensors_name = DEnum(values_name='_cell_tensors_list', desc='tensor cell data attribute to use') ######################################## # The VTK data file reader. reader = Instance(tvtk.XMLReader) # Information about what this object can produce. output_info = PipelineInfo(datasets=['any'], attribute_types=['any'], attributes=['any']) # Our view. view = View(Group(Include('time_step_group'), Item(name='point_scalars_name'), Item(name='point_vectors_name'), Item(name='point_tensors_name'), Item(name='cell_scalars_name'), Item(name='cell_vectors_name'), Item(name='cell_tensors_name'), Item(name='reader'), )) ######################################## # Private traits. # These private traits store the list of available data # attributes. The non-private traits use these lists internally. _point_scalars_list = List(Str) _point_vectors_list = List(Str) _point_tensors_list = List(Str) _cell_scalars_list = List(Str) _cell_vectors_list = List(Str) _cell_tensors_list = List(Str) # This filter allows us to change the attributes of the data # object and will ensure that the pipeline is properly taken care # of. Directly setting the array in the VTK object will not do # this. _assign_attribute = Instance(tvtk.AssignAttribute, args=(), allow_none=False) # Toggles if this is the first time this object has been used. _first = Bool(True) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(VTKXMLFileReader, self).__get_pure_state__() for name in ('_assign_attribute', '_first'): d.pop(name, None) # Pickle the 'point_scalars_name' etc. since these are # properties and not in __dict__. attr = {} for name in ('point_scalars', 'point_vectors', 'point_tensors', 'cell_scalars', 'cell_vectors', 'cell_tensors'): d.pop('_' + name + '_list', None) d.pop('_' + name + '_name', None) x = name + '_name' attr[x] = getattr(self, x) d.update(attr) return d def __set_pure_state__(self, state): # The reader has its own file_name which needs to be fixed. state.reader.file_name = state.file_path.abs_pth # Now call the parent class to setup everything. super(VTKXMLFileReader, self).__set_pure_state__(state) ###################################################################### # `Base` interface ###################################################################### def start(self): """This is invoked when this object is added to the mayavi pipeline. """ # Do nothing if we are already running. if self.running: return # Call the parent method to do its thing. This will typically # start all our children. super(VTKXMLFileReader, self).start() def stop(self): """Invoked when this object is removed from the mayavi pipeline. """ if not self.running: return # Call the parent method to do its thing. super(VTKXMLFileReader, self).stop() ###################################################################### # `FileDataSource` interface ###################################################################### def update(self): if len(self.file_path.get()) == 0: return reader = self.reader reader.update() self.render() def update_data(self): if len(self.file_path.get()) == 0: return self.reader.update() pnt_attr, cell_attr = get_all_attributes(self.reader.output) def _setup_data_traits(obj, attributes, d_type): """Given the object, the dict of the attributes from the `get_all_attributes` function and the data type (point/cell) data this will setup the object and the data. """ attrs = ['scalars', 'vectors', 'tensors'] aa = obj._assign_attribute data = getattr(obj.reader.output, '%s_data'%d_type) for attr in attrs: values = attributes[attr] values.append('') setattr(obj, '_%s_%s_list'%(d_type, attr), values) if len(values) > 1: default = getattr(obj, '%s_%s_name'%(d_type, attr)) if obj._first and len(default) == 0: default = values[0] getattr(data, 'set_active_%s'%attr)(default) aa.assign(default, attr.upper(), d_type.upper() +'_DATA') aa.update() kw = {'%s_%s_name'%(d_type, attr): default, 'trait_change_notify': False} obj.set(**kw) _setup_data_traits(self, cell_attr, 'cell') _setup_data_traits(self, pnt_attr, 'point') if self._first: self._first = False # Propagate the data changed event. self.data_changed = True ###################################################################### # Non-public interface ###################################################################### def _file_path_changed(self, fpath): value = fpath.get() if len(value) == 0: return else: if self.reader is None: d_type = find_file_data_type(fpath.get()) self.reader = eval('tvtk.XML%sReader()'%d_type) reader = self.reader reader.file_name = value reader.update() # Setup the outputs by resetting self.outputs. Changing # the outputs automatically fires a pipeline_changed # event. try: n = reader.number_of_outputs except AttributeError: # for VTK >= 4.5 n = reader.number_of_output_ports outputs = [] for i in range(n): outputs.append(reader.get_output(i)) # FIXME: Only the first output goes through the assign # attribute filter. aa = self._assign_attribute aa.input = outputs[0] outputs[0] = aa.output self.update_data() self.outputs = outputs # FIXME: The output info is only based on the first output. self.output_info.datasets = [get_tvtk_dataset_name(outputs[0])] # Change our name on the tree view self.name = self._get_name() def _set_data_name(self, data_type, attr_type, value): if value is None: return reader_output = self.reader.output if len(value) == 0: # If the value is empty then we deactivate that attribute. d = getattr(reader_output, attr_type + '_data') method = getattr(d, 'set_active_%s'%data_type) method(None) self.data_changed = True return aa = self._assign_attribute data = None if attr_type == 'point': data = reader_output.point_data elif attr_type == 'cell': data = reader_output.cell_data method = getattr(data, 'set_active_%s'%data_type) method(value) aa.assign(value, data_type.upper(), attr_type.upper() +'_DATA') aa.update() # Fire an event, so the changes propagate. self.data_changed = True def _point_scalars_name_changed(self, value): self._set_data_name('scalars', 'point', value) def _point_vectors_name_changed(self, value): self._set_data_name('vectors', 'point', value) def _point_tensors_name_changed(self, value): self._set_data_name('tensors', 'point', value) def _cell_scalars_name_changed(self, value): self._set_data_name('scalars', 'cell', value) def _cell_vectors_name_changed(self, value): self._set_data_name('vectors', 'cell', value) def _cell_tensors_name_changed(self, value): self._set_data_name('tensors', 'cell', value) def _get_name(self): """ Gets the name to display on the tree view. """ fname = basename(self.file_path.get()) ret = "VTK XML file (%s)"%fname if len(self.file_list) > 1: ret += " (timeseries)" if '[Hidden]' in self.name: ret += ' [Hidden]' return ret
class DlsAnalyzer(BaseFileAnalyzer): """ DlsAnalyzer is used to analyze multiple dls files. First you must define a function that returns x value for the data analyzed. A default function :attr:'get_x_value' returns just index value. This function must have two erguments as an input: index value and filename. It is up to you how the return value uses these inputs. For instance: >>> def get_x(fnames, index): ... return 100 + 0.1 * index Then create :class:`Filenames` instance (optional) >>> filenames = Filenames(directory = '../testdata', pattern = *.ASC) Now you cen create analyzer and do some analysis >>> fitter = create_dls_fitter('single_stretch_exp') >>> analyzer = DlsAnalyzer(filenames = filenames, ... fitter = fitter, ... get_x_value = get_x) >>> analyzer.log_name = 'analysis.rst' #specify logname to log results in reStructuredText format >>> analyzer.constants = (('s','n'),()) #set constant parameters in fitting process, >>> analyzer.x_name = 'position' #specify x data name When everything is set you can call process to fit all data. >>> analyzer.process() >>> analyzer.save_result('..testdata/output.npy') """ #: Filenames instance filenames = Instance(Filenames, ()) #: selected filename selected = DelegatesTo('filenames') #: data fitter object for data fitting fitter = Instance(DlsFitter) #: defines a list of constants tuple that are set in each fit run. See :meth:`process` constants = List(List(Str)) #: defines whethere fit plots are saved saves_fits = Bool(False) #: if defined it will generate a valif reStructuredText file log_name = Str #: actual log is written here log = Str #: fit results are storred here results = Instance(StructArrayData, ()) #: This function is used to get x value from index integer and filename string get_x_value = Function #: this specifies name of the x data of results x_name = Str('index') #: if this list is not empty it will be used to obtain x_values x_values = List(Float) view = View(Group(dls_analyzer_group, 'saves_fits', 'results'), Item('fitter', style='custom'), resizable=True) @on_trait_change('selected') def _open_dls(self, name): self.fitter.open_dls(name) self.fitter._plot() def _constants_default(self): return [['f', 's'], ['']] def _get_x_value_default(self): def get(fnames, index): return index return get def _selected_changed(self): self.process_selected() def process_selected(self): """Opens fname and fits data according to self.constants :param str fname: filename of asc data to be opened and fitted """ fname = self.selected self.fitter.open_dls(fname) self.fitter.function.reset() print(self.constants) for constants in self.constants: try: self.fitter.fit(constants=constants) except: self.fitter.configure_traits() if self.saves_fits: path, fname = os.path.split(fname) path = os.path.join(path, 'fits') try: os.mkdir(path) except: pass fname = os.path.join(path, fname) imagename = fname + '.png' log.info('Plotting %s' % imagename) self.fitter.plotter.title = imagename self.fitter.plotter.savefig(imagename) result = self.fitter.function.get_parameters() self._process_result(result, self.selected, self.index) return result def _process_result(self, result, fname, index): result = (i for sub in result for i in sub) #flatten results list first try: self.results.data[index] = (self.x_values[index], ) + tuple(result) except: self.results.data[index] = (self.get_x_value( self.filenames.filenames, index), ) + tuple(result) self.results.data_updated = True @on_trait_change('filenames.filenames') def _init(self): array_names = [self.x_name] for name in self.fitter.function.pnames: array_names.append(name) array_names.append(name + '_err') dtype = np.dtype(list(zip(array_names, ['float'] * len(array_names)))) self.results = StructArrayData( data=np.zeros(len(self.filenames), dtype=dtype)) #self.results_err = StructArrayData(data = np.zeros(len(self.filenames), dtype = dtype)) self.results.data_updated = True #self.results_err.data_updated = True #self.log = '===========\nFit results\n===========\n\n' return True def save_results(self, fname): """Saves results to disk :param str fname: output filename """ np.save(fname, self.results.data) if self.log_name: self.log = '===========\nFit results\n===========\n\n' for fname in self.filenames.filenames: imagename = fname + '.png' self.log += '.. image:: %s\n' % os.path.basename(imagename) with open(self.log_name, 'w') as f: f.write(self.log)
class MainWindow(HasTraits): """Main window for the viewer built using Traits.""" # mpl figure figure = Instance(Figure) # Range slider for selecing slice to view slice_index_low = Int(0) # These have to be trait ints or they don't work slice_index_high = Int( 91) # with the dynamic updating of the Range slider. slice_index = Range(low='slice_index_low', high='slice_index_high') # Radio box for selecting orthogonal slice slice_plane = Enum(_slice_planes) # Affine TextCtrl affine = Array(Float, (4, 4)) def __init__(self): super(MainWindow, self).__init__() # Initialize our nipy image object self.img = ImageData() # Initialize our matplotlib figure self.img_plot = SingleImage(self.figure, self.img.data) # # Initializers for Traited attrs # def _figure_default(self): """Initialize matplotlib figure.""" figure = Figure() return figure def _slice_index_default(self): """Initialize slice_index attr without triggering the on_trait_change method. """ return 0 # # Event handlers # @on_trait_change('slice_index, slice_plane') def update_slice_index(self): self.img.set_slice_index(self.slice_index) self.update_image_slicing() self.image_show() # # Data Model methods # def update_affine(self): self.affine = self.img.get_affine() def update_image_slicing(self): # XXX: BUG: self.slice_index is set by the slider of the # current slice. When we switch the slice plane, this index # may be outside the range of the new slice. Need to handle # this. if self.slice_plane == 'Axial': self.img.set_slice_plane(_slice_planes[0]) elif self.slice_plane == 'Sagittal': self.img.set_slice_plane(_slice_planes[1]) elif self.slice_plane == 'Coronal': self.img.set_slice_plane(_slice_planes[2]) else: raise AttributeError('Unknown slice plane') # update image array self.img.update_data() # update figure data self.img_plot.set_data(self.img.data) # get range information for slider low, high = self.img.get_range() # update range slider self.slice_index_low = low self.slice_index_high = high def image_show(self): self.img_plot.draw() # # View code # # Menus def open_menu(self): dlg = FileDialog() dlg.open() if dlg.return_code == OK: self.img.load_image(dlg.path) self.update_affine() self.update_slice_index() menu_open_action = Action(name='Open Nifti', action='open_menu') file_menubar = MenuBar(Menu(menu_open_action, name='File')) # Items fig_item = Item('figure', editor=MPLFigureEditor()) # radio button to pick slice _slice_opts = { 'Axial': '1:Axial', 'Sagittal': '2:Sagittal', 'Coronal': '3:Coronal' } slice_opt_item = Item(name='slice_plane', editor=EnumEditor(values=_slice_opts), style='custom') affine_item = Item('affine', label='Affine', style='readonly') # BUG: The rendering with the 'readonly' style creates an ugly wx # "multi-line" control. traits_view = View(HSplit( Group(fig_item), Group(affine_item, slice_opt_item, Item('slice_index'))), menubar=file_menubar, width=0.80, height=0.80, resizable=True)
class Scene(TVTKScene, Widget): """A VTK interactor scene widget for pyface and wxPython. This widget uses a RenderWindowInteractor and therefore supports interaction with VTK widgets. The widget uses TVTK. In addition to the features that the base TVTKScene provides this widget supports: - saving the rendered scene to the clipboard. - picking data on screen. Press 'p' or 'P' when the mouse is over a point that you need to pick. - The widget also uses a light manager to manage the lighting of the scene. Press 'l' or 'L' to activate a GUI configuration dialog for the lights. - Pressing the left, right, up and down arrow let you rotate the camera in those directions. When shift-arrow is pressed then the camera is panned. Pressing the '+' (or '=') and '-' keys let you zoom in and out. - Pressing the 'f' key will set the camera focal point to the current point. - full screen rendering via the full_screen button on the UI. """ # The version of this class. Used for persistence. __version__ = 0 ########################################################################### # Traits. ########################################################################### # Turn on full-screen rendering. full_screen = Button('Full Screen') # The picker handles pick events. picker = Instance(picker.Picker) ######################################## # Render_window's view. _stereo_view = Group( Item(name='stereo_render'), Item(name='stereo_type'), show_border=True, label='Stereo rendering', ) # The default view of this object. default_view = View(Group( Group( Item(name='background'), Item(name='foreground'), Item(name='parallel_projection'), Item(name='disable_render'), Item(name='off_screen_rendering'), Item(name='jpeg_quality'), Item(name='jpeg_progressive'), Item(name='magnification'), Item(name='anti_aliasing_frames'), Item(name='full_screen', show_label=False), ), Group( Item(name='render_window', style='custom', visible_when='object.stereo', editor=InstanceEditor(view=View(_stereo_view)), show_label=False), ), label='Scene'), Group(Item(name='light_manager', style='custom', show_label=False), label='Lights'), buttons=['OK', 'Cancel']) ######################################## # Private traits. _vtk_control = Instance(wxVTKRenderWindowInteractor) _fullscreen = Any _interacting = Bool ########################################################################### # 'object' interface. ########################################################################### def __init__(self, parent=None, **traits): """ Initializes the object. """ # Base class constructor. super(Scene, self).__init__(parent, **traits) # Setup the default picker. self.picker = picker.Picker(self) def __get_pure_state__(self): """Allows us to pickle the scene.""" # The control attribute is not picklable since it is a VTK # object so we remove it. d = super(Scene, self).__get_pure_state__() for x in ['_vtk_control', '_fullscreen', '_interacting']: d.pop(x, None) return d ########################################################################### # 'Scene' interface. ########################################################################### def render(self): """ Force the scene to be rendered. Nothing is done if the `disable_render` trait is set to True.""" if not self.disable_render: self._vtk_control.Render() def get_size(self): """Return size of the render window.""" return self._vtk_control.GetSize() def set_size(self, size): """Set the size of the window.""" self._vtk_control.SetSize(size) def hide_cursor(self): """Hide the cursor.""" self._vtk_control.HideCursor() def show_cursor(self): """Show the cursor.""" self._vtk_control.ShowCursor() ########################################################################### # 'TVTKScene' interface. ########################################################################### def save_to_clipboard(self): """Saves a bitmap of the scene to the clipboard.""" handler, name = tempfile.mkstemp() self.save_bmp(name) bmp = wx.Bitmap(name, wx.BITMAP_TYPE_BMP) bmpdo = wx.BitmapDataObject(bmp) wx.TheClipboard.Open() wx.TheClipboard.SetData(bmpdo) wx.TheClipboard.Close() os.close(handler) os.unlink(name) ########################################################################### # `wxVTKRenderWindowInteractor` interface. ########################################################################### def OnKeyDown(self, event): """This method is overridden to prevent the 's'/'w'/'e'/'q' keys from doing the default thing which is generally useless. It also handles the 'p' and 'l' keys so the picker and light manager are called. """ keycode = event.GetKeyCode() modifiers = event.HasModifiers() camera = self.camera if keycode < 256: key = chr(keycode) if key == '-': camera.zoom(0.8) self.render() self._record_methods('camera.zoom(0.8)\nrender()') return if key in ['=', '+']: camera.zoom(1.25) self.render() self._record_methods('camera.zoom(1.25)\nrender()') return if key.lower() in ['q', 'e'] or keycode == wx.WXK_ESCAPE: self._disable_fullscreen() if key.lower() in ['w']: event.Skip() return if key.lower() in ['r']: self._record_methods('reset_zoom()') # Handle picking. if key.lower() in ['p']: # In wxPython-2.6, there appears to be a bug in # EVT_CHAR so that event.GetX() and event.GetY() are # not correct. Therefore the picker is called on # KeyUp. event.Skip() return # Camera focal point. if key.lower() in ['f']: event.Skip() return # Light configuration. if key.lower() in ['l'] and not modifiers: self.light_manager.configure() return if key.lower() in ['s'] and not modifiers: parent = self._vtk_control.GetParent() fname = popup_save(parent) if len(fname) != 0: self.save(fname) return shift = event.ShiftDown() if keycode == wx.WXK_LEFT: if shift: camera.yaw(-5) self._record_methods('camera.yaw(-5)') else: camera.azimuth(5) self._record_methods('camera.azimuth(5)') self.render() self._record_methods('render()') return elif keycode == wx.WXK_RIGHT: if shift: camera.yaw(5) self._record_methods('camera.yaw(5)') else: camera.azimuth(-5) self._record_methods('camera.azimuth(-5)') self.render() self._record_methods('render()') return elif keycode == wx.WXK_UP: if shift: camera.pitch(-5) self._record_methods('camera.pitch(-5)') else: camera.elevation(-5) self._record_methods('camera.elevation(-5)') camera.orthogonalize_view_up() self.render() self._record_methods('camera.orthogonalize_view_up()\nrender()') return elif keycode == wx.WXK_DOWN: if shift: camera.pitch(5) self._record_methods('camera.pitch(5)') else: camera.elevation(5) self._record_methods('camera.elevation(5)') camera.orthogonalize_view_up() self.render() self._record_methods('camera.orthogonalize_view_up()\nrender()') return self._vtk_control.OnKeyDown(event) # Skipping the event is not ideal but necessary because we # have no way of knowing of the event was really handled or # not and not skipping will break any keyboard accelerators. # In practice this does not seem to pose serious problems. event.Skip() def OnKeyUp(self, event): """This method is overridden to prevent the 's'/'w'/'e'/'q' keys from doing the default thing which is generally useless. It also handles the 'p' and 'l' keys so the picker and light manager are called. The 'f' key sets the camera focus. """ keycode = event.GetKeyCode() modifiers = event.HasModifiers() if keycode < 256: key = chr(keycode) if key.lower() in ['s', 'w', 'e', 'q']: event.Skip() return # Set camera focal point. if key.lower() in ['f']: if not modifiers: if sys.platform == 'darwin': x, y = self._interactor.event_position else: x = event.GetX() y = self._vtk_control.GetSize()[1] - event.GetY() data = self.picker.pick_world(x, y) coord = data.coordinate if coord is not None: self.camera.focal_point = coord self.render() self._record_methods('camera.focal_point = %r\n'\ 'render()'%list(coord)) return # Handle picking. if key.lower() in ['p']: if not modifiers: if sys.platform == 'darwin': x, y = self._interactor.event_position else: x = event.GetX() y = self._vtk_control.GetSize()[1] - event.GetY() self.picker.pick(x, y) return else: # This is here to disable VTK's own pick handler # which can get called when you press Alt/Ctrl + # 'p'. event.Skip() return # Light configuration. if key.lower() in ['l']: event.Skip() return self._vtk_control.OnKeyUp(event) event.Skip() def OnPaint(self, event): """This method is overridden temporarily in order to create the light manager. This is necessary because it makes sense to create the light manager only when the widget is realized. Only when the widget is realized is the VTK render window created and only then are the default lights all setup correctly. This handler is removed on the first Paint event and the default paint handler of the wxVTKRenderWindowInteractor is used instead.""" # Call the original handler (this will Show the widget) self._vtk_control.OnPaint(event) if len(self.renderer.lights) == 0: # The renderer is not ready yet, we do not do anything, and # we do not remove this callback, so that it will be called # later. return # Now create the light manager. self.light_manager = light_manager.LightManager(self) renwin = self._renwin renwin.update_traits() vtk_rw = tvtk.to_vtk(renwin) renwin.add_observer('StartEvent', messenger.send) messenger.connect(vtk_rw, 'StartEvent', self._start_event_callback) renwin.add_observer('EndEvent', messenger.send) messenger.connect(vtk_rw, 'EndEvent', self._end_event_callback) # Reset the event handler to the default since our job is done. wx.EVT_PAINT(self._vtk_control, None) # Remove the default handler. wx.EVT_PAINT(self._vtk_control, self._vtk_control.OnPaint) def OnSize(self, event): """Overrides the default OnSize in order to refresh the traits of the render window.""" if self._renwin is not None: self._vtk_control.OnSize(event) self._renwin.update_traits() def OnButtonDown(self, event): """Overrides the default on button down method. """ self._interacting = True self._vtk_control.OnButtonDown(event) def OnButtonUp(self, event): self._interacting = False self._vtk_control.OnButtonUp(event) ########################################################################### # Non-public interface. ########################################################################### def _create_control(self, parent): """ Create the toolkit-specific control that represents the widget. """ # Create the VTK widget. self._vtk_control = window = wxVTKRenderWindowInteractor( parent, -1, stereo=self.stereo) # Override these handlers. wx.EVT_CHAR(window, None) # Remove the default handler. wx.EVT_CHAR(window, self.OnKeyDown) wx.EVT_KEY_UP(window, None) # Remove the default handler. wx.EVT_KEY_UP(window, self.OnKeyUp) wx.EVT_PAINT(window, None) # Remove the default handler. wx.EVT_PAINT(window, self.OnPaint) wx.EVT_SIZE(window, None) # Remove the default handler. wx.EVT_SIZE(window, self.OnSize) # Override the button down and up handlers as well to note the # interaction. This is to toggle the busy status nicely. for evt in (wx.EVT_LEFT_DOWN, wx.EVT_RIGHT_DOWN, wx.EVT_MIDDLE_DOWN): evt(window, None) evt(window, self.OnButtonDown) for evt in (wx.EVT_LEFT_UP, wx.EVT_RIGHT_UP, wx.EVT_MIDDLE_UP): evt(window, None) evt(window, self.OnButtonUp) # Enable the widget. window.Enable(1) # Switch the default interaction style to the trackball one. window.GetInteractorStyle().SetCurrentStyleToTrackballCamera() # Grab the renderwindow. renwin = self._renwin = tvtk.to_tvtk(window.GetRenderWindow()) renwin.set(point_smoothing=self.point_smoothing, line_smoothing=self.line_smoothing, polygon_smoothing=self.polygon_smoothing) # Create a renderer and add it to the renderwindow self._renderer = tvtk.Renderer() renwin.add_renderer(self._renderer) # Save a reference to our camera so it is not GC'd -- needed for # the sync_traits to work. self._camera = self.camera # Sync various traits. self._renderer.background = self.background self.sync_trait('background', self._renderer) self.renderer.on_trait_change(self.render, 'background') self._camera.parallel_projection = self.parallel_projection self.sync_trait('parallel_projection', self._camera) renwin.off_screen_rendering = self.off_screen_rendering self.sync_trait('off_screen_rendering', self._renwin) self.render_window.on_trait_change(self.render, 'off_screen_rendering') self.render_window.on_trait_change(self.render, 'stereo_render') self.render_window.on_trait_change(self.render, 'stereo_type') self.camera.on_trait_change(self.render, 'parallel_projection') def _show_parent_hack(window, parent): """A hack to get the VTK scene properly setup for use.""" # Force the parent to show itself. parent.Show(1) # on some platforms, this SetSize() is necessary to cause # an OnPaint() when the event loop begins; else we get an # empty window until we force a redraw. window.SetSize(parent.GetSize()) # This is necessary on slow machines in order to force the # wx events to be handled. wx.GetApp().Yield(True) window.Render() if wx.Platform == '__WXMSW__': _show_parent_hack(window, parent) else: if (wx.VERSION[0] == 2) and (wx.VERSION[1] < 5): _show_parent_hack(window, parent) window.Update() # Because of the way the VTK widget is setup, and because we # set the size above, the window sizing is usually completely # messed up when the application window is shown. To work # around this a dynamic IDLE event handler is added and # immediately removed once it executes. This event handler # simply forces a resize to occur. The _idle_count allows us # to execute the idle function a few times (this seems to work # better). def _do_idle(event, window=window): w = wx.GetTopLevelParent(window) # Force a resize sz = w.GetSize() w.SetSize((sz[0] - 1, sz[1] - 1)) w.SetSize(sz) window._idle_count -= 1 if window._idle_count < 1: wx.EVT_IDLE(window, None) del window._idle_count window._idle_count = 2 wx.EVT_IDLE(window, _do_idle) self._interactor = tvtk.to_tvtk(window._Iren) return window def _lift(self): """Lift the window to the top. Useful when saving screen to an image.""" if self.render_window.off_screen_rendering: # Do nothing if off screen rendering is being used. return w = self._vtk_control while w and not w.IsTopLevel(): w = w.GetParent() if w: w.Raise() wx.GetApp().Yield(True) self.render() def _start_event_callback(self, obj, event): if self._interacting: return else: self.busy = True def _end_event_callback(self, obj, event): if self._interacting: return else: self.busy = False def _busy_changed(self, val): GUI.set_busy(val) def _full_screen_fired(self): fs = self._fullscreen if isinstance(fs, PopupScene): fs.close() self._fullscreen = None elif fs is None: ver = tvtk.Version() popup = False if wx.Platform == '__WXMSW__': popup = True elif ver.vtk_major_version > 5: popup = True elif (ver.vtk_major_version == 5) and \ ((ver.vtk_minor_version >= 1) or \ (ver.vtk_build_version > 2)): popup = True if popup: # There is a bug with earlier versions of VTK that # breaks reparenting a window which is why we test for # the version above. f = PopupScene(self) self._fullscreen = f f.fullscreen() else: f = FullScreen(self) f.run() # This will block. self._fullscreen = None def _disable_fullscreen(self): fs = self._fullscreen if isinstance(fs, PopupScene): fs.close() self._fullscreen = None
class InPaintDemo(HasTraits): plot = Instance(Plot) painter = Instance(CirclePainter) r = Range(2.0, 20.0, 10.0) # inpaint的半径参数 method = Enum("INPAINT_NS", "INPAINT_TELEA") # inpaint的算法 show_mask = Bool(False) # 是否显示选区 clear_mask = Button("清除选区") apply = Button("保存结果") view = View(VGroup( VGroup( Item("object.painter.r", label="画笔半径"), Item("r", label="inpaint半径"), HGroup( Item("method", label="inpaint算法"), Item("show_mask", label="显示选区"), Item("clear_mask", show_label=False), Item("apply", show_label=False), )), Item("plot", editor=ComponentEditor(), show_label=False), ), title="inpaint Demo控制面板", width=500, height=450, resizable=True) def __init__(self, *args, **kwargs): super(InPaintDemo, self).__init__(*args, **kwargs) self.img = cv.imread("stuff.jpg") # 原始图像 self.img2 = self.img.clone() # inpaint效果预览图像 self.mask = cv.Mat(self.img.size(), cv.CV_8UC1) # 储存选区的图像 self.mask[:] = 0 self.data = ArrayPlotData(img=self.img[:, :, ::-1]) self.plot = Plot(self.data, padding=10, aspect_ratio=float(self.img.size().width) / self.img.size().height) self.plot.x_axis.visible = False self.plot.y_axis.visible = False imgplot = self.plot.img_plot("img", origin="top left")[0] self.painter = CirclePainter(component=imgplot) imgplot.overlays.append(self.painter) @on_trait_change("r,method") def inpaint(self): cv.inpaint(self.img, self.mask, self.img2, self.r, getattr(cv, self.method)) self.draw() @on_trait_change("painter:updated") def painter_updated(self): for _, _, x, y in self.painter.track: # 在储存选区的mask上绘制圆形 cv.circle(self.mask, cv.Point(int(x), int(y)), int(self.painter.r), cv.Scalar(255, 255, 255, 255), thickness=-1) # 宽度为负表示填充圆形 self.inpaint() self.painter.track = [] self.painter.request_redraw() def _clear_mask_fired(self): self.mask[:] = 0 self.inpaint() def _apply_fired(self): """保存inpaint的处理结果,并清除选区""" self.img[:] = self.img2[:] self._clear_mask_fired() @on_trait_change("show_mask") def draw(self): if self.show_mask: data = self.img[:, :, ::-1].copy() data[self.mask[:] > 0] = 255 self.data["img"] = data else: self.data["img"] = self.img2[:, :, ::-1]
class TriangleWave(HasTraits): # 指定三角波的最窄和最宽范围,由于Range类型不能将常数和Traits属性名混用 # 所以定义这两个值不变的Trait属性 low = Float(0.02) hi = Float(1.0) # 三角波形的宽度 wave_width = Range("low", "hi", 0.5) # 三角波的顶点C的x轴坐标 length_c = Range("low", "wave_width", 0.5) # 三角波的定点的y轴坐标 height_c = Float(1.0) # FFT计算所使用的取样点数,这里用一个Enum类型的属性以供用户从列表中选择 fftsize = Enum([(2**x) for x in range(6, 12)]) # FFT频谱图的x轴上限值 fft_graph_up_limit = Range(0, 400, 20) # 用于显示FFT的结果 peak_list = Str # 采用多少个频率合成三角波 N = Range(1, 40, 4) # 保存绘图数据的对象 plot_data = Instance(AbstractPlotData) # 绘制波形图的容器 plot_wave = Instance(Component) # 绘制FFT频谱图的容器 plot_fft = Instance(Component) # 包括两个绘图的容器 container = Instance(Component) # 设置用户界面的视图, 注意一定要指定窗口的大小,这样绘图容器才能正常初始化 view = View(HSplit( VSplit( VGroup(Item("wave_width", editor=scrubber, label="波形宽度"), Item("length_c", editor=scrubber, label="最高点x坐标"), Item("height_c", editor=scrubber, label="最高点y坐标"), Item("fft_graph_up_limit", editor=scrubber, label="频谱图范围"), Item("fftsize", label="FFT点数"), Item("N", label="合成波频率数")), Item("peak_list", style="custom", show_label=False, width=100, height=250)), VGroup(Item("container", editor=ComponentEditor(size=(600, 300)), show_label=False), orientation="vertical")), resizable=True, width=800, height=600, title="三角波FFT演示") # 创建绘图的辅助函数,创建波形图和频谱图有很多类似的地方,因此单独用一个函数以 # 减少重复代码 def _create_plot(self, data, name, type="line"): p = Plot(self.plot_data) p.plot(data, name=name, title=name, type=type) p.tools.append(PanTool(p)) zoom = ZoomTool(component=p, tool_mode="box", always_on=False) p.overlays.append(zoom) p.title = name return p def __init__(self): # 首先需要调用父类的初始化函数 super(TriangleWave, self).__init__() # 创建绘图数据集,暂时没有数据因此都赋值为空,只是创建几个名字,以供Plot引用 self.plot_data = ArrayPlotData(x=[], y=[], f=[], p=[], x2=[], y2=[]) # 创建一个垂直排列的绘图容器,它将频谱图和波形图上下排列 self.container = VPlotContainer() # 创建波形图,波形图绘制两条曲线: 原始波形(x,y)和合成波形(x2,y2) self.plot_wave = self._create_plot(("x", "y"), "Triangle Wave") self.plot_wave.plot(("x2", "y2"), color="red") # 创建频谱图,使用数据集中的f和p self.plot_fft = self._create_plot(("f", "p"), "FFT", type="scatter") # 将两个绘图容器添加到垂直容器中 self.container.add(self.plot_wave) self.container.add(self.plot_fft) # 设置 self.plot_wave.x_axis.title = "Samples" self.plot_fft.x_axis.title = "Frequency pins" self.plot_fft.y_axis.title = "(dB)" # 改变fftsize为1024,因为Enum的默认缺省值为枚举列表中的第一个值 self.fftsize = 1024 # FFT频谱图的x轴上限值的改变事件处理函数,将最新的值赋值给频谱图的响应属性 def _fft_graph_up_limit_changed(self): self.plot_fft.x_axis.mapper.range.high = self.fft_graph_up_limit def _N_changed(self): self.plot_sin_combine() # 多个trait属性的改变事件处理函数相同时,可以用@on_trait_change指定 @on_trait_change("wave_width, length_c, height_c, fftsize") def update_plot(self): # 计算三角波 global y_data x_data = np.arange(0, 1.0, 1.0 / self.fftsize) func = self.triangle_func() # 将func函数的返回值强制转换成float64 y_data = np.cast["float64"](func(x_data)) # 计算频谱 fft_parameters = np.fft.fft(y_data) / len(y_data) # 计算各个频率的振幅 fft_data = np.clip( 20 * np.log10(np.abs(fft_parameters))[:self.fftsize / 2 + 1], -120, 120) # 将计算的结果写进数据集 self.plot_data.set_data("x", np.arange(0, self.fftsize)) # x坐标为取样点 self.plot_data.set_data("y", y_data) self.plot_data.set_data("f", np.arange(0, len(fft_data))) # x坐标为频率编号 self.plot_data.set_data("p", fft_data) # 合成波的x坐标为取样点,显示2个周期 self.plot_data.set_data("x2", np.arange(0, 2 * self.fftsize)) # 更新频谱图x轴上限 self._fft_graph_up_limit_changed() # 将振幅大于-80dB的频率输出 peak_index = (fft_data > -80) peak_value = fft_data[peak_index][:20] result = [] for f, v in zip(np.flatnonzero(peak_index), peak_value): result.append("%s : %s" % (f, v)) self.peak_list = "\n".join(result) # 保存现在的fft计算结果,并计算正弦合成波 self.fft_parameters = fft_parameters self.plot_sin_combine() # 计算正弦合成波,计算2个周期 def plot_sin_combine(self): index, data = fft_combine(self.fft_parameters, self.N, 2) self.plot_data.set_data("y2", data) # 返回一个ufunc计算指定参数的三角波 def triangle_func(self): c = self.wave_width c0 = self.length_c hc = self.height_c def trifunc(x): x = x - int(x) # 三角波的周期为1,因此只取x坐标的小数部分进行计算 if x >= c: r = 0.0 elif x < c0: r = x / c0 * hc else: r = (c - x) / (c - c0) * hc return r # 用trifunc函数创建一个ufunc函数,可以直接对数组进行计算, 不过通过此函数 # 计算得到的是一个Object数组,需要进行类型转换 return np.frompyfunc(trifunc, 1, 1)
class IocbioCursorTool2D(CursorTool2D): # Allow using subclasses of CircleMarker: marker = Instance(CircleMarker, ()) invisible_layout = None
class NodeTree(Tree): """ A tree control with extensible node types. """ #### 'Tree' interface ##################################################### # The model that provides the data for the tree. model = Instance(NodeTreeModel, ()) #### 'NodeTree' interface ################################################# # The node manager looks after all node types. node_manager = Property(Instance(NodeManager)) # The node types in the tree. node_types = Property(List(NodeType)) ########################################################################### # 'NodeTree' interface. ########################################################################### #### Properties ########################################################### # node_manager def _get_node_manager(self): """ Returns the root node of the tree. """ return self.model.node_manager def _set_node_manager(self, node_manager): """ Sets the root node of the tree. """ self.model.node_manager = node_manager return # node_types def _get_node_types(self): """ Returns the node types in the tree. """ return self.model.node_manager.node_types def _set_node_types(self, node_types): """ Sets the node types in the tree. """ self.model.node_manager.node_types = node_types return ########################################################################### # 'Tree' interface. ########################################################################### #### Trait event handlers ################################################# def _node_activated_changed(self, obj): """ Called when a node has been activated (i.e., double-clicked). """ default_action = self.model.get_default_action(obj) if default_action is not None: self._perform_default_action(default_action, obj) return def _node_right_clicked_changed(self, (obj, point)): """ Called when the right mouse button is clicked on the tree. """ # Add the node that the right-click occurred on to the selection. self.select(obj) # fixme: This is a hack to allow us to attach the node that the # right-clicked occurred on to the action event. self._context = obj # Ask the model for the node's context menu. menu_manager = self.model.get_context_menu(obj) if menu_manager is not None: self._popup_menu(menu_manager, obj, point) return
class ExceptionHandler(HasTraits): """ Provides standardized exception handling. Instantiate immediately after 'except:' to capture the exception and stack frame information. """ # Application message. message = Str # Initialized form rom sys.exc_info on object creation. ex_type = Instance(EXCEPTION_BASE_TYPE) # Initialized form rom sys.exc_info on object creation. ex_value = Any # Instance(Exception) # Initialized form rom sys.exc_info on object creation. ex_traceback = Instance(types.TracebackType) # Formatted text for the exception. exception_text = Str # Formatted text for the exception only. I.e. without stack trace. exception_only_text = Str # Enter message in the log using enthought.logger; default is True. use_logger = Bool(True) def __init__(self, **traits): """ Creates an ExceptionHandler initialized with the most recent traceback information. Optionally logs the exception using enthought.logger. """ super(ExceptionHandler,self).__init__(**traits) self.ex_type, self.ex_value, self.ex_traceback = sys.exc_info() if self.use_logger: logger.error( str(self) ) return def __str__(self): """ Returns string representation of self. """ text = self.message + '\n' + self.exception_text return text def _exception_text_default(self): """ Returns formatted exception. """ list_o_lines = traceback.format_exception( self.ex_type, self.ex_value, self.ex_traceback) lines = ''.join(list_o_lines) # remove trailing \n return lines.strip() def _exception_only_text_default(self): """ Returns formatted exception only (see traceback). I.e. without stack trace. """ list_o_lines = traceback.format_exception_only( self.ex_type, self.ex_value) lines = ''.join(list_o_lines) # remove trailing \n return lines.strip() def trait_view(self, name=None, view_element=None): """ Returns a View """ if (name or view_element) != None: return super(ExceptionHandler, self).trait_view( name=name, view_element=view_element ) from exception_handler_view import ExceptionHandlerView return ExceptionHandlerView()
class TreeViewer(ContentViewer): """ A viewer based on a tree control. """ # The default tree style. STYLE = wx.TR_EDIT_LABELS | wx.TR_HAS_BUTTONS | wx.CLIP_CHILDREN #### 'TreeViewer' interface ############################################### # The content provider provides the actual tree data. content_provider = Instance(TreeContentProvider) # The label provider provides, err, the labels for the items in the tree # (a label can have text and/or an image). label_provider = Instance(TreeLabelProvider, ()) # Selection mode (must be either of 'single' or 'extended'). selection_mode = Enum('single', 'extended') # The currently selected elements. selection = List # Should an image be shown for each element? show_images = Bool(True) # Should the root of the tree be shown? show_root = Bool(True) #### Events #### # An element has been activated (ie. double-clicked). element_activated = Event # A drag operation was started on an element. element_begin_drag = Event # An element that has children has been collapsed. element_collapsed = Event # An element that has children has been expanded. element_expanded = Event # A left-click occurred on an element. element_left_clicked = Event # A right-click occurred on an element. element_right_clicked = Event # A key was pressed while the tree is in focus. key_pressed = Event ########################################################################### # 'object' interface. ########################################################################### def __init__(self, parent, image_size=(16, 16), **traits): """ Creates a new tree viewer. 'parent' is the toolkit-specific control that is the tree's parent. 'image_size' is a tuple in the form (int width, int height) that specifies the size of the label images (if any) displayed in the tree. """ # Base class constructor. super(TreeViewer, self).__init__(**traits) # Create the toolkit-specific control. self.control = tree = wx.TreeCtrl(parent, -1, style=self._get_style()) # Get our actual Id. wxid = tree.GetId() # Wire up the wx tree events. wx.EVT_CHAR(tree, self._on_char) wx.EVT_LEFT_DOWN(tree, self._on_left_down) wx.EVT_RIGHT_DOWN(tree, self._on_right_down) wx.EVT_TREE_ITEM_ACTIVATED(tree, wxid, self._on_tree_item_activated) wx.EVT_TREE_ITEM_COLLAPSED(tree, wxid, self._on_tree_item_collapsed) wx.EVT_TREE_ITEM_COLLAPSING(tree, wxid, self._on_tree_item_collapsing) wx.EVT_TREE_ITEM_EXPANDED(tree, wxid, self._on_tree_item_expanded) wx.EVT_TREE_ITEM_EXPANDING(tree, wxid, self._on_tree_item_expanding) wx.EVT_TREE_BEGIN_LABEL_EDIT(tree, wxid,self._on_tree_begin_label_edit) wx.EVT_TREE_END_LABEL_EDIT(tree, wxid, self._on_tree_end_label_edit) wx.EVT_TREE_BEGIN_DRAG(tree, wxid, self._on_tree_begin_drag) wx.EVT_TREE_SEL_CHANGED(tree, wxid, self._on_tree_sel_changed) # The image list is a wxPython-ism that caches all images used in the # control. self._image_list = ImageList(image_size[0], image_size[1]) if self.show_images: tree.AssignImageList(self._image_list) # Mapping from element to wx tree item Ids. self._element_to_id_map = {} # Add the root item. if self.input is not None: self._add_element(None, self.input) return ########################################################################### # 'TreeViewer' interface. ########################################################################### def is_expanded(self, element): """ Returns True if the element is expanded, otherwise False. """ key = self._get_key(element) if key in self._element_to_id_map: is_expanded = self.control.IsExpanded(self._element_to_id_map[key]) else: is_expanded = False return is_expanded def is_selected(self, element): """ Returns True if the element is selected, otherwise False. """ key = self._get_key(element) if key in self._element_to_id_map: is_selected = self.control.IsSelected(self._element_to_id_map[key]) else: is_selected = False return is_selected def refresh(self, element): """ Refresh the tree starting from the specified element. Call this when the STRUCTURE of the content has changed. """ # Has the element actually appeared in the tree yet? pid = self._element_to_id_map.get(self._get_key(element), None) if pid is not None: # The item data is a tuple. The first element indicates whether or # not we have already populated the item with its children. The # second element is the actual item data. populated, element = self.control.GetPyData(pid) # fixme: We should find a cleaner way other than deleting all of # the element's children and re-adding them! self._delete_children(pid) self.control.SetPyData(pid, (False, element)) # Does the element have any children? has_children = self.content_provider.has_children(element) self.control.SetItemHasChildren(pid, has_children) # Expand it. self.control.Expand(pid) else: print '**** pid is None!!! ****' return def update(self, element): """ Update the tree starting from the specified element. Call this when the APPEARANCE of the content has changed. """ pid = self._element_to_id_map.get(self._get_key(element), None) if pid is not None: self._refresh_element(pid, element) for child in self.content_provider.get_children(element): cid = self._element_to_id_map.get(self._get_key(child), None) if cid is not None: self._refresh_element(cid, child) return ########################################################################### # Private interface. ########################################################################### def _get_style(self): """ Returns the wx style flags for creating the tree control. """ # Start with the default flags. style = self.STYLE if not self.show_root: style = style | wx.TR_HIDE_ROOT | wx.TR_LINES_AT_ROOT if self.selection_mode != 'single': style = style | wx.TR_MULTIPLE | wx.TR_EXTENDED return style def _add_element(self, pid, element): """ Adds 'element' as a child of the element identified by 'pid'. If 'pid' is None then we are adding the root element. """ # Get the tree item image index and text. image_index = self._get_image_index(element) text = self._get_text(element) # Add the element. if pid is None: wxid = self.control.AddRoot(text, image_index, image_index) else: wxid = self.control.AppendItem(pid, text, image_index, image_index) # If we are adding the root element but the root is hidden, get its # children. if pid is None and not self.show_root: children = self.content_provider.get_children(element) for child in children: self._add_element(wxid, child) # Does the element have any children? has_children = self.content_provider.has_children(element) self.control.SetItemHasChildren(wxid, has_children) # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. if pid is None: if self.show_root: self.control.SetPyData(wxid, (False, element)) else: self.control.SetPyData(wxid, (False, element)) # Make sure that we can find the element's Id later. self._element_to_id_map[self._get_key(element)] = wxid # If we are adding the root item then automatically expand it. if pid is None and self.show_root: self.control.Expand(wxid) return def _get_image_index(self, element): """ Returns the tree item image index for an element. """ # Get the image used to represent the element. image = self.label_provider.get_image(self, element) if image is not None: image_index = self._image_list.GetIndex(image.absolute_path) else: image_index = -1 return image_index def _get_key(self, element): """ Generate the key for the element to id map. """ try: key = hash(element) except: key = id(element) return key def _get_text(self, element): """ Returns the tree item text for an element. """ text = self.label_provider.get_text(self, element) if text is None: text = '' return text def _refresh_element(self, wxid, element): """ Refreshes the image and text of the specified element. """ # Get the tree item image index. image_index = self._get_image_index(element) self.control.SetItemImage(wxid, image_index, wx.TreeItemIcon_Normal) self.control.SetItemImage(wxid, image_index, wx.TreeItemIcon_Selected) # Get the tree item text. text = self._get_text(element) self.control.SetItemText(wxid, text) # Does the item have any children? has_children = self.content_provider.has_children(element) self.control.SetItemHasChildren(wxid, has_children) return def _unpack_event(self, event): """ Unpacks the event to see whether a tree element was involved. """ try: point = event.GetPosition() except: point = event.GetPoint() wxid, flags = self.control.HitTest(point) # Warning: On GTK we have to check the flags before we call 'GetPyData' # because if we call it when the hit test returns 'nowhere' it will # barf (on Windows it simply returns 'None' 8^() if flags & wx.TREE_HITTEST_NOWHERE: data = None else: data = self.control.GetPyData(wxid) return data, wxid, flags, point def _get_selection(self): """ Returns a list of the selected elements. """ elements = [] for wxid in self.control.GetSelections(): data = self.control.GetPyData(wxid) if data is not None: populated, element = data elements.append(element) # 'data' can be None here if (for example) the element has been # deleted. # # fixme: Can we stop this happening?!?!? else: pass return elements def _delete_children(self, pid): """ Recursively deletes the children of the specified element. """ cookie = 0 (cid, cookie) = self.control.GetFirstChild(pid, cookie) while cid.IsOk(): # Recursively delete the child's children. self._delete_children(cid) # Remove the reference to the item's data. populated, element = self.control.GetPyData(cid) del self._element_to_id_map[self._get_key(element)] self.control.SetPyData(cid, None) # Next! (cid, cookie) = self.control.GetNextChild(pid, cookie) self.control.DeleteChildren(pid) return #### Trait event handlers ################################################# def _input_changed(self): """ Called when the tree's input has been changed. """ # Delete everything... if self.control is not None: self.control.DeleteAllItems() self._element_to_id_map = {} # ... and then add the root item back in. if self.input is not None: self._add_element(None, self.input) return def _element_begin_drag_changed(self, element): """ Called when a drag is started on a element. """ # We ask the label provider for the actual value to drag. drag_value = self.label_provider.get_drag_value(self, element) # Start the drag. PythonDropSource(self.control, drag_value) return #### wx event handlers #################################################### def _on_right_down(self, event): """ Called when the right mouse button is clicked on the tree. """ data, id, flags, point = self._unpack_event(event) # Did the right click occur on a tree item? if data is not None: populated, element = data # Trait notification. self.element_right_clicked = (element, point) # Give other event handlers a chance. event.Skip() return def _on_left_down(self, event): """ Called when the left mouse button is clicked on the tree. """ data, wxid, flags, point = self._unpack_event(event) # Save point for tree_begin_drag method to workaround a bug in ?? when # wx.TreeEvent.GetPoint returns only (0,0). This happens under linux # when using wx-2.4.2.4, for instance. self._point_left_clicked = point # Did the left click occur on a tree item? if data is not None: populated, element = data # Trait notification. self.element_left_clicked = (element, point) # Give other event handlers a chance. event.Skip() return def _on_tree_item_expanding(self, event): """ Called when a tree item is about to expand. """ # Which item is expanding? wxid = event.GetItem() # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. populated, element = self.control.GetPyData(wxid) # Give the label provider a chance to veto the expansion. if self.label_provider.is_expandable(self, element): # Lazily populate the item's children. if not populated: children = self.content_provider.get_children(element) # Sorting... if self.sorter is not None: self.sorter.sort(self, element, children) # Filtering.... for child in children: for filter in self.filters: if not filter.select(self, element, child): break else: self._add_element(wxid, child) # The element is now populated! self.control.SetPyData(wxid, (True, element)) else: event.Veto() return def _on_tree_item_expanded(self, event): """ Called when a tree item has been expanded. """ # Which item was expanded? wxid = event.GetItem() # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. populated, element = self.control.GetPyData(wxid) # Make sure that the element's 'open' icon is displayed etc. self._refresh_element(wxid, element) # Trait notification. self.element_expanded = element return def _on_tree_item_collapsing(self, event): """ Called when a tree item is about to collapse. """ # Which item is collapsing? wxid = event.GetItem() # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. populated, element = self.control.GetPyData(wxid) # Give the label provider a chance to veto the collapse. if not self.label_provider.is_collapsible(self, element): event.Veto() return def _on_tree_item_collapsed(self, event): """ Called when a tree item has been collapsed. """ # Which item was collapsed? wxid = event.GetItem() # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. populated, element = self.control.GetPyData(wxid) # Make sure that the element's 'closed' icon is displayed etc. self._refresh_element(wxid, element) # Trait notification. self.element_collapsed = element return def _on_tree_item_activated(self, event): """ Called when a tree item is activated (i.e., double clicked). """ # Which item was activated? wxid = event.GetItem() # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. populated, element = self.control.GetPyData(wxid) # Trait notification. self.element_activated = element return def _on_tree_sel_changed(self, event): """ Called when the selection is changed. """ # Trait notification. self.selection = self._get_selection() return def _on_tree_begin_drag(self, event): """ Called when a drag operation is starting on a tree item. """ # Get the element, its id and the point where the event occurred. data, wxid, flags, point = self._unpack_event(event) if point == (0,0): # Apply workaround. point = self._point_left_clicked wxid, flags = self.control.HitTest(point) data = self.control.GetPyData(wxid) if data is not None: populated, element = data # Trait notification. self.element_begin_drag = element return def _on_tree_begin_label_edit(self, event): """ Called when the user has started editing an item's label. """ wxid = event.GetItem() # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. populated, element = self.control.GetPyData(wxid) # Give the label provider a chance to veto the edit. if not self.label_provider.is_editable(self, element): event.Veto() return def _on_tree_end_label_edit(self, event): """ Called when the user has finished editing an item's label. """ wxid = event.GetItem() # The item data is a tuple. The first element indicates whether or not # we have already populated the item with its children. The second # element is the actual item data. populated, element = self.control.GetPyData(wxid) # Give the label provider a chance to veto the edit. label = event.GetLabel() if not self.label_provider.set_text(self, element, label): event.Veto() return def _on_char(self, event): """ Called when a key is pressed when the tree has focus. """ # Trait notification. self.key_pressed = event.GetKeyCode() return
class RunConfig(HasTraits): runcase = Instance(RunCase) runcase_config = Instance(RunOptionsConfig) def _runcase_config_default(self): return AdvCaseConfig(runcase=self.runcase) runtype = Trait('advanced', {'trim flight': 'c', 'advanced': 'm'}) calc_eigenmodes = Bool(True) def _runtype_changed(self, name, old, new): if self.runtype_ == 'm': self.runcase_config = AdvCaseConfig(runcase=self.runcase) else: if isinstance(self.runcase_config, TrimCaseConfig): self.runcase_config.trimcase.type = new else: trimcase = TrimCase(runcase=self.runcase) trimcase.update_parameters_from_avl() self.runcase_config = TrimCaseConfig(runcase=self.runcase, trimcase=trimcase) run_button = Button(label='Run Calculation') view = View( Item(label='Note: Case *must* be saved before running'), Item('runtype'), Group( Item('runcase_config', editor=InstanceEditor(), style='custom', show_label=False)), #HGroup(Item('calc_eigenmodes')), #spring, Item('run_button', show_label=False)), buttons=['OK', 'Cancel'], resizable=True) def get_constraints(self): consts, vars = {}, {} for cc in self.runcase_config.constraint_config: cmd = '%s %s {0}' % (cc.constraint.cmd, self.runcase.constraint_variables[ cc.constraint.constraint_name].cmd) if 'x' in cc.expr_.co_names: vars[cmd] = cc.expr_ else: consts[cmd] = cc.expr_ return consts, vars def get_parameters(self): consts, vars = {}, {} if self.runtype_ == 'm': type = 'm' for pc in self.runcase_config.parameter_config: cmd = '%s {0}' % pc.parameter.cmd if 'x' in pc.expr_.co_names: vars[cmd] = pc.expr_ else: consts[cmd] = pc.expr_ else: type = self.runcase_config.trimcase.type_ # type of parameter, cmd to be set in oper menu to set the parameter vpn = self.runcase_config.varying_param # varying parameter name for n, p in self.runcase_config.trimcase.parameters.iteritems( ): # name, parameter if n != vpn: consts['%s {0}' % p.cmd] = compile(str(p.value), '<string>', 'eval') vp = self.runcase_config.trimcase.parameters[vpn] vars['%s {0}' % vp.cmd] = self.runcase_config.varying_expr_ return type, consts, vars @on_trait_change('run_button') def run(self, progressbar=True): consts_c, vars_c = self.get_constraints() type_p, consts_p, vars_p = self.get_parameters() # confirm that we are in good state avl = self.runcase.avl avl.sendline() avl.expect(AVL.patterns['/']) avl.sendline('oper') avl.expect(AVL.patterns['/oper']) # first set the constants once and for all # constraints for c, v in consts_c.iteritems(): avl.sendline(c.format(eval(v))) # paramters avl.sendline(type_p) for p, v in consts_p.iteritems(): avl.sendline(p.format(eval(v))) avl.sendline() avl.sendline() avl.expect(AVL.patterns['/']) outs, modes, matrices = [], [], [] # now run the case and get output while changing the vars each time if progressbar: progress = ProgressDialog(title="progress", message="calculating...", max=self.runcase_config.x.shape[0], show_time=True, can_cancel=True) try: progress.open() except Exception, e: logger.warning(e) #print 'x[] = ', self.runcase_config.x for i, x in enumerate(self.runcase_config.x): # set the variables avl.sendline('oper') avl.expect(AVL.patterns['/oper']) # constraints logger.info('running case %d for x=%f' % (i + 1, x)) for c, v in vars_c.iteritems(): avl.sendline(c.format(eval(v))) # paramters avl.sendline(type_p) for p, v in vars_p.iteritems(): avl.sendline(p.format(eval(v))) avl.sendline() # go back to home state avl.sendline() avl.expect(AVL.patterns['/']) # get the output try: outs.append(self.runcase.get_run_output()) if self.calc_eigenmodes: modes.append(self.runcase.get_modes()) matrices.append(self.runcase.get_system_matrix()) except AttributeError, e: logger.warning(e) if progressbar: try: cont, skip = progress.update(i + 1) if not cont or skip: break pass except Exception, e: logger.warning(e)
class IScriptManager(Interface): """ The script manager interface. A script manager is responsible for the recording of appropriately annotated user actions as scripts that can be executed without user intervention at a later time. Typically an application would have a single script manager. """ #### 'IScriptManager' interface ########################################### # This event is fired whenever a scriptable object is bound or unbound. It # is intended to be used by an interactive Python shell to give the # advanced user access to the scriptable objects. If an object is created # via a factory then the event is fired when the factory is called, and not # when the factory is bound. bind_event = Event(IBindEvent) # This is set if user actions are being recorded as a script. It is # maintained by the script manager. recording = Bool(False) # This is the text of the script currently being recorded (or the last # recorded script if none is currently being recorded). It is updated # automatically as the user performs actions. script = Unicode # This event is fired when the recorded script changes. The value of the # event will be the ScriptManager instance. script_updated = Event( Instance('enthought.appscripting.api.IScriptManager')) ########################################################################### # 'IScriptManager' interface. ########################################################################### def bind(self, obj, name=None, bind_policy='unique', api=None, includes=None, excludes=None): """ Bind obj to name and make (by default) its public methods and traits (ie. those not beginning with an underscore) scriptable. The default value of name is the type of obj with the first character forced to lower case. bind_policy determines what happens if the name is already bound. If the policy is 'auto' then a numerical suffix will be added to the name, if necessary, to make it unique. If the policy is 'unique' then an exception is raised. If the policy is 'rebind' then the previous binding is discarded. The default is 'unique' If api is given then it is a class, or a list of classes, that define the attributes that will be made scriptable. Otherwise if includes is given it is a list of names of attributes that will be made scriptable. Otherwise all the public attributes of scripted_type will be made scriptable except those in the excludes list. """ def bind_factory(self, factory, name, bind_policy='unique', api=None, includes=None, excludes=None): """ Bind factory to name. This does the same as the bind() method except that it uses a factory that will be called later on to create the object only if the object is needed. See the documentation for bind() for a description of the remaining arguments. """ def run(self, script): """ Run the given script, either a string or a file-like object. """ def run_file(self, file_name): """ Run the given script file. """ def start_recording(self): """ Start the recording of user actions. The 'script' trait is cleared and all subsequent actions are added to 'script'. The 'recording' trait is updated appropriately. """ def stop_recording(self): """ Stop the recording of user actions. The 'recording' trait is
class PolyDataReader(FileDataSource): """A PolyData file reader. The reader supports all the different types of poly data files. """ # The version of this class. Used for persistence. __version__ = 0 # The PolyData file reader reader = Instance(tvtk.Object, allow_none=False, record=True) ###################################################################### # Private Traits _reader_dict = Dict(Str, Instance(tvtk.Object)) # Our View. view = View(Group(Include('time_step_group'), Item(name='base_file_name'), Item(name='reader', style='custom', resizable=True), show_labels=False), resizable=True) #output_info = PipelineInfo(datasets=['none']) output_info = PipelineInfo(datasets=['poly_data'], attribute_types=['any'], attributes=['any']) ###################################################################### # `object` interface ###################################################################### def __set_pure_state__(self, state): # The reader has its own file_name which needs to be fixed. state.reader.file_name = state.file_path.abs_pth # Now call the parent class to setup everything. super(PolyDataReader, self).__set_pure_state__(state) ###################################################################### # `FileDataSource` interface ###################################################################### def update(self): self.reader.update() if len(self.file_path.get()) == 0: return self.render() ###################################################################### # Non-public interface ###################################################################### def _file_path_changed(self, fpath): value = fpath.get() if len(value) == 0: return # Extract the file extension splitname = value.strip().split('.') extension = splitname[-1].lower() # Select polydata reader based on file type old_reader = self.reader if self._reader_dict.has_key(extension): self.reader = self._reader_dict[extension] else: error('Invalid extension for file: %s' % value) return self.reader.file_name = value.strip() self.reader.update() self.reader.update_information() if old_reader is not None: old_reader.on_trait_change(self.render, remove=True) self.reader.on_trait_change(self.render) old_outputs = self.outputs self.outputs = [self.reader.output] if self.outputs == old_outputs: self.data_changed = True # Change our name on the tree view self.name = self._get_name() def _get_name(self): """ Returns the name to display on the tree view. Note that this is not a property getter. """ fname = basename(self.file_path.get()) ret = "%s" % fname if len(self.file_list) > 1: ret += " (timeseries)" if '[Hidden]' in self.name: ret += ' [Hidden]' return ret def __reader_dict_default(self): """Default value for reader dict.""" rd = { 'stl': tvtk.STLReader(), 'stla': tvtk.STLReader(), 'stlb': tvtk.STLReader(), 'txt': tvtk.SimplePointsReader(), 'raw': tvtk.ParticleReader(), 'ply': tvtk.PLYReader(), 'pdb': tvtk.PDBReader(), 'slc': tvtk.SLCReader(), 'xyz': tvtk.XYZMolReader(), 'obj': tvtk.OBJReader(), 'facet': tvtk.FacetReader(), 'cube': tvtk.GaussianCubeReader(), 'g': tvtk.BYUReader(), } return rd # Callable to check if the reader can actually read the file def can_read(cls, filename): """ Class method to check if the reader can actually read the file. Returns 'True' if it can read it succesfully else 'False' """ # Extract the file extension splitname = filename.strip().split('.') extension = splitname[-1].lower() if extension == 'xyz': from vtk import vtkObject o = vtkObject w = o.GetGlobalWarningDisplay() o.SetGlobalWarningDisplay(0) # Turn it off. r = tvtk.XYZMolReader() r.file_name = filename r.update() o.SetGlobalWarningDisplay(w) if len(r.output.points) != 0: return True return False return None can_read = classmethod(can_read)
class TVTKBranchNode(TreeNodeObject): """Represents a branch in the tree view. The `children` trait produces an iterable that represents the children of the branch. """ # The tvtk object being wrapped. object = Instance(TVTKBase) # Children of the object. children = Property # Name to show on the view. name = Property # Tree generator to use. tree_generator = Instance(TreeGenerator) # Cache of children. children_cache = Dict # Work around problem with HasPrivateTraits. __ = Python def __init__(self, **traits): super(TVTKBranchNode, self).__init__(**traits) def __del__(self): try: self._remove_listners() except: pass def __hash__(self): return hash(tvtk.to_vtk(self.object)) def _get_children_from_cache(self): return [x for x in self.children_cache.values() if x is not None] def _create_children(self): kids = self.tree_generator.get_children(self.object) self.children_cache = kids self._setup_listners() def _setup_listners(self): object = self.object kids = self.children_cache for key, val in kids.items(): if isinstance(val, tvtk.Collection): vtk_obj = tvtk.to_vtk(val) messenger.connect(vtk_obj, 'ModifiedEvent', self._notify_children) else: object.on_trait_change(self._notify_children, key) def _remove_listners(self): object = self.object kids = self.children_cache for key, val in kids.items(): if isinstance(val, tvtk.Collection): vtk_obj = tvtk.to_vtk(val) messenger.disconnect(vtk_obj, 'ModifiedEvent', self._notify_children) else: object.on_trait_change(self._notify_children, key, remove=True) def _notify_children(self, obj=None, name=None, old=None, new=None): old_val = self._get_children_from_cache() self._remove_listners() self._create_children() new_val = self._get_children_from_cache() self.trait_property_changed('children', old_val, new_val) def _get_children(self): if not self.children_cache: self._create_children() kids = self._get_children_from_cache() tg = self.tree_generator return CompositeIterable(kids, tree_generator=tg) def _get_name(self): return self.object.__class__.__name__ ###################################################################### # `TreeNodeObject` interface ###################################################################### def tno_get_icon(self, node, is_expanded): """ Returns the icon for a specified object. """ icon = get_icon(self.name) if icon: return icon else: return super(TVTKBranchNode, self).tno_get_icon(node, is_expanded)
from item \ import Item from include \ import Include #------------------------------------------------------------------------------- # Trait definitions: #------------------------------------------------------------------------------- # Name of the view trait id_trait = Str(desc='the name of the view') # Contents of the view trait (i.e., a single Group object) content_trait = Instance(Group, desc='the content of the view') # The menu bar for the view #menubar_trait = Instance( 'enthought.pyface.action.MenuBarManager', # desc = 'the menu bar for the view' ) # The tool bar for the view #toolbar_trait = Instance( 'enthought.pyface.action.ToolBarManager', # desc = 'the tool bar for the view' ) # An optional model/view factory for converting the model into a viewable # 'model_view' object model_view_trait = Callable(desc='the factory function for converting a' 'model into a model/view object') # Reference to a Handler object trait
class PipelineBrowser(HasTraits): # The tree generator to use. tree_generator = Trait(FullTreeGenerator(), Instance(TreeGenerator)) # The TVTK render window(s) associated with this browser. renwins = List # The root object to view in the pipeline. If None (default), the # root object is the render_window of the Scene instance passed at # object instantiation time. root_object = List(TVTKBase) # Private traits. # The root of the tree to display. _root = Any ########################################################################### # `object` interface. ########################################################################### def __init__(self, renwin=None, **traits): """Initializes the object. Parameters ---------- - renwin: `Scene` instance. Defaults to None. This may be passed in addition to the renwins attribute which can be a list of scenes. """ super(PipelineBrowser, self).__init__(**traits) self.ui = None self.view = None if renwin: self.renwins.append(renwin) self._root_object_changed(self.root_object) menu = Menu(Action(name='Refresh', action='editor.update_editor'), Action(name='Expand all', action='editor.expand_all')) self.menu = menu nodes = self.tree_generator.get_nodes(menu) self.tree_editor = TreeEditor(nodes=nodes, editable=False, orientation='vertical', hide_root=True, on_dclick=self._on_dclick) self.view = View(Group(Item(name='_root', editor=self.tree_editor, resizable=True), show_labels=False, show_border=False, orientation='vertical'), title='Pipeline browser', help=False, resizable=True, undo=False, revert=False, width=.3, height=.3) ########################################################################### # `PipelineBrowser` interface. ########################################################################### def show(self, parent=None): """Show the tree view if not already show. If optional `parent` widget is passed, the tree is displayed inside the passed parent widget.""" # If UI already exists, raise it and return. if self.ui and self.ui.control: try: self.ui.control.Raise() except AttributeError: pass else: return else: # No active ui, create one. if parent: self.ui = self.view.ui(self, parent=parent, kind='subpanel') else: self.ui = self.view.ui(self, parent=parent) def update(self): """Update the tree view.""" # This is a hack. if self.ui and self.ui.control: try: ed = self.ui._editors[0] ed.update_editor() self.ui.control.Refresh() except (AttributeError, IndexError): pass # Another name for update. refresh = update def render(self): """Calls render on all render windows associated with this browser.""" for rw in self.renwins: rw.render() ########################################################################### # Non-public interface. ########################################################################### def _make_default_root(self): tree_gen = self.tree_generator objs = [x.render_window for x in self.renwins] node = TVTKCollectionNode(object=objs, name="Root", tree_generator=tree_gen) return node def _tree_generator_changed(self, tree_gen): """Traits event handler.""" if self._root: root_obj = self._root.object else: root_obj = self.root_object if root_obj: ro = root_obj if not hasattr(root_obj, '__len__'): ro = [root_obj] self._root = TVTKCollectionNode(object=ro, name="Root", tree_generator=tree_gen) else: self._root = self._make_default_root() self.tree_editor.nodes = tree_gen.get_nodes(self.menu) self.update() def _root_object_changed(self, root_obj): """Trait handler called when the root object is assigned to.""" tg = self.tree_generator if root_obj: self._root = TVTKCollectionNode(object=root_obj, name="Root", tree_generator=tg) else: self._root = self._make_default_root() self.root_object = self._root.object self.update() def _root_object_items_changed(self, list_event): """Trait handler called when the items of the list change.""" self._root_object_changed(self.root_object) def _on_dclick(self, obj): """Callback that is called when nodes are double-clicked.""" if hasattr(obj, 'object') and hasattr(obj.object, 'edit_traits'): object = obj.object view = object.trait_view() view.handler = UICloseHandler(browser=self) object.on_trait_change(self.render) ui = object.edit_traits(view=view)
class TriangleReader(FileDataSource): """ Reader for the Triangle file formats: 2D <http://www.cs.cmu.edu/~quake/triangle.html> Supports opening .egde files to construct a surface mesh comprised of lines and .ele files to construct a solid mesh comprised of triangles. 3D <http://tetgen.berlios.de/fformats.html> Supports opening .face files to construct a surface mesh comprised of triangles and .ele files to construct a solid mesh comprised of tetrahedra. Outputs an unstructured grid dataset. """ # The version of this class. Used for persistence. __version__ = 0 # Information about what this object can produce. output_info = PipelineInfo(datasets=['unstructured_grid'], attribute_types=['any'], attributes=['scalars']) # The active point scalar name. point_scalars_name = DEnum(values_name='_point_scalars_list', desc='scalar point data attribute to use') # The active cell scalar name. cell_scalars_name = DEnum(values_name='_cell_scalars_list', desc='scalar cell data attribute to use') ######################################## # Private traits. # These private traits store the list of available data # attributes. The non-private traits use these lists internally. _cell_scalars_list = List(String) _point_scalars_list = List(String) # The VTK dataset to manage. _grid = Instance(tvtk.UnstructuredGrid, args=(), allow_none=False) # The basename of the file which has been loaded. _basename = String # Indicates whether nodes are numbered from 0 or 1 (the file # format allows both). _numbered_from = Int # This filter allows us to change the attributes of the data # object and will ensure that the pipeline is properly taken care # of. _assign_attribute = Instance(tvtk.AssignAttribute, args=(), allow_none=False) ######################################## # The view. view = View(Item(name='point_scalars_name'), Item(name='cell_scalars_name')) ######################################## # `FileDataSource` interface. def initialize(self, base_file_name): split = path.splitext(base_file_name) self._basename = split[0] extension = split[1] self._assign_attribute.input = self._grid self._read_node_file() if (extension == '.face' or extension == '.edge'): self._read_face_edge_file(extension) else: self._read_ele_file() self.outputs = [self._assign_attribute.output] self.name = 'Triangle file (%s%s)' % (path.basename( self._basename), extension) ######################################## # File reading methods. def _read_node_file(self): """ Loads data from {basename}.node, and inserts points and point scalars into the unstructured grid. """ file_name = '%s.node' % self._basename # Load all data. all_data = self._get_data(file_name) # Grab values from the first line of data file. points, dimensions, attributes, boundary_marker = map( int, all_data[0:4]) # Reshape remainder of array. data_array = all_data[4:].reshape( points, 1 + dimensions + attributes + boundary_marker) self._numbered_from = int(data_array[0][0]) points_array = array(data_array[:, 1:(1 + dimensions)], 'double') if (dimensions == 2): # Add a 0 to each point if it is 2D. points_array = array(map(lambda a: append(a, 0), points_array)) self._grid.points = points_array for i in range(attributes): attribute_array = data_array[:, (i + dimensions + 1):(i + dimensions + 2)] self._add_attribute_array(attribute_array, i, 'point') if (boundary_marker): boundary_marker_array = data_array[:, (dimensions + attributes + 1):(dimensions + attributes + 2)] self._add_boundary_marker_array(boundary_marker_array, 'point') def _read_face_edge_file(self, extension): """ Loads data from 2D {basename}.edge or 3D {basename}.face, and inserts triangle/line cells and cell scalars into the unstructured grid. """ file_name = '%s%s' % (self._basename, extension) if (extension == '.edge'): # 2D. Expect two endpoints which form a line. npoints = 2 cell_type = tvtk.Line().cell_type else: # 3D. Expect three points which form a triangle. npoints = 3 cell_type = tvtk.Triangle().cell_type # Load all data. all_data = self._get_data(file_name) # Grab values from the first line of data file. faces_edges, boundary_marker = map(int, all_data[0:2]) # Reshape remainder of array. data_array = all_data[2:].reshape(faces_edges, npoints + 1 + boundary_marker) nodes_array = data_array[:, 1:npoints + 1] - self._numbered_from self._grid.set_cells(cell_type, nodes_array) if (boundary_marker): boundary_marker_array = data_array[:, npoints + 1:npoints + 2] self._add_boundary_marker_array(boundary_marker_array, 'cell') def _read_ele_file(self): """ Loads data from {basename}.ele, and inserts triangle/tetrahedron cells and cell scalars into the unstructured grid. """ file_name = '%s.ele' % self._basename # Load all data. all_data = self._get_data(file_name) # Grab values from the first line of data file. tet_tri, nodes_per_tet_tri, attributes = map(int, all_data[0:3]) # Reshape remainder of array. data_array = all_data[3:].reshape(tet_tri, 1 + nodes_per_tet_tri + attributes) nodes_array = data_array[:, 1:(nodes_per_tet_tri + 1)] - self._numbered_from if (nodes_per_tet_tri == 3): cell_type = tvtk.Triangle().cell_type else: cell_type = tvtk.Tetra().cell_type self._grid.set_cells(cell_type, nodes_array) for i in range(attributes): attribute_array = data_array[:, (i + nodes_per_tet_tri + 1):(i + nodes_per_tet_tri + 2)] self._add_attribute_array(attribute_array, i, 'cell') def _get_data(self, file_name): """ Returns a 1D array containing all the data from the given file. """ file = open(file_name) file_string = file.read() # Strip comments. pattern = compile('#.*?$', MULTILINE) file_string = pattern.sub('', file_string) # Load all data into array. return fromstring(file_string, dtype=float, sep=" ") ######################################## # Unstructured grid construction # methods. def _add_attribute_array(self, attribute_array, i, type): """ Adds the given attribute array to either point_data or cell_data of the unstructured grid. """ attribute_array_name = 'Attribute %i' % i if (type == 'cell'): # .ele file attributes are of type Int tvtk_attribute_array = tvtk.IntArray(name=attribute_array_name) attribute_array = map(int, attribute_array) else: # .node file attributes are of type Float tvtk_attribute_array = tvtk.FloatArray(name=attribute_array_name) tvtk_attribute_array.from_array(attribute_array) getattr(self._grid, '%s_data' % type).add_array(tvtk_attribute_array) getattr(self, '_%s_scalars_list' % type).append(attribute_array_name) if (i == 0): self._set_data_name(type, 'Attribute 0') def _add_boundary_marker_array(self, boundary_marker_array, type): """ Adds the given boundary marker array to either point_data or cell_data of the unstructured grid. """ boundary_marker_array_name = 'Boundary Marker' tvtk_boundary_marker_array = tvtk.IntArray( name=boundary_marker_array_name) tvtk_boundary_marker_array.from_array(boundary_marker_array) getattr(self._grid, '%s_data' % type).add_array(tvtk_boundary_marker_array) getattr(self, '_%s_scalars_list' % type).append(boundary_marker_array_name) self._set_data_name(type, 'Boundary Marker') ######################################## # Methods taken and modified from # SetActiveAttribute filter. def _point_scalars_name_changed(self, value): self._set_data_name('point', value) def _cell_scalars_name_changed(self, value): self._set_data_name('cell', value) def _set_data_name(self, attr_type, value): """ Sets the selected point or cell scalar to be active, and deactivates the scalar of the other type. """ if value is None: return if (attr_type == 'point'): data = self._grid.point_data other_data = self._grid.cell_data else: data = self._grid.cell_data other_data = self._grid.point_data method = getattr(data, 'set_active_scalars') method(value) # Deactivate other attribute. method = getattr(other_data, 'set_active_scalars') method(None) self._assign_attribute.assign(value, 'SCALARS', attr_type.upper() + '_DATA') self._assign_attribute.update() # Fire an event, so the changes propagate. self.data_changed = True
class Edge(HasTraits): """ Defines a graph edge. """ #-------------------------------------------------------------------------- # Trait definitions: #-------------------------------------------------------------------------- # Tail/from/source/start node. tail_node = Instance(Node, allow_none=False) # Head/to/target/end node. head_node = Instance(Node, allow_none=False) # String identifier (TreeNode label). name = Property( Str, depends_on=["tail_node", "tail_node.ID", "head_node", "head_node.ID"]) # Connection string used in string output. conn = Enum("->", "--") # Nodes from which the tail and head nodes may be selected. _nodes = List(Instance(Node)) # GUI specific. #-------------------------------------------------------------------------- # Xdot trait definitions: #-------------------------------------------------------------------------- # For a given graph object, one will typically a draw directive before the # label directive. For example, for a node, one would first use the # commands in _draw_ followed by the commands in _ldraw_. _draw_ = Str(desc="xdot drawing directive", label="draw") _ldraw_ = Str(desc="xdot label drawing directive", label="ldraw") _hdraw_ = Str(desc="edge head arrowhead drawing directive.", label="hdraw") _tdraw_ = Str(desc="edge tail arrowhead drawing directive.", label="tdraw") _hldraw_ = Str(desc="edge head label drawing directive.", label="hldraw") _tldraw_ = Str(desc="edge tail label drawing directive.", label="tldraw") #-------------------------------------------------------------------------- # Enable trait definitions: #-------------------------------------------------------------------------- # Container of drawing components, typically the edge spline. drawing = Instance(Container) # Container of label components. label_drawing = Instance(Container) # Container of head arrow components. arrowhead_drawing = Instance(Container) # Container of tail arrow components. arrowtail_drawing = Instance(Container) # Container of head arrow label components. arrowhead_label_drawing = Instance(Container) # Container of tail arrow label components. arrowtail_label_drawing = Instance(Container) # Container for the drawing, label, arrow and arrow label components. component = Instance(Container, desc="container of graph components.") # A view into a sub-region of the canvas. vp = Instance(Viewport, desc="a view of a sub-region of the canvas") # Use Graphviz to arrange all graph components. arrange = Button("Arrange All") #-------------------------------------------------------------------------- # Dot trait definitions: #-------------------------------------------------------------------------- # Style of arrowhead on the head node of an edge. # See also the <html:a rel="attr">dir</html:a> attribute, # and the <html:a rel="note">undirected</html:a> note. arrowhead = arrow_trait # Multiplicative scale factor for arrowheads. arrowsize = Float(1.0, desc="multiplicative scale factor for arrowheads", label="Arrow size", graphviz=True) # Style of arrowhead on the tail node of an edge. # See also the <html:a rel="attr">dir</html:a> attribute, # and the <html:a rel="note">undirected</html:a> note. arrowtail = arrow_trait # Basic drawing color for graphics, not text. For the latter, use the # <html:a rel="attr">fontcolor</html:a> attribute. # # For edges, the value # can either be a single <html:a rel="type">color</html:a> or a <html:a rel="type">colorList</html:a>. # In the latter case, the edge is drawn using parallel splines or lines, # one for each color in the list, in the order given. # The head arrow, if any, is drawn using the first color in the list, # and the tail arrow, if any, the second color. This supports the common # case of drawing opposing edges, but using parallel splines instead of # separately routed multiedges. color = color_trait # This attribute specifies a color scheme namespace. If defined, it specifies # the context for interpreting color names. In particular, if a # <html:a rel="type">color</html:a> value has form <html:code>xxx</html:code> or <html:code>//xxx</html:code>, # then the color <html:code>xxx</html:code> will be evaluated according to the current color scheme. # If no color scheme is set, the standard X11 naming is used. # For example, if <html:code>colorscheme=bugn9</html:code>, then <html:code>color=7</html:code> # is interpreted as <html:code>/bugn9/7</html:code>. colorscheme = color_scheme_trait # Comments are inserted into output. Device-dependent. comment = comment_trait # If <html:span class="val">false</html:span>, the edge is not used in # ranking the nodes. constraint = Bool(True, desc="if edge is used in ranking the nodes", graphviz=True) # If <html:span class="val">true</html:span>, attach edge label to edge by a 2-segment # polyline, underlining the label, then going to the closest point of spline. decorate = Bool( False, desc="to attach edge label to edge by a 2-segment " "polyline, underlining the label, then going to the closest point of " "spline", graphviz=True) # Set edge type for drawing arrowheads. This indicates which ends of the # edge should be decorated with an arrowhead. The actual style of the # arrowhead can be specified using the <html:a rel="attr">arrowhead</html:a> # and <html:a rel="attr">arrowtail</html:a> attributes. # See <html:a rel="note">undirected</html:a>. dir = Enum("forward", "back", "both", "none", label="Direction", desc="edge type for drawing arrowheads", graphviz=True) # Synonym for <html:a rel="attr">edgeURL</html:a>. # edgehref = Alias("edgeURL", desc="synonym for edgeURL") edgehref = Synced(sync_to="edgeURL", graphviz=True) # If the edge has a URL or edgeURL attribute, this attribute determines # which window of the browser is used for the URL attached to the non-label # part of the edge. Setting it to "_graphviz" will open a new window if it # doesn't already exist, or reuse it if it does. If undefined, the value of # the target is used. edgetarget = Str("", desc="which window of the browser is used for the " "URL attached to the non-label part of the edge", label="Edge target", graphviz=True) # Tooltip annotation attached to the non-label part of an edge. # This is used only if the edge has a <html:a rel="attr">URL</html:a> # or <html:a rel="attr">edgeURL</html:a> attribute. edgetooltip = Str("", desc="annotation attached to the non-label part of " "an edge", label="Edge tooltip", graphviz=True) # edgetooltip = EscString # If <html:a rel="attr">edgeURL</html:a> is defined, this is the link used for the non-label # parts of an edge. This value overrides any <html:a rel="attr">URL</html:a> # defined for the edge. # Also, this value is used near the head or tail node unless overridden # by a <html:a rel="attr">headURL</html:a> or <html:a rel="attr">tailURL</html:a> value, # respectively. # See <html:a rel="note">undirected</html:a>. edgeURL = Str("", desc="link used for the non-label parts of an edge", label="Edge URL", graphviz=True) #LabelStr # Color used for text. fontcolor = fontcolor_trait # Font used for text. This very much depends on the output format and, for # non-bitmap output such as PostScript or SVG, the availability of the font # when the graph is displayed or printed. As such, it is best to rely on # font faces that are generally available, such as Times-Roman, Helvetica or # Courier. # # If Graphviz was built using the # <html:a href="http://pdx.freedesktop.org/~fontconfig/fontconfig-user.html">fontconfig library</html:a>, the latter library # will be used to search for the font. However, if the <html:a rel="attr">fontname</html:a> string # contains a slash character "/", it is treated as a pathname for the font # file, though font lookup will append the usual font suffixes. # # If Graphviz does not use fontconfig, <html:a rel="attr">fontname</html:a> will be # considered the name of a Type 1 or True Type font file. # If you specify <html:code>fontname=schlbk</html:code>, the tool will look for a # file named <html:code>schlbk.ttf</html:code> or <html:code>schlbk.pfa</html:code> or <html:code>schlbk.pfb</html:code> # in one of the directories specified by # the <html:a rel="attr">fontpath</html:a> attribute. # The lookup does support various aliases for the common fonts. fontname = fontname_trait # Font size, in <html:a rel="note">points</html:a>, used for text. fontsize = fontsize_trait # If <html:span class="val">true</html:span>, the head of an edge is clipped to the boundary of the head node; # otherwise, the end of the edge goes to the center of the node, or the # center of a port, if applicable. headclip = Bool(True, desc="head of an edge to be clipped to the boundary " "of the head node", label="Head clip", graphviz=True) # Synonym for <html:a rel="attr">headURL</html:a>. headhref = Alias("headURL", desc="synonym for headURL", graphviz=True) # Text label to be placed near head of edge. # See <html:a rel="note">undirected</html:a>. headlabel = Str("", desc="text label to be placed near head of edge", label="Head label", graphviz=True) headport = port_pos_trait # If the edge has a headURL, this attribute determines which window of the # browser is used for the URL. Setting it to "_graphviz" will open a new # window if it doesn't already exist, or reuse it if it does. If undefined, # the value of the target is used. headtarget = Str(desc="which window of the browser is used for the URL", label="Head target", graphviz=True) # Tooltip annotation attached to the head of an edge. This is used only # if the edge has a <html:a rel="attr">headURL</html:a> attribute. headtooltip = Str("", desc="tooltip annotation attached to the head of an " "edge", label="Head tooltip", graphviz=True) # If <html:a rel="attr">headURL</html:a> is defined, it is # output as part of the head label of the edge. # Also, this value is used near the head node, overriding any # <html:a rel="attr">URL</html:a> value. # See <html:a rel="note">undirected</html:a>. headURL = Str("", desc="output as part of the head label of the edge", label="Head URL", graphviz=True) # Synonym for <html:a rel="attr">URL</html:a>. href = Alias("URL", desc="synonym for URL", graphviz=True) # Text label attached to objects. # If a node's <html:a rel="attr">shape</html:a> is record, then the label can # have a <html:a href="http://www.graphviz.org/doc/info/shapes.html#record">special format</html:a> # which describes the record layout. label = label_trait # This, along with <html:a rel="attr">labeldistance</html:a>, determine # where the # headlabel (taillabel) are placed with respect to the head (tail) # in polar coordinates. The origin in the coordinate system is # the point where the edge touches the node. The ray of 0 degrees # goes from the origin back along the edge, parallel to the edge # at the origin. # # The angle, in degrees, specifies the rotation from the 0 degree ray, # with positive angles moving counterclockwise and negative angles # moving clockwise. labelangle = Float( -25.0, desc=", along with labeldistance, where the " "headlabel (taillabel) are placed with respect to the head (tail)", label="Label angle", graphviz=True) # Multiplicative scaling factor adjusting the distance that # the headlabel (taillabel) is from the head (tail) node. # The default distance is 10 points. See <html:a rel="attr">labelangle</html:a> # for more details. labeldistance = Float( 1.0, desc="multiplicative scaling factor adjusting " "the distance that the headlabel (taillabel) is from the head (tail) " "node", label="Label distance", graphviz=True) # If true, allows edge labels to be less constrained in position. In # particular, it may appear on top of other edges. labelfloat = Bool(False, desc="edge labels to be less constrained in " "position", label="Label float", graphviz=True) # Color used for headlabel and taillabel. # If not set, defaults to edge's fontcolor. labelfontcolor = Color("black", desc="color used for headlabel and " "taillabel", label="Label font color", graphviz=True) # Font used for headlabel and taillabel. # If not set, defaults to edge's fontname. labelfontname = Font("Times-Roman", desc="Font used for headlabel and " "taillabel", label="Label font name", graphviz=True) # Font size, in <html:a rel="note">points</html:a>, used for headlabel and taillabel. # If not set, defaults to edge's fontsize. labelfontsize = Float(14.0, desc="Font size, in points, used for " "headlabel and taillabel", label="label_font_size", graphviz=True) # Synonym for <html:a rel="attr">labelURL</html:a>. labelhref = Alias("labelURL", desc="synonym for labelURL", graphviz=True) # If the edge has a URL or labelURL attribute, this attribute determines # which window of the browser is used for the URL attached to the label. # Setting it to "_graphviz" will open a new window if it doesn't already # exist, or reuse it if it does. If undefined, the value of the target is # used. labeltarget = Str("", desc="which window of the browser is used for the " "URL attached to the label", label="Label target", graphviz=True) # Tooltip annotation attached to label of an edge. # This is used only if the edge has a <html:a rel="attr">URL</html:a> # or <html:a rel="attr">labelURL</html:a> attribute. labeltooltip = Str("", desc="tooltip annotation attached to label of an " "edge", label="Label tooltip", graphviz=True) # If <html:a rel="attr">labelURL</html:a> is defined, this is the link used for the label # of an edge. This value overrides any <html:a rel="attr">URL</html:a> # defined for the edge. labelURL = Str(desc="link used for the label of an edge", graphviz=True) # Specifies layers in which the node or edge is present. layer = layer_trait # Preferred edge length, in inches. len = Float(1.0, desc="preferred edge length, in inches", graphviz=True) #0.3(fdp) # Logical head of an edge. When compound is true, if lhead is defined and # is the name of a cluster containing the real head, the edge is clipped to # the boundary of the cluster. lhead = Str(desc="Logical head of an edge", graphviz=True) # Label position, in points. The position indicates the center of the label. lp = point_trait # Logical tail of an edge. When compound is true, if ltail is defined and # is the name of a cluster containing the real tail, the edge is clipped to # the boundary of the cluster. ltail = Str(desc="logical tail of an edge", graphviz=True) # Minimum edge length (rank difference between head and tail). minlen = Int(1, desc="minimum edge length", graphviz=True) # By default, the justification of multi-line labels is done within the # largest context that makes sense. Thus, in the label of a polygonal node, # a left-justified line will align with the left side of the node (shifted # by the prescribed margin). In record nodes, left-justified line will line # up with the left side of the enclosing column of fields. If nojustify is # "true", multi-line labels will be justified in the context of itself. For # example, if the attribute is set, the first label line is long, and the # second is shorter and left-justified, the second will align with the # left-most character in the first line, regardless of how large the node # might be. nojustify = nojustify_trait # Position of node, or spline control points. # For nodes, the position indicates the center of the node. # On output, the coordinates are in <html:a href="#points">points</html:a>. # # In neato and fdp, pos can be used to set the initial position of a node. # By default, the coordinates are assumed to be in inches. However, the # <html:a href="http://www.graphviz.org/doc/info/command.html#d:s">-s</html:a> command line flag can be used to specify # different units. # # When the <html:a href="http://www.graphviz.org/doc/info/command.html#d:n">-n</html:a> command line flag is used with # neato, it is assumed the positions have been set by one of the layout # programs, and are therefore in points. Thus, <html:code>neato -n</html:code> can accept # input correctly without requiring a <html:code>-s</html:code> flag and, in fact, # ignores any such flag. pos = List(Tuple(Float, Float), desc="spline control points") # Edges with the same head and the same <html:a rel="attr">samehead</html:a> value are aimed # at the same point on the head. # See <html:a rel="note">undirected</html:a>. samehead = Str("", desc="dges with the same head and the same samehead " "value are aimed at the same point on the head", graphviz=True) # Edges with the same tail and the same <html:a rel="attr">sametail</html:a> value are aimed # at the same point on the tail. # See <html:a rel="note">undirected</html:a>. sametail = Str("", desc="edges with the same tail and the same sametail " "value are aimed at the same point on the tail", graphviz=True) # Print guide boxes in PostScript at the beginning of # routesplines if 1, or at the end if 2. (Debugging) showboxes = showboxes_trait # Set style for node or edge. For cluster subgraph, if "filled", the # cluster box's background is filled. style = ListStr(desc="style for node or edge", graphviz=True) # If <html:span class="val">true</html:span>, the tail of an edge is clipped to the boundary of the tail node; # otherwise, the end of the edge goes to the center of the node, or the # center of a port, if applicable. tailclip = Bool(True, desc="tail of an edge to be clipped to the boundary " "of the tail node", graphviz=True) # Synonym for <html:a rel="attr">tailURL</html:a>. tailhref = Alias("tailURL", desc="synonym for tailURL", graphviz=True) # Text label to be placed near tail of edge. # See <html:a rel="note">undirected</html:a>. taillabel = Str(desc="text label to be placed near tail of edge", graphviz=True) # Indicates where on the tail node to attach the tail of the edge. tailport = port_pos_trait # If the edge has a tailURL, this attribute determines which window of the # browser is used for the URL. Setting it to "_graphviz" will open a new # window if it doesn't already exist, or reuse it if it does. If undefined, # the value of the target is used. tailtarget = Str(desc="which window of the browser is used for the URL", graphviz=True) # Tooltip annotation attached to the tail of an edge. This is used only # if the edge has a <html:a rel="attr">tailURL</html:a> attribute. tailtooltip = Str("", desc="tooltip annotation attached to the tail of an " "edge", label="Tail tooltip", graphviz=True) # If <html:a rel="attr">tailURL</html:a> is defined, it is # output as part of the tail label of the edge. # Also, this value is used near the tail node, overriding any # <html:a rel="attr">URL</html:a> value. # See <html:a rel="note">undirected</html:a>. tailURL = Str("", desc="output as part of the tail label of the edge", label="Tail URL", graphviz=True) # If the object has a URL, this attribute determines which window # of the browser is used for the URL. # See <html:a href="http://www.w3.org/TR/html401/present/frames.html#adef-target">W3C documentation</html:a>. target = target_trait # Tooltip annotation attached to the node or edge. If unset, Graphviz # will use the object's <html:a rel="attr">label</html:a> if defined. # Note that if the label is a record specification or an HTML-like # label, the resulting tooltip may be unhelpful. In this case, if # tooltips will be generated, the user should set a <html:tt>tooltip</html:tt> # attribute explicitly. tooltip = tooltip_trait # Hyperlinks incorporated into device-dependent output. # At present, used in ps2, cmap, i*map and svg formats. # For all these formats, URLs can be attached to nodes, edges and # clusters. URL attributes can also be attached to the root graph in ps2, # cmap and i*map formats. This serves as the base URL for relative URLs in the # former, and as the default image map file in the latter. # # For svg, cmapx and imap output, the active area for a node is its # visible image. # For example, an unfilled node with no drawn boundary will only be active on its label. # For other output, the active area is its bounding box. # The active area for a cluster is its bounding box. # For edges, the active areas are small circles where the edge contacts its head # and tail nodes. In addition, for svg, cmapx and imap, the active area # includes a thin polygon approximating the edge. The circles may # overlap the related node, and the edge URL dominates. # If the edge has a label, this will also be active. # Finally, if the edge has a head or tail label, this will also be active. # # Note that, for edges, the attributes <html:a rel="attr">headURL</html:a>, # <html:a rel="attr">tailURL</html:a>, <html:a rel="attr">labelURL</html:a> and # <html:a rel="attr">edgeURL</html:a> allow control of various parts of an # edge. Also note that, if active areas of two edges overlap, it is unspecified # which area dominates. URL = url_trait # Weight of edge. In dot, the heavier the weight, the shorter, straighter # and more vertical the edge is. weight = Float(1.0, desc="weight of edge", graphviz=True) #-------------------------------------------------------------------------- # Views: #-------------------------------------------------------------------------- traits_view = View(VGroup( Group( Item(name="vp", editor=ComponentEditor(height=100), show_label=False, id=".component"), Item("arrange", show_label=False)), Tabbed( Group(Item(name="tail_node", editor=InstanceEditor(name="_nodes", editable=False)), Item(name="head_node", editor=InstanceEditor(name="_nodes", editable=False)), [ "style", "layer", "color", "colorscheme", "dir", "arrowsize", "constraint", "decorate", "showboxes", "tooltip", "edgetooltip", "edgetarget", "target", "comment" ], label="Edge"), Group([ "label", "fontname", "fontsize", "fontcolor", "nojustify", "labeltarget", "labelfloat", "labelfontsize", "labeltooltip", "labelangle", "lp", "labelURL", "labelfontname", "labeldistance", "labelfontcolor", "labelhref" ], label="Label"), Group(["minlen", "weight", "len", "pos"], label="Dimension"), Group([ "arrowhead", "samehead", "headURL", "headtooltip", "headclip", "headport", "headlabel", "headtarget", "lhead", "headhref" ], label="Head"), Group([ "arrowtail", "tailtarget", "tailhref", "ltail", "sametail", "tailport", "taillabel", "tailtooltip", "tailURL", "tailclip" ], label="Tail"), Group(["URL", "href", "edgeURL", "edgehref"], label="URL"), Group([ "_draw_", "_ldraw_", "_hdraw_", "_tdraw_", "_hldraw_", "_tldraw_" ], label="Xdot"), dock="tab"), layout="split", id=".splitter"), title="Edge", id="godot.edge", buttons=["OK", "Cancel", "Help"], resizable=True) #-------------------------------------------------------------------------- # "object" interface: #-------------------------------------------------------------------------- def __init__(self, tailnode_or_ID, headnode_or_ID, directed=False, **traits): """ Initialises a new Edge instance. """ if not isinstance(tailnode_or_ID, Node): tailnodeID = str(tailnode_or_ID) tail_node = Node(tailnodeID) else: tail_node = tailnode_or_ID if not isinstance(headnode_or_ID, Node): headnodeID = str(headnode_or_ID) head_node = Node(headnodeID) else: head_node = headnode_or_ID self.tail_node = tail_node self.head_node = head_node if directed: self.conn = "->" else: self.conn = "--" super(Edge, self).__init__(**traits) def __str__(self): """ Returns a string representation of the edge. """ attrs = [] # Traits to be included in string output have 'graphviz' metadata. for trait_name, trait in self.traits(graphviz=True).iteritems(): # Get the value of the trait for comparison with the default. value = getattr(self, trait_name) # Only print attribute value pairs if not defaulted. # FIXME: Alias/Synced traits default to None. if (value != trait.default) and (trait.default is not None): # Add quotes to the value if necessary. if isinstance(value, basestring): valstr = '"%s"' % value else: valstr = str(value) attrs.append('%s=%s' % (trait_name, valstr)) if attrs: attrstr = " [%s]" % ", ".join(attrs) else: attrstr = "" edge_str = "%s%s %s %s%s%s;" % (self.tail_node.ID, self.tailport, self.conn, self.head_node.ID, self.headport, attrstr) return edge_str #-------------------------------------------------------------------------- # Trait initialisers: #-------------------------------------------------------------------------- def _component_default(self): """ Trait initialiser. """ component = Container(auto_size=True, bgcolor="green") # component.tools.append( MoveTool(component) ) # component.tools.append( TraitsTool(component) ) return component def _vp_default(self): """ Trait initialiser. """ vp = Viewport(component=self.component) vp.enable_zoom = True vp.tools.append(ViewportPanTool(vp)) return vp #-------------------------------------------------------------------------- # Property getters: #-------------------------------------------------------------------------- def _get_name(self): """ Property getter. """ if (self.tail_node is not None) and (self.head_node is not None): return "%s %s %s" % (self.tail_node.ID, self.conn, self.head_node.ID) else: return "Edge" #-------------------------------------------------------------------------- # Event handlers: #-------------------------------------------------------------------------- @on_trait_change("arrange") def arrange_all(self): """ Arrange the components of the node using Graphviz. """ # FIXME: Circular reference avoidance. import godot.dot_data_parser import godot.graph graph = godot.graph.Graph(ID="g", directed=True) self.conn = "->" graph.edges.append(self) xdot_data = graph.create(format="xdot") # print "XDOT DATA:", xdot_data parser = godot.dot_data_parser.GodotDataParser() ndata = xdot_data.replace('\\\n', '') tokens = parser.dotparser.parseString(ndata)[0] for element in tokens[3]: cmd = element[0] if cmd == "add_edge": cmd, src, dest, opts = element self.set(**opts) # @on_trait_change("_draw_,_hdraw_") def _parse_xdot_directive(self, name, new): """ Handles parsing Xdot drawing directives. """ parser = XdotAttrParser() components = parser.parse_xdot_data(new) # The absolute coordinate of the drawing container wrt graph origin. x1 = min([c.x for c in components]) y1 = min([c.y for c in components]) print "X1/Y1:", name, x1, y1 # Components are positioned relative to their container. This # function positions the bottom-left corner of the components at # their origin rather than relative to the graph. # move_to_origin( components ) for c in components: if isinstance(c, Ellipse): component.x_origin -= x1 component.y_origin -= y1 # c.position = [ c.x - x1, c.y - y1 ] elif isinstance(c, (Polygon, BSpline)): print "Points:", c.points c.points = [(t[0] - x1, t[1] - y1) for t in c.points] print "Points:", c.points elif isinstance(c, Text): # font = str_to_font( str(c.pen.font) ) c.text_x, c.text_y = c.x - x1, c.y - y1 container = Container(auto_size=True, position=[x1, y1], bgcolor="yellow") container.add(*components) if name == "_draw_": self.drawing = container elif name == "_hdraw_": self.arrowhead_drawing = container else: raise @on_trait_change("drawing,arrowhead_drawing") def _on_drawing(self, object, name, old, new): """ Handles the containers of drawing components being set. """ attrs = ["drawing", "arrowhead_drawing"] others = [getattr(self, a) for a in attrs \ if (a != name) and (getattr(self, a) is not None)] x, y = self.component.position print "POS:", x, y, self.component.position abs_x = [d.x + x for d in others] abs_y = [d.y + y for d in others] print "ABS:", abs_x, abs_y # Assume that he new drawing is positioned relative to graph origin. x1 = min(abs_x + [new.x]) y1 = min(abs_y + [new.y]) print "DRAW:", new.position new.position = [new.x - x1, new.y - y1] print "DRAW:", new.position # for i, b in enumerate( others ): # self.drawing.position = [100, 100] # self.drawing.request_redraw() # print "OTHER:", b.position, abs_x[i] - x1 # b.position = [ abs_x[i] - x1, abs_y[i] - y1 ] # b.x = 50 # b.y = 50 # print "OTHER:", b.position, abs_x[i], x1 # for attr in attrs: # if attr != name: # if getattr(self, attr) is not None: # drawing = getattr(self, attr) # drawing.position = [50, 50] if old is not None: self.component.remove(old) if new is not None: self.component.add(new) print "POS NEW:", self.component.position self.component.position = [x1, y1] print "POS NEW:", self.component.position self.component.request_redraw() print "POS NEW:", self.component.position
class VariableMeshPannerView(HasTraits): plot = Instance(Plot) spawn_zoom = Button vm_plot = Instance(VMImagePlot) use_tools = Bool(True) full_container = Instance(HPlotContainer) container = Instance(OverlayPlotContainer) traits_view = View( Group(Item('full_container', editor=ComponentEditor(size=(512, 512)), show_label=False), Item('field', show_label=False), orientation="vertical"), width=800, height=800, resizable=True, title="Pan and Scan", ) def _vm_plot_default(self): return VMImagePlot(panner=self.panner) def __init__(self, **kwargs): super(VariableMeshPannerView, self).__init__(**kwargs) # Create the plot self.add_trait("field", DelegatesTo("vm_plot")) plot = self.vm_plot.plot img_plot = self.vm_plot.img_plot if self.use_tools: plot.tools.append(PanTool(img_plot)) zoom = ZoomTool(component=img_plot, tool_mode="box", always_on=False) plot.overlays.append(zoom) imgtool = ImageInspectorTool(img_plot) img_plot.tools.append(imgtool) overlay = ImageInspectorOverlay(component=img_plot, image_inspector=imgtool, bgcolor="white", border_visible=True) img_plot.overlays.append(overlay) image_value_range = DataRange1D(self.vm_plot.fid) cbar_index_mapper = LinearMapper(range=image_value_range) self.colorbar = ColorBar(index_mapper=cbar_index_mapper, plot=img_plot, padding_right=40, resizable='v', width=30) self.colorbar.tools.append( PanTool(self.colorbar, constrain_direction="y", constrain=True)) zoom_overlay = ZoomTool(self.colorbar, axis="index", tool_mode="range", always_on=True, drag_button="right") self.colorbar.overlays.append(zoom_overlay) # create a range selection for the colorbar range_selection = RangeSelection(component=self.colorbar) self.colorbar.tools.append(range_selection) self.colorbar.overlays.append( RangeSelectionOverlay(component=self.colorbar, border_color="white", alpha=0.8, fill_color="lightgray")) # we also want to the range selection to inform the cmap plot of # the selection, so set that up as well range_selection.listeners.append(img_plot) self.full_container = HPlotContainer(padding=30) self.container = OverlayPlotContainer(padding=0) self.full_container.add(self.colorbar) self.full_container.add(self.container) self.container.add(self.vm_plot.plot)
class IODriver(t.Thread, HasTraits): """ Base class for a generic input driver. Runs in its own thread and grabs input from the source and passes it out to the decoding layer. """ _variables = Instance(Variables) _decoders = List(DataDecoder) _wants_to_terminate = False _use_thread = True name = Str('Input Driver') def __init__(self, **kwargs): t.Thread.__init__(self) HasTraits.__init__(self, **kwargs) def open(self): """ Here is a place to put any initialisation needed to start your driver, e.g. opening a port or file. """ pass def close(self): """ Here is a place to put any code needed to cleanly stop your input driver e.g. closing a port """ pass def receive(self): """ In this function you should add the code to setup and read from your input source. Return the received data to be passed to the data decoding layer. This function is called repeatedly while the driver is running and should not block indefinately (a timeout should be used to allow the driver to stop). You can return None is no data is available. """ return None def run(self): """ Used internally for the threading interface, you should put your code in receive. """ if self._use_thread: while not self._wants_to_terminate: data = self.receive() if data: self.pass_data(data) print "IO driver thread terminating:", self.name self.close() def start(self): """ Used internally to start the thread for the input driver, if you have init code put it in open. """ self.open() t.Thread.start(self) def stop(self): """ Used internally to stop the thread for the input driver, if you have clean-up code put it in close. """ if self._use_thread: self._wants_to_terminate = True else: self.close() def _add_decoder(self, decoder): """ Used internally to add decoders so they receive data from the input driver. """ decoder._variables = self._variables self._decoders += [decoder] def _remove_decoder(self, decoder): """ Used internally to remove decoders from an input driver. """ self._decoders.remove(decoder) def _remove_all_decoders(self): for decoder in self._decoders: self._remove_decoder(decoder) def pass_data(self, data): """ Pass data on to the decoding layer. """ for decoder in self._decoders: #self._decoder_list.get_decoders(): decoder._receive_callback(data) def get_config(self): print "Warning: using defualt get_config handler on io driver '%s' (which may not work)." % self.__class__.__name__ state = self.__getstate__() for key in list(state.iterkeys()): if key.startswith('_'): del state[key] print " config:", state return state def set_config(self, config): print "Warning: using defualt set_config handler on io driver '%s' (which may not work)." % self.__class__.__name__ print " config:", config self.__setstate__(config) def _get_config(self): # get decoders decoder_configs = [] for decoder in self._decoders: decoder_config = {decoder.__class__.__name__: decoder.get_config()} decoder_configs.append(decoder_config) config = self.get_config() config.update({'decoders': decoder_configs}) return config def _set_config(self, config): from plugin_manager import get_decoder_plugin_by_name # add decoders self._remove_all_decoders() for decoder_config in config['decoders']: decoder_plugin_name = list(decoder_config.iterkeys())[0] decoder_plugin_config = decoder_config[decoder_plugin_name] new_decoder = get_decoder_plugin_by_name(decoder_plugin_name)() self._add_decoder(new_decoder) new_decoder.set_config(decoder_plugin_config) del config['decoders'] self.set_config(config)