class TransientPlotOverlay(BasePlotContainer, AbstractOverlay): """ Allows an arbitrary plot component to be overlaid on top of another one. """ # The PlotComponent to draw as an overlay overlay_component = Instance(Component) # Where this overlay should draw relative to our .component align = Enum("right", "left", "top", "bottom") # The amount of space between the overlaying component and the underlying # one. This is either horizontal or vertical (depending on the value of # self.align), but is not both. margin = Float(10) # An offset to apply in X and Y offset = Trait(None, None, Tuple) # Override default values of some inherited traits unified_draw = True resizable = "" def _bounds_default(self): return [450, 250] def _clear_bounds(self, gc, view_bounds): if view_bounds is None: view_bounds = (0, 0, self.width, self.height) gc.clip_to_rect(*view_bounds) gc.set_fill_color((1.0, 1.0, 1.0, 1.0)) gc.begin_path() gc.rect(*view_bounds) gc.fill_path() def overlay(self, component, gc, view_bounds=None, mode="normal"): self._do_layout() with gc: self._clear_bounds(gc, view_bounds) self.overlay_component._draw(gc, view_bounds, mode) # TODO: Implement this more intelligently than the one in BasePlotContainer #def get_preferred_size(self): # pass def _do_layout(self): component = self.component bounds = self.outer_bounds if self.align in ("right", "left"): y = component.outer_y - (bounds[1] - component.outer_height) / 2 if self.align == "right": x = component.outer_x2 + self.margin else: x = component.outer_x - bounds[0] - self.margin else: # "top", "bottom" x = component.outer_x - (bounds[0] - component.outer_width) / 2 if self.align == "top": y = component.outer_y2 + self.margin else: y = component.outer_y - bounds[1] - self.margin if self.offset is not None: x += self.offset[0] y += self.offset[1] overlay_component = self.overlay_component overlay_component.outer_bounds = self.outer_bounds overlay_component.outer_position = [x, y] overlay_component._layout_needed = True overlay_component.do_layout() def dispatch(self, event, suffix): if self.visible and self.overlay_component.is_in(event.x, event.y): return self.overlay_component.dispatch(event, suffix)
class UIEditor(Editor): """ An editor that creates an embedded Traits UI. """ #--------------------------------------------------------------------------- # Trait definitions: #--------------------------------------------------------------------------- # The Traits UI created by the editor editor_ui = Instance(UI) #--------------------------------------------------------------------------- # Finishes initializing the editor by creating the underlying toolkit # widget: #--------------------------------------------------------------------------- def init(self, parent): """ Finishes initializing the editor by creating the underlying toolkit widget. """ self.editor_ui = self.init_ui(parent).set(parent=self.ui) self.control = self.editor_ui.control #--------------------------------------------------------------------------- # Creates the traits UI for the editor (can be overridden by a subclass): #--------------------------------------------------------------------------- def init_ui(self, parent): """ Creates the traits UI for the editor. """ return self.value.edit_traits( view=self.trait_view(), context={'object': self.value, 'editor': self}, parent=parent) #--------------------------------------------------------------------------- # Updates the editor when the object trait changes external to the editor: #--------------------------------------------------------------------------- def update_editor(self): """ Updates the editor when the object trait changes external to the editor. """ # Do nothing, since the embedded traits UI should handle the updates # itself, without our meddling: pass #--------------------------------------------------------------------------- # Disposes of the contents of an editor: #--------------------------------------------------------------------------- def dispose(self): """ Disposes of the contents of an editor. """ # Make sure the embedded traits UI is disposed of properly: if self.editor_ui is not None: self.editor_ui.dispose() super(UIEditor, self).dispose() #--------------------------------------------------------------------------- # Returns the editor's control for indicating error status: #--------------------------------------------------------------------------- def get_error_control(self): """ Returns the editor's control for indicating error status. """ return self.editor_ui.get_error_controls() #-- UI preference save/restore interface --------------------------------------- #--------------------------------------------------------------------------- # Restores any saved user preference information associated with the # editor: #--------------------------------------------------------------------------- def restore_prefs(self, prefs): """ Restores any saved user preference information associated with the editor. """ self.editor_ui.set_prefs(prefs) #--------------------------------------------------------------------------- # Returns any user preference information associated with the editor: #--------------------------------------------------------------------------- def save_prefs(self): """ Returns any user preference information associated with the editor. """ return self.editor_ui.get_prefs()
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'), Group(Item(name='movie_maker', style='custom', show_label=False), label='Movie'), 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.""" if self._vtk_control is None: return # 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) ########################################################################### # 'event' interface. ########################################################################### def _closed_fired(self): super(Scene, self)._closed_fired() self.picker = None self._vtk_control = None ########################################################################### # 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 Pipeline(HasTraits): """ Function used to build pipelines for helper functions """ #doc = '' _source_function = Callable() _pipeline = List() # Traits here only for documentation purposes figure = Instance('mayavi.core.scene.Scene', help='Figure to populate.') def __call__(self, *args, **kwargs): """ Calls the logics of the factory, but only after disabling rendering, if needed. """ # First retrieve the scene, if any. if 'figure' in kwargs: figure = kwargs['figure'] assert isinstance(figure, (Scene, None)) scene = figure.scene else: scene = tools.gcf().scene if scene is not None: self._do_redraw = not scene.disable_render scene.disable_render = True # Then call the real logic output = self.__call_internal__(*args, **kwargs) # And re-enable the rendering, if needed. if scene is not None: scene.disable_render = not self._do_redraw return output def __call_internal__(self, *args, **kwargs): """ Builds the source and runs through the pipeline, returning the last object created by the pipeline.""" self.store_kwargs(kwargs) self.source = self._source_function(*args, **kwargs) # Copy the pipeline so as not to modify it for the next call self.pipeline = self._pipeline[:] return self.build_pipeline() def store_kwargs(self, kwargs): """ Merges the given keyword argument, with traits default and store the resulting dictionary in self.kwargs.""" kwargs = kwargs.copy() all_traits = self.get_all_traits() if not set(kwargs.keys()).issubset(all_traits.keys()): raise ValueError("Invalid keyword arguments : %s" % \ ', '.join( str(k) for k in set(kwargs.keys()).difference(all_traits.keys()))) traits = self.get(self.class_trait_names()) [traits.pop(key) for key in traits.keys() if key[0] == '_'] traits.update(kwargs) self.kwargs = traits def build_pipeline(self): """ Runs through the pipeline, applying pipe after pipe. """ object = self.source for pipe in self.pipeline: keywords = set(pipe.class_trait_names()) keywords.remove('trait_added') keywords.remove('trait_modified') this_kwargs = {} for key, value in self.kwargs.iteritems(): if key in keywords: this_kwargs[key] = value object = pipe(object, **this_kwargs)._target return object def get_all_traits(self): """ Returns all the traits of class, and the classes in the pipeline. """ traits = {} for pipe in self._pipeline: traits.update(pipe.class_traits()) traits.update(self.class_traits()) traits.pop('trait_added') traits.pop('trait_modified') return traits
class DataSourceFactory(HasStrictTraits): """ Factory for creating data sources. The information about the organisation of the data is given by setting the public traits. """ # Whether the position is implicitely inferred from the array indices position_implicit = false # Whether the data is on an orthogonal grid orthogonal_grid = false # If the data is unstructured unstructured = false # If the factory should attempt to connect the data points connected = true # The position of the data points position_x = ArrayOrNone position_y = ArrayOrNone position_z = ArrayOrNone # Connectivity array. If none, it is implicitely inferred from the array # indices connectivity_triangles = ArrayOrNone # Whether or not the data points should be connected. lines = false # The scalar data array scalar_data = ArrayOrNone # Whether there is vector data has_vector_data = false # The vector components vector_u = ArrayOrNone vector_v = ArrayOrNone vector_w = ArrayOrNone #---------------------------------------------------------------------- # Private traits #---------------------------------------------------------------------- _vtk_source = Instance(tvtk.DataSet) _mayavi_source = Instance(Source) #---------------------------------------------------------------------- # Private interface #---------------------------------------------------------------------- def _add_scalar_data(self): """ Adds the scalar data to the vtk source. """ if self.scalar_data is not None: scalars = self.scalar_data.ravel() self._vtk_source.point_data.scalars = scalars def _add_vector_data(self): """ Adds the vector data to the vtk source. """ if self.has_vector_data: vectors = c_[self.vector_u.ravel(), self.vector_v.ravel(), self.vector_w.ravel(), ] self._vtk_source.point_data.vectors = vectors def _mk_polydata(self): """ Creates a PolyData vtk data set using the factory's attributes. """ points = c_[self.position_x.ravel(), self.position_y.ravel(), self.position_z.ravel(), ] lines = None if self.lines: np = len(points) - 1 lines = zeros((np, 2), 'l') lines[:, 0] = arange(0, np - 0.5, 1, 'l') lines[:, 1] = arange(1, np + 0.5, 1, 'l') self._vtk_source = tvtk.PolyData(points=points, lines=lines) if (self.connectivity_triangles is not None and self.connected): assert self.connectivity_triangles.shape[1] == 3, \ "The connectivity list must be Nx3." self._vtk_source.polys = self.connectivity_triangles self._mayavi_source = VTKDataSource(data=self._vtk_source) def _mk_image_data(self): """ Creates an ImageData VTK data set and the associated ArraySource using the factory's attributes. """ self._mayavi_source = ArraySource(transpose_input_array=True, scalar_data=self.scalar_data, origin=[0., 0., 0], spacing=[1, 1, 1]) self._vtk_source = self._mayavi_source.image_data def _mk_rectilinear_grid(self): """ Creates a RectilinearGrid VTK data set using the factory's attributes. """ rg = tvtk.RectilinearGrid() x = self.position_x.squeeze() if x.ndim == 3: x = x[:, 0, 0] y = self.position_y.squeeze() if y.ndim == 3: y = y[0, :, 0] z = self.position_z.squeeze() if z.ndim == 3: z = z[0, 0, :] # FIXME: We should check array size here. rg.dimensions = (x.size, y.size, z.size) rg.x_coordinates = x rg.y_coordinates = y rg.z_coordinates = z self._vtk_source = rg self._mayavi_source = VTKDataSource(data=self._vtk_source) def _mk_structured_grid(self): """ Creates a StructuredGrid VTK data set using the factory's attributes. """ # FIXME: We need to figure out the dimensions of the data # here, if any. sg = tvtk.StructuredGrid(dimensions=self.scalar_data.shape) sg.points = c_[self.position_x.ravel(), self.position_y.ravel(), self.position_z.ravel(), ] self._vtk_source = sg self._mayavi_source = VTKDataSource(data=self._vtk_source) #---------------------------------------------------------------------- # Public interface #---------------------------------------------------------------------- def build_data_source(self, **traits): """ Uses all the information given by the user on his data structure to figure out the right data structure. """ self.set(**traits) if not self.lines: if self.position_implicit: self._mk_image_data() elif self.orthogonal_grid: self._mk_rectilinear_grid() elif self.connectivity_triangles is None: if self.unstructured: self._mk_polydata() else: self._mk_structured_grid() else: self._mk_polydata() else: self._mk_polydata() self._add_scalar_data() self._add_vector_data() return self._mayavi_source
class Align(HasTraits): # The position of the view position = Array(shape=(3, )) brightness = Range(-2., 2., value=0.) contrast = Range(0., 3., value=1.) opacity = Range(0., 1., value=.1) colormap = Enum(*lut_manager.lut_mode_list()) fliplut = Bool outlines_visible = Bool(default_value=True) outline_rep = Enum(outline_reps) outline_color = Color( default=options.config.get("mayavi_aligner", "outline_color")) line_width = Range(0.5, 10., value=float( options.config.get("mayavi_aligner", "line_width"))) point_size = Range(0.5, 10., value=float( options.config.get("mayavi_aligner", "point_size"))) epi_filter = Enum(None, "median", "gradient") filter_strength = Range(1, 20, value=3) scene_3d = Instance(MlabSceneModel, ()) scene_x = Instance(MlabSceneModel, ()) scene_y = Instance(MlabSceneModel, ()) scene_z = Instance(MlabSceneModel, ()) # The data source epi_src = Instance(Source) surf_src = Instance(Source) xfm = Instance(Filter) surf = Instance(Module) disable_render = Bool flip_fb = Bool flip_lr = Bool flip_ud = Bool save_callback = Instance(types.FunctionType) save_btn = Button(label="Save Transform") legend = Str(legend) #--------------------------------------------------------------------------- # Object interface #--------------------------------------------------------------------------- def __init__(self, pts, polys, epi, xfm=None, xfmtype='magnet', **traits): ''' Parameters ---------- xfm : array_like, optional The initial 4x4 rotation matrix into magnet space (epi with slice affine) ''' self.load_epi(epi, xfm, xfmtype) self.pts, self.polys = pts, polys self._undolist = [] self._redo = None super(Align, self).__init__(**traits) def load_epi(self, epifilename, xfm=None, xfmtype="magnet"): """Loads the EPI image from the specified epifilename. """ nii = nibabel.load(epifilename) self.epi_file = nii epi = nii.get_data().astype(float).squeeze() if epi.ndim > 3: epi = epi[:, :, :, 0] self.affine = nii.get_affine() base = nii.get_header().get_base_affine() self.base = base self.origin = base[:3, -1] self.spacing = np.diag(base)[:3] if xfm is None: self.startxfm = np.dot(base, np.linalg.inv(self.affine)) elif xfmtype == "magnet": self.startxfm = np.dot(np.dot(base, np.linalg.inv(self.affine)), xfm) else: print("using xfmtype %s" % xfmtype) self.startxfm = xfm self.center = self.spacing * nii.get_shape()[:3] / 2 + self.origin self.padshape = 2**(np.ceil(np.log2(np.array(epi.shape)))) epi = np.nan_to_num(epi) self.epi_orig = epi - epi.min() self.epi_orig /= self.epi_orig.max() self.epi_orig *= 2 self.epi_orig -= 1 self.epi = self.epi_orig.copy() #--------------------------------------------------------------------------- # Default values #--------------------------------------------------------------------------- def _position_default(self): return np.abs(self.origin) + ( (np.array(self.epi.shape) + 1) % 2) * np.abs(self.spacing) / 2 def _epi_src_default(self): sf = mlab.pipeline.scalar_field(self.epi, figure=self.scene_3d.mayavi_scene, name='EPI') sf.origin = self.origin sf.spacing = self.spacing return sf def _surf_src_default(self): return mlab.pipeline.triangular_mesh_source( self.pts[:, 0], self.pts[:, 1], self.pts[:, 2], self.polys, figure=self.scene_3d.mayavi_scene, name='Cortex') def _surf_default(self): smooth = mlab.pipeline.poly_data_normals( self.xfm, figure=self.scene_3d.mayavi_scene) smooth.filter.splitting = False surf = mlab.pipeline.surface(smooth, figure=self.scene_3d.mayavi_scene) surf.actor.mapper.scalar_visibility = 0 return surf def _xfm_default(self): xfm = mlab.pipeline.transform_data(self.surf_src, figure=self.scene_3d.mayavi_scene) def savexfm(info, evt): self._undolist.append(xfm.transform.matrix.to_array()) np.save("/tmp/last_xfm.npy", self.get_xfm()) xfm.widget.add_observer("EndInteractionEvent", savexfm) xfm.widget.add_observer("EndInteractionEvent", self.update_slabs) xfm.transform.set_matrix(self.startxfm.ravel()) xfm.widget.set_transform(xfm.transform) return xfm #--------------------------------------------------------------------------- # Scene activation callbacks #--------------------------------------------------------------------------- @on_trait_change('scene_3d.activated') def display_scene_3d(self): self.scene_3d.mlab.view(40, 50) self.scene_3d.scene.renderer.use_depth_peeling = True self.scene_3d.scene.background = (0, 0, 0) # Keep the view always pointing up self.scene_3d.scene.interactor.interactor_style = tvtk.InteractorStyleTerrain( ) self.scene_3d.scene_editor.aligner = self self.opacity = float(options.config.get("mayavi_aligner", "opacity")) self.xfm.widget.enabled = False self.colormap = options.config.get("mayavi_aligner", "colormap") self.disable_render = True for ax in [self.x_axis, self.y_axis, self.z_axis]: ax.update_position() ax.reset_view() self.disable_render = False @on_trait_change('scene_x.activated') def display_scene_x(self): self.x_axis = XAxis(parent=self) @on_trait_change('scene_y.activated') def display_scene_y(self): self.y_axis = YAxis(parent=self) @on_trait_change('scene_z.activated') def display_scene_z(self): self.z_axis = ZAxis(parent=self) #--------------------------------------------------------------------------- # Traits callback #--------------------------------------------------------------------------- def _save_btn_changed(self): if self.save_callback is not None: self.save_callback(self) def _disable_render_changed(self): self.scene_3d.scene.disable_render = self.disable_render def _position_changed(self): self.disable_render = True for ax in [self.x_axis, self.y_axis, self.z_axis]: ax.update_position() self.disable_render = False def _outlines_visible_changed(self): self.disable_render = True for ax in [self.x_axis, self.y_axis, self.z_axis]: ax.toggle_outline() self.disable_render = False @on_trait_change("colormap, fliplut") def update_colormap(self): for ax in [self.x_axis, self.y_axis, self.z_axis]: if ax.ipw_3d and ax.ipw: ax.ipw_3d.parent.scalar_lut_manager.set( lut_mode=self.colormap, reverse_lut=self.fliplut) ax.ipw.parent.scalar_lut_manager.set(lut_mode=self.colormap, reverse_lut=self.fliplut) def _opacity_changed(self): self.surf.actor.property.opacity = self.opacity @on_trait_change("brightness,contrast") def update_brightness(self): self.epi_src.scalar_data = (self.epi * self.contrast) + self.brightness @on_trait_change("flip_ud") def update_flipud(self): #self.epi_src.scalar_data = self.epi_src.scalar_data[:,:,::-1] flip = np.eye(4) flip[2, 2] = -1 mat = self.xfm.transform.matrix.to_array() self.set_xfm(np.dot(mat, flip), "base") @on_trait_change("flip_lr") def update_fliplr(self): #self.epi_src.scalar_data = self.epi_src.scalar_data[::-1] flip = np.eye(4) flip[0, 0] = -1 mat = self.xfm.transform.matrix.to_array() self.set_xfm(np.dot(mat, flip), "base") @on_trait_change("flip_fb") def update_flipfb(self): #self.epi_src.scalar_data = self.epi_src.scalar_data[:,::-1] flip = np.eye(4) flip[1, 1] = -1 mat = self.xfm.transform.matrix.to_array() self.set_xfm(np.dot(mat, flip), "base") @on_trait_change("epi_filter, filter_strength") def update_epifilter(self): if self.epi_filter is None: self.epi = self.epi_orig.copy() elif self.epi_filter == "median": fstr = np.floor(self.filter_strength / 2) * 2 + 1 self.epi = volume.detrend_median(self.epi_orig.T, fstr).T elif self.epi_filter == "gradient": self.epi = volume.detrend_gradient(self.epi_orig.T, self.filter_strength).T self.update_brightness() def update_slabs(self, *args, **kwargs): self.disable_render = True for ax in [self.x_axis, self.y_axis, self.z_axis]: ax.update_slab() self.disable_render = False def get_xfm(self, xfmtype="magnet"): if xfmtype in ["anat->epicoord", "coord"]: ibase = np.linalg.inv(self.base) xfm = self.xfm.transform.matrix.to_array() return np.dot(ibase, xfm) elif xfmtype in ["anat->epibase", "base"]: return self.xfm.transform.matrix.to_array() elif xfmtype in ['anat->magnet', "magnet"]: ibase = np.linalg.inv(self.base) xfm = self.xfm.transform.matrix.to_array() return np.dot(self.affine, np.dot(ibase, xfm)) def set_xfm(self, matrix, xfmtype='magnet'): assert xfmtype in "magnet coord base".split(), "Unknown transform type" if xfmtype == "coord": matrix = np.dot(self.base, matrix) elif xfmtype == "magnet": iaff = np.linalg.inv(self.affine) matrix = np.dot(self.base, np.dot(iaff, matrix)) self.xfm.transform.set_matrix(matrix.ravel()) self.xfm.widget.set_transform(self.xfm.transform) self.xfm.update_pipeline() self.update_slabs() def undo(self): if len(self._undolist) > 0: self.xfm.transform.set_matrix(self._undolist[-1].ravel()) self.xfm.widget.set_transform(self.xfm.transform) self.xfm.update_pipeline() self.update_slabs() self._redo = self._undolist.pop() #--------------------------------------------------------------------------- # The layout of the dialog created #--------------------------------------------------------------------------- view = View(HGroup( Group( Item('scene_y', editor=SceneEditor(scene_class=FlatScene)), Item('scene_z', editor=SceneEditor(scene_class=FlatScene)), show_labels=False, ), Group( Item('scene_x', editor=SceneEditor(scene_class=FlatScene)), Item('scene_3d', editor=SceneEditor(scene_class=ThreeDScene)), show_labels=False, ), Group(Group( Item("save_btn", show_label=False, visible_when="save_callback is not None"), "brightness", "contrast", "epi_filter", Item('filter_strength', visible_when="epi_filter is not None"), "_", "opacity", "_", Item('colormap', editor=ImageEnumEditor(values=lut_manager.lut_mode_list(), cols=6, path=lut_manager.lut_image_dir)), "fliplut", "_", "flip_ud", "flip_lr", "flip_fb", "_", Item('outline_color', editor=ColorEditor()), 'outline_rep', 'line_width', 'point_size', '_', ), Group( Item('legend', editor=TextEditor(), style='readonly', show_label=False, emphasized=True, dock='vertical'), show_labels=False, ), orientation='vertical'), ), resizable=True, title='Aligner')
class CoordinateBox(HasTraits): """ Represents a box in screen space, and provides convenience properties to access bounds and coordinates in a variety of ways. Primary attributes (not properties): position : [x, y] bounds : [width, height] Secondary attributes (properties): x, y : coordinates of the lower-left pixel of the box x2, y2 : coordinates of the upper-right pixel of the box width : the number of horizontal pixels in the box; equal to x2-x+1 height : the number of vertical pixels in the box; equal to y2-y+1 Note that setting x and y will modify the position, but setting any of the other secondary attributes will modify the bounds of the box. """ bounds = bounds_trait # The position relative to the container. If container is None, then # position will be set to (0,0). position = coordinate_trait x = Property y = Property x2 = Property y2 = Property width = Property height = Property # ------------------------------------------------------------------------ # Constraints-based layout # ------------------------------------------------------------------------ if ENABLE_CONSTRAINTS: # A read-only symbolic object that represents the left boundary of # the component left = Property(fget=get_from_constraints_namespace) # A read-only symbolic object that represents the right boundary # of the component right = Property(fget=get_from_constraints_namespace) # A read-only symbolic object that represents the bottom boundary # of the component bottom = Property(fget=get_from_constraints_namespace) # A read-only symbolic object that represents the top boundary of # the component top = Property(fget=get_from_constraints_namespace) # A read-only symbolic object that represents the width of the # component layout_width = Property(fget=get_from_constraints_namespace) # A read-only symbolic object that represents the height of the # component layout_height = Property(fget=get_from_constraints_namespace) # A read-only symbolic object that represents the vertical center # of the component v_center = Property(fget=get_from_constraints_namespace) # A read-only symbolic object that represents the horizontal # center of the component h_center = Property(fget=get_from_constraints_namespace) # A size hint for the layout layout_size_hint = Tuple(0.0, 0.0) # How strongly a layout box hugs it's width hint. hug_width = ConstraintPolicyEnum("weak") # How strongly a layout box hugs it's height hint. hug_height = ConstraintPolicyEnum("weak") # How strongly a layout box resists clipping its contents. resist_width = ConstraintPolicyEnum("strong") # How strongly a layout box resists clipping its contents. resist_height = ConstraintPolicyEnum("strong") # A namespace containing the constraints for this CoordinateBox _constraints_vars = Instance(ConstraintsNamespace) # The list of hard constraints which must be applied to the object. _hard_constraints = Property # The list of size constraints to apply to the object. _size_constraints = Property # ------------------------------------------------------------------------ # Public methods # ------------------------------------------------------------------------ def is_in(self, x, y): "Returns if the point x,y is in the box" p = self.position b = self.bounds dx = x - p[0] dy = y - p[1] return (dx >= 0) and (dx < b[0]) and (dy >= 0) and (dy < b[1]) def as_coordinates(self): "Returns a 4-tuple (x, y, x2, y2)" p = self.position b = self.bounds return (p[0], p[1], p[0] + b[0] - 1, p[1] + b[1] - 1) # ------------------------------------------------------------------------ # Property setters and getters # ------------------------------------------------------------------------ def _get_x(self): return self.position[0] def _set_x(self, val): self.position[0] = val def _get_y(self): return self.position[1] def _set_y(self, val): self.position[1] = val def _get_width(self): return self.bounds[0] def _set_width(self, val): if isinstance(val, str): try: val = float(val) except ValueError: pass old_value = self.bounds[0] self.bounds[0] = val self.trait_property_changed("width", old_value, val) def _get_height(self): return self.bounds[1] def _set_height(self, val): if isinstance(val, str): try: val = float(val) except ValueError: pass old_value = self.bounds[1] self.bounds[1] = val self.trait_property_changed("height", old_value, val) def _get_x2(self): if self.bounds[0] == 0: return self.position[0] return self.position[0] + self.bounds[0] - 1 def _set_x2(self, val): self.position[0] = val - self.bounds[0] + 1 def _old_set_x2(self, val): new_width = val - self.position[0] + 1 if new_width < 0.0: raise RuntimeError("Attempted to set negative component width.") else: self.bounds[0] = new_width def _get_y2(self): if self.bounds[1] == 0: return self.position[1] return self.position[1] + self.bounds[1] - 1 def _set_y2(self, val): self.position[1] = val - self.bounds[1] + 1 def _old_set_y2(self, val): new_height = val - self.position[1] + 1 if new_height < 0.0: raise RuntimeError("Attempted to set negative component height.") else: self.bounds[1] = new_height if ENABLE_CONSTRAINTS: def __constraints_vars_default(self): obj_name = self.id if hasattr(self, "id") else "" cns_names = ConstraintsNamespace(type(self).__name__, obj_name) add_symbolic_constraints(cns_names) return cns_names def _get__hard_constraints(self): """ Generate the constraints which must always be applied. """ left = self.left bottom = self.bottom width = self.layout_width height = self.layout_height cns = [left >= 0, bottom >= 0, width >= 0, height >= 0] return cns def _get__size_constraints(self): """ Creates the list of size hint constraints for this box. """ cns = [] push = cns.append width_hint, height_hint = self.layout_size_hint width = self.layout_width height = self.layout_height hug_width, hug_height = self.hug_width, self.hug_height resist_width, resist_height = self.resist_width, self.resist_height if width_hint >= 0: if hug_width != "ignore": cn = (width == width_hint) | hug_width push(cn) if resist_width != "ignore": cn = (width >= width_hint) | resist_width push(cn) if height_hint >= 0: if hug_height != "ignore": cn = (height == height_hint) | hug_height push(cn) if resist_height != "ignore": cn = (height >= height_hint) | resist_height push(cn) return cns
class AdvancedEditorAreaPane(TaskPane, MEditorAreaPane): """ The toolkit-specific implementation of an AdvancedEditorAreaPane. See the IAdvancedEditorAreaPane interface for API documentation. """ #### Private interface #################################################### _main_window_layout = Instance(MainWindowLayout) ########################################################################### # 'TaskPane' interface. ########################################################################### def create(self, parent): """ Create and set the toolkit-specific control that represents the pane. """ self.control = control = EditorAreaWidget(self, parent) self._filter = EditorAreaDropFilter(self) self.control.installEventFilter(self._filter) # Add shortcuts for scrolling through tabs. if sys.platform == 'darwin': next_seq = 'Ctrl+}' prev_seq = 'Ctrl+{' else: next_seq = 'Ctrl+PgDown' prev_seq = 'Ctrl+PgUp' shortcut = QtGui.QShortcut(QtGui.QKeySequence(next_seq), self.control) shortcut.activated.connect(self._next_tab) shortcut = QtGui.QShortcut(QtGui.QKeySequence(prev_seq), self.control) shortcut.activated.connect(self._previous_tab) # Add shortcuts for switching to a specific tab. mod = 'Ctrl+' if sys.platform == 'darwin' else 'Alt+' mapper = QtCore.QSignalMapper(self.control) mapper.mapped.connect(self._activate_tab) for i in xrange(1, 10): sequence = QtGui.QKeySequence(mod + str(i)) shortcut = QtGui.QShortcut(sequence, self.control) shortcut.activated.connect(mapper.map) mapper.setMapping(shortcut, i - 1) def destroy(self): """ Destroy the toolkit-specific control that represents the pane. """ self.control.removeEventFilter(self._filter) self._filter = None for editor in self.editors: editor_widget = editor.control.parent() self.control.destroy_editor_widget(editor_widget) editor.editor_area = None super(AdvancedEditorAreaPane, self).destroy() ########################################################################### # 'IEditorAreaPane' interface. ########################################################################### def activate_editor(self, editor): """ Activates the specified editor in the pane. """ editor_widget = editor.control.parent() editor_widget.setVisible(True) editor_widget.raise_() editor.control.setFocus() self.active_editor = editor def add_editor(self, editor): """ Adds an editor to the pane. """ editor.editor_area = self editor_widget = EditorWidget(editor, self.control) self.control.add_editor_widget(editor_widget) self.editors.append(editor) def remove_editor(self, editor): """ Removes an editor from the pane. """ editor_widget = editor.control.parent() self.editors.remove(editor) self.control.remove_editor_widget(editor_widget) editor.editor_area = None if not self.editors: self.active_editor = None ########################################################################### # 'IAdvancedEditorAreaPane' interface. ########################################################################### def get_layout(self): """ Returns a LayoutItem that reflects the current state of the editors. """ return self._main_window_layout.get_layout_for_area( QtCore.Qt.LeftDockWidgetArea) def set_layout(self, layout): """ Applies a LayoutItem to the editors in the pane. """ if layout is not None: self._main_window_layout.set_layout_for_area( layout, QtCore.Qt.LeftDockWidgetArea) ########################################################################### # Private interface. ########################################################################### def _activate_tab(self, index): """ Activates the tab with the specified index, if there is one. """ widgets = self.control.get_dock_widgets_ordered() if index < len(widgets): self.activate_editor(widgets[index].editor) def _next_tab(self): """ Activate the tab after the currently active tab. """ if self.active_editor: widgets = self.control.get_dock_widgets_ordered() index = widgets.index(self.active_editor.control.parent()) + 1 if index < len(widgets): self.activate_editor(widgets[index].editor) def _previous_tab(self): """ Activate the tab before the currently active tab. """ if self.active_editor: widgets = self.control.get_dock_widgets_ordered() index = widgets.index(self.active_editor.control.parent()) - 1 if index >= 0: self.activate_editor(widgets[index].editor) def _get_label(self, editor): """ Return a tab label for an editor. """ label = editor.name if editor.dirty: label = '*' + label return label #### Trait initializers ################################################### def __main_window_layout_default(self): return EditorAreaMainWindowLayout(editor_area=self) #### Trait change handlers ################################################ @on_trait_change('editors:[dirty, name]') def _update_label(self, editor, name, new): editor.control.parent().update_title() @on_trait_change('editors:tooltip') def _update_tooltip(self, editor, name, new): editor.control.parent().update_tooltip()
class XFETSEval(FETSEval): '''Base class of an eXtended or enriched element. ''' parent_fets_eval = Instance(IFETSEval) n_parents = Int(desc='number of parents included in the evaluation')
Undefined, cached_property, ) from traits.trait_base import not_none, xgetattr, xsetattr from .editor_factory import EditorFactory from .context_value import ContextValue from .undo import UndoItem from .item import Item # Reference to an EditorFactory object factory_trait = Instance(EditorFactory) class Editor(HasPrivateTraits): """ Represents an editing control for an object trait in a Traits-based user interface. """ #: The UI (user interface) this editor is part of: ui = Instance("traitsui.ui.UI", clean_up=True) #: Full name of the object the editor is editing (e.g. #: 'object.link1.link2'): object_name = Str("object") #: The object this editor is editing (e.g. object.link1.link2):
class Editor(HasPrivateTraits): """ Represents an editing control for an object trait in a Traits-based user interface. """ #: The UI (user interface) this editor is part of: ui = Instance("traitsui.ui.UI", clean_up=True) #: Full name of the object the editor is editing (e.g. #: 'object.link1.link2'): object_name = Str("object") #: The object this editor is editing (e.g. object.link1.link2): object = Instance(HasTraits, clean_up=True) #: The name of the trait this editor is editing (e.g. 'value'): name = ReadOnly() #: The context object the editor is editing (e.g. object): context_object = Property() #: The extended name of the object trait being edited. That is, #: 'object_name.name' minus the context object name at the beginning. For #: example: 'link1.link2.value': extended_name = Property() #: Original value of object.name (e.g. object.link1.link2.value): old_value = Any(clean_up=True) #: Text description of the object trait being edited: description = ReadOnly() #: The Item object used to create this editor: item = Instance(Item, (), clean_up=True) #: The GUI widget defined by this editor: control = Any(clean_up=True) #: The GUI label (if any) defined by this editor: label_control = Any(clean_up=True) #: Is the underlying GUI widget enabled? enabled = Bool(True) #: Is the underlying GUI widget visible? visible = Bool(True) #: Is the underlying GUI widget scrollable? scrollable = Bool(False) #: The EditorFactory used to create this editor: factory = Instance(EditorFactory, clean_up=True) #: Is the editor updating the object.name value? updating = Bool(False) #: Current value for object.name: value = Property() #: Current value of object trait as a string: str_value = Property() #: The trait the editor is editing (not its value, but the trait itself): value_trait = Property() #: The current editor invalid state status: invalid = Bool(False) # -- private trait definitions ------------------------------------------ #: A set to track values being updated to prevent infinite recursion. _no_trait_update = Set(Str) #: A list of all values synchronized to. _user_to = List(Tuple(Any, Str, Callable)) #: A list of all values synchronized from. _user_from = List(Tuple(Str, Callable)) # ------------------------------------------------------------------------ # Editor interface # ------------------------------------------------------------------------ # -- Abstract methods --------------------------------------------------- def init(self, parent): """ Create and initialize the underlying toolkit widget. This method must be overriden by subclasses. Implementations must ensure that the :attr:`control` trait is set to an appropriate toolkit object. Parameters ---------- parent : toolkit control The parent toolkit object of the editor's toolkit objects. """ raise NotImplementedError("This method must be overriden.") def update_editor(self): """ Updates the editor when the value changes externally to the editor. This should normally be overridden in a subclass. """ pass def error(self, excp): """ Handles an error that occurs while setting the object's trait value. This should normally be overridden in a subclass. Parameters ---------- excp : Exception The exception which occurred. """ pass def set_focus(self): """ Assigns focus to the editor's underlying toolkit widget. This method must be overriden by subclasses. """ raise NotImplementedError("This method must be overriden.") def set_tooltip_text(self, control, text): """ Sets the tooltip for a toolkit control to the provided text. This method must be overriden by subclasses. Parameters ---------- text : str The text to use for the tooltip. control : toolkit control The toolkit control that is having the tooltip set. """ raise NotImplementedError("This method must be overriden.") def string_value(self, value, format_func=None): """ Returns the text representation of a specified object trait value. This simply delegates to the factory's `string_value` method. Sub-classes may choose to override the default implementation. Parameters ---------- value : any The value being edited. format_func : callable or None A function that takes a value and returns a string. """ return self.factory.string_value(value, format_func) def restore_prefs(self, prefs): """ Restores saved user preference information for the editor. Editors with state may choose to override this. It will only be used if the editor has an `id` value. Parameters ---------- prefs : dict A dictionary of preference values. """ pass def save_prefs(self): """ Returns any user preference information for the editor. Editors with state may choose to override this. It will only be used if the editor has an `id` value. Returns ------- prefs : dict or None A dictionary of preference values, or None if no preferences to be saved. """ return None # -- Editor life-cycle methods ------------------------------------------ def prepare(self, parent): """ Finish setting up the editor. Parameters ---------- parent : toolkit control The parent toolkit object of the editor's toolkit objects. """ name = self.extended_name if name != "None": self.context_object.on_trait_change(self._update_editor, name, dispatch="ui") self.init(parent) self._sync_values() self.update_editor() def dispose(self): """ Disposes of the contents of an editor. This disconnects any synchronised values and resets references to other objects. Subclasses may chose to override this method to perform additional clean-up. """ if self.ui is None: return name = self.extended_name if name != "None": self.context_object.on_trait_change(self._update_editor, name, remove=True) for name, handler in self._user_from: self.on_trait_change(handler, name, remove=True) for object, name, handler in self._user_to: object.on_trait_change(handler, name, remove=True) # Break linkages to references we no longer need: for name in self.trait_names(clean_up=True): setattr(self, name, None) # -- Undo/redo methods -------------------------------------------------- def log_change(self, undo_factory, *undo_args): """ Logs a change made in the editor with undo/redo history. Parameters ---------- undo_factory : callable Callable that creates an undo item. Often self.get_undo_item. *undo_args Any arguments to pass to the undo factory. """ ui = self.ui # Create an undo history entry if we are maintaining a history: undoable = ui._undoable if undoable >= 0: history = ui.history if history is not None: item = undo_factory(*undo_args) if item is not None: if undoable == history.now: # Create a new undo transaction: history.add(item) else: # Extend the most recent undo transaction: history.extend(item) def get_undo_item(self, object, name, old_value, new_value): """ Creates an undo history entry. Can be overridden in a subclass for special value types. Parameters ---------- object : HasTraits instance The object being modified. name : str The name of the trait that is to be changed. old_value : any The original value of the trait. new_value : any The new value of the trait. """ return UndoItem(object=object, name=name, old_value=old_value, new_value=new_value) # -- Trait synchronization code ----------------------------------------- def sync_value( self, user_name, editor_name, mode="both", is_list=False, is_event=False, ): """ Synchronize an editor trait and a user object trait. Also sets the initial value of the editor trait from the user object trait (for modes 'from' and 'both'), and the initial value of the user object trait from the editor trait (for mode 'to'), as long as the relevant traits are not events. Parameters ---------- user_name : str The name of the trait to be used on the user object. If empty, no synchronization will be set up. editor_name : str The name of the relevant editor trait. mode : str, optional; one of 'to', 'from' or 'both' The direction of synchronization. 'from' means that trait changes in the user object should be propagated to the editor. 'to' means that trait changes in the editor should be propagated to the user object. 'both' means changes should be propagated in both directions. The default is 'both'. is_list : bool, optional If true, synchronization for item events will be set up in addition to the synchronization for the object itself. The default is False. is_event : bool, optional If true, this method won't attempt to initialize the user object or editor trait values. The default is False. """ if user_name == "": return key = "%s:%s" % (user_name, editor_name) parts = user_name.split(".") if len(parts) == 1: user_object = self.context_object xuser_name = user_name else: user_object = self.ui.context[parts[0]] xuser_name = ".".join(parts[1:]) user_name = parts[-1] if mode in {"from", "both"}: self._bind_from(key, user_object, xuser_name, editor_name, is_list) if not is_event: # initialize editor value from user value with self.raise_to_debug(): user_value = xgetattr(user_object, xuser_name) setattr(self, editor_name, user_value) if mode in {"to", "both"}: self._bind_to(key, user_object, xuser_name, editor_name, is_list) if mode == "to" and not is_event: # initialize user value from editor value with self.raise_to_debug(): editor_value = xgetattr(self, editor_name) xsetattr(user_object, xuser_name, editor_value) # -- Utility methods ----------------------------------------------------- def parse_extended_name(self, name): """ Extract the object, name and a getter from an extended name Parameters ---------- name : str The extended name to parse. Returns ------- object, name, getter : any, str, callable The object from the context, the (extended) name of the attributes holding the value, and a callable which gets the current value from the context. """ base_name, __, name = name.partition(".") if name: object = self.ui.context[base_name] else: name = base_name object = self.context_object return (object, name, partial(xgetattr, object, name)) def set_tooltip(self, control=None): """ Sets the tooltip for a specified toolkit control. This uses the tooltip_text method to get the text to use. Parameters ---------- control : optional toolkit control The toolkit control that is having the tooltip set. If None then the editor's control is used. Returns ------- tooltip_set : bool Whether or not a tooltip value could be set. """ text = self.tooltip_text() if text is None: return False if control is None: control = self.control self.set_tooltip_text(control, text) return True def tooltip_text(self): """ Get the text for a tooltip, checking various sources. This checks for text from, in order: - the editor's description trait - the base trait's 'tooltip' metadata - the base trait's 'desc' metadata Returns ------- text : str or None The text for the tooltip, or None if no suitable text can be found. """ if self.description: return self.description base_trait = self.object.base_trait(self.name) text = base_trait.tooltip if text is not None: return text text = base_trait.desc if text is not None: return "Specifies " + text return None # -- Utility context managers -------------------------------------------- @contextmanager def no_trait_update(self, name): """ Context manager that blocks updates from the named trait. """ if name in self._no_trait_update: yield return self._no_trait_update.add(name) try: yield finally: self._no_trait_update.remove(name) @contextmanager def raise_to_debug(self): """ Context manager that uses raise to debug to raise exceptions. """ try: yield except Exception: from traitsui.api import raise_to_debug raise_to_debug() @contextmanager def updating_value(self): """ Context manager to handle updating value. """ if self.updating: yield return self.updating = True try: yield finally: self.updating = False # ------------------------------------------------------------------------ # object interface # ------------------------------------------------------------------------ def __init__(self, parent, **traits): """ Initializes the editor object. """ super(HasPrivateTraits, self).__init__(**traits) try: self.old_value = getattr(self.object, self.name) except AttributeError: ctrait = self.object.base_trait(self.name) if ctrait.type == "event" or self.name == "spring": # Getting the attribute will fail for 'Event' traits: self.old_value = Undefined else: raise # Synchronize the application invalid state status with the editor's: self.sync_value(self.factory.invalid, "invalid", "from") # ------------------------------------------------------------------------ # private methods # ------------------------------------------------------------------------ def _update_editor(self, object, name, old_value, new_value): """ Performs updates when the object trait changes. This is designed to be used as a trait listener. """ # If background threads have modified the trait the editor is bound to, # their trait notifications are queued to the UI thread. It is possible # that by the time the UI thread dispatches these events, the UI the # editor is part of has already been closed. So we need to check if we # are still bound to a live UI, and if not, exit immediately: if self.ui is None: return # If the notification is for an object different than the one actually # being edited, it is due to editing an item of the form: # object.link1.link2.name, where one of the 'link' objects may have # been modified. In this case, we need to rebind the current object # being edited: if object is not self.object: self.object = self.ui.get_extended_value(self.object_name) # If the editor has gone away for some reason, disconnect and exit: if self.control is None: self.context_object.on_trait_change(self._update_editor, self.extended_name, remove=True) return # Log the change that was made (as long as the Item is not readonly # or it is not for an event): if (self.item.style != "readonly" and object.base_trait(name).type != "event"): # Indicate that the contents of the UI have been changed: self.ui.modified = True if self.updating: self.log_change(self.get_undo_item, object, name, old_value, new_value) # If the change was not caused by the editor itself: if not self.updating: # Update the editor control to reflect the current object state: self.update_editor() def _sync_values(self): """ Initialize and synchronize editor and factory traits Initializes and synchronizes (as needed) editor traits with the value of corresponding factory traits. The name of the factory trait and the editor trait must match and the factory trait needs to have ``sync_value`` metadata set. The strategy followed is: - for each factory trait with ``sync_value`` metadata: 1. if the value is a :class:`ContextValue` instance then call :meth:`sync_value` with the ``name`` from the context value. 2. if the trait has ``sync_name`` metadata, look at the referenced trait value and if it is a non-empty string then use this value as the name of the value in the context. 3. otherwise initialize the current value of the factory trait to the corresponding value of the editor. - synchronization mode in cases 1 and 2 is taken from the ``sync_value`` metadata of the editor trait first and then the ``sync_value`` metadata of the factory trait if that is empty. - if the value is a container type, then the `is_list` metadata is set to """ factory = self.factory for name, trait in factory.traits(sync_value=not_none).items(): value = getattr(factory, name) self_trait = self.trait(name) if self_trait.sync_value: mode = self_trait.sync_value else: mode = trait.sync_value if isinstance(value, ContextValue): self.sync_value( value.name, name, mode, bool(self_trait.is_list), self_trait.type == "event", ) elif (trait.sync_name is not None and getattr(factory, trait.sync_name, "") != ""): # Note: this is implemented as a stepping stone from things # like ``low_name`` and ``high_name`` to using context values. sync_name = getattr(factory, trait.sync_name) self.sync_value( sync_name, name, mode, bool(self_trait.is_list), self_trait.type == "event", ) elif value is not Undefined: setattr(self, name, value) def _bind_from(self, key, user_object, xuser_name, editor_name, is_list): """ Bind trait change handlers from a user object to the editor. Parameters ---------- key : str The key to use to guard against recursive updates. user_object : object The object in the TraitsUI context that is being bound. xuser_name: : str The extended name of the trait to be used on the user object. editor_name : str The name of the relevant editor trait. is_list : bool, optional If true, synchronization for item events will be set up in addition to the synchronization for the object itself. The default is False. """ def user_trait_modified(new): if key not in self._no_trait_update: with self.no_trait_update(key), self.raise_to_debug(): xsetattr(self, editor_name, new) user_object.on_trait_change(user_trait_modified, xuser_name) self._user_to.append((user_object, xuser_name, user_trait_modified)) if is_list: def user_list_modified(event): if (isinstance(event, TraitListEvent) and key not in self._no_trait_update): with self.no_trait_update(key), self.raise_to_debug(): n = event.index getattr(self, editor_name)[n:n + len(event.removed)] = event.added items = xuser_name + "_items" user_object.on_trait_change(user_list_modified, items) self._user_to.append((user_object, items, user_list_modified)) def _bind_to(self, key, user_object, xuser_name, editor_name, is_list): """ Bind trait change handlers from a user object to the editor. Parameters ---------- key : str The key to use to guard against recursive updates. user_object : object The object in the TraitsUI context that is being bound. xuser_name: : str The extended name of the trait to be used on the user object. editor_name : str The name of the relevant editor trait. is_list : bool, optional If true, synchronization for item events will be set up in addition to the synchronization for the object itself. The default is False. """ def editor_trait_modified(new): if key not in self._no_trait_update: with self.no_trait_update(key), self.raise_to_debug(): xsetattr(user_object, xuser_name, new) self.on_trait_change(editor_trait_modified, editor_name) self._user_from.append((editor_name, editor_trait_modified)) if is_list: def editor_list_modified(event): if key not in self._no_trait_update: with self.no_trait_update(key), self.raise_to_debug(): n = event.index value = xgetattr(user_object, xuser_name) value[n:n + len(event.removed)] = event.added self.on_trait_change(editor_list_modified, editor_name + "_items") self._user_from.append( (editor_name + "_items", editor_list_modified)) def __set_value(self, value): """ Set the value of the trait the editor is editing. This calls the appropriate setattr method on the handler to perform the actual change. """ with self.updating_value(): try: handler = self.ui.handler obj_name = self.object_name name = self.name method = (getattr(handler, "%s_%s_setattr" % (obj_name, name), None) or getattr(handler, "%s_setattr" % name, None) or getattr(handler, "setattr")) method(self.ui.info, self.object, name, value) except TraitError as excp: self.error(excp) raise # -- Traits property getters and setters -------------------------------- @cached_property def _get_context_object(self): """ Returns the context object the editor is using In some cases a proxy object is edited rather than an object directly in the context, in which case we return ``self.object``. """ object_name = self.object_name context_key = object_name.split(".", 1)[0] if (object_name != "") and (context_key in self.ui.context): return self.ui.context[context_key] # This handles the case of a 'ListItemProxy', which is not in the # ui.context, but is the editor 'object': return self.object @cached_property def _get_extended_name(self): """ Returns the extended trait name being edited. """ return ("%s.%s" % (self.object_name, self.name)).split(".", 1)[1] def _get_value_trait(self): """ Returns the trait the editor is editing (Property implementation). """ return self.object.trait(self.name) def _get_value(self): """ Returns the value of the trait the editor is editing. """ return getattr(self.object, self.name, Undefined) def _set_value(self, value): """ Set the value of the trait the editor is editing. Dispatches via the TraitsUI Undo/Redo mechanisms to make change reversible, if desired. """ if self.ui and self.name != "None": self.ui.do_undoable(self.__set_value, value) def _get_str_value(self): """ Returns the text representation of the object trait. """ return self.string_value(getattr(self.object, self.name, Undefined))
class RTraceDomainList(HasTraits): label = Str('RTraceDomainField') sd = WeakRef(ISDomain) position = Enum('nodes', 'int_pnts') subfields = List def redraw(self): '''Delegate the calculation to the pipeline ''' # self.mvp_mgrid_geo.redraw() # 'label_scalars') self.mvp_mgrid_geo.rebuild_pipeline(self.vtk_node_structure) vtk_node_structure = Property(Instance(tvtk.UnstructuredGrid)) # @cached_property def _get_vtk_node_structure(self): self.position = 'nodes' return self.vtk_structure vtk_ip_structure = Property(Instance(tvtk.UnstructuredGrid)) # @cached_property def _get_vtk_ip_structure(self): self.position = 'int_pnts' return self.vtk_structure vtk_structure = Property(Instance(tvtk.UnstructuredGrid)) def _get_vtk_structure(self): ug = tvtk.UnstructuredGrid() cell_array, cell_offsets, cell_types = self.vtk_cell_data n_cells = cell_types.shape[0] ug.points = self.vtk_X vtk_cell_array = tvtk.CellArray() vtk_cell_array.set_cells(n_cells, cell_array) ug.set_cells(cell_types, cell_offsets, vtk_cell_array) return ug vtk_X = Property def _get_vtk_X(self): point_arr_list = [] for sf in self.subfields: if sf.skip_domain: continue sf.position = self.position sf_vtk_X = sf.vtk_X if sf_vtk_X.shape[0] == 0: # all elem are deactivated continue point_arr_list.append(sf_vtk_X) if len(point_arr_list) > 0: # print 'point_arr_list ', point_arr_list return vstack(point_arr_list) else: return zeros((0, 3), dtype='float_') # point offset to use when more fields are patched together within # RTDomainList point_offset = Int(0) # cell offset to use when more fields are patched together within # RTDomainList cell_offset = Int(0) vtk_cell_data = Property def _get_vtk_cell_data(self): cell_array_list = [] cell_offset_list = [] cell_types_list = [] point_offset = self.point_offset cell_offset = self.cell_offset for sf in self.subfields: if sf.skip_domain: continue sf.position = self.position sf.point_offset = point_offset sf.cell_offset = cell_offset cell_array, cell_offsets, cell_types = sf.vtk_cell_data cell_array_list.append(cell_array) cell_offset_list.append(cell_offsets) cell_types_list.append(cell_types) point_offset += sf.n_points cell_offset += cell_array.shape[0] if len(cell_array_list) > 0: cell_array = hstack(cell_array_list) cell_offsets = hstack(cell_offset_list) cell_types = hstack(cell_types_list) else: cell_array = array([], dtype='int_') cell_offsets = array([], dtype='int_') cell_types = array([], dtype='int_') return (cell_array, cell_offsets, cell_types) #------------------------------------------------------------------------- # Visualization pipelines #------------------------------------------------------------------------- mvp_mgrid_geo = Trait(MVUnstructuredGrid) # def _mvp_mgrid_geo_default(self): # return MVUnstructuredGrid( name = 'Response tracer mesh', # points = self.vtk_r, # cell_data = self.vtk_cell_data, # ) def _mvp_mgrid_geo_default(self): return MVUnstructuredGrid(name='Response tracer mesh', warp=False, warp_var='') view = View(resizable=True)
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 MultithreadingRouter(HasRequiredTraits): """ Implementation of the IMessageRouter interface for the case where the sender will be in a background thread. Parameters ---------- event_loop : IEventLoop The event loop used to trigger message dispatch. """ def start(self): """ Start routing messages. This method must be called before any call to ``pipe`` or ``close_pipe`` can be made. Not thread-safe. Must always be called in the main thread. Raises ------ RuntimeError If the router has already been started. """ if self._running: raise RuntimeError("router is already running") self._message_queue = queue.Queue() self._link_to_event_loop() self._running = True logger.debug(f"{self} started") def stop(self): """ Stop routing messages. This method should be called in the main thread after all pipes are finished with. Calls to ``pipe`` or ``close_pipe`` are not permitted after this method has been called. Logs a warning if there are unclosed pipes. Not thread safe. Must always be called in the main thread. Raises ------ RuntimeError If the router is not running. """ if not self._running: raise RuntimeError("router is not running") if self._receivers: logger.warning(f"{self} has {len(self._receivers)} unclosed pipes") self._unlink_from_event_loop() self._message_queue = None self._running = False logger.debug(f"{self} stopped") def pipe(self): """ Create a (sender, receiver) pair for sending and receiving messages. The sender will be passed to the background task and used to send messages, while the receiver remains in the foreground. Not thread safe. Must always be called in the main thread. Returns ------- sender : MultithreadingSender Object to be passed to the background task. receiver : MultithreadingReceiver Object kept in the foreground, which reacts to messages. Raises ------ RuntimeError If the router is not currently running. """ if not self._running: raise RuntimeError("router is not running") connection_id = next(self._connection_ids) sender = MultithreadingSender( connection_id=connection_id, pingee=self._pingee, message_queue=self._message_queue, ) receiver = MultithreadingReceiver(connection_id=connection_id) self._receivers[connection_id] = receiver logger.debug( f"{self} created pipe #{connection_id} with receiver {receiver}") return sender, receiver def close_pipe(self, receiver): """ Close the receiver end of a pipe produced by ``pipe``. Removes the receiver from the routing table, so that no new messages can reach that receiver. Not thread safe. Must always be called in the main thread. Parameters ---------- receiver : MultithreadingReceiver Receiver half of the pair returned by the ``pipe`` method. Raises ------ RuntimeError If the router is not currently running. """ if not self._running: raise RuntimeError("router is not running") connection_id = receiver.connection_id self._receivers.pop(connection_id) logger.debug( f"{self} closed pipe #{connection_id} with receiver {receiver}") def route_until(self, condition, timeout=None): """ Manually drive the router until a given condition occurs, or timeout. This is primarily used as part of a clean shutdown. Note: this has the side-effect of moving the router from "event loop" mode to "manual" mode. This mode switch is permanent, in the sense that after this point, the router will no longer respond to pings: any messages will need to be processed through this function. Parameters ---------- condition Zero-argument callable returning a boolean. When this condition becomes true, this method will stop routing messages. If the condition is already true on entry, no messages will be routed. timeout : float, optional Maximum number of seconds to route messages for. Raises ------ RuntimeError If the condition did not become true before timeout. """ self._unlink_from_event_loop() if timeout is None: while not condition(): self._route_message(block=True) else: end_time = time.monotonic() + timeout while not condition(): try: time_remaining = end_time - time.monotonic() self._route_message(block=True, timeout=time_remaining) except queue.Empty: raise RuntimeError("Timed out waiting for messages") # Public traits ########################################################### #: The event loop used to trigger message dispatch. event_loop = Instance(IEventLoop, required=True) # Private traits ########################################################## #: Internal queue for messages from all senders. _message_queue = Instance(queue.Queue) #: Source of new connection ids. _connection_ids = Instance(collections.abc.Iterator) #: Receivers, keyed by connection_id. _receivers = Dict(Int(), Instance(MultithreadingReceiver)) #: Receiver for the "message_sent" signal. _pingee = Instance(IPingee) #: Bool keeping track of whether we're linked to the event loop #: or not. _linked = Bool(False) #: Router status: True if running, False if stopped. _running = Bool(False) # Private methods ######################################################### def _link_to_event_loop(self): """ Link this router to the event loop. """ if self._linked: # Raise, because lifetime management of self._pingee is delicate, # so if we ever get here then something likely needs fixing. raise RuntimeError("Already linked to the event loop") self._pingee = self.event_loop.pingee(on_ping=self._route_message) self._pingee.connect() self._linked = True def _unlink_from_event_loop(self): """ Unlink this router from the event loop, if it's linked. After this call, the router will no longer react to any pending tasks on the event loop. """ if self._linked: # Note: it might be tempting to set self._pingee to None at this # point, and to use the None-ness (or not) of self._pingee to avoid # needing self._linked. But it's important not to do so: we need to # be sure that the main thread reference to the Pingee outlives any # reference on background threads. Otherwise we end up collection a # Qt object (the Pingee) on a thread other than the one it was # created on, and that's unsafe in general. self._pingee.disconnect() self._linked = False def _route_message(self, *, block=False, timeout=None): """ Get and dispatch a message from the local message queue. Parameters ---------- block : bool, optional If True, block until either a message arrives or until timeout. If False (the default), we expect a message to already be present in the queue. timeout : float, optional Maximum time to wait for a message to arrive. If no timeout is given and ``block`` is True, wait indefinitely. If ``block`` is False, this parameter is ignored. Raises ------ queue.Empty If no message arrives within the given timeout. """ connection_id, message = self._message_queue.get( block=block, timeout=None if timeout is None else max(timeout, 0.0), ) try: receiver = self._receivers[connection_id] except KeyError: logger.warning( f"{self} discarding message from closed pipe #{connection_id}." ) else: receiver.message = message def __connection_ids_default(self): return itertools.count()
class MainGUI(HasTraits): """ MainGUI is the main class under which the Model-View-Control (MVC) model is defined """ camera_list = List imgplt_flag = 0 pass_init = Bool(False) update_thread_plot = Bool(False) tr_thread = Instance(TrackThread) selected = Any # Defines GUI view -------------------------- view = View( Group(Group(Item(name='exp1', editor=tree_editor_exp, show_label=False, width=-400, resizable=False), Item('camera_list', style='custom', editor=ListEditor(use_notebook=True, deletable=False, dock_style='tab', page_name='.name', selected='selected'), show_label=False), orientation='horizontal', show_left=False), orientation='vertical'), title='pyPTV', id='main_view', width=1., height=1., resizable=True, handler=TreeMenuHandler(), # <== Handler class is attached menubar=menu_bar) def _selected_changed(self): self.current_camera = int(self.selected.name.split(' ')[1]) - 1 #--------------------------------------------------- # Constructor and Chaco windows initialization #--------------------------------------------------- def __init__(self): super(MainGUI, self).__init__() colors = ['yellow', 'green', 'red', 'blue'] self.exp1 = Experiment() self.exp1.populate_runs(exp_path) self.plugins = Plugins() self.n_camera = self.exp1.active_params.m_params.Num_Cam print self.n_camera self.orig_image = [] self.hp_image = [] self.current_camera = 0 self.camera_list = [] for i in range(self.n_camera): self.camera_list.append(CameraWindow(colors[i])) self.camera_list[i].name = "Camera " + str(i + 1) self.camera_list[i].on_trait_change(self.right_click_process, 'rclicked') self.orig_image.append(np.array([], dtype=np.ubyte)) self.hp_image.append(np.array([])) ptv.py_init_proc_c() #intialization of globals in ptv C module #------------------------------------------------------ def right_click_process(self): x_clicked, y_clicked, n_camera = 0, 0, 0 h_img = self.exp1.active_params.m_params.imx v_img = self.exp1.active_params.m_params.imy print h_img, v_img for i in range(len(self.camera_list)): n_camera = i x_clicked,y_clicked=self.camera_list[i]._click_tool.x,\ self.camera_list[i]._click_tool.y x1, y1, x2, y2, x1_points, y1_points, intx1, inty1 = ptv.py_right_click( x_clicked, y_clicked, n_camera) if (x1 != -1 and y1 != -1): self.camera_list[n_camera].right_p_x0.append(intx1) self.camera_list[n_camera].right_p_y0.append(inty1) self.camera_list[n_camera].drawcross("right_p_x0","right_p_y0", self.camera_list[n_camera].right_p_x0\ ,self.camera_list[n_camera].right_p_y0,"cyan",3,marker1="circle") self.camera_list[n_camera]._plot.request_redraw() print "right click process" print x1, y1, x2, y2, x1_points, y1_points color_camera = ['yellow', 'red', 'blue', 'green'] #print [x1[i]],[y1[i]],[x2[i]],[y2[i]] for j in range(len(self.camera_list)): if j is not n_camera: count = self.camera_list[i]._plot.plots.keys() self.camera_list[j].drawline("right_cl_x"+str(len(count)),\ "right_cl_y"+str(len(count)),x1[j],y1[j],x2[j],y2[j],\ color_camera[n_camera]) self.camera_list[ j]._plot.index_mapper.range.set_bounds(0, h_img) self.camera_list[ j]._plot.value_mapper.range.set_bounds(0, v_img) self.camera_list[j].drawcross("right_p_x1","right_p_y1",x1_points[j],y1_points[j],\ color_camera[n_camera],2) self.camera_list[j]._plot.request_redraw() else: print("No nearby points for epipolar lines") self.camera_list[i].rclicked = 0 def update_plots(self, images, is_float=False): for i in range(len(images)): self.camera_list[i].update_image(images[i], is_float) self.camera_list[i]._plot.request_redraw() # set_images sets ptv's C module img[] array def set_images(self, images): for i in range(len(images)): ptv.py_set_img(images[i], i) def get_images(self, plot_index, images): for i in plot_index: ptv.py_get_img(images[i], i) def drawcross(self, str_x, str_y, x, y, color1, size1): for i in range(len(self.camera_list)): self.camera_list[i].drawcross(str_x, str_y, x[i], y[i], color1, size1) self.camera_list[i]._plot.request_redraw() def clear_plots(self, remove_background=True): # this function deletes all plotes except basic image plot if not remove_background: index = 'plot0' else: index = None for i in range(len(self.camera_list)): plot_list = self.camera_list[i]._plot.plots.keys() #if not remove_background: # index=None try: plot_list.remove(index) except: pass self.camera_list[i]._plot.delplot(*plot_list[0:]) self.camera_list[i]._plot.tools = [] self.camera_list[i]._plot.request_redraw() for j in range(len(self.camera_list[i]._quiverplots)): self.camera_list[i]._plot.remove( self.camera_list[i]._quiverplots[j]) self.camera_list[i]._quiverplots = [] self.camera_list[i].right_p_x0 = [] self.camera_list[i].right_p_y0 = [] self.camera_list[i].right_p_x1 = [] self.camera_list[i].right_p_y1 = [] def _update_thread_plot_changed(self): n_camera = len(self.camera_list) if self.update_thread_plot and self.tr_thread: print "updating plots..\n" step = self.tr_thread.track_step x0,x1,x2,y0,y1,y2,pnr1,pnr2,pnr3,m_tr=\ self.tr_thread.intx0,self.tr_thread.intx1,self.tr_thread.intx2,\ self.tr_thread.inty0,self.tr_thread.inty1,self.tr_thread.inty2,self.tr_thread.pnr1,\ self.tr_thread.pnr2,self.tr_thread.pnr3,self.tr_thread.m_tr for i in range(n_camera): self.camera_list[i].drawcross( str(step) + "x0", str(step) + "y0", x0[i], y0[i], "green", 2) self.camera_list[i].drawcross( str(step) + "x1", str(step) + "y1", x1[i], y1[i], "yellow", 2) self.camera_list[i].drawcross( str(step) + "x2", str(step) + "y2", x2[i], y2[i], "white", 2) self.camera_list[i].drawquiver(x0[i], y0[i], x1[i], y1[i], "orange") self.camera_list[i].drawquiver(x1[i], y1[i], x2[i], y2[i], "white") ## for j in range (m_tr): ## str_plt=str(step)+"_"+str(j) ## ## self.camera_list[i].drawline\ ## (str_plt+"vec_x0",str_plt+"vec_y0",x0[i][j],y0[i][j],x1[i][j],y1[i][j],"orange") ## self.camera_list[i].drawline\ ## (str_plt+"vec_x1",str_plt+"vec_y1",x1[i][j],y1[i][j],x2[i][j],y2[i][j],"white") self.load_set_seq_image(step, update_all=False, display_only=True) self.camera_list[self.current_camera]._plot.request_redraw() time.sleep(0.1) self.tr_thread.can_continue = True self.update_thread_plot = False def load_set_seq_image(self, seq, update_all=True, display_only=False): n_camera = len(self.camera_list) if not hasattr(self, 'base_name'): self.base_name = [] for i in range(n_camera): exec( "self.base_name.append(self.exp1.active_params.m_params.Basename_%d_Seq)" % (i + 1)) print self.base_name[i] i = seq seq_ch = "%04d" % i if not update_all: j = self.current_camera img_name = self.base_name[j] + seq_ch self.load_disp_image(img_name, j, display_only) else: for j in range(n_camera): img_name = self.base_name[j] + seq_ch self.load_disp_image(img_name, j, display_only) def load_disp_image(self, img_name, j, display_only=False): print("Setting image: %s" % str(img_name)) try: temp_img = img_as_ubyte(imread(img_name)) except: print("Error reading file, setting zero image") h_img = self.exp1.active_params.m_params.imx v_img = self.exp1.active_params.m_params.imy temp_img = img_as_ubyte(np.zeros((h_img, v_img))) if not display_only: ptv.py_set_img(temp_img, j) if len(temp_img) > 0: self.camera_list[j].update_image(temp_img)
class FFTDemo(HasTraits): select = Enum([cls.__name__ for cls in BaseWave.__subclasses__()]) wave = Instance(BaseWave) # FFT计算所使用的取样点数,这里用一个Enum类型的属性以供用户从列表中选择 fftsize = Enum(256, [(2**x) for x in range(6, 12)]) # 用于显示FFT的结果 peak_list = Str # 采用多少个频率合成形 N = Range(1, 40, 4) figure = Instance(Figure, ()) view = View( HSplit( VSplit( VGroup( Item("select", label=u"波形类型"), Item("wave", style="custom", label=u"波形属性"), Item("fftsize", label=u"FFT点数"), Item("N", label=u"合成波频率数"), show_labels=True, ), Item("peak_list", style="custom", show_label=False, width=100, height=250)), VGroup(Item("figure", editor=MPLFigureEditor(toolbar=True)), show_labels=False), ), title=u"FFT演示", resizable=True, width=1280, height=600, ) def __init__(self, **traits): super(FFTDemo, self).__init__(**traits) self.ax1 = self.figure.add_subplot(211) self.ax2 = self.figure.add_subplot(212) self.ax1.set_title("FFT") self.ax2.set_title("Wave") self.line_peaks, = self.ax1.plot([0], [0], "o") self.line_wave, = self.ax2.plot([0], [0]) self.line_wave2, = self.ax2.plot([0], [0]) self._select_changed() def _select_changed(self): klass = globals()[self.select] self.wave = klass() @on_trait_change("wave, wave.[], fftsize") def draw(self): fftsize = self.fftsize x_data = np.arange(0, 1.0, 1.0 / self.fftsize) func = self.wave.get_func() # 将func函数的返回值强制转换成float64 y_data = func(x_data).astype(float) # 计算频谱 fft_parameters = np.fft.fft(y_data) / len(y_data) self.fft_parameters = fft_parameters # 计算各个频率的振幅 fft_data = np.clip( 20 * np.log10(np.abs(fft_parameters))[:fftsize / 2 + 1], -120, 120) self.line_peaks.set_data(np.arange(0, len(fft_data)), fft_data) # x坐标为频率编号 self.line_wave.set_data(np.arange(0, self.fftsize), y_data) # 合成波的x坐标为取样点,显示2个周期 self.line_wave2.set_xdata(np.arange(0, 2 * self.fftsize)) # 将振幅大于-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("%02d : %g" % (f, v)) self.peak_list = "\n".join(result) self.ax1.relim() self.ax1.autoscale_view() self.plot_sin_combine() @on_trait_change("N") def plot_sin_combine(self): index, data = fft_combine(self.fft_parameters, self.N, 2) self.line_wave2.set_ydata(data) self.ax2.relim() self.ax2.autoscale_view() if self.figure.canvas is not None: self.figure.canvas.draw()
class Axis(HasTraits): axis = Int parent = Instance('Align') invert = Bool(value=False) ipw_3d = Instance(PipelineBase) ipw = Instance(PipelineBase) cursor = Instance(Module) surf = Instance(PipelineBase) outline = Instance(PipelineBase) slab = Instance(tvtk.ClipPolyData) handle = Instance(RotationWidget) planes = List scene_3d = DelegatesTo('parent') position = DelegatesTo('parent') disable_render = DelegatesTo('parent') xfm = DelegatesTo('parent') outline_color = DelegatesTo('parent') outline_rep = DelegatesTo('parent') line_width = DelegatesTo('parent') point_size = DelegatesTo('parent') def __init__(self, **kwargs): super(Axis, self).__init__(**kwargs) self.slab self.outline self.ipw_3d self.ipw self._last = -1 self._keytime = None spacing = list(np.abs(self.parent.spacing)) shape = list(self.parent.epi.shape) spacing.pop(self.axis) shape.pop(self.axis) if self.axis == 1: shape = shape[::-1] spacing = spacing[::-1] shape.append(0) spacing.append(1) self.spacing = np.array(spacing) self.shape = np.array(shape) self.handle def reset_view(self): center = self.shape * self.spacing / 2. + (self.shape + 1) % 2 * self.spacing / 2. width = (self.shape * self.spacing)[:2] width = np.min(width) * 0.5 self.scene.scene.background = (0, 0, 0) mlab.view(*([(0, 0), (90, 0), (0, 0)][self.axis]), focalpoint=center, figure=self.scene.mayavi_scene) self.scene.scene.parallel_projection = True self.scene.scene.camera.parallel_scale = width * 1.2 self.scene.scene.interactor.interactor_style = tvtk.InteractorStyleImage( ) try: #WX window self.scene.scene_editor.control.SetFocusFromKbd def focusfunc(vtkobj, i): self.scene.scene_editor.control.SetFocusFromKbd() except AttributeError: #QT window self.scene.scene_editor.control.setFocus def focusfunc(vtkobj, i): self.scene.scene_editor.control.setFocus() self.scene.interactor.add_observer("MouseMoveEvent", focusfunc) self.scene.interactor.add_observer("KeyReleaseEvent", self.handle_keys) self._outline_color_changed() def handle_keys(self, evt, name): key, sym = evt.GetKeyCode(), evt.GetKeySym() #print repr(key), repr(sym), evt.GetShiftKey(), evt.GetControlKey() if key in ('', chr(127)): i = -1 if self.invert else 1 mult = (2, .2)[evt.GetShiftKey()] smult = (1.1, 1.01)[evt.GetShiftKey()] rotccw, rotcw = "Insert", "Prior" moves = dict(Up=(0, 1, 0), Down=(0, -1, 0), Left=(-1, 0, 0), Right=(1, 0, 0)) if self.invert: rotccw, rotcw = rotcw, rotccw moves = dict(Up=(1, 0, 0), Down=(-1, 0, 0), Left=(0, 1, 0), Right=(0, -1, 0)) if sym in moves: self.handle.move(np.array(moves[sym]) * mult) elif sym == rotccw: #ins self.handle.move(angle=np.pi / 120. * i * mult) elif sym == rotcw: #pgup self.handle.move(angle=-np.pi / 120. * i * mult) elif sym == "Del": self.transform(scale=(smult, 1)) elif sym == "Home": self.transform(scale=(1, smult)) elif sym == "End": self.transform(scale=(1, 1 / smult)) elif sym == "Next": self.transform(scale=(1 / smult, 1)) elif key == ']': self.next_slice() elif key == '[': self.prev_slice() elif key == 'H': self.parent.outlines_visible = not self.parent.outlines_visible elif key == 'Z' and evt.GetControlKey() == 1: self.parent.undo() #clear out key buffer, otherwise the ctrl release will have the wrong state evt.SetKeyEventInformation(0, 0, '', 0, '') @on_trait_change("parent.scene_3d.activated") def activate_3d(self): self.ipw_3d.ipw.interaction = 0 self.surf def _planes_default(self): pos = [0, 0, 0] vec = [0, 0, 0] off = (self.parent.epi.shape[self.axis] + 1) % 2 * abs( self.parent.spacing[self.axis]) / 2. vec[self.axis] = 1 pos[self.axis] = off + abs(self.parent.spacing[self.axis]) / 2. top = tvtk.Planes(normals=[vec[:]], points=[pos[:]]) vec[self.axis] = -1 pos[self.axis] = off - abs(self.parent.spacing[self.axis]) / 2. bot = tvtk.Planes(normals=[vec[:]], points=[pos[:]]) return [top, bot] def _slab_default(self): top = tvtk.ClipPolyData( clip_function=self.planes[0], inside_out=1, input=self.parent.surf.parent.parent.filter.output) bot = tvtk.ClipPolyData(clip_function=self.planes[1], inside_out=1, input=top.output) bot.update() return bot def _outline_default(self): origin, spacing = self.parent.origin, self.parent.spacing translate = origin * np.sign(spacing) - np.abs(spacing) / 2. mlab.figure(self.scene.mayavi_scene) if self.slab.output.points is None or len(self.slab.output.points) < 3: pts = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 0]]) polys = [[0, 1, 2]] else: pts = self.slab.output.points.to_array() polys = self.slab.output.polys.to_array().reshape(-1, 4)[:, 1:] src = mlab.pipeline.triangular_mesh_source( pts[:, 0], pts[:, 1], pts[:, 2], polys, figure=self.scene.mayavi_scene) xfm = mlab.pipeline.transform_data(src, figure=self.scene.mayavi_scene) xfm.filter.transform.post_multiply() xfm.filter.transform.translate(-translate) xfm.widget.enabled = False surf = mlab.pipeline.surface(xfm, figure=self.scene.mayavi_scene, color=(1, 1, 1), representation=self.outline_rep) surf.actor.property.line_width = self.line_width surf.actor.property.point_size = self.point_size return src def _surf_default(self): if self.slab.output.points is None or len(self.slab.output.points) < 3: pts = np.array([[0, 0, 0], [0, 0, 0], [0, 0, 0]]) polys = [[0, 1, 2]] else: pts = self.slab.output.points.to_array() polys = self.slab.output.polys.to_array().reshape(-1, 4)[:, 1:] src = mlab.pipeline.triangular_mesh_source( pts[:, 0], pts[:, 1], pts[:, 2], polys, figure=self.scene_3d.mayavi_scene) surf = mlab.pipeline.surface(src, color=(1, 1, 1), figure=self.scene_3d.mayavi_scene, representation=self.outline_rep) surf.actor.property.line_width = self.line_width surf.actor.property.point_size = self.point_size return src def _ipw_3d_default(self): spos = self.position + self.parent.origin * np.sign( self.parent.spacing) space = list(np.abs(self.parent.spacing)) shape = list(np.array(self.parent.epi.shape) / self.parent.padshape) space.pop(self.axis) shape.pop(self.axis) if self.axis == 1: space = space[::-1] shape = shape[::-1] space.append(1) shape = [(0, 0), (shape[0], 0), (0, shape[1]), (shape[0], shape[1])] origin = [space[0] / 2., space[1] / 2., 0] self.ipw_space = (space, shape) ipw = mlab.pipeline.image_plane_widget( self.parent.epi_src, figure=self.scene_3d.mayavi_scene, plane_orientation='%s_axes' % 'xyz'[self.axis], name='Cut %s' % self.axis) ipw.ipw.color_map.output_format = 'rgb' ipw.ipw.set(texture_interpolate=0, reslice_interpolate='nearest_neighbour', slice_position=spos[self.axis]) ipw.ipw.reslice.set(output_spacing=space, output_origin=origin) ipw.ipw.poly_data_algorithm.output.point_data.t_coords = shape return ipw def _ipw_default(self): extent = list(np.abs(self.parent.epi.shape * self.parent.spacing)) extent.pop(self.axis) if self.axis == 1: extent = extent[::-1] side_src = self.ipw_3d.ipw.reslice_output # Add a callback on the image plane widget interaction to # move the others def move_view(obj, evt): # Disable rendering on all scene cpos = obj.GetCurrentCursorPosition() position = list(cpos * side_src.spacing)[:2] position.insert(self.axis, self.position[self.axis]) # We need to special case y, as the view has been rotated. if self.axis == 1: position = position[::-1] self.position = position ipw = mlab.pipeline.image_plane_widget(side_src, plane_orientation='z_axes', figure=self.scene.mayavi_scene, name='Cut view %s' % self.axis) ipw.ipw.plane_property.opacity = 0 ipw.ipw.selected_plane_property.opacity = 0 ipw.ipw.poly_data_algorithm.set(point1=[extent[0], 0, 0], point2=[0, extent[1], 0]) ipw.ipw.set(left_button_action=0, middle_button_auto_modifier=2, right_button_auto_modifier=2, texture_interpolate=0, reslice_interpolate='nearest_neighbour') ipw.parent.scalar_lut_manager.set(use_default_range=False, default_data_range=[-1, 1], data_range=[-1, 1]) ipw.ipw.add_observer('InteractionEvent', move_view) ipw.ipw.add_observer('StartInteractionEvent', move_view) return ipw def _cursor_default(self): return mlab.points3d(*self.position, mode='axes', color=(0, 0, 0), scale_factor=2 * max(self.parent.epi[0].shape), figure=self.scene.mayavi_scene, name='Cursor view %s' % self.axis) def _handle_default(self): center = self.shape * self.spacing / 2. + (self.shape + 1) % 2 * self.spacing / 2. width = (self.shape * self.spacing)[:2] width = np.min(width) * 0.5 def handlemove(handle, pos, angle, radius): self.transform(pos, angle, radius) return RotationWidget(self.scene.scene.mayavi_scene, handlemove, radius=width, pos=center) def _disable_render_changed(self): self.scene.scene.disable_render = self.disable_render def toggle_outline(self): self.outline.children[0].children[ 0].visible = self.parent.outlines_visible def _outline_color_changed(self): try: if isinstance(self.outline_color, string_types): color = cc.to_rgb(self.outline_color) else: color = tuple([c / 255. for c in tuple(self.outline_color)]) except TypeError: color = self.outline_color.getRgbF()[:3] self.surf.children[0].children[0].actor.property.color = color self.outline.children[0].children[0].children[ 0].actor.property.color = color def _outline_rep_changed(self): self.surf.children[0].children[ 0].actor.property.representation = self.outline_rep self.outline.children[0].children[0].children[ 0].actor.property.representation = self.outline_rep def _line_width_changed(self): self.surf.children[0].children[ 0].actor.property.line_width = self.line_width self.outline.children[0].children[0].children[ 0].actor.property.line_width = self.line_width def _point_size_changed(self): self.surf.children[0].children[ 0].actor.property.point_size = self.point_size self.outline.children[0].children[0].children[ 0].actor.property.point_size = self.point_size def next_slice(self): '''View the next slice''' pos = list(self.position) pos[self.axis] += np.abs(self.parent.spacing)[self.axis] self.position = pos def prev_slice(self): '''View the previous slice''' pos = list(self.position) pos[self.axis] -= np.abs(self.parent.spacing)[self.axis] self.position = pos def transform(self, pos=(0, 0), angle=0, scale=1): '''In-plane transformation function. Update the 3D transform based on the 2D changes''' center = self.shape * self.spacing / 2. + (self.shape + 1) % 2 * self.spacing / 2. inv = self.xfm.transform.homogeneous_inverse wpos = self.handle.center.representation.world_position wpos -= center if not isinstance(scale, (tuple, list, np.ndarray)): scale = [scale, scale] if self.axis == 1: trans = np.insert(pos[:2][::-1], self.axis, 0) wpos = np.insert(wpos[:2][::-1], self.axis, self.ipw_3d.ipw.slice_position) #angle = -angle else: trans = np.insert(pos[:2], self.axis, 0) wpos = np.insert(wpos[:2], self.axis, self.ipw_3d.ipw.slice_position) scale = np.insert(scale, self.axis, 1) self.parent._undolist.append(self.xfm.transform.matrix.to_array()) self.xfm.transform.post_multiply() self.xfm.transform.translate(-wpos) self.xfm.transform.rotate_wxyz(np.degrees(angle), *self.ipw_3d.ipw.normal) self.xfm.transform.scale(scale) self.xfm.transform.translate(wpos) self.xfm.transform.translate(trans) self.xfm.transform.pre_multiply() self.xfm.widget.set_transform(self.xfm.filter.transform) self.xfm.update_pipeline() self.parent.update_slabs() np.save("/tmp/last_xfm.npy", self.parent.get_xfm()) def update_position(self): """ Update the position of the cursors on each side view, as well as the image_plane_widgets in the 3D view. """ offset = np.abs(self.parent.spacing) / 2 p = list(self.position + offset) p.pop(self.axis) if self.axis == 1: p = p[::-1] p.append(0) self.cursor.parent.parent.data.points = [p] if self.position[self.axis] != self._last: self._last = self.position[self.axis] origin = self.parent.origin * np.sign(self.parent.spacing) space, shape = self.ipw_space self.ipw_3d.ipw.slice_position = self.position[self.axis] + origin[ self.axis] self.ipw_3d.ipw.reslice.set( output_spacing=space, output_origin=[space[0] / 2., space[1] / 2., 0]) self.ipw_3d.ipw.poly_data_algorithm.output.point_data.t_coords = shape self.ipw.ipw.poly_data_algorithm.output.point_data.t_coords = shape origin, spacing = self.parent.origin, self.parent.spacing origin = origin * np.sign(spacing) - np.abs(spacing) / 2. gap = abs(spacing[self.axis]) / 2. pos = self.ipw_3d.ipw.slice_position pts = [0, 0, 0] pts[self.axis] = pos + gap self.planes[0].points = [tuple(pts)] pts[self.axis] = pos - gap self.planes[1].points = [tuple(pts)] self.update_slab() def update_slab(self): self.slab.update() self.outline.data.set(points=self.slab.output.points, polys=self.slab.output.polys) self.surf.data.set(points=self.slab.output.points, polys=self.slab.output.polys)
class CompositeGridModel(GridModel): """ A CompositeGridModel is a model whose underlying data is a collection of other grid models. """ # The models this model is comprised of. data = List(Instance(GridModel)) # The rows in the model. rows = Union(None, List(Instance(GridRow))) # The cached data indexes. _data_index = Dict() # ------------------------------------------------------------------------ # 'object' interface. # ------------------------------------------------------------------------ def __init__(self, **traits): """ Create a CompositeGridModel object. """ # Base class constructor super(CompositeGridModel, self).__init__(**traits) self._row_count = None # ------------------------------------------------------------------------ # 'GridModel' interface. # ------------------------------------------------------------------------ def get_column_count(self): """ Return the number of columns for this table. """ # for the composite grid model, this is simply the sum of the # column counts for the underlying models count = 0 for model in self.data: count += model.get_column_count() return count def get_column_name(self, index): """ Return the name of the column specified by the (zero-based) index. """ model, new_index = self._resolve_column_index(index) return model.get_column_name(new_index) def get_column_size(self, index): """ Return the size in pixels of the column indexed by col. A value of -1 or None means use the default. """ model, new_index = self._resolve_column_index(index) return model.get_column_size(new_index) def get_cols_drag_value(self, cols): """ Return the value to use when the specified columns are dragged or copied and pasted. cols is a list of column indexes. """ values = [] for col in cols: model, real_col = self._resolve_column_index(col) values.append(model.get_cols_drag_value([real_col])) return values def get_cols_selection_value(self, cols): """ Return the value to use when the specified cols are selected. This value should be enough to specify to other listeners what is going on in the grid. rows is a list of row indexes. """ return self.get_cols_drag_value(self, cols) def get_column_context_menu(self, col): """ Return a MenuManager object that will generate the appropriate context menu for this column.""" model, new_index = self._resolve_column_index(col) return model.get_column_context_menu(new_index) def sort_by_column(self, col, reverse=False): """ Sort model data by the column indexed by col. The reverse flag indicates that the sort should be done in reverse. """ pass def is_column_read_only(self, index): """ Return True if the column specified by the zero-based index is read-only. """ model, new_index = self._resolve_column_index(index) return model.is_column_read_only(new_index) def get_row_count(self): """ Return the number of rows for this table. """ # see if we've already calculated the row_count if self._row_count is None: row_count = 0 # return the maximum rows of any of the contained models for model in self.data: rows = model.get_row_count() if rows > row_count: row_count = rows # save the result for next time self._row_count = row_count return self._row_count def get_row_name(self, index): """ Return the name of the row specified by the (zero-based) index. """ label = None # if the rows list exists then grab the label from there... if self.rows is not None: if len(self.rows) > index: label = self.rows[index].label # ... otherwise generate it from the zero-based index. else: label = str(index + 1) return label def get_rows_drag_value(self, rows): """ Return the value to use when the specified rows are dragged or copied and pasted. rows is a list of row indexes. """ row_values = [] for rindex in rows: row = [] for model in self.data: new_data = model.get_rows_drag_value([rindex]) # if it's a list then we assume that it represents more than # one column's worth of values if isinstance(new_data, list): row.extend(new_data) else: row.append(new_data) # now save our new row value row_values.append(row) return row_values def is_row_read_only(self, index): """ Return True if the row specified by the zero-based index is read-only. """ read_only = False if self.rows is not None and len(self.rows) > index: read_only = self.rows[index].read_only return read_only def get_type(self, row, col): """ Return the type of the value stored in the table at (row, col). """ model, new_col = self._resolve_column_index(col) return model.get_type(row, new_col) def get_value(self, row, col): """ Return the value stored in the table at (row, col). """ model, new_col = self._resolve_column_index(col) return model.get_value(row, new_col) def get_cell_selection_value(self, row, col): """ Return the value stored in the table at (row, col). """ model, new_col = self._resolve_column_index(col) return model.get_cell_selection_value(row, new_col) def resolve_selection(self, selection_list): """ Returns a list of (row, col) grid-cell coordinates that correspond to the objects in selection_list. For each coordinate, if the row is -1 it indicates that the entire column is selected. Likewise coordinates with a column of -1 indicate an entire row that is selected. Note that the objects in selection_list are model-specific. """ coords = [] for selection in selection_list: # we have to look through each of the models in order # for the selected object for model in self.data: cells = model.resolve_selection([selection]) # we know this model found the object if cells comes back # non-empty if cells is not None and len(cells) > 0: coords.extend(cells) break return coords # fixme: this context menu stuff is going in here for now, but it # seems like this is really more of a view piece than a model piece. # this is how the tree control does it, however, so we're duplicating # that here. def get_cell_context_menu(self, row, col): """ Return a MenuManager object that will generate the appropriate context menu for this cell.""" model, new_col = self._resolve_column_index(col) return model.get_cell_context_menu(row, new_col) def is_cell_empty(self, row, col): """ Returns True if the cell at (row, col) has a None value, False otherwise.""" model, new_col = self._resolve_column_index(col) if model is None: return True else: return model.is_cell_empty(row, new_col) def is_cell_editable(self, row, col): """ Returns True if the cell at (row, col) is editable, False otherwise. """ model, new_col = self._resolve_column_index(col) return model.is_cell_editable(row, new_col) def is_cell_read_only(self, row, col): """ Returns True if the cell at (row, col) is not editable, False otherwise. """ model, new_col = self._resolve_column_index(col) return model.is_cell_read_only(row, new_col) def get_cell_bg_color(self, row, col): """ Return a wxColour object specifying what the background color of the specified cell should be. """ model, new_col = self._resolve_column_index(col) return model.get_cell_bg_color(row, new_col) def get_cell_text_color(self, row, col): """ Return a wxColour object specifying what the text color of the specified cell should be. """ model, new_col = self._resolve_column_index(col) return model.get_cell_text_color(row, new_col) def get_cell_font(self, row, col): """ Return a wxFont object specifying what the font of the specified cell should be. """ model, new_col = self._resolve_column_index(col) return model.get_cell_font(row, new_col) def get_cell_halignment(self, row, col): """ Return a string specifying what the horizontal alignment of the specified cell should be. Return 'left' for left alignment, 'right' for right alignment, or 'center' for center alignment. """ model, new_col = self._resolve_column_index(col) return model.get_cell_halignment(row, new_col) def get_cell_valignment(self, row, col): """ Return a string specifying what the vertical alignment of the specified cell should be. Return 'top' for top alignment, 'bottom' for bottom alignment, or 'center' for center alignment. """ model, new_col = self._resolve_column_index(col) return model.get_cell_valignment(row, new_col) # ------------------------------------------------------------------------ # protected 'GridModel' interface. # ------------------------------------------------------------------------ def _delete_rows(self, pos, num_rows): """ Implementation method for delete_rows. Should return the number of rows that were deleted. """ for model in self.data: model._delete_rows(pos, num_rows) return num_rows def _insert_rows(self, pos, num_rows): """ Implementation method for insert_rows. Should return the number of rows that were inserted. """ for model in self.data: model._insert_rows(pos, num_rows) return num_rows def _set_value(self, row, col, value): """ Implementation method for set_value. Should return the number of rows, if any, that were appended. """ model, new_col = self._resolve_column_index(col) model._set_value(row, new_col, value) return 0 # ------------------------------------------------------------------------ # private interface # ------------------------------------------------------------------------ def _resolve_column_index(self, index): """ Resolves a column index into the correct model and adjusted index. Returns the target model and the corrected index. """ real_index = index cached = None # self._data_index.get(index) if cached is not None: model, col_index = cached else: model = None for m in self.data: cols = m.get_column_count() if real_index < cols: model = m break else: real_index -= cols self._data_index[index] = (model, real_index) return model, real_index def _data_changed(self): """ Called when the data trait is changed. Since this is called when our underlying models change, the cached results of the column lookups is wrong and needs to be invalidated. """ self._data_index.clear() def _data_items_changed(self): """ Called when the members of the data trait have changed. Since this is called when our underlying model change, the cached results of the column lookups is wrong and needs to be invalidated. """ self._data_index.clear()
class PreferenceDialog(SplitDialog): """ The preference dialog. """ #### 'Dialog' interface ################################################### # The dialog title. title = Str('Preferences') #### 'SplitDialog' interface ############################################## # The ratio of the size of the left/top pane to the right/bottom pane. ratio = Float(0.25) #### 'PreferenceDialog' interface ######################################### # The root of the preference hierarchy. root = Instance(PreferenceNode) #### Private interface #################################################### # The preference pages in the dialog (they are created lazily). _pages = Dict # The current visible preference page. _current_page = Any ########################################################################### # Protected 'Dialog' interface. ########################################################################### def _create_buttons(self, parent): """ Creates the buttons. """ sizer = wx.BoxSizer(wx.HORIZONTAL) # 'Done' button. done = wx.Button(parent, wx.ID_OK, "Done") done.SetDefault() parent.Bind(wx.EVT_BUTTON, self._wx_on_ok, wx.ID_OK) sizer.Add(done) return sizer ########################################################################### # Protected 'SplitDialog' interface. ########################################################################### def _create_lhs(self, parent): """ Creates the panel containing the preference page tree. """ return self._create_tree(parent) def _create_rhs(self, parent): """ Creates the panel containing the selected preference page. """ panel = wx.Panel(parent, -1, style=wx.CLIP_CHILDREN) sizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(sizer) panel.SetAutoLayout(True) # The 'pretty' title bar ;^) self.__title = HeadingText(panel) sizer.Add(self.__title.control, 0, wx.EXPAND | wx.BOTTOM | wx.LEFT | wx.RIGHT, 5) # The preference page of the node currently selected in the tree. self._layered_panel = LayeredPanel(panel, min_width=-1, min_height=-1) sizer.Add(self._layered_panel.control, 1, wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, 5) # The 'Restore Defaults' button etc. buttons = self._create_page_buttons(panel) sizer.Add(buttons, 0, wx.ALIGN_RIGHT | wx.TOP | wx.RIGHT, 5) # A separator. line = wx.StaticLine(panel, -1) sizer.Add(line, 0, wx.EXPAND | wx.TOP | wx.LEFT | wx.RIGHT, 5) # Resize the panel to fit the sizer's minimum size. sizer.Fit(panel) return panel ########################################################################### # Private interface. ########################################################################### def _create_tree(self, parent): """ Creates the preference page tree. """ tree_viewer = TreeViewer(parent, input=self.root, show_images=False, show_root=False, content_provider=DefaultTreeContentProvider()) tree_viewer.on_trait_change(self._on_selection_changed, 'selection') return tree_viewer.control def _create_page_buttons(self, parent): """ Creates the 'Restore Defaults' button, etc. At the moment the "etc." is an optional 'Help' button. """ self._button_sizer = sizer = wx.BoxSizer(wx.HORIZONTAL) # 'Help' button. Comes first so 'Restore Defaults' doesn't jump around. self._help = help = wx.Button(parent, -1, "Help") parent.Bind(wx.EVT_BUTTON, self._on_help, help.GetId()) sizer.Add(help, 0, wx.RIGHT, 5) # 'Restore Defaults' button. restore = wx.Button(parent, -1, "Restore Defaults") parent.Bind(wx.EVT_BUTTON, self._on_restore_defaults, restore.GetId()) sizer.Add(restore) return sizer ########################################################################### # wx event handlers. ########################################################################### def _on_restore_defaults(self, event): """ Called when the 'Restore Defaults' button is pressed. """ page = self._pages[self._layered_panel.current_layer_name] page.restore_defaults() return def _on_help(self, event): """ Called when the 'Help' button is pressed. """ page = self._pages[self._layered_panel.current_layer_name] page.show_help_topic() return ########################################################################### # Trait event handlers. ########################################################################### def _on_selection_changed(self, selection): """ Called when a node in the tree is selected. """ if len(selection) > 0: # The tree is in single selection mode. node = selection[0] # We only show the help button if the selected node has a help # topic Id. if len(node.help_id) > 0: self._button_sizer.Show(self._help, True) else: self._button_sizer.Show(self._help, False) # Show the selected preference page. layered_panel = self._layered_panel parent = self._layered_panel.control # If we haven't yet displayed the node's preference page during the # lifetime of this dialog, then we have to create it. if not layered_panel.has_layer(node.name): page = node.create_page() layered_panel.add_layer(node.name, page.create_control(parent)) self._pages[node.name] = page layered_panel.show_layer(node.name) self.__title.text = node.name return
class ExecutionModel(HasTraits): """ TO DO: - Generate Block from self.statements """ implements(IExecutable) # List of function calls, groups, and expressions # These must all have inputs, outputs, uuid statements = List(Any) # List of Groups present in the exec model identified by their UUID groups = Property(depends_on=['statements']) _groups = List(Instance(UUID)) # Topologically sorted list of function calls in statements sorted_statements = Property(depends_on=['statements', 'statements_items']) # Dependency Graph of all the statements in the body dep_graph = Property(depends_on=['statements', 'statements_items']) # The imports and local definitions imports_and_locals = Property(depends_on=['statements', 'statements_items']) # The body of the code corresponding to the statements body = Property(depends_on=['statements', 'statements_items']) # All the code put together = imports + local_defs + body code = Property(depends_on=['statements', 'statements_items', 'statements.call_signature', 'statements.inputs.binding', 'statements.outputs.binding']) # The block corresponding to the source code: block = Property(depends_on=['statements']) # Flag to impose if the code can be executed or not. # It is needed to speed up the use of the canvas when we are adding new # blocks instead of executing the code. allow_execute = Bool(True) #--------------------------------------------------------------------------- # object interface: #--------------------------------------------------------------------------- #--- Class Methods -- constructors ----------------------------------------- @classmethod def from_code(cls, code): """ Create an ExecutionModel object from the code. Get the ast for the code and pass to from_ast() class method. """ ast = compiler.parse(code) return ExecutionModel.from_ast(ast) @classmethod def from_file(cls, filename): ast = compiler.parseFile(filename) return ExecutionModel.from_ast(ast) @classmethod def from_ast(cls, ast): """ Create an ExecutionModel object from the ast. """ # Build dictionaries for import information and local defs info = find_functions(ast) statement_info = walk(ast, StatementWalker()) statements = parse_stmts_info(statement_info,info) return cls(statements=statements) def generate_unique_function_name(self,base_name=""): """ Returns a unique name for a new function based on the names of existing functions and imports in the code. """ statements = self.statements functions = funcs_name_retrieve(statements) # Basic name generation method: template + counter if base_name == "": base_name = "new_function" if base_name not in functions: return base_name i = 1 while base_name + str(i) in functions: i += 1 return base_name + str(i) #--------------------------------------------------------------------------- # IExecutable interface #--------------------------------------------------------------------------- def execute(self, context, globals=None, inputs=None, outputs=None): """ Execute the code in the given context. Parameters ---------- context : sufficiently dict-like object The namespace to execute the code in. globals : dict, optional The global namespace for the code. inputs : list of str, optional Names that can be used to restrict the execution to portions of the code. Ideally, only code that is affected by these inputs variables is executed. outputs : list of str, optional Names that can be used to restrict the execution to portions of the code. Ideally, only code that affects these outputs variables is executed. """ if not self.allow_execute: return if globals is None: globals = {} if inputs is not None or outputs is not None: # Only do this if we have to. restricted = self.restricted(inputs=inputs, outputs=outputs) else: restricted = self # Only execute the portions of the code which can be executed given the # names in the context. available_names = set(context.keys()) required_names, _ = restricted.mark_unsatisfied_inputs( available_names) if required_names: bad = restricted.restricted(inputs=required_names) good_statements = [stmt for stmt in restricted.statements if stmt not in bad.statements] restricted = self.__class__(statements=good_statements) # Big optimization with small effort! The exec command works # really bad when the passed context contains "big" object like # that produced by our computations. We remove all of them that are # going to be overwritten. restricted._clean_old_results_from_context(context) try: t_in = time.time() # This is likely the most important line in block canvas exec restricted.code in globals, context t_out = time.time() print '%f seconds: Execution time' % (t_out-t_in) except Exception, _: print 'Got exception from code:' print restricted.code print traceback.print_exc()
class DrawingCanvas(Container): """ A DrawingCanvas has some buttons which toggle what kind of drawing tools are active on the canvas, then allow arbitrary painting on the canvas. """ # The active tool is the primary interactor on the canvas. It gets # a chance to handle events before they are passed on to other components # and listener tools. active_tool = Any # Listening tools are always enabled and get all events (unless the active # tool has vetoed it), but they cannot prevent other tools from getting events. listening_tools = List # The background color of the canvas bgcolor = ColorTrait("white") toolbar = Instance(DrawingCanvasToolbar, args=()) fit_window = True def dispatch(self, event, suffix): # See if the event happened on the toolbar: event.offset_xy(*self.position) if self.toolbar.is_in(event.x, event.y): self.toolbar.dispatch(event, suffix) event.pop() if event.handled: return if self.active_tool is not None: self.active_tool.dispatch(event, suffix) if event.handled: return for tool in self.listening_tools: tool.dispatch(event, suffix) super(DrawingCanvas, self).dispatch(event, suffix) return def activate(self, tool): """ Makes the indicated tool the active tool on the canvas and moves the current active tool back into the list of tools. """ self.active_tool = tool return def _draw_container_mainlayer(self, gc, view_bounds=None, mode="default"): active_tool = self.active_tool if active_tool and active_tool.draw_mode == "exclusive": active_tool.draw(gc, view_bounds, mode) else: #super(DrawingCanvas, self)._draw(gc, view_bounds, mode) for tool in self.listening_tools: tool.draw(gc, view_bounds, mode) if active_tool: active_tool.draw(gc, view_bounds, mode) self.toolbar.draw(gc, view_bounds, mode) return def _draw_container_background(self, gc, view_bounds=None, mode="default"): if self.bgcolor not in ("clear", "transparent", "none"): with gc: gc.set_antialias(False) gc.set_fill_color(self.bgcolor_) gc.draw_rect((int(self.x), int( self.y), int(self.width) - 1, int(self.height) - 1), FILL) return #------------------------------------------------------------------------ # Event listeners #------------------------------------------------------------------------ def _tools_items_changed(self): self.request_redraw() return
class Glyph(Component): # The version of this class. Used for persistence. __version__ = 0 # Type of Glyph: 'tensor' or 'vector' glyph_type = Enum('vector', 'tensor', desc='if the glyph is vector or tensor') # The scaling mode to use when scaling the glyphs. We could have # used the glyph's own scale mode but it allows users to set the # mode to use vector components for the scaling which I'd like to # disallow. scale_mode = Trait( 'scale_by_scalar', TraitRevPrefixMap({ 'scale_by_vector': 1, 'scale_by_vector_components': 2, 'data_scaling_off': 3, 'scale_by_scalar': 0 }), desc="if scaling is done using scalar or vector/normal magnitude") # The color mode to use when coloring the glyphs. We could have # used the glyph's own color_mode trait but it allows users to set # the mode to use vector components for the scaling which I'd # like to disallow. color_mode = Trait( 'color_by_scalar', TraitRevPrefixMap({ 'color_by_vector': 2, 'color_by_scalar': 1, 'no_coloring': 0 }), desc="if coloring is done by scalar or vector/normal magnitude") color_mode_tensor = Trait( 'scalar', TraitRevPrefixMap({ 'scalars': 1, 'eigenvalues': 2, 'no_coloring': 0 }), desc="if coloring is done by scalar or eigenvalues") # Specify if the input points must be masked. By mask we mean # that only a subset of the input points must be displayed. mask_input_points = Bool(False, desc="if input points are masked") # The MaskPoints filter. mask_points = Instance(tvtk.MaskPoints, args=(), kw={'random_mode': True}, record=True) # The Glyph3D instance. glyph = Instance(tvtk.Object, allow_none=False, record=True) # The Source to use for the glyph. This is chosen from # `self._glyph_list` or `self.glyph_dict`. glyph_source = Instance(glyph_source.GlyphSource, allow_none=False, record=True) # The module associated with this component. This is used to get # the data range of the glyph when the scale mode changes. This # *must* be set if this module is to work correctly. module = Instance(Module) # Should we show the GUI option for changing the scalar mode or # not? This is useful for vector glyphing modules where there it # does not make sense to scale the data based on scalars. show_scale_mode = Bool(True) ######################################## # Private traits. # Used for optimization. _updating = Bool(False) ######################################## # View related traits. view = View(Group( Item(name='mask_input_points'), Group( Item(name='mask_points', enabled_when='object.mask_input_points', style='custom', resizable=True), show_labels=False, ), label='Masking', ), Group( Group( Item(name='scale_mode', enabled_when='show_scale_mode', visible_when='show_scale_mode'), Item(name='color_mode', enabled_when='glyph_type == "vector"', visible_when='glyph_type == "vector"'), Item(name='color_mode_tensor', enabled_when='glyph_type == "tensor"', visible_when='glyph_type == "tensor"'), ), Group(Item(name='glyph', style='custom', resizable=True), show_labels=False), label='Glyph', selected=True, ), Group( Item(name='glyph_source', style='custom', resizable=True), show_labels=False, label='Glyph Source', ), resizable=True) ###################################################################### # `object` interface ###################################################################### def __get_pure_state__(self): d = super(Glyph, self).__get_pure_state__() for attr in ('module', '_updating'): d.pop(attr, None) return d ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ self._glyph_type_changed(self.glyph_type) self.glyph_source = glyph_source.GlyphSource() # Handlers to setup our source when the sources pipeline changes. self.glyph_source.on_trait_change(self._update_source, 'pipeline_changed') self.mask_points.on_trait_change(self.render) def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ if ((len(self.inputs) == 0) or (len(self.inputs[0].outputs) == 0)): return self._mask_input_points_changed(self.mask_input_points) if self.glyph_type == 'vector': self._color_mode_changed(self.color_mode) else: self._color_mode_tensor_changed(self.color_mode_tensor) self._scale_mode_changed(self.scale_mode) # Set our output. tvtk_common.configure_outputs(self, self.glyph) self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ self._scale_mode_changed(self.scale_mode) self.data_changed = True def render(self): if not self._updating: super(Glyph, self).render() def start(self): """Overridden method. """ if self.running: return self.glyph_source.start() super(Glyph, self).start() def stop(self): if not self.running: return self.glyph_source.stop() super(Glyph, self).stop() def has_output_port(self): """ The filter has an output port.""" return True def get_output_object(self): """ Returns the output port.""" return self.glyph.output_port ###################################################################### # Non-public methods. ###################################################################### def _update_source(self): self.configure_source_data(self.glyph, self.glyph_source.outputs[0]) def _glyph_source_changed(self, value): self.configure_source_data(self.glyph, value.outputs[0]) def _color_mode_changed(self, value): if len(self.inputs) == 0: return if value != 'no_coloring': self.glyph.color_mode = value def _color_mode_tensor_changed(self, value): if len(self.inputs) == 0: return self._updating = True if value != 'no_coloring': self.glyph.color_mode = value self.glyph.color_glyphs = True else: self.glyph.color_glyphs = False self._updating = False self.render() def _scale_mode_changed(self, value): if (self.module is None) or (len(self.inputs) == 0)\ or self.glyph_type == 'tensor': return self._updating = True try: glyph = self.glyph glyph.scale_mode = value mm = self.module.module_manager if glyph.scale_mode == 'scale_by_scalar': glyph.range = tuple(mm.scalar_lut_manager.data_range) else: glyph.range = tuple(mm.vector_lut_manager.data_range) finally: self._updating = False self.render() def _mask_input_points_changed(self, value): inputs = self.inputs if len(inputs) == 0: return if value: mask = self.mask_points tvtk_common.configure_input(mask, inputs[0].outputs[0]) self.configure_connection(self.glyph, mask) else: self.configure_connection(self.glyph, inputs[0]) self.glyph.update() def _glyph_type_changed(self, value): if self.glyph_type == 'vector': self.glyph = tvtk.Glyph3D(clamping=True) else: self.glyph = tvtk.TensorGlyph(scale_factor=0.1) self.show_scale_mode = False self.glyph.on_trait_change(self.render) def _scene_changed(self, old, new): super(Glyph, self)._scene_changed(old, new) self.glyph_source.scene = new
class DashboardServer(Loggable): devices = List selected_device = Instance(DashboardDevice) db_manager = Instance(DashboardDBManager, ()) notifier = Instance(Notifier, ()) url = Str _alive = False def activate(self): self._load_devices() if self.devices: self.setup_database() self.setup_notifier() self.start_poll() def deactivate(self): self.db_manager.stop() def setup_database(self): self.db_manager.start() def setup_notifier(self): parser = self._get_parser() port = 8100 elem = parser.get_elements('port') if elem is not None: try: port = int(elem[0].text.strip()) except ValueError: pass self.notifier.port = port host = gethostbyname(gethostname()) self.url = '{}:{}'.format(host, port) #add a config request handler self.notifier.add_request_handler('config', self._handle_config) def start_poll(self): self.info('starting dashboard poll') self._alive = True t = Thread(name='poll', target=self._poll) t.setDaemon(1) t.start() def _load_devices(self): #read devices from config #get device from app app = self.application parser = self._get_parser() ds = [] for dev in parser.get_elements('device'): name = dev.text.strip() dname = dev.find('name') if dname is None: self.warning( 'no device name for {}. use a <name> tag'.format(name)) continue dev_name = dname.text.strip() device = None if app: device = app.get_service(ICoreDevice, query='name=="{}"'.format(dev_name)) if device is None: self.warning( 'no device named "{}" available'.format(dev_name)) continue enabled = dev.find('use') if enabled is not None: enabled = to_bool(enabled.text.strip()) d = DashboardDevice(name=name, use=bool(enabled), _device=device) for v in dev.findall('value'): n = v.text.strip() tag = '<{},{}>'.format(name, n) func_name = self._get_xml_value(v, 'func', 'get') period = self._get_xml_value(v, 'period', 60) if not period == 'on_change': try: period = int(period) except ValueError: period = 60 enabled = to_bool(self._get_xml_value(v, 'enabled', False)) timeout = self._get_xml_value(v, 'timeout', 0) d.add_value(n, tag, func_name, period, enabled, timeout) ds.append(d) self.devices = ds def _get_xml_value(self, elem, tag, default): ret = default tt = elem.find(tag) if not tt is None: ret = tt.text.strip() return ret def _handle_config(self): """ return a pickled dictionary string """ config = [pv for dev in self.devices for pv in dev.values] return pickle.dumps(config) def _poll(self): mperiod = min([v.period for dev in self.devices for v in dev.values]) if mperiod == 'on_change': mperiod = 1 self.debug('min period {}'.format(mperiod)) while self._alive: for dev in self.devices: if not dev.use: continue dev.trigger() time.sleep(mperiod) def _get_parser(self): p = os.path.join(paths.setup_dir, 'dashboard.xml') parser = XMLParser(p) return parser @on_trait_change('devices:publish_event') def _handle_publish(self, obj, name, old, new): self.notifier.send_message(new) self.db_manager.publish_device(obj) @on_trait_change('devices:values:+') def _value_changed(self, obj, name, old, new): if name.startswith('last_'): return print obj, name, old, new
class KeithleyControllerUI(KeithleyController,BaseInstrumentUI): """Same as :class:`~.controller.KeithleyController`, but with added gui layer. See also :class:`.BaseInstrumentUI` for details. """ _time = List(Float) _data = List() _value_str = Str() _alarm = Bool #: here IO settings are defined io_settings = Instance(IOSettings,()) initcmd = DelegatesTo('io_settings') #: inteerval at which data is collected interval = Range(0.01,10.,1.) #: actual data objoect used for measurements data = Instance(StructArrayData,()) #: determines if loggging (data readout) is initiated. do_log = Bool(True, desc = 'whether to log data or not') view = View(Group(instrument_group,value_group, Group(HGroup(Item('do_log'),'interval'), Item('data',style = 'custom', show_label = False), show_border = True, label = 'Log'),status_group)) def init(self): """Opens connection to a device. IO parameters are defined in the :attr:`.io_settings`. """ if self.io_settings.instr != '': return super(KeithleyControllerUI, self).init(instr = self.io_settings.instr, timeout = self.io_settings.timeout, initcmd = self.io_settings.initcmd) else: return super(KeithleyControllerUI, self).init(timeout = self.io_settings.timeout,initcmd = self.io_settings.initcmd) def update(self): report = self.report() self.update_status_message(report) self.update_LED(report) self.update_alarm(report) self.update_value(report) def update_alarm(self, report): self._alarm = report.get('alarm', False) def update_value(self, report): try: self._value_str = '%.6f' % report['value'] except KeyError: pass if len(self._data) != 0 and self.do_log == True: data, self._data = self._data, [] self.data.append(data) def report(self): r = super(KeithleyControllerUI, self).report() try: #if logging is on.. get data from the storred data, else measure 1 if self.do_log == True: r['time'], r['value'] = self._data[-1] else: r['time'], r['value'] = self.measure(1) except IndexError: pass return r def __initialized_changed(self, value): if value == False: self._LED = 0 self._status_message = '' self._value_str = '' @display_exception('Could not initialize.') def _init_button_fired(self): if self.initialized: self.stop_readout() self.stop_timer() self.close() else: self.init() self.start_readout() self.start_timer() def _io_button_fired(self): try: instr = visa.get_instruments_list()[0] except IndexError: instr = '' if self.io_settings.instr == '': self.io_settings.instr = instr if self.io_settings.edit_traits(kind = 'livemodal'): print('OK') def start_readout(self): t = ReadThread(self) t.daemon = True t.start() self._thread = t def stop_readout(self): try: self._thread.wants_to_stop.set() except AttributeError: pass self._data = [] def _data_default(self): return StructArrayData(width = 420, height = 230, dtype = [('time','float'),('voltage','float')])
class DocAction(WorkbenchAction): """ (Pyface) Action for displaying a help doc. """ ### Action interface ############################################## # Image associated with this action instance. image = Property ### IExtensionPointUser interface extension_registry = Property(Instance(IExtensionRegistry)) def _get_extension_registry(self): return self.window.application.extension_registry def _get_image(self): """ Returns the image to be used for this DocAction instance. """ # The current implementation searches for an image file matching # 'name' in all of the image paths. If such a file is not to be found, # the '_image_not_found' file for the DocImageResourceClass is used. return DocImageResource(self.name) ### HelpDocAction interface # Help doc associated with this action. my_help_doc = Instance(HelpDoc) def _my_help_doc_default(self): exns = self.extension_registry.get_extensions(PARENT + '.help_docs') for hd in exns: if hd.label == self.name: return hd return None def _get_filename(self, doc): filename = None if doc is not None: if doc.url: filename = doc.filename else: filename = get_sys_prefix_relative_filename(doc.filename) return filename def perform(self, event): """ Perform the action by displaying the document. """ filename = self._get_filename(self.my_help_doc) if filename is not None: if self.my_help_doc.url or self.my_help_doc.viewer == 'browser': import webbrowser try: webbrowser.open(filename) except (OSError, webbrowser.Error), msg: logger.error('Could not open page in browser for '+ \ 'Document "%s":\n\n' % self.my_help_doc.label + \ str(msg) + '\n\nTry changing Dcoument Preferences.') elif self.my_help_doc.viewer is not None: # Run the viewer, passing it the filename try: Popen([self.my_help_doc.viewer, filename]) except OSError, msg: logger.error('Could not execute program for Document' + \ ' "%s":\n\n ' % self.my_help_doc.label + str(msg) + \ '\n\nTry changing Document Preferences.')
class Visualize2dTask(Task): """ A task for visualizing attractors in 2D. """ #### 'Task' interface ##################################################### id = "example.attractors.task_2d" name = "2D Visualization" menu_bar = SMenuBar( SMenu(id="File", name="&File"), SMenu(id="Edit", name="&Edit"), SMenu(TaskToggleGroup(), id="View", name="&View"), ) #### 'Visualize2dTask' interface ########################################## # The attractor model that is currently active (visible in the center # pane). active_model = Any # The list of available attractor models. models = List(Instance(IPlottable2d)) ########################################################################### # 'Task' interface. ########################################################################### def create_central_pane(self): """ Create a plot pane with a list of models. Keep track of which model is active so that dock panes can introspect it. """ pane = Plot2dPane(models=self.models) self.active_model = pane.active_model pane.on_trait_change(self._update_active_model, "active_model") return pane def create_dock_panes(self): return [ ModelConfigPane(model=self.active_model), ModelHelpPane(model=self.active_model), ] ########################################################################### # Protected interface. ########################################################################### #### Trait initializers ################################################### def _default_layout_default(self): return TaskLayout(left=Tabbed( PaneItem("example.attractors.model_config_pane"), PaneItem("example.attractors.model_help_pane"), )) def _models_default(self): from model.henon import Henon from model.lorenz import Lorenz from model.rossler import Rossler models = [Henon(), Lorenz(), Rossler()] return [adapt(model, IPlottable2d) for model in models] #### Trait change handlers ################################################ def _update_active_model(self): self.active_model = self.window.central_pane.active_model for dock_pane in self.window.dock_panes: dock_pane.model = self.active_model
class IsoDBTransfer(Loggable): """ transfer analyses from an isotope_db database to a dvc database """ dvc = Instance(DVC) processor = Instance(IsotopeDatabaseManager) persister = Instance(DVCPersister) quiet = False def init(self): conn = dict(host=os.environ.get('ARGONSERVER_HOST'), username=os.environ.get('ARGONSERVER_DB_USER'), password=os.environ.get('ARGONSERVER_DB_PWD'), kind='mysql') self.dvc = DVC(bind=False, organization='NMGRLData', meta_repo_name='MetaData') paths.meta_root = os.path.join(paths.dvc_dir, self.dvc.meta_repo_name) use_local = True if use_local: dest_conn = dict(host='localhost', username=os.environ.get('LOCALHOST_DB_USER'), password=os.environ.get('LOCALHOST_DB_PWD'), kind='mysql', # echo=True, name='pychrondvc_dev') else: dest_conn = conn.copy() dest_conn['name'] = 'pychrondvc' self.dvc.db.trait_set(**dest_conn) if not self.dvc.initialize(): self.warning_dialog('Failed to initialize DVC') return self.dvc.meta_repo.smart_pull(quiet=self.quiet) self.persister = DVCPersister(dvc=self.dvc, stage_files=False) proc = IsotopeDatabaseManager(bind=False, connect=False) use_local_src = True if use_local_src: conn = dict(host='localhost', username=os.environ.get('LOCALHOST_DB_USER'), password=os.environ.get('LOCALHOST_DB_PWD'), kind='mysql', # echo=True, name='pychrondata') else: conn['name'] = 'pychrondata' proc.db.trait_set(**conn) src = proc.db src.connect() self.processor = proc def copy_productions(self): src = self.processor.db dvc = self.dvc dest = dvc.db with src.session_ctx(): pr = src.get_irradiation_productions() for p in pr: # copy to database pname = p.name.replace(' ', '_') if not dest.get_production(pname): dest.add_production(pname) # copy to meta dvc.copy_production(p) def set_spectrometer_files(self, repository): set_spectrometer_files(self.processor.db, self.dvc.db, repository, os.path.join(paths.repository_dataset_dir, repository)) def bulk_import_irradiations(self, irradiations, creator, dry=True): # for i in xrange(251, 277): # for i in xrange(258, 259): # for i in (258, 259, 260, 261,): # for i in (262, 263, 264, 265): # for i in (266, 267, 268, 269): # for i in (270, 271, 272, 273): for i in irradiations: irradname = 'NM-{}'.format(i) runs = self.bulk_import_irradiation(irradname, creator, dry=dry) # if runs: # with open('/Users/ross/Sandbox/bulkimport/irradiation_runs.txt', 'a') as wfile: # for o in runs: # wfile.write('{}\n'.format(o)) def bulk_import_irradiation(self, irradname, creator, dry=True): src = self.processor.db tol_hrs = 6 self.debug('bulk import irradiation {}'.format(irradname)) oruns = [] ts, idxs = self._get_irradiation_timestamps(irradname, tol_hrs=tol_hrs) print ts repository_identifier = 'Irradiation-{}'.format(irradname) # add project with self.dvc.db.session_ctx(): self.dvc.db.add_project(repository_identifier, creator) def filterfunc(x): a = x.labnumber.irradiation_position is None b = False if not a: b = x.labnumber.irradiation_position.level.irradiation.name == irradname d = False if x.extraction: ed = x.extraction.extraction_device if not ed: d = True else: d = ed.name == 'Fusions CO2' return (a or b) and d # for ms in ('jan', 'obama'): # monitors not run on obama for ms in ('jan',): for i, ais in enumerate(array_split(ts, idxs + 1)): if not ais.shape[0]: self.debug('skipping {}'.format(i)) continue low = get_datetime(ais[0]) - timedelta(hours=tol_hrs / 2.) high = get_datetime(ais[-1]) + timedelta(hours=tol_hrs / 2.) with src.session_ctx(): ans = src.get_analyses_date_range(low, high, mass_spectrometers=(ms,), samples=('FC-2', 'blank_unknown', 'blank_air', 'blank_cocktail', 'air', 'cocktail')) # runs = filter(lambda x: x.labnumber.irradiation_position is None or # x.labnumber.irradiation_position.level.irradiation.name == irradname, ans) runs = filter(filterfunc, ans) if dry: for ai in runs: oruns.append(ai.record_id) print ms, ai.record_id else: self.debug('================= Do Export i: {} low: {} high: {}'.format(i, low, high)) self.debug('N runs: {}'.format(len(runs))) self.do_export([ai.record_id for ai in runs], repository_identifier, creator, monitor_mapping=('FC-2', 'Sanidine', repository_identifier)) return oruns def bulk_import_project(self, project, principal_investigator, dry=True): src = self.processor.db tol_hrs = 6 self.debug('bulk import project={}, pi={}'.format(project, principal_investigator)) oruns = [] repository_identifier = project # def filterfunc(x): # a = x.labnumber.irradiation_position is None # b = False # if not a: # b = x.labnumber.irradiation_position.level.irradiation.name == irradname # # d = False # if x.extraction: # ed = x.extraction.extraction_device # if not ed: # d = True # else: # d = ed.name == 'Fusions CO2' # # return (a or b) and d # for ms in ('jan', 'obama'): ts, idxs = self._get_project_timestamps(project, ms, tol_hrs=tol_hrs) for i, ais in enumerate(array_split(ts, idxs + 1)): if not ais.shape[0]: self.debug('skipping {}'.format(i)) continue low = get_datetime(ais[0]) - timedelta(hours=tol_hrs / 2.) high = get_datetime(ais[-1]) + timedelta(hours=tol_hrs / 2.) print '========{}, {}, {}'.format(ms, low, high) with src.session_ctx(): runs = src.get_analyses_date_range(low, high, projects=('REFERENCES', project), mass_spectrometers=(ms,)) if dry: for ai in runs: oruns.append(ai.record_id) print ai.measurement.mass_spectrometer.name, ai.record_id, ai.labnumber.sample.name, \ ai.analysis_timestamp else: self.debug('================= Do Export i: {} low: {} high: {}'.format(i, low, high)) self.debug('N runs: {}'.format(len(runs))) self.do_export([ai.record_id for ai in runs], repository_identifier, principal_investigator) return oruns def find_project_overlaps(self, projects): tol_hrs = 6 src = self.processor.db pdict = {} for p in projects: for ms in ('jan', 'obama'): ts, idxs = self._get_project_timestamps(p, ms, tol_hrs=tol_hrs) for i, ais in enumerate(array_split(ts, idxs + 1)): if not ais.shape[0]: self.debug('skipping {}'.format(i)) continue low = get_datetime(ais[0]) - timedelta(hours=tol_hrs / 2.) high = get_datetime(ais[-1]) + timedelta(hours=tol_hrs / 2.) print '========{}, {}, {}'.format(ms, low, high) with src.session_ctx(): runs = src.get_analyses_date_range(low, high, projects=('REFERENCES',), mass_spectrometers=(ms,)) pdict[p] = [ai.record_id for ai in runs] for p in projects: for o in projects: if p == o: continue pruns = pdict[p] oruns = pdict[o] for ai in pruns: if ai in oruns: print p, o, ai def import_date_range(self, low, high, spectrometer, repository_identifier, creator): src = self.processor.db with src.session_ctx(): runs = src.get_analyses_date_range(low, high, mass_spectrometers=spectrometer) ais = [ai.record_id for ai in runs] self.do_export(ais, repository_identifier, creator) def do_export(self, runs, repository_identifier, creator, create_repo=False, monitor_mapping=None): # self._init_src_dest() src = self.processor.db dest = self.dvc.db with src.session_ctx(): key = lambda x: x.split('-')[0] runs = sorted(runs, key=key) with dest.session_ctx(): repo = self._add_repository(dest, repository_identifier, creator, create_repo) self.persister.active_repository = repo self.dvc.current_repository = repo total = len(runs) j = 0 for ln, ans in groupby(runs, key=key): ans = list(ans) n = len(ans) for i, a in enumerate(ans): with dest.session_ctx() as sess: st = time.time() try: if self._transfer_analysis(a, repository_identifier, monitor_mapping=monitor_mapping): j += 1 self.debug('{}/{} transfer time {:0.3f}'.format(j, total, time.time() - st)) except BaseException, e: import traceback traceback.print_exc() self.warning('failed transfering {}. {}'.format(a, e))
class CameraWindow(HasTraits): """ CameraWindow class contains the relevant information and functions for a single camera window: image, zoom, pan important members: _plot_data - contains image data to display (used by update_image) _plot - instance of Plot class to use with _plot_data _click_tool - instance of Clicker tool for the single camera window, to handle mouse processing """ _plot_data = Instance(ArrayPlotData) _plot = Instance(Plot) _click_tool = Instance(Clicker) rclicked = Int(0) cam_color = '' name = Str view = View(Item(name='_plot', editor=ComponentEditor(), show_label=False)) # view = View( Item(name='_plot',show_label=False) ) def __init__(self, color): """ Initialization of plot system """ padd = 25 self._plot_data = ArrayPlotData() self._plot = Plot(self._plot_data, default_origin="top left") self._plot.padding_left = padd self._plot.padding_right = padd self._plot.padding_top = padd self._plot.padding_bottom = padd self.right_p_x0,self.right_p_y0,self.right_p_x1,self.right_p_y1,self._quiverplots=[],[],[],[],[] self.cam_color = color def attach_tools(self): """ attach_tools(self) contains the relevant tools: clicker, pan, zoom """ self._click_tool = Clicker(self._img_plot) self._click_tool.on_trait_change( self.left_clicked_event, 'left_changed') #set processing events for Clicker self._click_tool.on_trait_change(self.right_clicked_event, 'right_changed') self._img_plot.tools.append(self._click_tool) pan = PanTool(self._plot, drag_button='middle') zoom_tool = ZoomTool(self._plot, tool_mode="box", always_on=False) # zoom_tool = BetterZoom(component=self._plot, tool_mode="box", always_on=False) zoom_tool.max_zoom_out_factor = 1.0 # Disable "bird view" zoom out self._img_plot.overlays.append(zoom_tool) self._img_plot.tools.append(pan) def left_clicked_event( self ): #TODO: why do we need the clicker_tool if we can handle mouse clicks here? """ left_clicked_event - processes left click mouse avents and displays coordinate and grey value information on the screen """ print("x = %d, y= %d, grey= %d " % (self._click_tool.x, self._click_tool.y, self._click_tool.data_value)) #need to priny gray value def right_clicked_event(self): self.rclicked = 1 #flag that is tracked by main_gui, for right_click_process function of main_gui #self._click_tool.y,self.name]) #self.drawcross("coord_x","coord_y",self._click_tool.x,self._click_tool.y,"red",5) #print ("right clicked, "+self.name) #need to print cross and manage other camera's crosses def update_image(self, image, is_float=False): """ update_image - displays/updates image in the curren camera window parameters: image - image data is_float - if true, displays an image as float array, else displays as byte array (B&W or gray) example usage: update_image(image,is_float=False) """ print('image shape = ', image.shape, 'is_float =', is_float) if image.ndim > 2: image = img_as_ubyte(rgb2gray(image)) is_float = False if is_float: self._plot_data.set_data('imagedata', image.astype(np.float)) else: self._plot_data.set_data('imagedata', image.astype(np.byte)) if not hasattr( self, '_img_plot'): #make a new plot if there is nothing to update self._img_plot = Instance(ImagePlot) self._img_plot = self._plot.img_plot('imagedata', colormap=gray)[0] self.attach_tools() # self._plot.request_redraw() def drawcross(self, str_x, str_y, x, y, color1, mrk_size, marker1="plus"): """ drawcross draws crosses at a given location (x,y) using color and marker in the current camera window parameters: str_x - label for x coordinates str_y - label for y coordinates x - array of x coordinates y - array of y coordinates mrk_size - marker size makrer1 - type of marker, e.g "plus","circle" example usage: drawcross("coord_x","coord_y",[100,200,300],[100,200,300],2) draws plus markers of size 2 at points (100,100),(200,200),(200,300) """ self._plot_data.set_data(str_x, x) self._plot_data.set_data(str_y, y) self._plot.plot((str_x, str_y), type="scatter", color=color1, marker=marker1, marker_size=mrk_size) #self._plot.request_redraw() def drawquiver(self, x1c, y1c, x2c, y2c, color, linewidth=1.0): """ drawquiver draws multiple lines at once on the screen x1,y1->x2,y2 in the current camera window parameters: x1c - array of x1 coordinates y1c - array of y1 coordinates x2c - array of x2 coordinates y2c - array of y2 coordinates color - color of the line linewidth - linewidth of the line example usage: drawquiver ([100,200],[100,100],[400,400],[300,200],'red',linewidth=2.0) draws 2 red lines with thickness = 2 : 100,100->400,300 and 200,100->400,200 """ x1, y1, x2, y2 = self.remove_short_lines(x1c, y1c, x2c, y2c) if len(x1) > 0: xs = ArrayDataSource(x1) ys = ArrayDataSource(y1) quiverplot=QuiverPlot(index=xs,value=ys,\ index_mapper=LinearMapper(range=self._plot.index_mapper.range),\ value_mapper=LinearMapper(range=self._plot.value_mapper.range),\ origin = self._plot.origin,arrow_size=0,\ line_color=color,line_width=linewidth,ep_index=np.array(x2),ep_value=np.array(y2) ) self._plot.add(quiverplot) self._quiverplots.append( quiverplot ) #we need this to track how many quiverplots are in the current plot # import pdb; pdb.set_trace() def remove_short_lines(self, x1, y1, x2, y2): """ removes short lines from the array of lines parameters: x1,y1,x2,y2 - start and end coordinates of the lines returns: x1f,y1f,x2f,y2f - start and end coordinates of the lines, with short lines removed example usage: x1,y1,x2,y2=remove_short_lines([100,200,300],[100,200,300],[100,200,300],[102,210,320]) 3 input lines, 1 short line will be removed (100,100->100,102) returned coordinates: x1=[200,300]; y1=[200,300]; x2=[200,300]; y2=[210,320] """ dx, dy = 2, 2 #minimum allowable dx,dy x1f, y1f, x2f, y2f = [], [], [], [] for i in range(len(x1)): if abs(x1[i] - x2[i]) > dx or abs(y1[i] - y2[i]) > dy: x1f.append(x1[i]) y1f.append(y1[i]) x2f.append(x2[i]) y2f.append(y2[i]) return x1f, y1f, x2f, y2f def drawline(self, str_x, str_y, x1, y1, x2, y2, color1): """ drawline draws 1 line on the screen by using lineplot x1,y1->x2,y2 parameters: str_x - label of x coordinate str_y - label of y coordinate x1,y1,x2,y2 - start and end coordinates of the line color1 - color of the line example usage: drawline("x_coord","y_coord",100,100,200,200,red) draws a red line 100,100->200,200 """ self._plot_data.set_data(str_x, [x1, x2]) self._plot_data.set_data(str_y, [y1, y2]) self._plot.plot((str_x, str_y), type="line", color=color1)
class DesmoratModel(Simulator, Vis2D): node_name = Str('Bond slip model') tree_node_list = List([]) def _tree_node_list_default(self): return [self.tline, self.mats_eval, self.loading_scenario] @on_trait_change('MAT,+BC') def _update_node_list(self): self.tree_node_list = [ self.tline, self.mats_eval, self.loading_scenario ] interaction_type = Trait('predefined', { 'interactive': TFunPWLInteractive, 'predefined': LoadingScenario }, BC=True, symbol='option', unit='-', desc=r'type of loading scenario, possible values:' r'[predefined, interactive]') '''Type of control - either interactive or predefined. ''' def _interaction_type_changed(self): self.loading_scenario = self.interaction_type_() loading_scenario = Instance( MFnLineArray, report=True, desc=r'object describing the loading scenario as a function') '''Loading scenario in form of a function that maps the time variable to the control slip. ''' def _loading_scenario_default(self): return self.interaction_type_() material_model = Trait('plasticity', { 'damage-plasticity': MATS1DDesmorat, }, enter_set=True, auto_set=False, MAT=True, symbol='option', unit='-', desc=r'type of material model - possible values:' r'[damage-plasticity]') '''Available material models. ''' @on_trait_change('material_model') def _set_mats_eval(self): self.mats_eval = self.material_model_() self._update_node_list() mats_eval = Instance(IMATSEval, report=True, desc='object defining the material behavior') '''Material model''' def _mats_eval_default(self): return self.material_model_() material = Property def _get_material(self): return self.mats_eval sv_names = Property(List(Str), depends_on='material_model') '''Names of state variables of the current material model. ''' @cached_property def _get_sv_names(self): return ['t', 's'] + self.mats_eval.sv_names sv_hist = Dict((Str, List)) '''Record of state variables. The number of names of the variables depend on the active material model. See s_names. ''' def _sv_hist_default(self): sv_hist = {} for sv_name in self.sv_names: sv_hist[sv_name] = [] return sv_hist @on_trait_change('MAT,BC') def _sv_hist_reset(self): for sv_name in self.sv_names: self.sv_hist[sv_name] = [] def paused(self): self._paused = True def stop(self): self._sv_hist_reset() self._restart = True self.loading_scenario.reset() _paused = Bool(False) _restart = Bool(True) n_steps = Int(1000, ALG=True, symbol='n_\mathrm{s}', unit='-', desc=r'number of increments', enter_set=True, auto_set=False) state_vars = Tuple def init(self): if self._paused: self._paused = False if self._restart: self.tline.val = self.tline.min self.state_vars = self.mats_eval.init_state_vars() self._restart = False def eval(self): '''this method is just called by the tloop_thread''' t_max = self.loading_scenario.xdata[-1] self.set_tmax(t_max) t_min = self.tline.val n_steps = self.n_steps tarray = np.linspace(t_min, t_max, n_steps) sv_names = self.sv_names sv_records = [[] for s_n in sv_names] s_last = 0 for idx, t in enumerate(tarray): if self._restart or self._paused: break s = self.loading_scenario(t) d_s = s - s_last self.state_vars = \ self.mats_eval.get_next_state(s, d_s, self.state_vars) # record the current simulation step # @todo: fix this to avoid array t_ = np.array([t], dtype=np.float_) for sv_record, state in zip(sv_records, (t_, s) + self.state_vars): sv_record.append(np.copy(state)) # append the data to the previous simulation steps for sv_name, sv_record in zip(sv_names, sv_records): self.sv_hist[sv_name].append(np.array(sv_record)) self.tline.val = min(t, self.tline.max) def get_sv_hist(self, sv_name): return np.vstack(self.sv_hist[sv_name]) viz2d_classes = { 'bond history': Viz2DBondHistory, 'load function': Viz2DLoadControlFunction, } tree_view = View(Item('material_model'), Item('interaction_type'))
class ExpandablePanel(Widget): """ An expandable panel. """ # The default style. STYLE = wx.CLIP_CHILDREN collapsed_image = Instance(ImageResource, ImageResource('mycarat1')) expanded_image = Instance(ImageResource, ImageResource('mycarat2')) ########################################################################### # 'object' interface. ########################################################################### def __init__(self, parent, **traits): """ Creates a new LayeredPanel. """ # Base class constructor. super(ExpandablePanel, self).__init__(**traits) # Create the toolkit-specific control that represents the widget. self.control = self._create_control(parent) # The layers in the panel. # # { str name : wx.Window layer } self._layers = {} self._headers = {} return ########################################################################### # 'Expandale' interface. ########################################################################### def add_panel(self, name, layer): """ Adds a layer with the specified name. All layers are hidden when they are added. Use 'show_layer' to make a layer visible. """ parent = self.control sizer = self.control.GetSizer() # Add the heading text. header = self._create_header(parent, text=name) sizer.Add(header, 0, wx.EXPAND) # Add the layer to our sizer. sizer.Add(layer, 1, wx.EXPAND) # All layers are hidden when they are added. Use 'show_layer' to make # a layer visible. sizer.Show(layer, False) # fixme: Should we warn if a layer is being overridden? self._layers[name] = layer return layer def remove_panel(self, name): """ Removes a layer and its header from the container.""" #if not self._layers.has_key(name): if not name in list(self._layers.keys()): return sizer = self.control.GetSizer() panel = self._layers[name] header = self._headers[name] #sizer.Remove(panel) panel.Destroy() #sizer.Remove(header) header.Destroy() sizer.Layout() return ########################################################################### # Private interface. ########################################################################### def _create_control(self, parent): """ Create the toolkit-specific control that represents the widget. """ panel = wx.Panel(parent, -1, style=self.STYLE) sizer = wx.BoxSizer(wx.VERTICAL) panel.SetSizer(sizer) panel.SetAutoLayout(True) return panel def _create_header(self, parent, text): """ Creates a panel header. """ sizer = wx.BoxSizer(wx.HORIZONTAL) panel = wx.Panel(parent, -1, style=wx.CLIP_CHILDREN) panel.SetSizer(sizer) panel.SetAutoLayout(True) # Add the panel header. heading = ExpandableHeader(panel, self, title=text) sizer.Add(heading.control, 1, wx.EXPAND) heading.on_trait_change(self._on_button, 'panel_expanded') # Resize the panel to match the sizer's minimum size. sizer.Fit(panel) # hang onto it for when we destroy self._headers[text] = panel return panel #### wx event handlers #################################################### def _on_button(self, event): """ called when one of the expand/contract buttons is pressed. """ header = event name = header.title visible = header.state sizer = self.control.GetSizer() sizer.Show(self._layers[name], visible) sizer.Layout() # fixme: Errrr, maybe we can NOT do this! w, h = self.control.GetSize().Get() self.control.SetSize((w + 1, h + 1)) self.control.SetSize((w, h))