class ExperimentEditor(BaseTraitsEditor): queue = Instance(ExperimentQueue, ()) # Any path = Unicode name = Property(Unicode, depends_on='path') tooltip = Property(Unicode, depends_on='path') executed = DelegatesTo('queue') tabular_adapter_klass = AutomatedRunSpecAdapter executed_tabular_adapter_klass = ExecutedAutomatedRunSpecAdapter bgcolor = Color tabular_adapter = Instance(AutomatedRunSpecAdapter) executed_tabular_adapter = Instance(ExecutedAutomatedRunSpecAdapter) automated_runs_editable = Bool table_configurer = Instance(ExperimentTableConfigurer) def show_table_configurer(self): t = self.table_configurer t.edit_traits() def refresh(self): self.queue.refresh_table_needed = True def setup_tabular_adapters(self, c, ec, colors): self.bgcolor = c self.tabular_adapter = self.tabular_adapter_klass() self.executed_tabular_adapter = self.executed_tabular_adapter_klass() self.executed_tabular_adapter.colors = colors self.tabular_adapter.odd_bg_color = c self.executed_tabular_adapter.odd_bg_color = c self.tabular_adapter.even_bg_color = ec self.executed_tabular_adapter.even_bg_color = ec v = ExperimentTableConfigurer(adapter=self.tabular_adapter, children=[self.executed_tabular_adapter], auto_set=True, refresh_func=self.refresh, id='experiment.table') self.table_configurer = v def new_queue(self, txt=None, **kw): queue = self.queue_factory(**kw) if txt: if queue.load(txt): self.queue = queue else: self.warning('failed to load queue') else: self.queue = queue def queue_factory(self, **kw): print 'application', self.application return ExperimentQueue(application=self.application, **kw) def save(self, path, queues=None): if queues is None: queues = [self.queue] if self._validate_experiment_queues(queues): path = self._dump_experiment_queues(path, queues) if path: self.path = path self.dirty = False return True def traits_view(self): # show row titles is causing a layout issue when resetting queues # disabling show_row_titles for the moment. operations = ['delete', 'move'] if self.automated_runs_editable: operations.append('edit') arun_grp = UItem( 'automated_runs', editor=myTabularEditor( adapter=self.tabular_adapter, operations=operations, bgcolor=self.bgcolor, editable=True, mime_type='pychron.automated_run_spec', # show_row_titles=True, dclicked='dclicked', selected='selected', paste_function='paste_function', update='refresh_table_needed', refresh='refresh_table_needed', scroll_to_row='automated_runs_scroll_to_row', # copy_cache='linked_copy_cache', stretch_last_section=False, multi_select=True), height=200) executed_grp = UItem( 'executed_runs', editor=myTabularEditor( adapter=self.executed_tabular_adapter, bgcolor=self.bgcolor, editable=False, auto_update=True, selectable=True, pastable=False, mime_type='pychron.automated_run_spec', # link_copyable=False, paste_function='executed_paste_function', # copy_cache='linked_copy_cache', selected='executed_selected', multi_select=True, stretch_last_section=False, scroll_to_row='executed_runs_scroll_to_row'), height=500, visible_when='executed') v = View(executed_grp, arun_grp, handler=ExperimentEditorHandler(), resizable=True) return v def trait_context(self): """ Use the model object for the Traits UI context, if appropriate. """ if self.queue: return {'object': self.queue} return super(ExperimentEditor, self).trait_context() # =============================================================================== # handlers # =============================================================================== # def _dirty_changed(self): # self.debug('dirty changed {}'.format(self.dirty)) def _queue_changed(self, old, new): f = self._set_queue_dirty if old: old.on_trait_change(f, 'automated_runs[]', remove=True) old.on_trait_change(f, 'changed', remove=True) new.on_trait_change(f, 'automated_runs[]') new.on_trait_change(f, 'changed') new.path = self.path def _path_changed(self): self.queue.path = self.path def _set_queue_dirty(self, obj, name, old, new): if not self.queue._no_update and self.queue.initialized: self.dirty = True def _validate_experiment_queues(self, eqs): # check runs curtag = get_curtag() for qi in eqs: runs = qi.cleaned_automated_runs no_repo = [] for i, ai in enumerate(runs): if not ai.repository_identifier: self.warning( 'No repository identifier for i={}, {}'.format( i + 1, ai.runid)) no_repo.append(ai) if no_repo: if not self.confirmation_dialog( 'Missing repository identifiers. Automatically populate?' ): break populate_repository_identifiers(runs, qi.mass_spectrometer, curtag, debug=self.debug) hec = qi.human_error_checker qi.executable = True qi.initialized = True err = hec.check_runs(runs, test_all=True, test_scripts=True) if err: qi.executable = False qi.initialized = False hec.report_errors(err) # self.information_dialog(err) break err = hec.check_queue(qi) if err: break else: return True def _dump_experiment_queues(self, p, queues): if not p: return p = add_extension(p) self.info('saving experiment to {}'.format(p)) with open(p, 'wb') as wfile: n = len(queues) for i, exp in enumerate(queues): exp.path = p exp.dump(wfile) if i < (n - 1): wfile.write('\n') wfile.write('*' * 80) return p # =============================================================================== # property get/set # =============================================================================== def _get_tooltip(self): return self.path def _get_name(self): if self.path: name = os.path.basename(self.path) name, _ = os.path.splitext(name) else: name = 'Untitled' return name
class VideoServer(Loggable): video = Instance(Video) port = Int(1084) quality = Int(75) _started = False use_color = True start_button = Button start_label = Property(depends_on='_started') _started = Bool(False) def _get_start_label(self): return 'Start' if not self._started else 'Stop' def _start_button_fired(self): if self._started: self.stop() else: self.start() def traits_view(self): v = View(Item('start_button', editor=ButtonEditor(label_value='start_label'))) return v def _video_default(self): return Video(swap_rb=True) def stop(self): # if self._started: self.info('stopping video server') self._stop_signal.set() self._started = False def start(self): self.info('starting video server') self._new_frame_ready = Event() self._stop_signal = Event() self.video.open(user='******') bt = Thread(name='broadcast', target=self._broadcast) bt.start() self.info('video server started') self._started = True def _broadcast(self): # new_frame = self._new_frame_ready self.info('video broadcast thread started') context = zmq.Context() # sock = context.socket(zmq.PUB) sock = context.socket(zmq.REP) sock.bind('tcp://*:{}'.format(self.port)) poll = zmq.Poller() poll.register(sock, zmq.POLLIN) self.request_reply(sock, poll) # if use_color: # kw = dict(swap_rb=True) # depth = 3 # else: # kw = dict(gray=True) # depth = 1 # pt = time.time() def request_reply(self, sock, poll): stop = self._stop_signal video = self.video fps = 10 from . import Image from cStringIO import StringIO quality = self.quality while not stop.isSet(): socks = dict(poll.poll(100)) if socks.get(sock) == zmq.POLLIN: resp = sock.recv() if resp == 'FPS': buf = str(fps) elif resp.startswith('QUALITY'): quality = int(resp[7:]) buf = '' else: f = video.get_frame() # new_frame.clear() im = Image.fromarray(array(f)) s = StringIO() im.save(s, 'JPEG', quality=quality) buf = s.getvalue() sock.send(buf) def publisher(self, sock): stop = self._stop_signal video = self.video use_color = self.use_color fps = 10 from . import Image from cStringIO import StringIO while not stop.isSet(): f = video.get_frame(gray=False) # new_frame.clear() im = Image.fromarray(array(f)) s = StringIO() im.save(s, 'JPEG') sock.send(str(fps)) sock.send(s.getvalue()) time.sleep(1.0 / fps)
class ExRun(HasTraits): ''' Represent a single test specifying the design parameters. and access to the measured data. ''' data_dir = Str exdesign_reader = WeakRef def __init__(self, exdesign_reader, row, **kw): '''Retrieve the traits from the exdesign reader ''' self.exdesign_reader = exdesign_reader factors = self.exdesign_reader.exdesign_spec.factors for idx, ps in enumerate(factors): cmd = '%s( %s("%s") )' % (ps[0], ps[1], row[idx]) self.add_trait(ps[2], eval(cmd)) super(ExRun, self).__init__(**kw) data_file = File def _data_file_default(self): return os.path.join(self.data_dir, self._get_file_name()) @on_trait_change('data_file') def _reset_data_file(self): self.data_file = os.path.join(self.data_dir, self._get_file_name()) def _get_file_name(self): return eval(self.exdesign_reader.exdesign_spec.data_file_name) _arr = Property(Array(float), depends_on='data_file') def _get__arr(self): return loadtxt( self.data_file, skiprows=2, delimiter=self.exdesign_reader.exdesign_spec.data_delimiter, converters=self.exdesign_reader.exdesign_spec.data_converters) xdata = Property(Array(float), depends_on='data_file') @cached_property def _get_xdata(self): return self._arr[:, 0] ydata = Property(Array(float), depends_on='data_file') @cached_property def _get_ydata(self): return self._arr[:, 1] max_stress_idx = Property(Int) def _get_max_stress_idx(self): return argmax(self._get_ydata()) max_stress = Property(Float) def _get_max_stress(self): return self.ydata[self.max_stress_idx] strain_at_max_stress = Property(Float) def _get_strain_at_max_stress(self): return self.xdata[self.max_stress_idx] # get the ascending branch of the response curve xdata_asc = Property(Array(float)) def _get_xdata_asc(self): return self.xdata[:self.max_stress_idx + 1] ydata_asc = Property(Array(float)) def _get_ydata_asc(self): return self.ydata[:self.max_stress_idx + 1] polyfit = Property(Any, depends_on='data_file') def _get_polyfit(self): # # get the fit with 10-th-order polynomial # p = polyfit(self.xdata_asc, self.ydata_asc, 5) # # define the polynomial function # return poly1d(p) # interplate the polynomial pfun = Property(Array(float), depends_on='data_file') @cached_property def _get_pfun(self): '''Define universal function for the value (used just for visualization) ''' return frompyfunc(self.polyfit, 1, 1) xdata_asc_fit = Property(Array(float), depends_on='data_file') def _get_xdata_asc_fit(self): ''' Discretize the ascending parts in 100 equidistant segments ''' return linspace(0, self.xdata_asc[-1], 101) ydata_asc_fit = Property(Array(float), depends_on='data_file') def _get_ydata_asc_fit(self): ''' Evaluate the polynomial fit on the x grid ''' return array(self.pfun(self.xdata_asc_fit), dtype=float) # interplate the polynomial pfun_der = Property(Array(float), depends_on='data_file') @cached_property def _get_pfun_der(self): '''Get the ufunc for the fitted polynomial derivative ''' d_pf = polyder(self.polyfit, 1) # # Construct the universal function for the derivative # return frompyfunc(d_pf, 1, 1) d_ydata_asc_fit = Property(Array(float), depends_on='data_file') @cached_property def _get_d_ydata_asc_fit(self): '''Evaluate the function at the x-points of the original measurements. ''' return array(self.pfun_der(self.xdata_asc_fit), 'float_') strain_at_max_stiffness = Property(Float, depends_on='data_file') def _get_strain_at_max_stiffness(self): # # Find the index of the maximum derivative # idx = argmax(self.d_ydata_asc_fit) # # Get the data itself # return self.xdata_asc_fit[idx] max_stiffness = Property(Float, depends_on='data_file') def _get_max_stiffness(self): # # evaluate the maximum # x_at_max_d_y = self.strain_at_max_stiffness return self.pfun_der(x_at_max_d_y) def get_linear_data(self): # # stress_at_max_stiffness = self.pfun(self.strain_at_max_stiffness) strain_0 = self.strain_at_max_stiffness - \ stress_at_max_stiffness / self.max_stiffness xdata = array([ strain_0, self.strain_at_max_stiffness, self.strain_at_max_stress, self.strain_at_max_stress ], dtype='float_') ydata = array([ 0., stress_at_max_stiffness, (self.strain_at_max_stress - strain_0) * self.max_stiffness, 0. ], dtype='float_') return xdata, ydata traits_view = View( Item('data_dir', style='readonly'), Item('max_stress_idx', style='readonly'), Item('max_stress', style='readonly'), Item('strain_at_max_stress', style='readonly'), Item('max_stiffness', style='readonly'), )
class TStepper(HasTraits): mats_eval = Instance(MATSEval, arg=(), kw={}) # material model fets_eval = Instance(FETS1D52ULRH, arg=(), kw={}) # element formulation A = Property() '''array containing the A_m, L_b, A_f ''' def _get_A(self): return np.array( [self.fets_eval.A_m, self.fets_eval.P_b, self.fets_eval.A_f]) # Number of elements n_e_x = 30 # length L_x = Float(600.0) domain = Property(Instance(FEGrid), depends_on='L_x') '''Diescretization object. ''' @cached_property def _get_domain(self): # Element definition domain = FEGrid(coord_max=(self.L_x, ), shape=(self.n_e_x, ), fets_eval=self.fets_eval) return domain bc_list = List(Instance(BCDof)) J_mtx = Property(depends_on='L_x') '''Array of Jacobian matrices. ''' @cached_property def _get_J_mtx(self): fets_eval = self.fets_eval domain = self.domain # [ d, n ] geo_r = fets_eval.geo_r.T # [ d, n, i ] dNr_geo = geo_r[:, :, None] * np.array([1, 1]) * 0.5 # [ i, n, d ] dNr_geo = np.einsum('dni->ind', dNr_geo) # [ n_e, n_geo_r, n_dim_geo ] elem_x_map = domain.elem_X_map # [ n_e, n_ip, n_dim_geo, n_dim_geo ] J_mtx = np.einsum('ind,enf->eidf', dNr_geo, elem_x_map) return J_mtx J_det = Property(depends_on='L_x') '''Array of Jacobi determinants. ''' @cached_property def _get_J_det(self): return np.linalg.det(self.J_mtx) B = Property(depends_on='L_x') '''The B matrix ''' @cached_property def _get_B(self): '''Calculate and assemble the system stiffness matrix. ''' mats_eval = self.mats_eval fets_eval = self.fets_eval domain = self.domain n_s = mats_eval.n_s n_dof_r = fets_eval.n_dof_r n_nodal_dofs = fets_eval.n_nodal_dofs n_ip = fets_eval.n_gp n_e = domain.n_active_elems #[ d, i] r_ip = fets_eval.ip_coords[:, :-2].T # [ d, n ] geo_r = fets_eval.geo_r.T J_inv = np.linalg.inv(self.J_mtx) # shape function for the unknowns # [ d, n, i] Nr = 0.5 * (1. + geo_r[:, :, None] * r_ip[None, :]) dNr = 0.5 * geo_r[:, :, None] * np.array([1, 1]) # [ i, n, d ] Nr = np.einsum('dni->ind', Nr) dNr = np.einsum('dni->ind', dNr) Nx = Nr # [ n_e, n_ip, n_dof_r, n_dim_dof ] dNx = np.einsum('eidf,inf->eind', J_inv, dNr) B = np.zeros((n_e, n_ip, n_dof_r, n_s, n_nodal_dofs), dtype='f') B_N_n_rows, B_N_n_cols, N_idx = [1, 1], [0, 1], [0, 0] B_dN_n_rows, B_dN_n_cols, dN_idx = [0, 2], [0, 1], [0, 0] B_factors = np.array([-1, 1], dtype='float_') B[:, :, :, B_N_n_rows, B_N_n_cols] = (B_factors[None, None, :] * Nx[:, :, N_idx]) B[:, :, :, B_dN_n_rows, B_dN_n_cols] = dNx[:, :, :, dN_idx] return B def apply_essential_bc(self): '''Insert initial boundary conditions at the start up of the calculation.. ''' self.K = SysMtxAssembly() for bc in self.bc_list: bc.apply_essential(self.K) def apply_bc(self, step_flag, K_mtx, F_ext, t_n, t_n1): '''Apply boundary conditions for the current load increement ''' for bc in self.bc_list: bc.apply(step_flag, None, K_mtx, F_ext, t_n, t_n1) def get_corr_pred(self, step_flag, U, d_U, eps, sig, t_n, t_n1, alpha, q, kappa): '''Function calculationg the residuum and tangent operator. ''' mats_eval = self.mats_eval fets_eval = self.fets_eval domain = self.domain elem_dof_map = domain.elem_dof_map n_e = domain.n_active_elems n_dof_r, n_dim_dof = self.fets_eval.dof_r.shape n_nodal_dofs = self.fets_eval.n_nodal_dofs n_el_dofs = n_dof_r * n_nodal_dofs # [ i ] w_ip = fets_eval.ip_weights d_u_e = d_U[elem_dof_map] #[n_e, n_dof_r, n_dim_dof] d_u_n = d_u_e.reshape(n_e, n_dof_r, n_nodal_dofs) #[n_e, n_ip, n_s] d_eps = np.einsum('einsd,end->eis', self.B, d_u_n) # material response state variables at integration point sig, D, alpha, q, kappa = mats_eval.get_corr_pred( eps, d_eps, sig, t_n, t_n1, alpha, q, kappa) # update strain ---this should be integrated into the material model eps += d_eps # system matrix self.K.reset_mtx() Ke = np.einsum('i,s,einsd,eist,eimtf,ei->endmf', w_ip, self.A, self.B, D, self.B, self.J_det) self.K.add_mtx_array(Ke.reshape(-1, n_el_dofs, n_el_dofs), elem_dof_map) # internal forces # [n_e, n_n, n_dim_dof] Fe_int = np.einsum('i,s,eis,einsd,ei->end', w_ip, self.A, sig, self.B, self.J_det) F_int = -np.bincount(elem_dof_map.flatten(), weights=Fe_int.flatten()) self.apply_bc(step_flag, self.K, F_int, t_n, t_n1) return F_int, self.K, eps, sig, alpha, q, kappa
class Dimensions(HasTraits): """The dimensions of a physical quantity. This is essentially a thin wrapper around a dictionary which we perform certain operations on. Example ------- >>> m = Dimensions({'mass': 1.0}) >>> a = Dimensions({'length': 1.0, 'time': -2.0}) >>> f = Dimensions({'length': 1.0, 'mass': 1.0, 'time': -2.0}) >>> f == m*a True >>> f.expansion "length*mass*time**-2.0" """ # a dictionary holding dimension names and quantities # this should be frozen if you want to hash - don't change it dimension_dict = DictStrFloat # the quantity type as an expression in powers of base dimensions expansion = Property(String, depends_on='dimension_dict') def __init__(self, dimension_dict, **kwargs): for key, value in dimension_dict.items(): if not value: del dimension_dict[key] super(self.__class__, self).__init__(dimension_dict=dimension_dict, **kwargs) @classmethod def from_expansion(cls, expansion): """Create a Dimension class instance from an expansion string This is a fairly simplistic parser - no parens, division, etc. Parameters ---------- expansion : string an expansion of the dimensions (eg. mass*length**-3.0) """ terms = expansion.split("*") dimension_dict = {} try: while terms: dim = terms.pop(0) if terms[0] == "": terms.pop(0) power = float(terms.pop(0)) dimension_dict[dim] = dimension_dict.get(dim, 0) + power except: raise InvalidExpansionError(expansion) return cls(dimension_dict) @cached_property def _get_expansion(self): if self.dimension_dict: return format_expansion(self.dimension_dict) else: return "dimensionless" def __repr__(self): return "Dimensions(%s)" % repr(self.dimension_dict) def __str__(self): return self.expansion def __eq__(self, other): return isinstance(other, self.__class__) \ and self.dimension_dict == other.dimension_dict def __hash__(self): return hash(tuple(item for item in self.dimension_dict.items())) def __mul__(self, other): if isinstance(other, Dimensions): return Dimensions( dict_add(self.dimension_dict, other.dimension_dict)) else: raise NotImplementedError def __div__(self, other): if isinstance(other, Dimensions): return Dimensions( dict_sub(self.dimension_dict, other.dimension_dict)) else: raise NotImplementedError def __pow__(self, other): if isinstance(other, (float, int, long)): return Dimensions(dict_mul(self.dimension_dict, other)) else: raise NotImplementedError
class ImageResource(MImageResource, HasTraits): """ The toolkit specific implementation of an ImageResource. See the IImageResource interface for the API documentation. """ # Private interface ---------------------------------------------------- # The resource manager reference for the image. _ref = Any() # 'ImageResource' interface -------------------------------------------- absolute_path = Property(Str) name = Str() search_path = List() # ------------------------------------------------------------------------ # 'ImageResource' interface. # ------------------------------------------------------------------------ def create_bitmap(self, size=None): return self.create_image(size).ConvertToBitmap() def create_icon(self, size=None): ref = self._get_ref(size) if ref is not None: icon = wx.Icon(self.absolute_path, wx.BITMAP_TYPE_ANY) else: image = self._get_image_not_found_image() # We have to convert the image to a bitmap first and then create an # icon from that. bmp = image.ConvertToBitmap() icon = wx.Icon() icon.CopyFromBitmap(bmp) return icon def image_size(cls, image): """ Get the size of a toolkit image Parameters ---------- image : toolkit image A toolkit image to compute the size of. Returns ------- size : tuple The (width, height) tuple giving the size of the image. """ size = image.GetSize() return size.Get() # ------------------------------------------------------------------------ # Private interface. # ------------------------------------------------------------------------ def _get_absolute_path(self): # FIXME: This doesn't quite wotk the new notion of image size. We # should find out who is actually using this trait, and for what! # (AboutDialog uses it to include the path name in some HTML.) ref = self._get_ref() if ref is not None: absolute_path = os.path.abspath(self._ref.filename) else: absolute_path = self._get_image_not_found().absolute_path return absolute_path
class Plugin(ExtensionProvider): """ The default implementation of the 'IPlugin' interface. This class is intended to be subclassed for each plugin that you create. """ #### 'IPlugin' interface ################################################## # The activator used to start and stop the plugin. # # By default the *same* activator instance is used for *all* plugins of # this type. activator = Instance(IPluginActivator, PluginActivator()) # The application that the plugin is part of. application = Instance(IApplication) # The name of a directory (created for you) that the plugin can read and # write to at will. home = Str # The plugin's unique identifier. # # If no identifier is specified then the module and class name of the # plugin are used to create an Id with the form 'module_name.class_name'. id = Str # The plugin's name (suitable for displaying to the user). # # If no name is specified then the plugin's class name is used with an # attempt made to turn camel-case class names into words separated by # spaces (e.g. if the class name is 'MyPlugin' then the name would be # 'My Plugin'). Of course, if you really care about the actual name, then # just set it! name = Str #### 'IExtensionPointUser' interface ###################################### # The extension registry that the object's extension points are stored in. extension_registry = Property(Instance(IExtensionRegistry)) #### 'IServiceUser' interface ############################################# # The service registry that the object's services are stored in. service_registry = Property(Instance(IServiceRegistry)) #### Private interface #################################################### # The Ids of the services that were automatically registered. _service_ids = List ########################################################################### # 'IExtensionPointUser' interface. ########################################################################### def _get_extension_registry(self): """ Trait property getter. """ return self.application ########################################################################### # 'IServiceUser' interface. ########################################################################### def _get_service_registry(self): """ Trait property getter. """ return self.application ########################################################################### # 'IExtensionProvider' interface. ########################################################################### def get_extension_points(self): """ Return the extension points offered by the provider. """ extension_points = [ trait.trait_type for trait in self.traits(__extension_point__=True).values() ] return extension_points def get_extensions(self, extension_point_id): """ Return the provider's extensions to an extension point. """ # Each class can have at most *one* trait that contributes to a # particular extension point. # # fixme: We make this restriction in case that in future we can wire up # the list traits directly. If we don't end up doing that then it is # fine to allow mutiple traits! trait_names = self.trait_names(contributes_to=extension_point_id) # FIXME: This is a temporary fix, which was necessary due to the # namespace refactor, but should be removed at some point. if len(trait_names) == 0: old_id = 'enthought.' + extension_point_id trait_names = self.trait_names(contributes_to=old_id) # if trait_names: # print 'deprecated:', old_id if len(trait_names) == 0: # If there is no contributing trait then look for any decorated # methods. extensions = self._harvest_methods(extension_point_id) # FIXME: This is a temporary fix, which was necessary due to the # namespace refactor, but should be removed at some point. if not extensions: old_id = 'enthought.' + extension_point_id extensions = self._harvest_methods(old_id) # if extensions: # print 'deprecated:', old_id elif len(trait_names) == 1: extensions = self._get_extensions_from_trait(trait_names[0]) else: raise self._create_multiple_traits_exception(extension_point_id) return extensions ########################################################################### # 'IPlugin' interface. ########################################################################### #### Trait initializers ################################################### def _home_default(self): """ Trait initializer. """ # Each plugin gets a sub-directory of a 'plugins' directory in # 'application.home'. # # i.e. .../my.application.id/plugins/ plugins_dir = join(self.application.home, 'plugins') if not exists(plugins_dir): os.mkdir(plugins_dir) # Now create the 'home' directory of this plugin. home_dir = join(plugins_dir, self.id) if not exists(home_dir): os.mkdir(home_dir) return home_dir def _id_default(self): """ Trait initializer. """ id = '%s.%s' % (type(self).__module__, type(self).__name__) logger.warning('plugin %s has no Id - using <%s>' % (self, id)) return id def _name_default(self): """ Trait initializer. """ name = camel_case_to_words(type(self).__name__) logger.warning('plugin %s has no name - using <%s>' % (self, name)) return name #### Methods ############################################################## def start(self): """ Start the plugin. This method will *always* be empty so that you never have to call 'super(xxx, self).start()' if you provide an implementation in a derived class. The framework does what it needs to do when it starts a plugin by means of the plugin's activator. """ pass def stop(self): """ Stop the plugin. This method will *always* be empty so that you never have to call 'super(xxx, self).stop()' if you provide an implementation in a derived class. The framework does what it needs to do when it stops a plugin by means of the plugin's activator. """ pass ########################################################################### # 'Plugin' interface. ########################################################################### def connect_extension_point_traits(self): """ Connect all of the plugin's extension points. This means that the plugin will be notified if and when contributions are add or removed. """ ExtensionPoint.connect_extension_point_traits(self) return def disconnect_extension_point_traits(self): """ Disconnect all of the plugin's extension points.""" ExtensionPoint.disconnect_extension_point_traits(self) return def register_services(self): """ Register the services offered by the plugin. """ for trait_name, trait in self.traits(service=True).items(): logger.warning( 'DEPRECATED: Do not use the "service=True" metadata anymore. ' 'Services should now be offered using the service ' 'offer extension point (envisage.service_offers) ' 'from the core plugin. ' 'Plugin %s trait <%s>' % (self, trait_name)) # Register a service factory for the trait. service_id = self._register_service_factory(trait_name, trait) # We save the service Id so that so that we can unregister the # service when the plugin is stopped. self._service_ids.append(service_id) return def unregister_services(self): """ Unregister any service offered by the plugin. """ # Unregister the services in the reverse order that we registered # them. service_ids = self._service_ids[:] service_ids.reverse() for service_id in service_ids: self.application.unregister_service(service_id) # Just in case the plugin is started again! self._service_ids = [] return ########################################################################### # Private interface. ########################################################################### #### Trait change handlers ################################################ def _anytrait_changed(self, trait_name, old, new): """ Static trait change handler. """ # Ignore the '_items' part of the trait name (if it is there!), and get # the actual trait. base_trait_name = trait_name.split('_items')[0] trait = self.trait(base_trait_name) # If the trait is one that contributes to an extension point then fire # an appropriate 'extension point changed' event. if trait.contributes_to is not None: if trait_name.endswith('_items'): added = new.added removed = new.removed index = new.index else: added = new removed = old index = slice(0, max(len(old), len(new))) # Let the extension registry know about the change. self._fire_extension_point_changed(trait.contributes_to, added, removed, index) return #### Methods ############################################################## def _create_multiple_traits_exception(self, extension_point_id): """ Create the exception raised when multiple traits are found. """ exception = ValueError( 'multiple traits for extension point <%s> in plugin <%s>' % (extension_point_id, self.id)) return exception def _get_extensions_from_trait(self, trait_name): """ Return the extensions contributed via the specified trait. """ try: extensions = getattr(self, trait_name) except: logger.exception('getting extensions from %s, trait <%s>' % (self, trait_name)) raise return extensions def _get_service_protocol(self, trait): """ Determine the protocol to register a service trait with. """ # If a specific protocol was specified then use it. if trait.service_protocol is not None: protocol = trait.service_protocol # Otherwise, use the type of the objects that can be assigned to the # trait. # # fixme: This works for 'Instance' traits, but what about 'AdaptsTo' # and 'Supports' traits? else: # Note that in traits the protocol can be an actual class or # interfacem or the *name* of a class or interface. This allows # us to lazy load them! protocol = trait.trait_type.klass return protocol def _harvest_methods(self, extension_point_id): """ Harvest all method-based contributions. """ extensions = [] # Using inspect.getmembers(self) here will cause an infinite recursion, # so use an internal HasTraits method for inspecting the MRO of the # instance's type to find all methods instead. for name in self._each_trait_method(self): value = getattr(self, name) if self._is_extension_method(value, extension_point_id): result = value() if not isinstance(result, list): result = [result] extensions.extend(result) return extensions def _is_extension_method(self, value, extension_point_id): """ Return True if the value is an extension method. i.e. If the method is one that makes a contribution to the extension point. Currently there is exactly one way to make a method make a contribution, and that is to mark it using the 'contributes_to' decorator, e.g:: @contributes_to('acme.motd.messages') def get_messages(self): ... messages = [...] ... return messages """ is_extension_method = inspect.ismethod(value) \ and extension_point_id == getattr(value,'__extension_point__',None) return is_extension_method def _register_service_factory(self, trait_name, trait): """ Register a service factory for the specified trait. """ # Determine the protocol that the service should be registered with. protocol = self._get_service_protocol(trait) # Register a factory for the service so that it will be lazily loaded # the first time somebody asks for a service with the same protocol # (this could obviously be a lambda function, but I thought it best to # be more explicit 8^). def factory(**properties): """ A service factory. """ return getattr(self, trait_name) return self.application.register_service(protocol, factory)
class ColorBar(AbstractPlotRenderer): """ A color bar for a color-mapped plot. """ # Screen mapper for index data. index_mapper = Instance(AbstractMapper) # Screen mapper for color data color_mapper = Property #Instance(ColorMapper) # Screen mapper for value data (synonym for color_mapper) value_mapper = Property(depends_on='color_mapper') # Optional index data source for generic tools to attach metadata to. index = Property # Optional color-mapped plot that this color bar references. If specified, # the plot must have a **color_mapper** attribute. plot = Any # Is there a visible grid on the colorbar? grid_visible = Bool(True) # Is there a visible axis on the colorbar? axis_visible = Bool(True) # Corresponds to either **index_mapper** or None, depending on # the orientation of the plot. x_mapper = Property # Corresponds to either **index_mapper** or None, depending on # the orientation of the plot. y_mapper = Property #------------------------------------------------------------------------ # Override default values of inherited traits #------------------------------------------------------------------------ # The border is visible (overrides enable.Component). border_visible = True # The orientation of the index axis. orientation = Enum('v', 'h') # Should the bar go left-to-right or bottom-to-top (normal) or the reverse? direction = Enum('normal', 'flipped') # Overrides the default background color trait in PlotComponent. bgcolor = 'transparent' # Draw layers in "draw order" use_draw_order = True # Default width is 40 pixels (overrides enable.CoordinateBox) width = 40 # Faux origin for the axis to look at origin = Enum('bottom left', 'top left', 'bottom right', 'top right') #------------------------------------------------------------------------ # Private attributes #------------------------------------------------------------------------ # The grid _grid = Instance(PlotGrid) # The axis _axis = Instance(PlotAxis) # Shadow attribute for color_mapper _color_mapper = Any # Shadow attribute for index _index = Instance(ArrayDataSource, args=()) def __init__(self, *args, **kw): """ In creating an instance, this method ensures that the grid and the axis are created before setting their visibility. """ grid_visible = kw.pop("grid_visible", True) axis_visible = kw.pop("axis_visible", True) super(ColorBar, self).__init__(*args, **kw) if self.orientation == 'h': if self.direction == 'normal': self.origin = 'bottom left' else: self.origin = 'bottom right' grid_orientation = 'vertical' axis_orientation = 'bottom' else: if self.direction == 'normal': self.origin = 'bottom left' else: self.origin = 'top left' grid_orientation = 'horizontal' axis_orientation = 'left' self._grid = PlotGrid(orientation=grid_orientation, mapper=self.index_mapper, component=self) self._axis = PlotAxis(orientation=axis_orientation, mapper=self.index_mapper, component=self) self.overlays.append(self._grid) self.overlays.append(self._axis) # Now that we have a grid and an axis, we can safely set the visibility self.grid_visible = grid_visible self.axis_visible = axis_visible return def _draw_plot(self, gc, view_bounds=None, mode='normal'): """ Draws the 'plot' layer. """ self._update_mappers() with gc: if self.orientation == 'h': perpendicular_dim = 1 axis_dim = 0 else: perpendicular_dim = 0 axis_dim = 1 mapper = self.index_mapper scrn_points = arange(mapper.low_pos, mapper.high_pos+1) # Get the data values associated with the list of screen points. if mapper.range.low == mapper.range.high: # LogMapper.map_data() returns something unexpected if low==high, # so we'll handle that case here. data_points = array([mapper.range.high]) else: data_points = mapper.map_data(scrn_points) if self.direction == 'flipped': data_points = data_points[::-1] # Get the colors associated with the data points. colors = self.color_mapper.map_screen(data_points) img = self._make_color_image(colors, self.bounds[perpendicular_dim], self.orientation, self.direction) gc.draw_image(img, (self.x, self.y, self.width, self.height)) def _make_color_image(self, color_values, width, orientation, direction): """ Returns an image graphics context representing the array of color values (Nx3 or Nx4). The *width* parameter is the width of the colorbar, and *orientation* is the orientation of the plot. """ bmparray = ones((width, color_values.shape[0], color_values.shape[1]))* color_values * 255 if orientation == "v": bmparray = transpose(bmparray, axes=(1,0,2))[::-1] bmparray = bmparray.astype(uint8) img = GraphicsContext(bmparray, "rgba32") return img #------------------------------------------------------------------------ # Trait events #------------------------------------------------------------------------ def _update_mappers(self): if not self.index_mapper or not self.color_mapper: return if self.orientation == 'h' and 'left' in self.origin: self.index_mapper.low_pos = self.x self.index_mapper.high_pos = self.x2 elif self.orientation == 'h' and 'right' in self.origin: self.index_mapper.low_pos = self.x2 self.index_mapper.high_pos = self.x elif self.orientation == 'v' and 'bottom' in self.origin: self.index_mapper.low_pos = self.y self.index_mapper.high_pos = self.y2 elif self.orientation == 'v' and 'top' in self.origin: self.index_mapper.low_pos = self.y2 self.index_mapper.high_pos = self.y self.index_mapper.range = self.color_mapper.range def _bounds_changed(self, old, new): super(ColorBar, self)._bounds_changed(old, new) self._update_mappers() def _bounds_items_changed(self, event): super(ColorBar, self)._bounds_items_changed(event) self._update_mappers() def _position_changed(self, old, new): super(ColorBar, self)._position_changed(old, new) self._update_mappers() def _position_items_changed(self, event): super(ColorBar, self)._position_items_changed(event) self._update_mappers() def _updated_changed_for_index_mapper(self): self._update_mappers() def _updated_changed_for_color_mapper(self): self._update_mappers() @on_trait_change('[index_mapper,color_mapper].+') def _either_mapper_changed(self): self.invalidate_draw() self.request_redraw() def _index_mapper_changed(self): # Keep the grid and axis index_mappers the same as our index_mapper. if self._grid is not None: self._grid.mapper = self.index_mapper if self._axis is not None: self._axis.mapper = self.index_mapper self._either_mapper_changed() def _color_mapper_changed(self): self._either_mapper_changed() def _value_mapper_changed(self): self._color_mapper_changed() def _plot_changed(self): self.request_redraw() def _grid_visible_changed(self, old, new): self._grid.visible = new self.request_redraw() def _axis_visible_changed(self, old, new): self._axis.visible = new self.request_redraw() #------------------------------------------------------------------------ # Property setters and getters #------------------------------------------------------------------------ def _get_x_mapper(self): if self.orientation == "h": return self.index_mapper else: return None def _get_y_mapper(self): if self.orientation == "h": return None else: return self.index_mapper def _get_color_mapper(self): if self.plot: return self.plot.color_mapper elif self._color_mapper: return self._color_mapper else: return None def _set_color_mapper(self, val): self._color_mapper = val @cached_property def _get_value_mapper(self): return self._get_color_mapper() def _set_value_mapper(self, val): self._set_color_mapper(val) def _get_index(self): if self.plot and hasattr(self.plot, "color_data"): return self.plot.color_data elif self.plot and isinstance(self.plot, BaseXYPlot): return self.plot.index elif self._index: return self._index else: return None def _set_index(self, val): self._index = val
class BarPlot(AbstractPlotRenderer): """ A renderer for bar charts. """ #: The data source to use for the index coordinate. index = Instance(ArrayDataSource) #: The data source to use as value points. value = Instance(ArrayDataSource) #: The data source to use as "starting" values for bars (along value axis). #: For instance, if the values are [10, 20] and starting_value #: is [3, 7], BarPlot will plot two bars, one between 3 and 10, and #: one between 7 and 20 starting_value = Instance(ArrayDataSource) #: Labels for the indices. index_mapper = Instance(AbstractMapper) #: Labels for the values. value_mapper = Instance(AbstractMapper) #: The orientation of the index axis. orientation = Enum("h", "v") #: The direction of the index axis with respect to the graphics context's #: direction. index_direction = Enum("normal", "flipped") #: The direction of the value axis with respect to the graphics context's #: direction. value_direction = Enum("normal", "flipped") #: Type of width used for bars: #: #: 'data' #: The width is in the units along the x-dimension of the data space. #: 'screen' #: The width uses a fixed width of pixels. bar_width_type = Enum("data", "screen") #: Width of the bars, in data or screen space (determined by #: **bar_width_type**). bar_width = Float(10) #: Round on rectangle dimensions? This is not strictly an "antialias", but #: it has the same effect through exact pixel drawing. antialias = Bool(True) #: Width of the border of the bars. line_width = Float(1.0) #: Color of the border of the bars. line_color = black_color_trait #: Color to fill the bars. fill_color = black_color_trait #: The RGBA tuple for rendering lines. It is always a tuple of length 4. #: It has the same RGB values as :attr:`line_color`, and its alpha value #: is the alpha value of self.line_color multiplied by self.alpha. effective_line_color = Property(Tuple, depends_on=['line_color', 'alpha']) #: The RGBA tuple for rendering the fill. It is always a tuple of length #: 4. It has the same RGB values as :attr:`fill_color`, and its alpha #: value is the alpha value of self.fill_color multiplied by self.alpha. effective_fill_color = Property(Tuple, depends_on=['fill_color', 'alpha']) #: Overall alpha value of the image. Ranges from 0.0 for transparent to 1.0 alpha = Range(0.0, 1.0, 1.0) #use_draw_order = False # Convenience properties that correspond to either index_mapper or # value_mapper, depending on the orientation of the plot. #: Corresponds to either **index_mapper** or **value_mapper**, depending on #: the orientation of the plot. x_mapper = Property #: Corresponds to either **value_mapper** or **index_mapper**, depending on #: the orientation of the plot. y_mapper = Property #: Corresponds to either **index_direction** or **value_direction**, #: depending on the orientation of the plot. x_direction = Property #: Corresponds to either **value_direction** or **index_direction**, #: depending on the orientation of the plot y_direction = Property #: Convenience property for accessing the index data range. index_range = Property #: Convenience property for accessing the value data range. value_range = Property #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ # Indicates whether or not the data cache is valid _cache_valid = Bool(False) # Cached data values from the datasources. If **bar_width_type** is "data", # then this is an Nx4 array of (bar_left, bar_right, start, end) for a # bar plot in normal orientation. If **bar_width_type** is "screen", then # this is an Nx3 array of (bar_center, start, end). _cached_data_pts = Any #------------------------------------------------------------------------ # AbstractPlotRenderer interface #------------------------------------------------------------------------ def __init__(self, *args, **kw): # These Traits depend on others, so we'll defer setting them until # after the HasTraits initialization has been completed. later_list = ['index_direction', 'value_direction'] postponed = {} for name in later_list: if name in kw: postponed[name] = kw.pop(name) super(BarPlot, self).__init__(*args, **kw) # Set any keyword Traits that were postponed. self.trait_set(**postponed) def map_screen(self, data_array): """ Maps an array of data points into screen space and returns it as an array. Implements the AbstractPlotRenderer interface. """ # data_array is Nx2 array if len(data_array) == 0: return [] x_ary, y_ary = transpose(data_array) sx = self.index_mapper.map_screen(x_ary) sy = self.value_mapper.map_screen(y_ary) if self.orientation == "h": return transpose(array((sx, sy))) else: return transpose(array((sy, sx))) def map_data(self, screen_pt): """ Maps a screen space point into the "index" space of the plot. Implements the AbstractPlotRenderer interface. """ if self.orientation == "h": screen_coord = screen_pt[0] else: screen_coord = screen_pt[1] return self.index_mapper.map_data(screen_coord) def map_index(self, screen_pt, threshold=2.0, outside_returns_none=True, index_only=False): """ Maps a screen space point to an index into the plot's index array(s). Implements the AbstractPlotRenderer interface. """ data_pt = self.map_data(screen_pt) if ((data_pt < self.index_mapper.range.low) or \ (data_pt > self.index_mapper.range.high)) and outside_returns_none: return None index_data = self.index.get_data() value_data = self.value.get_data() if len(value_data) == 0 or len(index_data) == 0: return None try: ndx = reverse_map_1d(index_data, data_pt, self.index.sort_order) except IndexError: return None x = index_data[ndx] y = value_data[ndx] result = self.map_screen(array([[x, y]])) if result is None: return None sx, sy = result[0] if index_only and ((screen_pt[0] - sx) < threshold): return ndx elif ((screen_pt[0] - sx)**2 + (screen_pt[1] - sy)**2 < threshold * threshold): return ndx else: return None #------------------------------------------------------------------------ # PlotComponent interface #------------------------------------------------------------------------ def _gather_points(self): """ Collects data points that are within the range of the plot, and caches them in **_cached_data_pts**. """ index, index_mask = self.index.get_data_mask() value, value_mask = self.value.get_data_mask() if not self.index or not self.value: return if len(index) == 0 or len(value) == 0 or len(index) != len(value): logger.warning( "Chaco: using empty dataset; index_len=%d, value_len=%d." % (len(index), len(value))) self._cached_data_pts = array([]) self._cache_valid = True return # TODO: Until we code up a better handling of value-based culling that # takes into account starting_value and dataspace bar widths, just use # the index culling for now. # value_range_mask = self.value_mapper.range.mask_data(value) # nan_mask = invert(isnan(index_mask)) & invert(isnan(value_mask)) # point_mask = index_mask & value_mask & nan_mask & \ # index_range_mask & value_range_mask index_range_mask = self.index_mapper.range.mask_data(index) nan_mask = invert(isnan(index_mask)) point_mask = index_mask & nan_mask & index_range_mask if self.starting_value is None: starting_values = zeros(len(index)) else: starting_values = self.starting_value.get_data() if self.bar_width_type == "data": half_width = self.bar_width / 2.0 points = column_stack((index - half_width, index + half_width, starting_values, value)) else: points = column_stack((index, starting_values, value)) self._cached_data_pts = compress(point_mask, points, axis=0) self._cache_valid = True return def _draw_plot(self, gc, view_bounds=None, mode="normal"): """ Draws the 'plot' layer. """ if not self._cache_valid: self._gather_points() data = self._cached_data_pts if data.size == 0: # Nothing to draw. return with gc: gc.clip_to_rect(self.x, self.y, self.width, self.height) gc.set_antialias(self.antialias) gc.set_stroke_color(self.effective_line_color) gc.set_fill_color(self.effective_fill_color) gc.set_line_width(self.line_width) if self.bar_width_type == "data": # map the bar start and stop locations into screen space lower_left_pts = self.map_screen(data[:, (0, 2)]) upper_right_pts = self.map_screen(data[:, (1, 3)]) else: half_width = self.bar_width / 2.0 # map the bar centers into screen space and then compute the bar # start and end positions lower_left_pts = self.map_screen(data[:, (0, 1)]) upper_right_pts = self.map_screen(data[:, (0, 2)]) lower_left_pts[:, 0] -= half_width upper_right_pts[:, 0] += half_width bounds = upper_right_pts - lower_left_pts gc.rects(column_stack((lower_left_pts, bounds))) gc.draw_path() def _draw_default_axes(self, gc): if not self.origin_axis_visible: return with gc: gc.set_stroke_color(self.origin_axis_color_) gc.set_line_width(self.origin_axis_width) gc.set_line_dash(None) for range in (self.index_mapper.range, self.value_mapper.range): if (range.low < 0) and (range.high > 0): if range == self.index_mapper.range: dual = self.value_mapper.range data_pts = array([[0.0, dual.low], [0.0, dual.high]]) else: dual = self.index_mapper.range data_pts = array([[dual.low, 0.0], [dual.high, 0.0]]) start, end = self.map_screen(data_pts) gc.move_to(int(start[0]) + 0.5, int(start[1]) + 0.5) gc.line_to(int(end[0]) + 0.5, int(end[1]) + 0.5) gc.stroke_path() return def _render_icon(self, gc, x, y, width, height): with gc: gc.set_fill_color(self.effective_fill_color) gc.set_stroke_color(self.effective_line_color) gc.rect(x + width / 4, y + height / 4, width / 2, height / 2) gc.draw_path(FILL_STROKE) def _post_load(self): super(BarPlot, self)._post_load() return #------------------------------------------------------------------------ # Properties #------------------------------------------------------------------------ def _get_index_range(self): return self.index_mapper.range def _set_index_range(self, val): self.index_mapper.range = val def _get_value_range(self): return self.value_mapper.range def _set_value_range(self, val): self.value_mapper.range = val def _get_x_mapper(self): if self.orientation == "h": return self.index_mapper else: return self.value_mapper def _get_y_mapper(self): if self.orientation == "h": return self.value_mapper else: return self.index_mapper def _get_x_direction(self): if self.orientation == "h": return self.index_direction else: return self.value_direction def _get_y_direction(self): if self.orientation == "h": return self.value_direction else: return self.index_direction #------------------------------------------------------------------------ # Event handlers - these are mostly copied from BaseXYPlot #------------------------------------------------------------------------ def _update_mappers(self): """ Updates the index and value mappers. Called by trait change handlers for various traits. """ x_mapper = self.index_mapper y_mapper = self.value_mapper x_dir = self.index_direction y_dir = self.value_direction if self.orientation == "v": x_mapper, y_mapper = y_mapper, x_mapper x_dir, y_dir = y_dir, x_dir x = self.x x2 = self.x2 y = self.y y2 = self.y2 if x_mapper is not None: if x_dir == "normal": x_mapper.low_pos = x x_mapper.high_pos = x2 else: x_mapper.low_pos = x2 x_mapper.high_pos = x if y_mapper is not None: if y_dir == "normal": y_mapper.low_pos = y y_mapper.high_pos = y2 else: y_mapper.low_pos = y2 y_mapper.high_pos = y self.invalidate_draw() self._cache_valid = False @on_trait_change('line_color, line_width, fill_color, alpha') def _attributes_changed(self): self.invalidate_draw() self.request_redraw() def _bounds_changed(self, old, new): super(BarPlot, self)._bounds_changed(old, new) self._update_mappers() def _bounds_items_changed(self, event): super(BarPlot, self)._bounds_items_changed(event) self._update_mappers() def _orientation_changed(self): self._update_mappers() def _index_changed(self, old, new): if old is not None: old.on_trait_change(self._either_data_changed, "data_changed", remove=True) if new is not None: new.on_trait_change(self._either_data_changed, "data_changed") self._either_data_changed() def _index_direction_changed(self): m = self.index_mapper m.low_pos, m.high_pos = m.high_pos, m.low_pos self.invalidate_draw() def _value_direction_changed(self): m = self.value_mapper m.low_pos, m.high_pos = m.high_pos, m.low_pos self.invalidate_draw() def _either_data_changed(self): self.invalidate_draw() self._cache_valid = False self.request_redraw() def _value_changed(self, old, new): if old is not None: old.on_trait_change(self._either_data_changed, "data_changed", remove=True) if new is not None: new.on_trait_change(self._either_data_changed, "data_changed") self._either_data_changed() def _index_mapper_changed(self, old, new): return self._either_mapper_changed(old, new) def _value_mapper_changed(self, old, new): return self._either_mapper_changed(old, new) def _either_mapper_changed(self, old, new): if old is not None: old.on_trait_change(self._mapper_updated_handler, "updated", remove=True) if new is not None: new.on_trait_change(self._mapper_updated_handler, "updated") self.invalidate_draw() def _mapper_updated_handler(self): self._cache_valid = False self.invalidate_draw() self.request_redraw() def _bar_width_changed(self): self._cache_valid = False self.invalidate_draw() self.request_redraw() def _bar_width_type_changed(self): self._cache_valid = False self.invalidate_draw() self.request_redraw() #------------------------------------------------------------------------ # Property getters #------------------------------------------------------------------------ @cached_property def _get_effective_line_color(self): if len(self.line_color_) == 4: line_alpha = self.line_color_[-1] else: line_alpha = 1.0 c = self.line_color_[:3] + (line_alpha * self.alpha, ) return c @cached_property def _get_effective_fill_color(self): if len(self.fill_color_) == 4: fill_alpha = self.fill_color_[-1] else: fill_alpha = 1.0 c = self.fill_color_[:3] + (fill_alpha * self.alpha, ) return c
class GridCell(SDomain): ''' A single mgrid cell for geometrical representation of the domain. Based on the grid_cell_spec attribute, the node distribution is determined. ''' # Everything depends on the grid_cell_specification # grid_cell_spec = Instance(CellSpec) def _grid_cell_spec_default(self): return CellSpec() # Generated grid cell coordinates as they come from mgrid. # The dimensionality of the mgrid comes from the # grid_cell_spec_attribute # grid_cell_coords = Property(depends_on='grid_cell_spec') @cached_property def _get_grid_cell_coords(self): grid_cell = mgrid[self.grid_cell_spec.get_cell_slices()] return c_[tuple([x.flatten() for x in grid_cell])] n_nodes = Property(depends_on='grid_cell_spec') @cached_property def _get_n_nodes(self): '''Return the number of all nodes within the cell. ''' return self.grid_cell_coords.shape[0] # Node map lists the active nodes within the grid cell # in the specified order # node_map = Property(Array(int), depends_on='grid_cell_spec') @cached_property def _get_node_map(self): n_map = [] for node in self.grid_cell_spec._node_array: for idx, grid_cell_node in enumerate(self.grid_cell_coords): if allclose(node, grid_cell_node, atol=1.0e-3): n_map.append(idx) continue return array(n_map, int) #----------------------------------------------------------------- # Visualization related methods #----------------------------------------------------------------- mvp_mgrid_ngeo_labels = Trait(MVPointLabels) def _mvp_mgrid_ngeo_labels_default(self): return MVPointLabels(name='Geo node numbers', points=self._get_points, scalars=self._get_node_distribution) refresh_button = Button('Draw') @on_trait_change('refresh_button') def redraw(self): ''' ''' self.mvp_mgrid_ngeo_labels.redraw('label_scalars') def _get_points(self): points = self.grid_cell_coords[ix_(self.node_map)] shape = points.shape if shape[1] < 3: _points = zeros((shape[0], 3), dtype=float) _points[:, 0:shape[1]] = points return _points else: return points def _get_node_distribution(self): #return arange(len(self.node_map)) n_points = self.grid_cell_coords.shape[0] full_node_map = ones(n_points, dtype=float) * -1. full_node_map[ix_(self.node_map)] = arange(len(self.node_map)) return full_node_map def __getitem__(self, idx): # construct the full boolean map of the grid cell' node_bool_map = repeat(False, self.n_nodes).reshape( self.grid_cell_spec.get_cell_shape()) # put true at the sliced positions node_bool_map[idx] = True # extract the used nodes using the node map node_selection = node_bool_map.flatten()[self.node_map] return node_selection #------------------------------------------------------------------ # UI - related methods #------------------------------------------------------------------ traits_view = View(Item('grid_cell_spec'), Item('refresh_button'), Item('node_map'), resizable=True, height=0.5, width=0.5)
class Window(Widget): """ A top-level Window component. A Window component is represents of a top-level visible component with a frame decoration. It may have at most one child widget which is dubbed the 'central widget'. The central widget is an instance of Container and is expanded to fit the size of the window. A Window does not support features like MenuBars or DockPanes, for such functionality, use a MainWindow widget. """ #: The titlebar text. title = Unicode #: The initial size of the window. A value of (-1, -1) indicates #: to let the client choose the initial size initial_size = SizeTuple #: An enum which indicates the modality of the window. The default #: value is 'non_modal'. modality = Enum('non_modal', 'application_modal', 'window_modal') #: Whether or not the window remains on top of all others. always_on_top = Bool(False) #: If this value is set to True, the window will be destroyed on #: the completion of the `closed` event. destroy_on_close = Bool(True) #: An event fired when the window is closed. closed = EnamlEvent #: Returns the central widget in use for the Window central_widget = Property(depends_on='children') #: The source url for the titlebar icon. icon_source = Str #-------------------------------------------------------------------------- # Initialization #-------------------------------------------------------------------------- def snapshot(self): """ Return the snapshot for a Window. """ snap = super(Window, self).snapshot() snap['title'] = self.title snap['initial_size'] = self.initial_size snap['modality'] = self.modality snap['always_on_top'] = self.always_on_top snap['icon_source'] = self.icon_source return snap def bind(self): """ A method called after initialization which allows the widget to bind any event handlers necessary. """ super(Window, self).bind() attrs = ('title', 'modality', 'always_on_top', 'icon_source') self.publish_attributes(*attrs) #-------------------------------------------------------------------------- # Private API #-------------------------------------------------------------------------- @cached_property def _get_central_widget(self): """ The getter for the 'central_widget' property. Returns ------- result : Container or None The central widget for the Window, or None if not provieded. """ widget = None for child in self.children: if isinstance(child, Container): widget = child return widget #-------------------------------------------------------------------------- # Message Handling #-------------------------------------------------------------------------- def on_action_closed(self, content): """ Handle the 'closed' action from the client widget. """ self.set_guarded(visible=False) self.closed() if self.destroy_on_close: self.destroy() #-------------------------------------------------------------------------- # Public API #-------------------------------------------------------------------------- def close(self): """ Send the 'close' action to the client widget. """ self.send_action('close', {}) def maximize(self): """ Send the 'maximize' action to the client widget. """ self.send_action('maximize', {}) def minimize(self): """ Send the 'minimize' action to the client widget. """ self.send_action('minimize', {}) def restore(self): """ Send the 'restore' action to the client widget. """ self.send_action('restore', {}) def send_to_front(self): """ Send the 'send_to_front' action to the client widget. This moves the window to the front of all the toplevel windows. """ self.send_action('send_to_front', {}) def send_to_back(self): """ Send the 'send_to_back' action to the client widget. This moves the window to the back of all the toplevel windows. """ self.send_action('send_to_back', {})
class CellSpec(HasTraits): ''' ''' node_coords = Array(float, value=[[-1, -1, 0], [1, -1, 0], [1, 1, 0], [1, 1, 1], [-1, 1, -1], [-1 / 2., 1, 0], [0., 1, 0], [1 / 2., 1, 0]]) # xnode_coords = List( [[-1,0,-1], # [ 1,0,-1], # [ 1,0, 1], # [-1,0, 1]] ) # # node_coords = List( [[-1,-1,-1], # [ 1, 0, 0], # [ 1, 1, 1], # ] ) traits_view = View(Item('node_coords', style='readonly'), resizable=True, height=0.5, width=0.5) _node_array = Property(Array('float_'), depends_on='node_coords') @cached_property def _get__node_array(self): '''Get the node array as float_ ''' # check that the nodes are equidistant return array(self.node_coords, 'float_') n_dims = Property(depends_on='node_coords') @cached_property def _get_n_dims(self): '''Get the number of dimension of the cell ''' return self._node_array.shape[1] def get_cell_shape(self): '''Get the shape of the cell grid. ''' cell_shape = ones(3, dtype=int) ndims = self.n_dims narray = self._node_array cell_shape[0:ndims] = array( [len(unique(narray[:, i])) for i in range(ndims)], dtype=int) cell_shape = array([len(unique(narray[:, i])) for i in range(ndims)], dtype=int) return cell_shape def get_cell_slices(self): '''Get slices for the generation of the cell grid. ''' ndims = self.n_dims narray = self._node_array return tuple([ slice( min(narray[:, i]), max(narray[:, i]), complex(0, len(unique(narray[:, i]))), ) for i in range(ndims) ]) #------------------------------------------------------------------- # Visualization-related specification #------------------------------------------------------------------- cell_lines = Array(int, value=[[0, 1], [1, 2], [2, 0]]) cell_faces = Array(int, value=[[0, 1, 2]])
class ScatterPlot(BaseXYPlot): """ Renders a scatter plot, given an index and value arrays. """ # The CompiledPath to use if **marker** is set to "custom". This attribute # must be a compiled path for the Kiva context onto which this plot will # be rendered. Usually, importing kiva.GraphicsContext will do # the right thing. custom_symbol = Any #------------------------------------------------------------------------ # Styles on a ScatterPlot #------------------------------------------------------------------------ # The type of marker to use. This is a mapped trait using strings as the # keys. marker = MarkerTrait # The pixel size of the marker, not including the thickness of the outline. marker_size = Float(4.0) # The function which actually renders the markers render_markers_func = Callable(render_markers) # The thickness, in pixels, of the outline to draw around the marker. If # this is 0, no outline is drawn. line_width = Float(1.0) # The fill color of the marker. color = black_color_trait # The color of the outline to draw around the marker. outline_color = black_color_trait # The RGBA tuple for rendering lines. It is always a tuple of length 4. # It has the same RGB values as color_, and its alpha value is the alpha # value of self.color multiplied by self.alpha. effective_color = Property(Tuple, depends_on=['color', 'alpha']) # The RGBA tuple for rendering the fill. It is always a tuple of length 4. # It has the same RGB values as outline_color_, and its alpha value is the # alpha value of self.outline_color multiplied by self.alpha. effective_outline_color = Property(Tuple, depends_on=['outline_color', 'alpha']) # Traits UI View for customizing the plot. traits_view = ScatterPlotView() #------------------------------------------------------------------------ # Selection and selection rendering # A selection on the lot is indicated by setting the index or value # datasource's 'selections' metadata item to a list of indices, or the # 'selection_mask' metadata to a boolean array of the same length as the # datasource. #------------------------------------------------------------------------ show_selection = Bool(True) selection_marker = MarkerTrait selection_marker_size = Float(4.0) selection_line_width = Float(1.0) selection_color = ColorTrait("yellow") selection_outline_color = black_color_trait #------------------------------------------------------------------------ # Private traits #------------------------------------------------------------------------ _cached_selected_pts = Trait(None, None, Array) _cached_selected_screen_pts = Array _cached_point_mask = Array _cached_selection_point_mask = Array _selection_cache_valid = Bool(False) #------------------------------------------------------------------------ # Overridden PlotRenderer methods #------------------------------------------------------------------------ def map_screen(self, data_array): """ Maps an array of data points into screen space and returns it as an array. Implements the AbstractPlotRenderer interface. """ # data_array is Nx2 array if len(data_array) == 0: return [] # XXX: For some reason, doing the tuple unpacking doesn't work: # x_ary, y_ary = transpose(data_array) # There is a mysterious error "object of too small depth for # desired array". However, if you catch this exception and # try to execute the very same line of code again, it works # without any complaints. # # For now, we just use slicing to assign the X and Y arrays. data_array = asarray(data_array) if len(data_array.shape) == 1: x_ary = data_array[0] y_ary = data_array[1] else: x_ary = data_array[:, 0] y_ary = data_array[:, 1] sx = self.index_mapper.map_screen(x_ary) sy = self.value_mapper.map_screen(y_ary) if self.orientation == "h": return transpose(array((sx, sy))) else: return transpose(array((sy, sx))) def map_data(self, screen_pt, all_values=True): """ Maps a screen space point into the "index" space of the plot. Overrides the BaseXYPlot implementation, and always returns an array of (index, value) tuples. """ x, y = screen_pt if self.orientation == 'v': x, y = y, x return array( (self.index_mapper.map_data(x), self.value_mapper.map_data(y))) def map_index(self, screen_pt, threshold=0.0, outside_returns_none=True, \ index_only = False): """ Maps a screen space point to an index into the plot's index array(s). Overrides the BaseXYPlot implementation.. """ if index_only and self.index.sort_order != "none": data_pt = self.map_data(screen_pt)[0] # The rest of this was copied out of BaseXYPlot. # We can't just used BaseXYPlot.map_index because # it expect map_data to return a value, not a pair. if ((data_pt < self.index_mapper.range.low) or \ (data_pt > self.index_mapper.range.high)) and outside_returns_none: return None index_data = self.index.get_data() value_data = self.value.get_data() if len(value_data) == 0 or len(index_data) == 0: return None try: ndx = reverse_map_1d(index_data, data_pt, self.index.sort_order) except IndexError, e: # if reverse_map raises this exception, it means that data_pt is # outside the range of values in index_data. if outside_returns_none: return None else: if data_pt < index_data[0]: return 0 else: return len(index_data) - 1 if threshold == 0.0: # Don't do any threshold testing return ndx x = index_data[ndx] y = value_data[ndx] if isnan(x) or isnan(y): return None sx, sy = self.map_screen([x, y]) if ((threshold == 0.0) or (screen_pt[0] - sx) < threshold): return ndx else: return None else:
class MayaviViewer(HasTraits): """ This class represents a Mayavi based viewer for the particles. They are queried from a running solver. """ particle_arrays = List(Instance(ParticleArrayHelper), []) pa_names = List(Str, []) interpolator = Instance(InterpolatorView) # The default scalar to load up when running the viewer. scalar = Str("rho") scene = Instance(MlabSceneModel, ()) ######################################## # Traits to pull data from a live solver. live_mode = Bool(False, desc='if data is obtained from a running solver ' 'or from saved files') shell = Button('Launch Python Shell') host = Str('localhost', desc='machine to connect to') port = Int(8800, desc='port to use to connect to solver') authkey = Password('pysph', desc='authorization key') host_changed = Bool(True) client = Instance(MultiprocessingClient) controller = Property(depends_on='live_mode, host_changed') ######################################## # Traits to view saved solver output. files = List(Str, []) directory = Directory() current_file = Str('', desc='the file being viewed currently') update_files = Button('Refresh') file_count = Range(low='_low', high='_n_files', value=0, desc='the file counter') play = Bool(False, desc='if all files are played automatically') play_delay = Float(0.2, desc='the delay between loading files') loop = Bool(False, desc='if the animation is looped') # This is len(files) - 1. _n_files = Int(0) _low = Int(0) ######################################## # Timer traits. timer = Instance(Timer) interval = Range(0.5, 20.0, 2.0, desc='frequency in seconds with which plot is updated') ######################################## # Solver info/control. current_time = Float(0.0, desc='the current time in the simulation') time_step = Float(0.0, desc='the time-step of the solver') iteration = Int(0, desc='the current iteration number') pause_solver = Bool(False, desc='if the solver should be paused') ######################################## # Movie. record = Bool(False, desc='if PNG files are to be saved for animation') frame_interval = Range(1, 100, 5, desc='the interval between screenshots') movie_directory = Str # internal counters. _count = Int(0) _frame_count = Int(0) _last_time = Float _solver_data = Any _file_name = Str _particle_array_updated = Bool ######################################## # The layout of the dialog created view = View(HSplit( Group( Group(Group( Item(name='directory'), Item(name='current_file'), Item(name='file_count'), HGroup(Item(name='play'), Item(name='play_delay', label='Delay', resizable=True), Item(name='loop'), Item(name='update_files', show_label=False), padding=0), padding=0, label='Saved Data', selected=True, enabled_when='not live_mode', ), Group( Item(name='live_mode'), Group( Item(name='host'), Item(name='port'), Item(name='authkey'), enabled_when='live_mode', ), label='Connection', ), layout='tabbed'), Group( Group( Item(name='current_time'), Item(name='time_step'), Item(name='iteration'), Item(name='pause_solver', enabled_when='live_mode'), Item(name='interval', enabled_when='not live_mode'), label='Solver', ), Group( Item(name='record'), Item(name='frame_interval'), Item(name='movie_directory'), label='Movie', ), layout='tabbed', ), Group(Item(name='particle_arrays', style='custom', show_label=False, editor=ListEditor(use_notebook=True, deletable=False, page_name='.name')), Item(name='interpolator', style='custom', show_label=False), layout='tabbed'), Item(name='shell', show_label=False), ), Group( Item('scene', editor=SceneEditor(scene_class=MayaviScene), height=400, width=600, show_label=False), )), resizable=True, title='PySPH Particle Viewer', height=640, width=1024, handler=ViewerHandler) ###################################################################### # `MayaviViewer` interface. ###################################################################### def on_close(self): self._handle_particle_array_updates() @on_trait_change('scene:activated') def start_timer(self): if not self.live_mode: # No need for the timer if we are rendering files. return # Just accessing the timer will start it. t = self.timer if not t.IsRunning(): t.Start(int(self.interval * 1000)) @on_trait_change('scene:activated') def update_plot(self): # No need to do this if files are being used. if not self.live_mode: return # do not update if solver is paused if self.pause_solver: return if self.client is None: self.host_changed = True controller = self.controller if controller is None: return self.current_time = t = controller.get_t() self.time_step = controller.get_dt() self.iteration = controller.get_count() arrays = [] for idx, name in enumerate(self.pa_names): pa = controller.get_named_particle_array(name) arrays.append(pa) pah = self.particle_arrays[idx] pah.set(particle_array=pa, time=t) self.interpolator.particle_arrays = arrays if self.record: self._do_snap() def run_script(self, path): """Execute a script in the namespace of the viewer. """ with open(path) as fp: data = fp.read() ns = self._get_shell_namespace() exec(compile(data, path, 'exec'), ns) ###################################################################### # Private interface. ###################################################################### def _do_snap(self): """Generate the animation.""" p_arrays = self.particle_arrays if len(p_arrays) == 0: return if self.current_time == self._last_time: return if len(self.movie_directory) == 0: controller = self.controller output_dir = controller.get_output_directory() movie_dir = os.path.join(output_dir, 'movie') self.movie_directory = movie_dir else: movie_dir = self.movie_directory if not os.path.exists(movie_dir): os.mkdir(movie_dir) interval = self.frame_interval count = self._count if count % interval == 0: fname = 'frame%06d.png' % (self._frame_count) p_arrays[0].scene.save_png(os.path.join(movie_dir, fname)) self._frame_count += 1 self._last_time = self.current_time self._count += 1 @on_trait_change('host,port,authkey') def _mark_reconnect(self): if self.live_mode: self.host_changed = True @cached_property def _get_controller(self): ''' get the controller, also sets the iteration count ''' if not self.live_mode: return None reconnect = self.host_changed if not reconnect: try: c = self.client.controller except Exception as e: logger.info('Error: no connection or connection closed: ' 'reconnecting: %s' % e) reconnect = True self.client = None else: try: self.client.controller.get_count() except IOError: self.client = None reconnect = True if reconnect: self.host_changed = False try: if MultiprocessingClient.is_available((self.host, self.port)): self.client = MultiprocessingClient(address=(self.host, self.port), authkey=self.authkey) else: logger.info('Could not connect: Multiprocessing Interface' ' not available on %s:%s' % (self.host, self.port)) return None except Exception as e: logger.info('Could not connect: check if solver is ' 'running:%s' % e) return None c = self.client.controller self.iteration = c.get_count() if self.client is None: return None else: return self.client.controller def _client_changed(self, old, new): if not self.live_mode: return self._clear() if new is None: return else: self.pa_names = self.client.controller.get_particle_array_names() self.particle_arrays = [ self._make_particle_array_helper(self.scene, x) for x in self.pa_names ] self.interpolator = InterpolatorView(scene=self.scene) # Turn on the legend for the first particle array. if len(self.particle_arrays) > 0: self.particle_arrays[0].set(show_legend=True, show_time=True) def _timer_event(self): # catch all Exceptions else timer will stop try: self.update_plot() except Exception as e: logger.info('Exception: %s caught in timer_event' % e) def _interval_changed(self, value): t = self.timer if t is None: return if t.IsRunning(): t.Stop() t.Start(int(value * 1000)) def _timer_default(self): return Timer(int(self.interval * 1000), self._timer_event) def _pause_solver_changed(self, value): if self.live_mode: c = self.controller if c is None: return if value: c.pause_on_next() else: c.cont() def _record_changed(self, value): if value: self._do_snap() def _files_changed(self, value): if len(value) == 0: return else: d = os.path.dirname(os.path.abspath(value[0])) self.movie_directory = os.path.join(d, 'movie') self.set(directory=d, trait_change_notify=False) self._n_files = len(value) - 1 self._frame_count = 0 self._count = 0 self.frame_interval = 1 fc = self.file_count self.file_count = 0 if fc == 0: # Force an update when our original file count is 0. self._file_count_changed(fc) t = self.timer if not self.live_mode: if t.IsRunning(): t.Stop() else: if not t.IsRunning(): t.Stop() t.Start(self.interval * 1000) def _file_count_changed(self, value): # Save out any updates for the previous file if needed. self._handle_particle_array_updates() # Load the new file. fname = self.files[value] self._file_name = fname self.current_file = os.path.basename(fname) # Code to read the file, create particle array and setup the helper. data = load(fname) solver_data = data["solver_data"] arrays = data["arrays"] self._solver_data = solver_data self.current_time = t = float(solver_data['t']) self.time_step = float(solver_data['dt']) self.iteration = int(solver_data['count']) names = list(arrays.keys()) pa_names = self.pa_names if len(pa_names) == 0: self.interpolator = InterpolatorView(scene=self.scene) self.pa_names = names pas = [] for name in names: pa = arrays[name] pah = self._make_particle_array_helper(self.scene, name) # Must set this after setting the scene. pah.set(particle_array=pa, time=t) pas.append(pah) self.particle_arrays = pas else: for idx, name in enumerate(pa_names): pa = arrays[name] pah = self.particle_arrays[idx] pah.set(particle_array=pa, time=t) self.interpolator.particle_arrays = list(arrays.values()) if self.record: self._do_snap() def _loop_changed(self, value): if value and self.play: self._play_changed(self.play) def _play_changed(self, value): t = self.timer if value: t.Stop() t.callable = self._play_event t.Start(1000 * self.play_delay) else: t.Stop() t.callable = self._timer_event def _clear(self): self.pa_names = [] self.scene.mayavi_scene.children[:] = [] def _play_event(self): nf = self._n_files pc = self.file_count pc += 1 if pc > nf: if self.loop: pc = 0 else: self.timer.Stop() pc = nf self.file_count = pc self._handle_particle_array_updates() def _play_delay_changed(self): if self.play: self._play_changed(self.play) def _scalar_changed(self, value): for pa in self.particle_arrays: pa.scalar = value def _update_files_fired(self): fc = self.file_count files = glob_files(self.files[fc]) sort_file_list(files) self.files = files self.file_count = fc if self.play: self._play_changed(self.play) def _shell_fired(self): ns = self._get_shell_namespace() obj = PythonShellView(ns=ns) obj.edit_traits() def _get_shell_namespace(self): return dict(viewer=self, particle_arrays=self.particle_arrays, interpolator=self.interpolator, scene=self.scene, mlab=self.scene.mlab) def _directory_changed(self, d): ext = os.path.splitext(self.files[-1])[1] files = glob.glob(os.path.join(d, '*' + ext)) if len(files) > 0: self._clear() sort_file_list(files) self.files = files self.file_count = min(self.file_count, len(files)) else: pass def _live_mode_changed(self, value): if value: self._file_name = '' self.client = None self._clear() self._mark_reconnect() self.start_timer() else: self.client = None self._clear() self.timer.Stop() def _particle_array_helper_updated(self, value): self._particle_array_updated = True def _handle_particle_array_updates(self): # Called when the particle array helper fires an updated event. if self._particle_array_updated and self._file_name: sd = self._solver_data arrays = [x.particle_array for x in self.particle_arrays] dump(self._file_name, arrays, sd) self._particle_array_updated = False def _make_particle_array_helper(self, scene, name): pah = ParticleArrayHelper(scene=scene, name=name, scalar=self.scalar) pah.on_trait_change(self._particle_array_helper_updated, 'updated') return pah
class WorkflowItem(HasStrictTraits): """ The basic unit of a Workflow: wraps an operation and a list of views. Notes ----- Because we serialize instances of this, we have to pay careful attention to which traits are ``transient`` (and aren't serialized) """ # the operation's id friendly_id = DelegatesTo('operation') # the operation's name name = DelegatesTo('operation') # the operation this Item wraps operation = Instance(IOperation, copy="ref") # for the vertical notebook view, is this page deletable? deletable = Bool(True) # the handler that's associated with this operation; we get it from the # operation plugin, and it controls what operation traits are in the UI # and any special handling (heh) of them. since the handler doesn't # maintain any state, we can make and destroy as needed. operation_handler = Property(depends_on='operation', trait=Instance(Handler), transient=True) operation_traits = View( Item('operation_handler', style='custom', show_label=False)) # the Experiment that is the result of applying *operation* to the # previous WorkflowItem's ``result`` result = Instance(Experiment, transient=True) # the channels, conditions and statistics from result. usually these would be # Properties (ie, determined dynamically), but that's hard with the # multiprocess model. channels = List(Str, status=True) conditions = Dict(Str, pd.Series, status=True) conditions_names = Property(depends_on='conditions') metadata = Dict(Str, Any, status=True) statistics = Dict(Tuple(Str, Str), pd.Series, status=True) statistics_names = Property(depends_on='statistics') # we have to keep copies of these around to facilitate loading # and saving. previous_channels = List(Str, status=True) previous_conditions = Dict(Str, pd.Series, status=True) previous_conditions_names = Property(depends_on='previous_conditions') previous_metadata = Dict(Str, Any, status=True) previous_statistics = Dict(Tuple(Str, Str), pd.Series, status=True) previous_statistics_names = Property(depends_on='previous_statistics') # the IViews against the output of this operation views = List(IView, copy="ref") # the currently selected view current_view = Instance(IView, copy="ref") # the handler for the currently selected view current_view_handler = Property(depends_on='current_view', trait=Instance(Handler), transient=True) current_view_traits = View( Item('current_view_handler', style='custom', show_label=False)) # the plot names for the currently selected view current_view_plot_names = List(Any, status=True) # if there are multiple plots, which are we viewing? current_plot = Any # the view for the current plot current_plot_view = View( Item('current_view_plot_names', editor=TabListEditor(selected='current_plot'), show_label=False)) # the default view for this workflow item default_view = Instance(IView, copy="ref") # the previous WorkflowItem in the workflow previous = Instance('WorkflowItem', transient=True) # the next WorkflowItem in the workflow next = Instance('WorkflowItem', transient=True) # is the wi valid? # MAGIC: first value is the default status = Enum("invalid", "estimating", "applying", "valid", status=True) # report the errors and warnings op_error = Str(status=True) op_warning = Str(status=True) estimate_error = Str(status=True) estimate_warning = Str(status=True) view_error = Str(status=True) view_warning = Str(status=True) # the event to make the workflow item re-estimate its internal model do_estimate = Event # the icon for the vertical notebook view. Qt specific, sadly. icon = Property(depends_on='status', transient=True) # synchronization primitives for plotting matplotlib_events = Any(transient=True) plot_lock = Any(transient=True) @cached_property def _get_icon(self): if self.status == "valid": return QtGui.QStyle.SP_DialogApplyButton elif self.status == "estimating" or self.status == "applying": return QtGui.QStyle.SP_BrowserReload else: # self.valid == "invalid" or None return QtGui.QStyle.SP_DialogCancelButton @cached_property def _get_operation_handler(self): return self.operation.handler_factory(model=self.operation) @cached_property def _get_current_view_handler(self): if self.current_view: return self.current_view.handler_factory(model=self.current_view) else: return None @cached_property def _get_conditions_names(self): if self.conditions: return self.conditions.keys() else: return [] @cached_property def _get_statistics_names(self): if self.statistics: return self.statistics.keys() else: return [] @cached_property def _get_previous_conditions_names(self): if self.previous_conditions: return self.previous_conditions.keys() else: return [] @cached_property def _get_previous_statistics_names(self): if self.previous_statistics: return self.previous_statistics.keys() else: return [] def __str__(self): return "<{}: {}>".format(self.__class__.__name__, self.operation.__class__.__name__) def __repr__(self): return "<{}: {}>".format(self.__class__.__name__, self.operation.__class__.__name__)
class BendingTestModel(Simulator): #========================================================================= # Tree node attributes #========================================================================= node_name = 'bending test simulation' tree_node_list = List([]) def _tree_node_list_default(self): return [self.tline, self.mats, self.cross_section, self.geometry] def _update_node_list(self): self.tree_node_list = [ self.tline, self.mats, self.cross_section, self.geometry ] #========================================================================= # Test setup parameters #========================================================================= loading_scenario = Instance(LoadingScenario) def _loading_scenario_default(self): return LoadingScenario() cross_section = Instance(CrossSection) def _cross_section_default(self): return CrossSection() geometry = Instance(Geometry) def _geometry_default(self): return Geometry() #========================================================================= # Discretization #========================================================================= n_e_x = Int(20, label='# of elems in x-dir', MESH=True, auto_set=False, enter_set=True) n_e_y = Int(8, label='# of elems in y-dir', MESH=True, auto_set=False, enter_set=True) w_max = Float(-50, BC=True, auto_set=False, enter_set=True) controlled_elem = Property def _get_controlled_elem(self): return 0 #========================================================================= # Material model #========================================================================= mats_type = Trait( 'scalar damage', { 'elastic': MATS2DElastic, 'microplane damage (eeq)': MATS2DMplDamageEEQ, 'microplane CSD (eeq)': MATS2DMplCSDEEQ, #'microplane CSD (odf)': MATS2DMplCSDODF, 'scalar damage': MATS2DScalarDamage }, MAT=True) @on_trait_change('mats_type') def _set_mats(self): self.mats = self.mats_type_() self._update_node_list() mats = Instance(IModel, report=True) '''Material model''' def _mats_default(self): return self.mats_type_() #========================================================================= # Finite element type #========================================================================= fets_eval = Property(Instance(FETS2D4Q), depends_on='CS,MAT') '''Finite element time stepper implementing the corrector predictor operators at the element level''' @cached_property def _get_fets_eval(self): return FETS2D4Q() bc = Property(Instance(BCondMngr), depends_on='GEO,CS,BC,MAT,MESH') '''Boundary condition manager ''' @cached_property def _get_bc(self): return [self.fixed_right_bc, self.fixed_x, self.control_bc] fixed_right_bc = Property(depends_on='CS,BC,GEO,MAT,MESH') '''Foxed boundary condition''' @cached_property def _get_fixed_right_bc(self): return BCSlice(slice=self.fe_grid[-1, 0, -1, 0], var='u', dims=[1], value=0) n_a = Property '''Element at the notch ''' def _get_n_a(self): a_L = self.geometry.a / self.geometry.H return int(a_L * self.n_e_y) fixed_x = Property(depends_on='CS,BC,GEO,MAT,MESH') '''Foxed boundary condition''' @cached_property def _get_fixed_x(self): return BCSlice(slice=self.fe_grid[0, self.n_a:, 0, -1], var='u', dims=[0], value=0) control_bc = Property(depends_on='CS,BC,GEO,MAT,MESH') '''Control boundary condition - make it accessible directly for the visualization adapter as property ''' @cached_property def _get_control_bc(self): return BCSlice(slice=self.fe_grid[0, -1, :, -1], var='u', dims=[1], value=-self.w_max) xdomain = Property(depends_on='CS,MAT,GEO,MESH,FE') '''Discretization object. ''' @cached_property def _get_xdomain(self): dgrid = XDomainFEGrid(coord_max=(self.geometry.L / 2., self.geometry.H), integ_factor=self.cross_section.b, shape=(self.n_e_x, self.n_e_y), fets=self.fets_eval) L = self.geometry.L / 2.0 L_c = self.geometry.L_c x_x, x_y = dgrid.mesh.geo_grid.point_x_grid L_1 = x_x[1, 0] d_L = L_c - L_1 x_x[1:, :] += d_L * (L - x_x[1:, :]) / (L - L_1) return dgrid fe_grid = Property def _get_fe_grid(self): return self.xdomain.mesh domains = Property(depends_on=itags_str) @cached_property def _get_domains(self): return [(self.xdomain, self.mats)] traits_view = View( VGroup(Item('w_max', full_size=True, resizable=True), ), UItem('cross_section@'), UItem('geometry@'), VGroup( Item('n_e_x'), Item('n_e_y'), label='Numerical parameters', ), ) tree_view = traits_view
class OpenFileDialog(Handler): """ Defines the model and handler for the open file dialog. """ # The starting and current file path: file_name = File # The list of file filters to apply: filter = CList(Str) # Number of history entries to allow: entries = Int(10) # The file dialog title: title = Str('Open File') # The Traits UI persistence id to use: id = Str('traitsui.file_dialog.OpenFileDialog') # A list of optional file dialog extensions: extensions = CList(IFileDialogModel) #-- Private Traits ------------------------------------------------------- # The UIInfo object for the view: info = Instance(UIInfo) # Event fired when the file tree view should be reloaded: reload = Event # Event fired when the user double-clicks on a file name: dclick = Event # Allow extension models to be added dynamically: extension__ = Instance(IFileDialogModel) # Is the file dialog for saving a file (or opening a file)? is_save_file = Bool(False) # Is the currently specified file name valid? is_valid_file = Property(depends_on='file_name') # Can a directory be created now? can_create_dir = Property(depends_on='file_name') # The OK, Cancel and create directory buttons: ok = Button('OK') cancel = Button('Cancel') create = Button(image='@icons:folder-new', style='toolbar') #-- Handler Class Method Overrides --------------------------------------- def init_info(self, info): """ Handles the UIInfo object being initialized during view start-up. """ self.info = info #-- Property Implementations --------------------------------------------- def _get_is_valid_file(self): if self.is_save_file: return (isfile(self.file_name) or (not exists(self.file_name))) return isfile(self.file_name) def _get_can_create_dir(self): dir = dirname(self.file_name) return (isdir(dir) and access(dir, R_OK | W_OK)) #-- Handler Event Handlers ----------------------------------------------- def object_ok_changed(self, info): """ Handles the user clicking the OK button. """ if self.is_save_file and exists(self.file_name): do_later(self._file_already_exists) else: info.ui.dispose(True) def object_cancel_changed(self, info): """ Handles the user clicking the Cancel button. """ info.ui.dispose(False) def object_create_changed(self, info): """ Handles the user clicking the create directory button. """ if not isdir(self.file_name): self.file_name = dirname(self.file_name) CreateDirHandler().edit_traits(context=self, parent=info.create.control) #-- Traits Event Handlers ------------------------------------------------ def _dclick_changed(self): """ Handles the user double-clicking a file name in the file tree view. """ if self.is_valid_file: self.object_ok_changed(self.info) #-- Private Methods ------------------------------------------------------ def open_file_view(self): """ Returns the file dialog view to use. """ # Set up the default file dialog view and size information: item = Item('file_name', id='file_tree', style='custom', show_label=False, width=0.5, editor=FileEditor(filter=self.filter, allow_dir=True, reload_name='reload', dclick_name='dclick')) width = height = 0.20 # Check to see if we have any extensions being added: if len(self.extensions) > 0: # fixme: We should use the actual values of the view's Width and # height traits here to adjust the overall width and height... width *= 2.0 # Assume we can used a fixed width Group: klass = HGroup # Set up to build a list of view Item objects: items = [] # Add each extension to the dialog: for i, extension in enumerate(self.extensions): # Save the extension in a new trait (for use by the View): name = 'extension_%d' % i setattr(self, name, extension) extension_view = extension # Sync up the 'file_name' trait with the extension: self.sync_trait('file_name', extension, mutual=True) # Check to see if it also defines the optional IFileDialogView # interface, and if not, use the default view information: if not extension.has_traits_interface(IFileDialogView): extension_view = default_view # Get the view that the extension wants to use: view = extension.trait_view(extension_view.view) # Determine if we should use a splitter for the dialog: if not extension_view.is_fixed: klass = HSplit # Add the extension as a new view item: items.append( Item(name, label=user_name_for(extension.__class__.__name__), show_label=False, style='custom', width=0.5, height=0.5, dock='horizontal', resizable=True, editor=InstanceEditor(view=view, id=name))) # Finally, combine the normal view element with the extensions: item = klass(item, VSplit(id='splitter2', springy=True, *items), id='splitter') # Return the resulting view: return View(VGroup( VGroup(item), HGroup( Item('create', id='create', show_label=False, style='custom', defined_when='is_save_file', enabled_when='can_create_dir', tooltip='Create a new directory'), Item('file_name', id='history', editor=HistoryEditor(entries=self.entries, auto_set=True), springy=True), Item('ok', id='ok', show_label=False, enabled_when='is_valid_file'), Item('cancel', show_label=False))), title=self.title, id=self.id, kind='livemodal', width=width, height=height, close_result=False, resizable=True) def _file_already_exists(self): """ Handles prompting the user when the selected file already exists, and the dialog is a 'save file' dialog. """ feh = FileExistsHandler(message=("The file '%s' already exists.\nDo " "you wish to overwrite it?") % basename(self.file_name)) feh.edit_traits( context=self, parent=self.info.ok.control).trait_set(parent=self.info.ui)
class MDataViewWidget(HasTraits): """ Mixin class for data view widgets. """ # IDataViewWidget Interface traits -------------------------------------- #: The data model for the data view. data_model = Instance(AbstractDataModel, allow_none=False) #: Whether or not the column headers are visible. header_visible = Bool(True) #: The global drop handlers for the data view. These are intended to #: handle drop actions which either affect the whole data view, or where #: the data handler can work out how to change the underlying data without #: additional input. drop_handlers = List(Instance(IDropHandler, allow_none=False)) #: The selected indices in the view. This should never be mutated, any #: changes should be by replacement of the entire list. selection = Property(observe='_selection.items') #: Exporters available for the DataViewWidget. exporters = List(Instance(AbstractDataExporter)) # Private traits -------------------------------------------------------- #: Whether the selection is currently being updated. _selection_updating_flag = Bool() #: The selected indices in the view. This should never be mutated, any #: changes should be by replacement of the entire list. _selection = List(Tuple) # ------------------------------------------------------------------------ # MDataViewWidget Interface # ------------------------------------------------------------------------ def _header_visible_updated(self, event): """ Observer for header_visible trait. """ if self.control is not None: self._set_control_header_visible(event.new) def _get_control_header_visible(self): """ Toolkit specific method to get the visibility of the header. Returns ------- control_header_visible : bool Whether or not the control's header is visible. """ raise NotImplementedError() def _set_control_header_visible(self, control_header_visible): """ Toolkit specific method to set the visibility of the header. Parameters ---------- control_header_visible : bool Whether or not the control's header is visible. """ raise NotImplementedError() def _selection_type_updated(self, event): """ Observer for selection_type trait. """ if self.control is not None: self._set_control_selection_type(event.new) self.selection = [] def _get_control_selection_type(self): """ Toolkit specific method to get the selection type. Returns ------- selection_type : str The type of selection the control is using. """ raise NotImplementedError() def _set_control_selection_type(self, selection_type): """ Toolkit specific method to change the selection type. Parameters ---------- selection_type : str The type of selection the control is using. """ raise NotImplementedError() def _selection_mode_updated(self, event): """ Observer for selection_mode trait. """ if self.control is not None: self._set_control_selection_mode(event.new) self.selection = [] def _get_control_selection_mode(self): """ Toolkit specific method to get the selection mode. Returns ------- selection_mode : str The selection mode the control is using (eg. single vs. extended selection). """ raise NotImplementedError() def _set_control_selection_mode(self, selection_mode): """ Toolkit specific method to change the selection mode. Parameters ---------- selection_mode : str The selection mode the control is using (eg. single vs. extended selection). """ raise NotImplementedError() def _selection_updated(self, event): """ Observer for selection trait. """ if self.control is not None and not self._selection_updating_flag: with self._selection_updating(): self._set_control_selection(self.selection) def _get_control_selection(self): """ Toolkit specific method to get the selection. Returns ------- selection : list of pairs of row and column indices The selected elements of the control. """ raise NotImplementedError() def _set_control_selection(self, selection): """ Toolkit specific method to change the selection. Parameters ---------- selection : list of pairs of row and column indices The selected elements of the control. """ raise NotImplementedError() def _observe_control_selection(self, remove=False): """ Toolkit specific method to watch for changes in the selection. The _update_selection method is available as a toolkit-independent callback when the selection changes, but particular toolkits may choose to implement their own callbacks with similar functionality if appropriate. """ raise NotImplementedError() def _update_selection(self, *args, **kwargs): """ Handle a toolkit even that changes the selection. This is designed to be usable as a callback for a toolkit event or signal handler, so it accepts any arguments. """ if not self._selection_updating_flag: with self._selection_updating(): self._selection = self._get_control_selection() # ------------------------------------------------------------------------ # Widget Interface # ------------------------------------------------------------------------ def _create(self): """ Creates the toolkit specific control. This method should create the control and assign it to the :py:attr:``control`` trait. """ super()._create() self.show(self.visible) self.enable(self.enabled) def _initialize_control(self): """ Initializes the toolkit specific control. """ logger.debug('Initializing DataViewWidget') super()._initialize_control() self._set_control_header_visible(self.header_visible) self._set_control_selection_mode(self.selection_mode) self._set_control_selection_type(self.selection_type) self._set_control_selection(self.selection) def _add_event_listeners(self): logger.debug('Adding DataViewWidget listeners') super()._add_event_listeners() self.observe( self._header_visible_updated, 'header_visible', dispatch='ui', ) self.observe( self._selection_type_updated, 'selection_type', dispatch='ui', ) self.observe( self._selection_mode_updated, 'selection_mode', dispatch='ui', ) self.observe( self._selection_updated, '_selection.items', dispatch='ui', ) if self.control is not None: self._observe_control_selection() def _remove_event_listeners(self): logger.debug('Removing DataViewWidget listeners') if self.control is not None: self._observe_control_selection(remove=True) self.observe( self._header_visible_updated, 'header_visible', dispatch='ui', remove=True, ) self.observe( self._selection_type_updated, 'selection_type', dispatch='ui', remove=True, ) self.observe( self._selection_mode_updated, 'selection_mode', dispatch='ui', remove=True, ) self.observe( self._selection_updated, '_selection.items', dispatch='ui', remove=True, ) super()._remove_event_listeners() # ------------------------------------------------------------------------ # Private methods # ------------------------------------------------------------------------ @contextmanager def _selection_updating(self): """ Context manager to prevent loopback when updating selections. """ if self._selection_updating_flag: yield else: self._selection_updating_flag = True try: yield finally: self._selection_updating_flag = False # Trait property handlers @cached_property def _get_selection(self): return self._selection def _set_selection(self, selection): if self.selection_mode == 'none' and len(selection) != 0: raise TraitError( "Selection must be empty when selection_mode is 'none', " "got {!r}".format(selection)) elif self.selection_mode == 'single' and len(selection) > 1: raise TraitError( "Selection must have at most one element when selection_mode " "is 'single', got {!r}".format(selection)) if self.selection_type == 'row': for row, column in selection: if column != (): raise TraitError( "Column values must be () when selection_type is " "'row', got {!r}".format(column)) if not self.data_model.is_row_valid(row): raise TraitError("Invalid row index {!r}".format(row)) elif self.selection_type == 'column': for row, column in selection: if not (self.data_model.is_row_valid(row) and self.data_model.can_have_children(row) and self.data_model.get_row_count(row) > 0): raise TraitError( "Row values must have children when selection_type " "is 'column', got {!r}".format(column)) if not self.data_model.is_column_valid(column): raise TraitError( "Invalid column index {!r}".format(column)) else: for row, column in selection: if not self.data_model.is_row_valid(row): raise TraitError("Invalid row index {!r}".format(row)) if not self.data_model.is_column_valid(column): raise TraitError( "Invalid column index {!r}".format(column)) self._selection = selection
class XFESubDomain(FESubDomain): '''Subgrid derived from another grid domain. ''' # Distinguish which part of the level set to take # domain_tag = Enum('both', 'pos', 'neg') rt_tol = Float(0.0001) # specialized label _tree_label = Str('extension domain') # container domain (why is this needed?) _domain = Instance('ibvpy.mesh.fe_domain.FEDomain') domain = Property def _set_domain(self, value): 'reset the domain of this domain' if self._domain: # unregister in the old domain raise NotImplementedError( 'FESubDomain cannot be relinked to another FEDomain') self._domain = value # register in the domain as a subdomain self._domain.xdomains.append(self) self._domain._append_in_series(self) def _get_domain(self): return self._domain #----------------------------------------------------------------- # Associated time stepper #----------------------------------------------------------------- # element level fets_eval = Instance(FETSEval) # domain level dots = Property @cached_property def _get_dots(self): '''Construct and return a new instance of domain time stepper. ''' return XDOTSEval(sdomain=self) #----------------------------------------------------------------- # Boundary level set - define the boundary of the subdomain #----------------------------------------------------------------- # Specify the boundary of the subdomain # The elements outside of the domain are # not included in the integration and are # not deactivated from the original mesh. # bls_function = Callable def _bls_function_default(self): def unity(*x): # by default the level set function is positive in the whole domain # - the level set is valid in the whole domain return sign(fabs(x[0])) return unity #----------------------------------------------------------------- # Level set separating the domain into the positive and negative parts #----------------------------------------------------------------- # current slice instance _slice = Instance(IFEGridSlice) # slice property slice = Property def _set_slice(self, new_slice): '''Set the slice by keeping track of the elements included in the subdomain so far. ''' # remember the element numbers that were in the slice so far. old_state_array = None if self._slice: old_elems = self.elems # get the new elemes new_elems = new_slice.elems # remember the state associated with the elements print('old_elems', old_elems) print('new_elems', new_elems) old_size = old_elems.size new_size = new_elems.size # if the old array is contained at the beginning of the new array # reuse the state array if old_size < new_size and array_equal(old_elems, new_elems[:old_size]): old_state_array = copy(self.dots.state_array) self._slice = new_slice # reuse the old state array if old_state_array: self.dots.state_array[:old_size] = old_state_array def _get_slice(self): '''Get current slice. ''' return self._slice fe_grid = Property def _get_fe_grid(self): return self._slice.fe_grid #--------------------------------------------------------------------- # Element groups accessed as properties #--------------------------------------------------------------------- # Elements cut by the boundary LS function and by the discontinuity LS # boundary_elems = Property(depends_on='_slice, boundary') @cached_property def _get_boundary_elems(self): # # boundary elements are defined as cells that are # contain both positive and negative values of the level set function # and are crossed with the domain boundary. # b_elems_grid = logical_and( nfabs(self.bls_elem_grid) < 4, nfabs(self.ls_elem_grid) < 4) return self._get_elems_from_elem_grid(b_elems_grid) interior_elems = Property(depends_on='_slice, boundary') @cached_property def _get_interior_elems(self): # evaluate the boundary operator - with a result boolean array # in the corner nodes. # b_elem_grid = self.bls_elem_grid ls_elem_grid = self.ls_elem_grid # make the intersection # elem_grid = logical_and(nfabs(ls_elem_grid) < 4, b_elem_grid == 4) return self._get_elems_from_elem_grid(elem_grid) def _get_elems_from_elem_grid(self, elem_grid): '''Get an array of element offsets based on the from an array with boolean identifiers of elements. All elements corresponding to the boolean identifiers are returned. Subsequently, the array based properties of the domain, like elem_dof_map, elem_geo_x and elem_X_geo can be accessed. ''' # get the mask of elements belonging to the level set domain # elem_idx = argwhere(elem_grid) n_dims = elem_idx.shape[1] # prepare the index as a sequence of arrays in x, y, and z dimensions # elem_idx_tuple = tuple([elem_idx[:, i] for i in range(n_dims)]) # get the enumeration of cells in a n-dim array # c_g = self.fe_grid.dof_grid.cell_grid.cell_idx_grid # select the elements cut by the level set # return c_g[elem_idx_tuple] elems = Property(depends_on='_slice, boundary') @cached_property def _get_elems(self): return hstack([self.interior_elems, self.boundary_elems]) bls_elem_grid = Property(depends_on='_slice, boundary') @cached_property def _get_bls_elem_grid(self): X_grid = self.fe_grid.dof_grid.cell_grid.vertex_X_grid # evaluate the boundary operator - with a result boolean array # in the corner nodes. # b_vertex_grid = sign(self.bls_function(*X_grid)) return edge_sum(b_vertex_grid) ls_elem_grid = Property(depends_on='_slice, boundary') @cached_property def _get_ls_elem_grid(self): X_grid = self.fe_grid.dof_grid.cell_grid.vertex_X_grid # find the indices of the elems ls_function = self.slice.ls_function n_dims = X_grid.shape[0] vect_fn = frompyfunc(ls_function, n_dims, 1) ls_vertex_grid = sign(vect_fn(*X_grid)) return array(edge_sum(ls_vertex_grid), dtype=float) def deactivate_sliced_elems(self): for e in self.elems: self.fe_grid.deactivate(e) elements = Property(depends_on='_slice, boundary') @cached_property def _get_elements(self): elem_dof_map = self.elem_dof_map elem_X_map = self.elem_X_map elem_x_map = self.elem_x_map return [ MElem(dofs=dofs, point_X_arr=point_X_arr, point_x_arr=point_x_arr) for dofs, point_X_arr, point_x_arr in zip(elem_dof_map, elem_X_map, elem_x_map) ] elem_dof_map = Property(depends_on='_slice, boundary') @cached_property def _get_elem_dof_map(self): return self.dof_enum_data[0] dof_enum_data = Property(depends_on='_slice, boundary') @cached_property def _get_dof_enum_data(self): ''' generates elem_dof_map and n_dofs TODO - optimize the the algorithm, replace loop with sucessive slices to find jump in the enumeration TODO - generalize the algorithm from dofs to nodes, this will allow to have more extended dofs than parent ones ''' # (elems, dofs) # @todo # It would be better to provide fe_grid with # several element access methods - specify an array of elements # and you get a consistent array of dofs, geo_x and geo_X values. # dofs = self.fe_grid.dof_grid.cell_dof_map[self.elems] sh = dofs.shape # number of added dofs n_xdofs = self.fets_eval.n_e_dofs - self.fets_eval.parent_fets.n_e_dofs # This is ugly hack for rough version # @todo # the number of enriching dofs must be specified explicitly in # the fets_eval - used for enrichment. # if (n_xdofs / 2) > self.fets_eval.parent_fets.n_e_dofs: #(array([[0,1,2,3,4,5,6,7,8,9,10,11]]),10) return (array([[1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13]]), 10) # x_slice stands for number of enriched dofs in a single node. # elem_dof_map = copy(dofs).reshape(sh[0], self.fets_eval.parent_fets.n_e_dofs) elem_xdof_map = (copy(dofs)[:, :, self.fets_eval.x_slice]).reshape( sh[0], n_xdofs) # special case for selective integration - no enrichment. returns # the original dofs and zero n_dofs # if elem_xdof_map.shape[1] == 0: return (dofs, 0) # without additional dofs # compress the dof enumeration to be a consecutive sequence # of integers without gaps # xidx = sort(unique(elem_xdof_map.flatten())) last_idx = 0 for i in range(len(xidx)): shrink = xidx[i] - last_idx if shrink > 0: # shrink everything behind shrink elem_xdof_map[where(elem_xdof_map > i)] -= shrink last_idx = xidx[i] + 1 elem_xdof_map += self.dof_offset # # return an array of dof numbers - the original dofs of the parent # domain are reused by the xfe_subdomain. # return (hstack([elem_dof_map, elem_xdof_map]), xidx.shape[0]) elem_X_map = Property(depends_on='_slice, boundary') @cached_property def _get_elem_X_map(self): geo_X = self.fe_grid.geo_grid.elem_X_map[self.elems] return geo_X elem_x_map = Property(depends_on='_slice, boundary') @cached_property def _get_elem_x_map(self): geo_x = self.fe_grid.geo_grid.elem_x_map[self.elems] return geo_x dof_node_ls_values = Property(depends_on='_slice, boundary') @cached_property def _get_dof_node_ls_values(self): X_grid = self.fe_grid.dof_grid.cell_grid.point_X_arr ls_function = self.slice.ls_function n_dims = X_grid.shape[1] vect_fn = frompyfunc(ls_function, n_dims, 1) ls_vertex_grid = sign(vect_fn(*X_grid.T)) # select the affected elements from the ls_vertex_grid elem_node_map = self.fe_grid.dof_grid.cell_grid.cell_node_map return ls_vertex_grid[elem_node_map[self.elems]] ls_intersection_r = Property(depends_on='_slice, boundary') @cached_property def _get_ls_intersection_r(self): '''Get the intersection points in local coordinates ''' # fe_grid_slice.r_i # intersecting points fe_grid = self.fe_grid i_elements = self.elems el_pnts = [] for elem in i_elements: inter_pts = [] # X_mtx = self.elements[elem].get_X_mtx() # skips deactivated # elements X_mtx = fe_grid.geo_grid.get_cell_point_X_arr(elem) dim = X_mtx.shape[1] # TODO:merge 1 and 2d if dim == 1: r_coord = get_intersect_pt(self.ls_fn_r, (0., X_mtx)) if r_coord != None: inter_pts.append([r_coord]) elif dim == 2: for c_coord in [-1., 1.]: args = (c_coord, X_mtx) s_coord = get_intersect_pt(self.ls_fn_s, args) r_coord = get_intersect_pt(self.ls_fn_r, args) if s_coord != None: inter_pts.append([c_coord, s_coord]) if r_coord != None: inter_pts.append([r_coord, c_coord]) elif dim == 3: raise NotImplementedError('not available for 3D yet') el_pnts.append(inter_pts) return array(el_pnts) def ls_fn_r(self, r, s, X_mtx): # TODO:dimensionless treatment ls_function = self.slice.ls_function X_pnt = self.fe_grid.fets_eval.map_r2X([r, s], X_mtx) if X_pnt.shape[0] == 1: Y = 0. else: Y = X_pnt[1] return ls_function(X_pnt[0], Y) def ls_fn_s(self, s, r, X_mtx): ls_function = self.slice.ls_function X_pnt = self.fe_grid.fets_eval.map_r2X([r, s], X_mtx) return ls_function(X_pnt[0], X_pnt[1]) n_elems = Property(depends_on='_slice, boundary') @cached_property def _get_n_elems(self): return self.elems.shape[0] n_dofs = Property @cached_property def _get_n_dofs(self): #n_xdofs = self.fets_eval.n_e_dofs - self.fets_eval.parent_fets.n_e_dofs # return n_xdofs * self.n_active_elems return self.dof_enum_data[1] idx_active_elems = Property(depends_on='_slice, boundary') @cached_property def _get_idx_active_elems(self): return arange(self.shape) n_active_elems = Property(depends_on='_slice, boundary') @cached_property def _get_n_active_elems(self): return self.shape # TODO: def apply_on_ip_grid(self, fn, ip_mask): ''' Apply the function fn over the first dimension of the array. @param fn: function to apply for each ip from ip_mask and each element. @param ip_mask: specifies the local coordinates within the element. ''' X_el = self.elem_X_map # test call to the function with single output - to get the shape of # the result. out_single = fn(ip_mask[0], X_el[0]) out_grid_shape = ( X_el.shape[0], ip_mask.shape[0], ) + out_single.shape out_grid = zeros(out_grid_shape) for el in range(X_el.shape[0]): for ip in range(ip_mask.shape[0]): out_grid[el, ip, ...] = fn(ip_mask[ip], X_el[el]) return out_grid
class Axes(Module): # The version of this class. Used for persistence. __version__ = 0 # The tvtk axes actor. axes = Instance(CubeAxesActor2D, allow_none=False, record=True) # The property of the axes (color etc.). property = Property(record=True) # The title text property of the axes. title_text_property = Property(record=True) # The label text property of the axes. label_text_property = Property(record=True) ######################################## # Private traits. _property = Instance(tvtk.Property2D) _title_text_property = Instance(tvtk.TextProperty) _label_text_property = Instance(tvtk.TextProperty) ######################################## # The view of this object. view = View(Group(Item(name='axes', style='custom', resizable=True), label='Axes', show_labels=False), Group(Item(name='_property', style='custom', resizable=True), label='Property', show_labels=False), Group(Item(name='_title_text_property', style='custom', resizable=True), label='Title Text', show_labels=False), Group(Item(name='_label_text_property', style='custom', resizable=True), label='Label Text', show_labels=False), scrollable=True, resizable=True, width=500, height=600) ###################################################################### # `object` interface ###################################################################### def __set_pure_state__(self, state): for prop in [ 'axes', '_property', '_title_text_property', '_label_text_property' ]: obj = getattr(self, prop) state_pickler.set_state(obj, state[prop]) ###################################################################### # `Module` interface ###################################################################### def setup_pipeline(self): """Override this method so that it *creates* the tvtk pipeline. This method is invoked when the object is initialized via `__init__`. Note that at the time this method is called, the tvtk data pipeline will *not* yet be setup. So upstream data will not be available. The idea is that you simply create the basic objects and setup those parts of the pipeline not dependent on upstream sources and filters. You should also set the `actors` attribute up at this point. """ # Create the axes and set things up. axes = CubeAxesActor2D(number_of_labels=2, font_factor=1.5, fly_mode='outer_edges', corner_offset=0.0, scaling=False) axes.axis_title_text_property.shadow = False axes.axis_label_text_property.shadow = False # Set the axes. self.axes = axes def update_pipeline(self): """Override this method so that it *updates* the tvtk pipeline when data upstream is known to have changed. This method is invoked (automatically) when any of the inputs sends a `pipeline_changed` event. """ mm = self.module_manager if mm is None or not self.axes.use_data_bounds: self.configure_input_data(self.axes, None) return src = mm.source self.configure_input(self.axes, src.outputs[0]) self.pipeline_changed = True def update_data(self): """Override this method so that it flushes the vtk pipeline if that is necessary. This method is invoked (automatically) when any of the inputs sends a `data_changed` event. """ # Just set data_changed, the component should do the rest. self.data_changed = True ###################################################################### # Non-public interface ###################################################################### def _scene_changed(self, old, new): super(Axes, self)._scene_changed(old, new) self.axes.camera = new.camera self._foreground_changed_for_scene(None, new.foreground) def _foreground_changed_for_scene(self, old, new): # Change the default color for the actor. self.property.color = new self.label_text_property.color = new self.title_text_property.color = new self.render() def _axes_changed(self, old, new): if old is not None: for obj in (old, self._property, self._title_text_property, self._label_text_property): obj.on_trait_change(self.render, remove=True) self.actors.remove(old) # Setup the axes. scene = self.scene if scene is not None: new.camera = scene.camera # Setup the private traits. self._property = new.property for prop in ['_title_text_property', '_label_text_property']: setattr(self, prop, getattr(new, 'axis' + prop)) # The handlers. for obj in (new, self._property, self._title_text_property, self._label_text_property): obj.on_trait_change(self.render) self.actors.append(new) if old is not None: self.update_pipeline() def _get_property(self): return self._property def _get_title_text_property(self): return self._title_text_property def _get_label_text_property(self): return self._label_text_property def _use_data_bounds_changed_for_axes(self): """ Updating the pipeline for this module is inexpensive and fits the actor to the source (if any). We are using it here. """ self.update_pipeline()
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 return def _get_y(self): return self.position[1] def _set_y(self, val): self.position[1] = val return def _get_width(self): return self.bounds[0] def _set_width(self, val): if isinstance(val, six.string_types): try: val = float(val) except: pass old_value = self.bounds[0] self.bounds[0] = val self.trait_property_changed('width', old_value, val) return def _get_height(self): return self.bounds[1] def _set_height(self, val): if isinstance(val, six.string_types): try: val = float(val) except: pass old_value = self.bounds[1] self.bounds[1] = val self.trait_property_changed('height', old_value, val) return 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 return 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 return 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 return 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 return 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 RangeSelectionOverlay(AbstractOverlay): """ Highlights the selection region on a component. Looks at a given metadata field of self.component for regions to draw as selected. """ #: The axis to which this tool is perpendicular. axis = Enum("index", "value") #: Mapping from screen space to data space. By default, it is just #: self.component. plot = Property(depends_on='component') #: The mapper (and associated range) that drive this RangeSelectionOverlay. #: By default, this is the mapper on self.plot that corresponds to self.axis. mapper = Instance(AbstractMapper) #: The element of an (x,y) tuple that corresponds to the axis index. #: By default, this is set based on self.asix and self.plot.orientation, #: but it can be overriden and set to 0 or 1. axis_index = Property #: The name of the metadata to look at for dataspace bounds. The metadata #: can be either a tuple (dataspace_start, dataspace_end) in "selections" or #: a boolean array mask of seleted dataspace points with any other name metadata_name = Str("selections") #------------------------------------------------------------------------ # Appearance traits #------------------------------------------------------------------------ #: The color of the selection border line. border_color = ColorTrait("dodgerblue") #: The width, in pixels, of the selection border line. border_width = Float(1.0) #: The line style of the selection border line. border_style = LineStyle("solid") #: The color to fill the selection region. fill_color = ColorTrait("lightskyblue") #: The transparency of the fill color. alpha = Float(0.3) #------------------------------------------------------------------------ # AbstractOverlay interface #------------------------------------------------------------------------ def overlay(self, component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on another component. Overrides AbstractOverlay. """ axis_ndx = self.axis_index lower_left = [0, 0] upper_right = [0, 0] # Draw the selection coords = self._get_selection_screencoords() for coord in coords: start, end = coord lower_left[axis_ndx] = start lower_left[1 - axis_ndx] = component.position[1 - axis_ndx] upper_right[axis_ndx] = end - start upper_right[1 - axis_ndx] = component.bounds[1 - axis_ndx] with gc: gc.clip_to_rect(component.x, component.y, component.width, component.height) gc.set_alpha(self.alpha) gc.set_fill_color(self.fill_color_) gc.set_stroke_color(self.border_color_) gc.set_line_width(self.border_width) gc.set_line_dash(self.border_style_) gc.draw_rect((lower_left[0], lower_left[1], upper_right[0], upper_right[1])) #------------------------------------------------------------------------ # Private methods #------------------------------------------------------------------------ def _get_selection_screencoords(self): """ Returns a tuple of (x1, x2) screen space coordinates of the start and end selection points. If there is no current selection, then returns an empty list. """ ds = getattr(self.plot, self.axis) selection = ds.metadata.get(self.metadata_name, None) if selection is None: return [] # "selections" metadata must be a tuple if self.metadata_name == "selections" or \ (selection is not None and isinstance(selection, tuple)): if selection is not None and len(selection) == 2: return [self.mapper.map_screen(array(selection))] else: return [] # All other metadata is interpreted as a mask on dataspace else: ar = arange(0, len(selection), 1) runs = arg_find_runs(ar[selection]) coords = [] for inds in runs: start = ds._data[ar[selection][inds[0]]] end = ds._data[ar[selection][inds[1] - 1]] coords.append(self.mapper.map_screen(array((start, end)))) return coords def _determine_axis(self): """ Determines which element of an (x,y) coordinate tuple corresponds to the tool's axis of interest. This method is only called if self._axis_index hasn't been set (or is None). """ if self.axis == "index": if self.plot.orientation == "h": return 0 else: return 1 else: # self.axis == "value" if self.plot.orientation == "h": return 1 else: return 0 #------------------------------------------------------------------------ # Trait event handlers #------------------------------------------------------------------------ def _component_changed(self, old, new): self._attach_metadata_handler(old, new) return def _axis_changed(self, old, new): self._attach_metadata_handler(old, new) return def _attach_metadata_handler(self, old, new): # This is used to attach a listener to the datasource so that when # its metadata has been updated, we catch the event and update properly if not self.plot: return datasource = getattr(self.plot, self.axis) if old: datasource.on_trait_change(self._metadata_change_handler, "metadata_changed", remove=True) if new: datasource.on_trait_change(self._metadata_change_handler, "metadata_changed") return def _metadata_change_handler(self, event): self.component.request_redraw() return #------------------------------------------------------------------------ # Default initializers #------------------------------------------------------------------------ def _mapper_default(self): # If the plot's mapper is a GridMapper, return either its # x mapper or y mapper mapper = getattr(self.plot, self.axis + "_mapper") if isinstance(mapper, GridMapper): if self.axis == 'index': return mapper._xmapper else: return mapper._ymapper else: return mapper #------------------------------------------------------------------------ # Property getter/setters #------------------------------------------------------------------------ @cached_property def _get_plot(self): return self.component @cached_property def _get_axis_index(self): return self._determine_axis()
class FusionsLaserManager(LaserManager): ''' ''' laser_controller = Instance(FusionsLogicBoard) fiber_light = Instance(FiberLight) # optics_view = Instance(OpticsView) # beam = DelegatesTo('laser_controller') # beammin = DelegatesTo('laser_controller') # beammax = DelegatesTo('laser_controller') # update_beam = DelegatesTo('laser_controller') # beam_enabled = DelegatesTo('laser_controller') # # beam_enabled = Bool(True) # # zoom = DelegatesTo('laser_controller') # zoommin = DelegatesTo('laser_controller') # zoommax = DelegatesTo('laser_controller') # update_zoom = DelegatesTo('laser_controller') # execute_button = DelegatesTo('laser_script_executor') # execute_label = DelegatesTo('laser_script_executor') pointer = Event pointer_state = Bool(False) pointer_label = Property(depends_on='pointer_state') step_heat_manager = None lens_configuration = Str('gaussian') lens_configuration_dict = Dict lens_configuration_names = List power_timer = None brightness_timer = None power_graph = None _prev_power = 0 record_brightness = Bool # recording_zoom = Float # record = Event # record_label = Property(depends_on='_recording_power_state') _recording_power_state = Bool(False) simulation = DelegatesTo('laser_controller') data_manager = None _data_manager_lock = None _current_rid = None # brightness_meter = Instance(BrightnessPIDManager) chiller = Any motor_event = Event _degas_thread = None @on_trait_change('laser_controller:refresh_canvas') def refresh_canvas(self): if self.stage_manager: self.stage_manager.canvas.request_redraw() #=============================================================================== # IExtractionDevice interface #=============================================================================== def extract(self, power, **kw): self.enable_laser() self.set_laser_power(power, **kw) def end_extract(self): self.disable_laser() def open_motor_configure(self): self.laser_controller.open_motor_configure() # def _record_fired(self): # if self._recording_power_state: # save = self.db_save_dialog() # self.stop_power_recording(delay=0, save=save) # else: # t = Thread(name='fusions.power_record', # target=self.start_power_recording, args=('Manual',)) # t.start() # self._recording_power_state = not self._recording_power_state def bind_preferences(self, pref_id): super(FusionsLaserManager, self).bind_preferences(pref_id) bind_preference(self, 'recording_zoom', '{}.recording_zoom'.format(pref_id) ) bind_preference(self, 'record_brightness', '{}.record_brightness'.format(pref_id) ) def set_light(self, state): if state: self.fiber_light.power_off() else: self.fiber_light.power_on() def set_light_intensity(self, v): self.fiber_light.intensity = v # def kill(self, **kw): # if self.step_heat_manager is not None: # self.step_heat_manager.kill(**kw) # super(FusionsLaserManager, self).kill(**kw) @on_trait_change('pointer') def pointer_ononff(self): ''' ''' self.pointer_state = not self.pointer_state self.laser_controller.set_pointer_onoff(self.pointer_state) def get_laser_watts(self): return self._requested_power def get_coolant_temperature(self, **kw): ''' ''' chiller = self.chiller if chiller is not None: return chiller.get_coolant_out_temperature(**kw) def get_coolant_status(self, **kw): chiller = self.chiller if chiller is not None: return chiller.get_faults(**kw) def do_motor_initialization(self, name): if self.laser_controller: motor = self.laser_controller.get_motor(name) if motor is not None: n = 4 from pychron.core.ui.progress_dialog import myProgressDialog pd = myProgressDialog(max=n, size=(550, 15)) pd.open() motor.initialize(progress=pd) pd.close() def set_beam_diameter(self, bd, force=False, **kw): ''' ''' result = False motor = self.get_motor('beam') if motor is not None: if motor.enabled or force: self.set_motor('beam', bd, **kw) result = True else: self.info('beam disabled by lens configuration {}'.format(self.lens_configuration)) return result def set_zoom(self, z, **kw): ''' ''' self.set_motor('zoom', z, **kw) def set_motor_lock(self, name, value): m = self.get_motor(name) if m is not None: m.locked = to_bool(value) return True def set_motor(self, *args, **kw): self.motor_event = (args, kw) return self.laser_controller.set_motor(*args, **kw) def get_motor(self, name): return next((mi for mi in self.laser_controller.motors if mi.name == name), None) def do_autofocus(self, **kw): if self.use_video: am = self.stage_manager.autofocus_manager am.passive_focus(block=True, **kw) def take_snapshot(self, *args, **kw): if self.use_video: return self.stage_manager.snapshot( auto=True, inform=False, *args, **kw) def start_video_recording(self, name='video', *args, **kw): if self.use_video: self.stage_manager.start_recording(basename=name) def stop_video_recording(self, *args, **kw): if self.use_video: self.stage_manager.stop_recording() def degasser_factory(self): from pychron.mv.degas.degasser import Degasser dm = Degasser( laser_manager=self, video=self.stage_manager.video, ) return dm def do_machine_vision_degas(self, lumens, duration, new_thread=False): if self.use_video: dm = self.degasser_factory() def func(): dm.degas(lumens, duration) if new_thread: self._degas_thread = Thread(target=func) self._degas_thread.start() else: func() def is_degassing(self): if self._degas_thread: return self._degas_thread.isRunning() #=============================================================================== # pyscript interface #=============================================================================== def _move_to_position(self, position, autocenter): if self.stage_manager is not None: if isinstance(position, tuple): if len(position) > 1: x, y = position[:2] self.stage_manager.linear_move(x, y) if len(position) == 3: self.stage_manager.set_z(position[2]) else: self.stage_manager.move_to_hole(position) return True def set_stage_map(self, mapname): if self.stage_manager is not None: self.stage_manager.set_stage_map(mapname) def _enable_hook(self): resp = self.laser_controller._enable_laser() if self.laser_controller.simulation: resp = True return resp def _disable_hook(self): resp = self.laser_controller._disable_laser() if self.laser_controller.simulation: resp = True return resp def show_motion_controller_manager(self): ''' ''' stage_controller = self.stage_manager.stage_controller package = 'pychron.managers.motion_controller_managers' if 'Aerotech' in stage_controller.__class__.__name__: klass = 'AerotechMotionControllerManager' package += '.aerotech_motion_controller_manager' else: klass = 'NewportMotionControllerManager' package += '.newport_motion_controller_manager' module = __import__(package, globals(), locals(), [klass], -1) factory = getattr(module, klass) m = factory(motion_controller=stage_controller) m.edit_traits() #========================= views ========================= def get_control_buttons(self): ''' ''' return [('enable', 'enable_label', None), # ('record', 'record_label', None), # ('pointer', 'pointer_label', None), # ('light', 'light_label', None) ] # def get_control_items(self): # ''' # ''' # # return Item('laser_controller', show_label=False, # # editor=InstanceEditor(view='control_view'), # # style='custom', # # springy=False, height= -100) # # # return self.laser_controller.get_control_group() # s = [('zoom', 'zoom', {}), # ('beam', 'beam', {'enabled_when':'object.beam_enabled'}) # ] # return self._update_slider_group_factory(s) # def get_lens_configuration_group(self): # return Item('lens_configuration', # editor=EnumEditor(values=self.lens_configuration_names) # ) # def get_optics_group(self): # csliders = self.get_control_items() # vg = HGroup(csliders, # show_border=True, # label='Optics', springy=False # ) # # lens_config = self.get_lens_configuration_group() # if lens_config: # vg.content.insert(0, lens_config) # # return vg # def get_control_button_group(self): # grp = HGroup(spring, Item('enabled_led', show_label=False, style='custom', editor=LEDEditor()), # self._button_group_factory(self.get_control_buttons(), orientation='h'), # # springy=True # ) # return grp def get_power_group(self): power_grp = VGroup( self.get_control_button_group(), HGroup( Item('requested_power', style='readonly', format_str='%0.2f', width=100 ), spring, Item('units', show_label=False, style='readonly'), spring ), # Item('laser_script_executor', show_label=False, style='custom'), # self._button_factory('execute_button', 'execute_label'), show_border=True, # springy=True, label='Power' ) ps = self.get_power_slider() if ps: # ps.springy = True power_grp.content.append(ps) return power_grp # def get_additional_group(self): # og = Group(Item('laser_controller', show_label=False, # editor=InstanceEditor(view='control_view'), # style='custom'), # label='Optics', # ) # ac = Group( # og, # show_border=True, # label='Additional Controls', # layout='tabbed') # # aclist = self.get_additional_controls() # if aclist is None: # og.label = 'Optics' # og.show_border = True # ac = og # else: # for ai in aclist: # ac.content.append(ai) # return ac # def get_control_group(self): # ''' # ''' # power_grp = self.get_power_group() # pulse_grp = Group(Item('pulse', style='custom', show_label=False), # label='Pulse', show_border=True # ) # power_grp = HGroup(power_grp, pulse_grp) # ac = self.get_additional_group() # g = HGroup(power_grp, ac) # # return g def _get_pointer_label(self): ''' ''' return 'Pointer ON' if not self.pointer_state else 'Pointer OFF' def _get_record_label(self): return 'Record' if not self._recording_power_state else 'Stop' def _get_record_brightness(self): return self.record_brightness and self._get_machine_vision() is not None #========================= defaults ======================= def get_power_database(self): from pychron.database.adapters.power_adapter import PowerAdapter db = PowerAdapter(name=self.dbname, kind='sqlite') return db def get_power_calibration_database(self): from pychron.database.adapters.power_calibration_adapter import PowerCalibrationAdapter db = PowerCalibrationAdapter(name=self.dbname, kind='sqlite') return db # def _subsystem_default(self): # ''' # ''' # return ArduinoSubsystem(name='arduino_subsystem_2') # def _brightness_meter_default(self): # if self.use_video: # b = BrightnessPIDManager(parent=self) # # b.brightness_manager.video = self.stage_manager.video # # return b def _fiber_light_default(self): ''' ''' return FiberLight(name='fiber_light')
class BarrellVaultGravityFormingProcess(HasTraits): ''' Define the simulation task prescribing the boundary conditions, target surfaces and configuration of the algorithm itself. ''' L_x = Float(3.0, auto_set=False, enter_set=True, input=True) L_y = Float(2.0, auto_set=False, enter_set=True, input=True) n_x = Int(3, auto_set=False, enter_set=True, input=True) n_y = Int(4, auto_set=False, enter_set=True, input=True) u_x = Float(0.5, auto_set=False, enter_set=True, input=True) n_steps = Int(30, auto_set=False, enter_set=True, input=True) ctf = Property(depends_on='+input') '''control target surface''' @cached_property def _get_ctf(self): return [r_, s_, -0.1 * (r_ * (1 - r_ / self.L_x))] factory_task = Property(Instance(FormingTask)) '''Factory task generating the crease pattern. ''' @cached_property def _get_factory_task(self): return YoshimuraCPFactory(L_x=self.L_x, L_y=self.L_y, n_x=self.n_x, n_y=self.n_y) init_displ_task = Property(Instance(FormingTask)) '''Initialization to render the desired folding branch. ''' @cached_property def _get_init_displ_task(self): cp = self.factory_task.formed_object return MapToSurface(previous_task=self.factory_task, target_faces=[(self.ctf, cp.N)]) fold_task = Property(Instance(FormingTask)) '''Configure the simulation task. ''' @cached_property def _get_fold_task(self): x_1 = self.init_displ_task.x_1 cp = self.factory_task n_l_h = cp.N_h[0, :].flatten() n_r_h = cp.N_h[-1, :].flatten() n_lr_h = cp.N_h[(0, -1), :].flatten() n_fixed_y = cp.N_h[(0, -1), 1].flatten() u_max = self.u_x dof_constraints = fix(n_l_h, [0], lambda t: t * u_max) + fix(n_lr_h, [2]) + \ fix(n_fixed_y, [1]) + fix(n_r_h, [0], lambda t: t * -u_max) + \ link(cp.N_v[0, :].flatten(), 0, 1.0, cp.N_v[1, :].flatten(), 0, 1.0) gu_dof_constraints = GuDofConstraints(dof_constraints=dof_constraints) gu_constant_length = GuConstantLength() sim_config = SimulationConfig(goal_function_type='gravity potential energy', gu={'cl': gu_constant_length, 'dofs': gu_dof_constraints}, acc=1e-5, MAX_ITER=500, debug_level=0) return SimulationTask(previous_task=self.init_displ_task, config=sim_config, n_steps=self.n_steps) load_task = Property(Instance(FormingTask)) '''Configure the simulation task. ''' @cached_property def _get_load_task(self): self.fold_task.x_1 cp = self.factory_task n_l_h = cp.N_h[0, (0, -1)].flatten() n_r_h = cp.N_h[-1, (0, -1)].flatten() n_fixed_y = cp.N_h[(0, -1), 1].flatten() n_scheitel = cp.N_h[2, :] n_aussen = cp.N_v dof_constraints = fix( n_l_h, [0, 2]) + fix(n_r_h, [0, 2]) + fix(n_fixed_y, [0, 1]) gu_dof_constraints = GuDofConstraints(dof_constraints=dof_constraints) gu_constant_length = GuConstantLength() sim_config = SimulationConfig(goal_function_type='total potential energy', gu={'cl': gu_constant_length, 'dofs': gu_dof_constraints}, acc=1e-10, MAX_ITER=1000, debug_level=0) FN = lambda F: lambda t: t * F F_max = 2 F_min = 1 F_ext_list = [(7, 2, FN(F_max)), (6, 2, FN(F_min)), (8, 2, FN(F_min))] fu_tot_poteng = FuPotEngTotal(kappa=np.array([4.97]), F_ext_list=F_ext_list) sim_config._fu = fu_tot_poteng st = SimulationTask(previous_task=self.fold_task, config=sim_config, n_steps=10) cp = st.formed_object cp.L = np.vstack([cp.L, np.array([[6, 7], [7, 8]], dtype='int_')]) cp.x_0 = self.fold_task.x_1 cp.u[:, :] = 0.0 cp.u[[6, 7, 8], 2] = 0.0001 fu_tot_poteng.forming_task = st return st
class TVTKScene(HasPrivateTraits): """A TVTK interactor scene widget. This widget uses a RenderWindowInteractor and therefore supports interaction with VTK widgets. The widget uses TVTK. The widget also supports the following: - Save the scene to a bunch of common (and not so common) image formats. - save the rendered scene to the clipboard. - adding/removing lists/tuples of actors - setting the view to useful predefined views (just like in MayaVi). - If one passes `stereo=1` to the constructor, stereo rendering is enabled. By default this is disabled. Changing the stereo trait has no effect during runtime. - One can disable rendering by setting `disable_render` to True. """ # The version of this class. Used for persistence. __version__ = 0 ########################################################################### # Traits. ########################################################################### # Turn on/off stereo rendering. This is set on initialization and # has no effect once the widget is realized. stereo = Bool(False) # Perform line smoothing for all renderered lines. This produces # much nicer looking lines but renders slower. This setting works # only when called before the first render. line_smoothing = Bool(False) # Perform point smoothing for all renderered points. This # produces much nicer looking points but renders slower. This # setting works only when called before the first render. point_smoothing = Bool(False) # Perform polygon smoothing (anti-aliasing) for all rendered # polygons. This produces much nicer looking points but renders # slower. This setting works only when called before the first # render. polygon_smoothing = Bool(False) # Enable parallel projection. This trait is synchronized with # that of the camera. parallel_projection = Bool(False, desc='if the camera uses parallel projection') # Disable rendering. disable_render = Bool(False, desc='if rendering is to be disabled') # Enable off-screen rendering. This allows a user to render the # scene to an image without the need to have the window active. # For example, the application can be minimized and the saved # scene should be generated correctly. This is handy for batch # scripts and the like. This works under Win32. Under Mac OS X # and Linux it requires a recent VTK version (later than Oct 2005 # and ideally later than March 2006) to work correctly. off_screen_rendering = Bool(False, desc='if off-screen rendering is enabled') # The background color of the window. This is really a shadow # trait of the renderer's background. Delegation does not seem to # work nicely for this. background = Trait(vtk_color_trait((0.5, 0.5, 0.5)), desc='the background color of the window') # The default foreground color of any actors. This basically # saves the preference and actors will listen to changes -- # the scene itself does not use this. foreground = Trait(vtk_color_trait((1.0, 1.0, 1.0)), desc='the default foreground color of actors') # The magnification to use when generating images from the render # window. magnification = Range( 1, 2048, 1, desc='the magnification used when the screen is saved to an image') # Specifies the number of frames to use for anti-aliasing when # saving a scene. This basically increases # `self.render_window.aa_frames` in order to produce anti-aliased # figures when a scene is saved to an image. It then restores the # `aa_frames` in order to get interactive rendering rates. anti_aliasing_frames = Range( 0, 20, 8, desc='number of frames to use for anti-aliasing when saving a scene') # Default JPEG quality. jpeg_quality = Range(10, 100, 95, desc='the quality of the JPEG image to produce') # Default JPEG progressive setting. jpeg_progressive = Bool(True, desc='if the generated JPEG should be progressive') # The light manager. light_manager = Instance(light_manager.LightManager, record=True) # The movie maker instance. movie_maker = Instance('tvtk.pyface.movie_maker.MovieMaker', record=True) # Is the scene busy or not. busy = Property(Bool, record=False) ######################################## # Events # Lifecycle events: there are no opening/opened events since the # control is actually created in __init__. # The control is going to be closed. closing = Event(record=False) # The control has been closed. closed = Event(record=False) # Event fired when an actor is added to the scene. actor_added = Event(record=False) # Event fired when any actor is removed from the scene. actor_removed = Event(record=False) ######################################## # Properties. # The interactor used by the scene. interactor = Property(Instance(tvtk.GenericRenderWindowInteractor)) # The render_window. render_window = Property(Instance(tvtk.RenderWindow)) # The renderer. renderer = Property(Instance(tvtk.Renderer)) # The camera. camera = Property(Instance(tvtk.Camera)) # The control to mimic the Widget behavior. control = Any ######################################## # Private traits. # A recorder for script recording. recorder = Instance(HasTraits, record=False, transient=True) # Cached last camera state. _last_camera_state = Any(transient=True) _camera_observer_id = Int(transient=True) _script_id = Str(transient=True) # Saved light_manager settings while loading a scene. The light manager # may not be created at the time a scene is loaded from disk, so if it # is saved here, when it is created the state is set. _saved_light_manager_state = Any(transient=True) # The renderer instance. _renderer = Instance(tvtk.Renderer) _renwin = Instance(tvtk.RenderWindow) _interactor = Instance(tvtk.RenderWindowInteractor) _camera = Instance(tvtk.Camera) _busy_count = Int(0) ########################################################################### # 'object' interface. ########################################################################### def __init__(self, parent=None, **traits): """ Initializes the object. """ # Base class constructor. super(TVTKScene, self).__init__(**traits) # Used to set the view of the scene. self._def_pos = 1 self.control = self._create_control(parent) self._renwin.update_traits() 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 = self.__dict__.copy() for x in [ 'control', '_renwin', '_interactor', '_camera', '_busy_count', '__sync_trait__', 'recorder', '_last_camera_state', '_camera_observer_id', '_saved_light_manager_state', '_script_id', '__traits_listener__' ]: d.pop(x, None) # Additionally pickle these. d['camera'] = self.camera return d def __getstate__(self): return state_pickler.dumps(self) def __setstate__(self, str_state): # This method is unnecessary since this object will almost # never be pickled by itself and only via an object that # contains it, therefore __init__ will be called when the # scene is constructed. However, setstate is defined just for # completeness. state_pickler.set_state(self, state_pickler.loads_state(str_state)) ########################################################################### # 'event' interface. ########################################################################### def _closed_fired(self): self.light_manager = None self._interactor = None self.movie_maker = None ########################################################################### # '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._renwin.render() def add_actors(self, actors): """ Adds a single actor or a tuple or list of actors to the renderer.""" # Reset the zoom if this is the first actor. reset_zoom = (len(self._renderer.actors) == 0 and len(self._renderer.volumes) == 0) if hasattr(actors, '__iter__'): for actor in actors: self._renderer.add_actor(actor) else: self._renderer.add_actor(actors) self.actor_added = actors if reset_zoom: self.reset_zoom() else: self.render() def remove_actors(self, actors): """ Removes a single actor or a tuple or list of actors from the renderer.""" if hasattr(actors, '__iter__'): for actor in actors: self._renderer.remove_actor(actor) else: self._renderer.remove_actor(actors) self.actor_removed = actors self.render() # Conevenience methods. add_actor = add_actors remove_actor = remove_actors def add_widgets(self, widgets, enabled=True): """Adds a single 3D widget or a sequence of widgets to the renderer. If `enabled` is True the widget is also enabled once it is added.""" if not hasattr(widgets, '__iter__'): widgets = [widgets] iren = self._interactor for widget in widgets: widget.interactor = iren widget.enabled = enabled self.render() def remove_widgets(self, widgets): """Removes a single 3D widget or a sequence of widgets from the renderer.""" if not hasattr(widgets, '__iter__'): widgets = [widgets] iren = self._interactor for widget in widgets: if widget.interactor is not None: widget.enabled = False widget.interactor = None self.render() def close(self): """Close the scene cleanly. This ensures that the scene is shutdown cleanly. This should be called if you are getting async errors when closing a scene from a UI. This is based on the observations of Charl Botha here: http://public.kitware.com/pipermail/vtkusers/2008-May/095291.html """ # Return if we are already closed. if self._renwin is None: return # Fire the "closing" event. self.closing = True # Disable any renders through traits listner callbacks. self.disable_render = True # Remove sync trait listeners. self.sync_trait('background', self._renderer, remove=True) self.sync_trait('parallel_projection', self.camera, remove=True) self.sync_trait('off_screen_rendering', self._renwin, remove=True) # Remove all the renderer's props. self._renderer.remove_all_view_props() # Set the renderwindow to release all resources and the OpenGL # context. self._renwin.finalize() # Disconnect the interactor from the renderwindow. self._interactor.render_window = None # Remove the reference to the render window. del self._renwin # Fire the "closed" event. self.closed = True def x_plus_view(self): """View scene down the +X axis. """ self._update_view(self._def_pos, 0, 0, 0, 0, 1) self._record_methods('x_plus_view()') def x_minus_view(self): """View scene down the -X axis. """ self._update_view(-self._def_pos, 0, 0, 0, 0, 1) self._record_methods('x_minus_view()') def z_plus_view(self): """View scene down the +Z axis. """ self._update_view(0, 0, self._def_pos, 0, 1, 0) self._record_methods('z_plus_view()') def z_minus_view(self): """View scene down the -Z axis. """ self._update_view(0, 0, -self._def_pos, 0, 1, 0) self._record_methods('z_minus_view()') def y_plus_view(self): """View scene down the +Y axis. """ self._update_view(0, self._def_pos, 0, 1, 0, 0) self._record_methods('y_plus_view()') def y_minus_view(self): """View scene down the -Y axis. """ self._update_view(0, -self._def_pos, 0, 1, 0, 0) self._record_methods('y_minus_view()') def isometric_view(self): """Set the view to an iso-metric view. """ self._update_view(self._def_pos, self._def_pos, self._def_pos, 0, 0, 1) self._record_methods('isometric_view()') def reset_zoom(self): """Reset the camera so everything in the scene fits.""" self._renderer.reset_camera() self.render() self._record_methods('reset_zoom()') def save(self, file_name, size=None, **kw_args): """Saves rendered scene to one of several image formats depending on the specified extension of the filename. If an additional size (2-tuple) argument is passed the window is resized to the specified size in order to produce a suitably sized output image. Please note that when the window is resized, the window may be obscured by other widgets and the camera zoom is not reset which is likely to produce an image that does not reflect what is seen on screen. Any extra keyword arguments are passed along to the respective image format's save method. """ ext = os.path.splitext(file_name)[1] meth_map = { '.ps': 'ps', '.bmp': 'bmp', '.tiff': 'tiff', '.png': 'png', '.jpg': 'jpg', '.jpeg': 'jpg', '.iv': 'iv', '.wrl': 'vrml', '.vrml': 'vrml', '.oogl': 'oogl', '.rib': 'rib', '.obj': 'wavefront', '.eps': 'gl2ps', '.pdf': 'gl2ps', '.tex': 'gl2ps', '.x3d': 'x3d', '.pov': 'povray' } if ext.lower() not in meth_map: raise ValueError( 'Unable to find suitable image type for given file extension.') meth = getattr(self, 'save_' + meth_map[ext.lower()]) if size is not None: orig_size = self.get_size() self.set_size(size) meth(file_name, **kw_args) self.set_size(orig_size) self._record_methods('save(%r, %r)' % (file_name, size)) else: meth(file_name, **kw_args) self._record_methods('save(%r)' % (file_name)) def save_ps(self, file_name): """Saves the rendered scene to a rasterized PostScript image. For vector graphics use the save_gl2ps method.""" if len(file_name) != 0: w2if = self._get_window_to_image() ex = tvtk.PostScriptWriter() ex.file_name = file_name configure_input(ex, w2if) self._exporter_write(ex) def save_bmp(self, file_name): """Save to a BMP image file.""" if len(file_name) != 0: w2if = self._get_window_to_image() ex = tvtk.BMPWriter() ex.file_name = file_name configure_input(ex, w2if) self._exporter_write(ex) def save_tiff(self, file_name): """Save to a TIFF image file.""" if len(file_name) != 0: w2if = self._get_window_to_image() ex = tvtk.TIFFWriter() ex.file_name = file_name configure_input(ex, w2if) self._exporter_write(ex) def save_png(self, file_name): """Save to a PNG image file.""" if len(file_name) != 0: w2if = self._get_window_to_image() ex = tvtk.PNGWriter() ex.file_name = file_name configure_input(ex, w2if) self._exporter_write(ex) def save_jpg(self, file_name, quality=None, progressive=None): """Arguments: file_name if passed will be used, quality is the quality of the JPEG(10-100) are valid, the progressive arguments toggles progressive jpegs.""" if len(file_name) != 0: if not quality and not progressive: quality, progressive = self.jpeg_quality, self.jpeg_progressive w2if = self._get_window_to_image() ex = tvtk.JPEGWriter() ex.quality = quality ex.progressive = progressive ex.file_name = file_name configure_input(ex, w2if) self._exporter_write(ex) def save_iv(self, file_name): """Save to an OpenInventor file.""" if len(file_name) != 0: ex = tvtk.IVExporter() self._lift() ex.input = self._renwin ex.file_name = file_name self._exporter_write(ex) def save_vrml(self, file_name): """Save to a VRML file.""" if len(file_name) != 0: ex = tvtk.VRMLExporter() self._lift() ex.input = self._renwin ex.file_name = file_name self._exporter_write(ex) def save_oogl(self, file_name): """Saves the scene to a Geomview OOGL file. Requires VTK 4 to work.""" if len(file_name) != 0: ex = tvtk.OOGLExporter() self._lift() ex.input = self._renwin ex.file_name = file_name self._exporter_write(ex) def save_rib(self, file_name, bg=0, resolution=None, resfactor=1.0): """Save scene to a RenderMan RIB file. Keyword Arguments: file_name -- File name to save to. bg -- Optional background option. If 0 then no background is saved. If non-None then a background is saved. If left alone (defaults to None) it will result in a pop-up window asking for yes/no. resolution -- Specify the resolution of the generated image in the form of a tuple (nx, ny). resfactor -- The resolution factor which scales the resolution. """ if resolution is None: # get present window size Nx, Ny = self.render_window.size else: try: Nx, Ny = resolution except TypeError: raise TypeError( "Resolution (%s) should be a sequence with two elements" % resolution) if len(file_name) == 0: return f_pref = os.path.splitext(file_name)[0] ex = tvtk.RIBExporter() ex.size = int(resfactor * Nx), int(resfactor * Ny) ex.file_prefix = f_pref ex.texture_prefix = f_pref + "_tex" self._lift() ex.render_window = self._renwin ex.background = bg if VTK_VER[:3] in ['4.2', '4.4']: # The vtkRIBExporter is broken in respect to VTK light # types. Therefore we need to convert all lights into # scene lights before the save and later convert them # back. ######################################## # Internal functions def x3to4(x): # convert 3-vector to 4-vector (w=1 -> point in space) return (x[0], x[1], x[2], 1.0) def x4to3(x): # convert 4-vector to 3-vector return (x[0], x[1], x[2]) def cameralight_transform(light, xform, light_type): # transform light by 4x4 matrix xform origin = x3to4(light.position) focus = x3to4(light.focal_point) neworigin = xform.multiply_point(origin) newfocus = xform.multiply_point(focus) light.position = x4to3(neworigin) light.focal_point = x4to3(newfocus) light.light_type = light_type ######################################## save_lights_type = [] for light in self.light_manager.lights: save_lights_type.append(light.source.light_type) # Convert lights to scene lights. cam = self.camera xform = tvtk.Matrix4x4() xform.deep_copy(cam.camera_light_transform_matrix) for light in self.light_manager.lights: cameralight_transform(light.source, xform, "scene_light") # Write the RIB file. self._exporter_write(ex) # Now re-convert lights to camera lights. xform.invert() for i, light in enumerate(self.light_manager.lights): cameralight_transform(light.source, xform, save_lights_type[i]) # Change the camera position. Otherwise VTK would render # one broken frame after the export. cam.roll(0.5) cam.roll(-0.5) else: self._exporter_write(ex) def save_wavefront(self, file_name): """Save scene to a Wavefront OBJ file. Two files are generated. One with a .obj extension and another with a .mtl extension which contains the material properties. Keyword Arguments: file_name -- File name to save to """ if len(file_name) != 0: ex = tvtk.OBJExporter() self._lift() ex.input = self._renwin f_pref = os.path.splitext(file_name)[0] ex.file_prefix = f_pref self._exporter_write(ex) def save_gl2ps(self, file_name, exp=None): """Save scene to a vector PostScript/EPS/PDF/TeX file using GL2PS. If you choose to use a TeX file then note that only the text output is saved to the file. You will need to save the graphics separately. Keyword Arguments: file_name -- File name to save to. exp -- Optionally configured vtkGL2PSExporter object. Defaults to None and this will use the default settings with the output file type chosen based on the extention of the file name. """ # Make sure the exporter is available. if not hasattr(tvtk, 'GL2PSExporter'): msg = "Saving as a vector PS/EPS/PDF/TeX file using GL2PS is "\ "either not supported by your version of VTK or "\ "you have not configured VTK to work with GL2PS -- read "\ "the documentation for the vtkGL2PSExporter class." print(msg) return if len(file_name) != 0: f_prefix, f_ext = os.path.splitext(file_name) ex = None if exp: ex = exp if not isinstance(exp, tvtk.GL2PSExporter): msg = "Need a vtkGL2PSExporter you passed a "\ "%s"%exp.__class__.__name__ raise TypeError(msg) ex.file_prefix = f_prefix else: ex = tvtk.GL2PSExporter() # defaults ex.file_prefix = f_prefix if f_ext == ".ps": ex.file_format = 'ps' elif f_ext == ".tex": ex.file_format = 'tex' elif f_ext == ".pdf": ex.file_format = 'pdf' else: ex.file_format = 'eps' ex.sort = 'bsp' ex.compress = 1 ex.edit_traits(kind='livemodal') self._lift() ex.render_window = self._renwin if ex.write3d_props_as_raster_image: self._exporter_write(ex) else: ex.write() # Work around for a bug in VTK where it saves the file as a # .pdf.gz when the file is really a PDF file. if f_ext == '.pdf' and os.path.exists(f_prefix + '.pdf.gz'): os.rename(f_prefix + '.pdf.gz', file_name) def save_x3d(self, file_name): """Save scene to an X3D file (http://www.web3d.org/x3d/). Keyword Arguments: file_name -- File name to save to. """ # Make sure the exporter is available. if not hasattr(tvtk, 'X3DExporter'): msg = "Saving as a X3D file does not appear to be "\ "supported by your version of VTK." print(msg) return if len(file_name) != 0: ex = tvtk.X3DExporter() ex.input = self._renwin ex.file_name = file_name ex.update() ex.write() def save_povray(self, file_name): """Save scene to a POVRAY (Persistence of Vision Raytracer), file (http://www.povray.org). Keyword Arguments: file_name -- File name to save to. """ # Make sure the exporter is available. if not hasattr(tvtk, 'POVExporter'): msg = "Saving as a POVRAY file does not appear to be "\ "supported by your version of VTK." print(msg) return if len(file_name) != 0: ex = tvtk.POVExporter() ex.input = self._renwin if hasattr(ex, 'file_name'): ex.file_name = file_name else: ex.file_prefix = os.path.splitext(file_name)[0] ex.update() ex.write() def get_size(self): """Return size of the render window.""" return self._interactor.size def set_size(self, size): """Set the size of the window.""" self._interactor.size = size self._renwin.size = size ########################################################################### # Properties. ########################################################################### def _get_interactor(self): """Returns the vtkRenderWindowInteractor of the parent class""" return self._interactor def _set_interactor(self, iren): if self._interactor is not None: self._interactor.render_window = None self._interactor = iren iren.render_window = self._renwin def _get_render_window(self): """Returns the scene's render window.""" return self._renwin def _get_renderer(self): """Returns the scene's renderer.""" return self._renderer def _get_camera(self): """ Returns the active camera. """ return self._renderer.active_camera def _get_busy(self): return self._busy_count > 0 def _set_busy(self, value): """The `busy` trait is either `True` or `False`. However, this could be problematic since we could have two methods `foo` and `bar that both set `scene.busy = True`. As soon as `bar` is done it sets `busy` back to `False`. This is wrong since the UI is still busy as `foo` is not done yet. We therefore store the number of busy calls and either increment it or decrement it and change the state back to `False` only when the count is zero. """ bc = self._busy_count if value: bc += 1 else: bc -= 1 bc = max(0, bc) self._busy_count = bc if bc == 1: self.trait_property_changed('busy', False, True) if bc == 0: self.trait_property_changed('busy', True, False) ########################################################################### # Non-public interface. ########################################################################### def _create_control(self, parent): """ Create the toolkit-specific control that represents the widget. """ if self.off_screen_rendering: if hasattr(tvtk, 'EGLRenderWindow'): renwin = tvtk.EGLRenderWindow() elif hasattr(tvtk, 'OSOpenGLRenderWindow'): renwin = tvtk.OSOpenGLRenderWindow() else: renwin = tvtk.RenderWindow() # If we are doing offscreen rendering we set the window size to # (1,1) so the window does not appear at all renwin.size = (1, 1) self._renwin = renwin self._interactor = tvtk.GenericRenderWindowInteractor( render_window=renwin) else: renwin = self._renwin = tvtk.RenderWindow() self._interactor = tvtk.RenderWindowInteractor( render_window=renwin) renwin.trait_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') self._interactor.initialize() self._interactor.render() self.light_manager = light_manager.LightManager(self) if self.off_screen_rendering: # We want the default size to be the normal (300, 300). # Setting the size now should not resize the window if # offscreen is working properly in VTK. renwin.size = (300, 300) return self._interactor def _get_window_to_image(self): w2if = tvtk.WindowToImageFilter( read_front_buffer=not self.off_screen_rendering) set_magnification(w2if, self.magnification) self._lift() w2if.input = self._renwin return w2if def _lift(self): """Lift the window to the top. Useful when saving screen to an image.""" return def _exporter_write(self, ex): """Abstracts the exporter's write method.""" # Bumps up the anti-aliasing frames when the image is saved so # that the saved picture looks nicer. rw = self.render_window if hasattr(rw, 'aa_frames'): aa_frames = rw.aa_frames rw.aa_frames = self.anti_aliasing_frames else: aa_frames = rw.multi_samples rw.multi_samples = self.anti_aliasing_frames rw.render() ex.update() ex.write() # Set the frames back to original setting. if hasattr(rw, 'aa_frames'): rw.aa_frames = aa_frames else: rw.multi_samples = aa_frames rw.render() def _update_view(self, x, y, z, vx, vy, vz): """Used internally to set the view.""" camera = self.camera camera.focal_point = 0.0, 0.0, 0.0 camera.position = x, y, z camera.view_up = vx, vy, vz self._renderer.reset_camera() self.render() def _disable_render_changed(self, val): if not val and self._renwin is not None: self.render() def _record_methods(self, calls): """A method to record a simple method called on self. We need a more powerful and less intrusive way like decorators to do this. Note that calls can be a string with new lines in which case we interpret this as multiple calls. """ r = self.recorder if r is not None: sid = self._script_id for call in calls.split('\n'): r.record('%s.%s' % (sid, call)) def _record_camera_position(self, vtk_obj=None, event=None): """Callback to record the camera position.""" r = self.recorder if r is not None: state = self._get_camera_state() lcs = self._last_camera_state if state != lcs: self._last_camera_state = state sid = self._script_id for key, value in state: r.record('%s.camera.%s = %r' % (sid, key, value)) r.record('%s.camera.compute_view_plane_normal()' % sid) r.record('%s.render()' % sid) def _get_camera_state(self): c = self.camera state = [] state.append(('position', list(c.position))) state.append(('focal_point', list(c.focal_point))) state.append(('view_angle', c.view_angle)) state.append(('view_up', list(c.view_up))) state.append(('clipping_range', list(c.clipping_range))) return state def _recorder_changed(self, r): """When the recorder is set we add an event handler so we can record the change to the camera position after the interaction. """ iren = self._interactor if r is not None: self._script_id = r.get_script_id(self) id = iren.add_observer('EndInteractionEvent', messenger.send) self._camera_observer_id = id i_vtk = tvtk.to_vtk(iren) messenger.connect(i_vtk, 'EndInteractionEvent', self._record_camera_position) else: self._script_id = '' iren.remove_observer(self._camera_observer_id) i_vtk = tvtk.to_vtk(iren) messenger.disconnect(i_vtk, 'EndInteractionEvent', self._record_camera_position) def _light_manager_changed(self, lm): if lm is not None: if self._saved_light_manager_state is not None: lm.__set_pure_state__(self._saved_light_manager_state) self._saved_light_manager_state = None def _movie_maker_default(self): from tvtk.pyface.movie_maker import MovieMaker return MovieMaker(scene=self)
class MRIHeadWithFiducialsModel(HasPrivateTraits): """Represent an MRI head shape with fiducials Attributes ---------- points : array (n_points, 3) MRI head surface points. tris : array (n_tris, 3) Triangles based on points. lpa : array (1, 3) Left peri-auricular point coordinates. nasion : array (1, 3) Nasion coordinates. rpa : array (1, 3) Right peri-auricular point coordinates. """ subject_source = Instance(MRISubjectSource, ()) bem = Instance(BemSource, ()) fid = Instance(FiducialsSource, ()) fid_file = DelegatesTo('fid', 'file') fid_fname = DelegatesTo('fid', 'fname') fid_points = DelegatesTo('fid', 'points') subjects_dir = DelegatesTo('subject_source') subject = DelegatesTo('subject_source') subject_has_bem = DelegatesTo('subject_source') points = DelegatesTo('bem') norms = DelegatesTo('bem') tris = DelegatesTo('bem') lpa = Array(float, (1, 3)) nasion = Array(float, (1, 3)) rpa = Array(float, (1, 3)) reset = Event(desc="Reset fiducials to the file.") # info can_save = Property(depends_on=['file', 'can_save_as']) can_save_as = Property(depends_on=['lpa', 'nasion', 'rpa']) can_reset = Property(depends_on=['file', 'fid.points', 'lpa', 'nasion', 'rpa']) fid_ok = Property(depends_on=['lpa', 'nasion', 'rpa'], desc="All points " "are set") default_fid_fname = Property(depends_on=['subjects_dir', 'subject'], desc="the default file name for the " "fiducials fif file") # switch for the GUI (has no effect in the model) lock_fiducials = Bool(False, desc="Used by GIU, has no effect in the " "model.") @on_trait_change('fid_points') def reset_fiducials(self): if self.fid_points is not None: self.lpa = self.fid_points[0:1] self.nasion = self.fid_points[1:2] self.rpa = self.fid_points[2:3] def save(self, fname=None): """Save the current fiducials to a file Parameters ---------- fname : str Destination file path. If None, will use the current fid filename if available, or else use the default pattern. """ if fname is None: fname = self.fid_file if not fname: fname = self.default_fid_fname dig = [{'kind': 1, 'ident': 1, 'r': np.array(self.lpa[0])}, {'kind': 1, 'ident': 2, 'r': np.array(self.nasion[0])}, {'kind': 1, 'ident': 3, 'r': np.array(self.rpa[0])}] write_fiducials(fname, dig, FIFF.FIFFV_COORD_MRI) self.fid_file = fname @cached_property def _get_can_reset(self): if not self.fid_file: return False elif np.any(self.lpa != self.fid.points[0:1]): return True elif np.any(self.nasion != self.fid.points[1:2]): return True elif np.any(self.rpa != self.fid.points[2:3]): return True return False @cached_property def _get_can_save_as(self): can = not (np.all(self.nasion == self.lpa) or np.all(self.nasion == self.rpa) or np.all(self.lpa == self.rpa)) return can @cached_property def _get_can_save(self): if not self.can_save_as: return False elif self.fid_file: return True elif self.subjects_dir and self.subject: return True else: return False @cached_property def _get_default_fid_fname(self): fname = fid_fname.format(subjects_dir=self.subjects_dir, subject=self.subject) return fname @cached_property def _get_fid_ok(self): return all(np.any(pt) for pt in (self.nasion, self.lpa, self.rpa)) def _reset_fired(self): self.reset_fiducials() # if subject changed because of a change of subjects_dir this was not # triggered @on_trait_change('subjects_dir,subject') def _subject_changed(self): subject = self.subject subjects_dir = self.subjects_dir if not subjects_dir or not subject: return # update bem head path = head_bem_fname.format(subjects_dir=subjects_dir, subject=subject) self.bem.file = path # find fiducials file path = fid_fname.format(subjects_dir=subjects_dir, subject=subject) if os.path.exists(path): self.fid_file = path self.lock_fiducials = True else: path = fid_fname_general.format(subjects_dir=subjects_dir, subject=subject, head='*') fnames = glob(path) if fnames: path = fnames[0] self.fid.file = path self.lock_fiducials = True else: self.fid.reset_traits(['file']) self.lock_fiducials = False # does not seem to happen by itself ... so hard code it: self.reset_fiducials()
class EXDesignReader(HasTraits): '''Read the data from the directory The design is described in semicolon-separated csv file providing the information about design parameters. Each file has the name n.txt ''' #-------------------------------------------------------------------- # Specification of the design - factor list, relative paths, etc #-------------------------------------------------------------------- open_exdesign = Button() def _open_exdesign_fired(self): file_name = open_file(filter=['*.eds'], extensions=[FileInfo(), TextInfo()]) if file_name != '': self.exdesign_spec_file = file_name exdesign_spec_file = File def _exdesign_spec_file_changed(self): f = file(self.exdesign_spec_file) str = f.read() self.exdesign_spec = eval('ExDesignSpec( %s )' % str) exdesign_spec = Instance(ExDesignSpec) def _exdesign_spec_default(self): return ExDesignSpec() @on_trait_change('exdesign_spec') def _reset_design_file(self): dir = os.path.dirname(self.exdesign_spec_file) exdesign_file = self.exdesign_spec.design_file self.design_file = os.path.join(dir, exdesign_file) exdesign_table_columns = Property(List, depends_on='exdesign_spec+') @cached_property def _get_exdesign_table_columns(self): return [ ObjectColumn(name=ps[2], editable=False, width=0.15) for ps in self.exdesign_spec.factors ] #-------------------------------------------------------------------- # file containing the association between the factor combinations # and data files having the data #-------------------------------------------------------------------- design_file = File def _design_file_changed(self): self.exdesign = self._read_exdesign() exdesign = List(ExRun) def _exdesign_default(self): return self._read_exdesign() def _read_exdesign(self): ''' Read the experiment design. ''' if exists(self.design_file): reader = csv.reader(open(self.design_file, 'r'), delimiter=';') data_dir = os.path.join(os.path.dirname(self.design_file), self.exdesign_spec.data_dir) return [ExRun(self, row, data_dir=data_dir) for row in reader] else: return [] response_array = Property(Array(float), depends_on='exdesign') @cached_property def _get_response_array(self): '''Get the specified response values ''' return array([[ e.max_stress, e.strain_at_max_stress, e.max_stiffness, e.strain_at_max_stiffness ] for e in self.exdesign], dtype='float_') selected_exrun = Instance(ExRun) def _selected_exrun_default(self): if len(self.exdesign) > 0: return self.exdesign[0] else: return None last_exrun = Instance(ExRun) selected_exruns = List(ExRun) #------------------------------------------------------------------ # Array plotting #------------------------------------------------------------------- # List of arrays to be plotted data = Instance(AbstractPlotData) def _data_default(self): return ArrayPlotData(x=array([]), y=array([])) @on_trait_change('selected_exruns') def _rest_last_exrun(self): if len(self.selected_exruns) > 0: self.last_exrun = self.selected_exruns[-1] @on_trait_change('selected_exruns') def _reset_data(self): ''' ''' runs, xlabels, ylabels, xlabels_afit, ylabels_afit, xlins, ylins = self._generate_data_labels( ) for name in list(self.plot.plots.keys()): self.plot.delplot(name) for idx, exrun in enumerate(self.selected_exruns): if xlabels[idx] not in self.plot.datasources: self.plot.datasources[xlabels[idx]] = ArrayDataSource( exrun.xdata, sort_order='none') if ylabels[idx] not in self.plot.datasources: self.plot.datasources[ylabels[idx]] = ArrayDataSource( exrun.ydata, sort_order='none') if xlabels_afit[idx] not in self.plot.datasources: self.plot.datasources[xlabels_afit[idx]] = ArrayDataSource( exrun.xdata_asc_fit, sort_order='none') if ylabels_afit[idx] not in self.plot.datasources: self.plot.datasources[ylabels_afit[idx]] = ArrayDataSource( exrun.ydata_asc_fit, sort_order='none') xlin, ylin = exrun.get_linear_data() if xlins[idx] not in self.plot.datasources: self.plot.datasources[xlins[idx]] = ArrayDataSource( xlin, sort_order='none') if ylins[idx] not in self.plot.datasources: self.plot.datasources[ylins[idx]] = ArrayDataSource( ylin, sort_order='none') for run, xlabel, ylabel, xlabel_afit, ylabel_afit, xlin, ylin in zip( runs, xlabels, ylabels, xlabels_afit, ylabels_afit, xlins, ylins): self.plot.plot((xlabel, ylabel), color='brown') self.plot.plot((xlabel_afit, ylabel_afit), color='blue') self.plot.plot((xlin, ylin), color='red') def _generate_data_labels(self): ''' Generate the labels consisting of the axis and run-number. ''' return ( [e.std_num for e in self.selected_exruns], ['x-%d' % e.std_num for e in self.selected_exruns], ['y-%d' % e.std_num for e in self.selected_exruns], ['x-%d-fitted' % e.std_num for e in self.selected_exruns], ['y-%d-fitted' % e.std_num for e in self.selected_exruns], ['x-%d-lin' % e.std_num for e in self.selected_exruns], ['y-%d-lin' % e.std_num for e in self.selected_exruns], ) plot = Instance(Plot) def _plot_default(self): p = Plot() p.tools.append(PanTool(p)) p.overlays.append(ZoomTool(p)) return p view_traits = View(HSplit( VGroup( Item('open_exdesign', style='simple'), Item('exdesign', editor=exrun_table_editor, show_label=False, style='custom')), VGroup( Item('last_exrun@', show_label=False), Item('plot', editor=ComponentEditor(), show_label=False, resizable=True), ), ), resizable=True, buttons=[OKButton, CancelButton], height=1., width=1.)
class LoggerService(HasTraits): """ The persistent service exposing the Logger plugin's API. """ # The Envisage application. application = Any() # The logging Handler we use. handler = Any() # Our associated LoggerPreferences. preferences = Any() # The view we use. plugin_view = Instance(WorkbenchView) # Contributions from other plugins. mail_files = Property(List(Callable)) def save_preferences(self): """ Save the preferences. """ self.preferences.preferences.save() def whole_log_text(self): """ Return all of the logged data as formatted text. """ lines = [self.handler.format(rec) for rec in self.handler.get()] # Ensure that we end with a newline. lines.append('') text = '\n'.join(lines) return text def create_email_message(self, fromaddr, toaddrs, ccaddrs, subject, priority, include_userdata=False, stack_trace="", comments="", include_environment=True): """ Format a bug report email from the log files. """ from email.mime.application import MIMEApplication from email.mime.multipart import MIMEMultipart from email.mime.text import MIMEText message = MIMEMultipart() message['Subject'] = "%s [priority=%s]" % (subject, priority) message['To'] = ', '.join(toaddrs) message['Cc'] = ', '.join(ccaddrs) message['From'] = fromaddr message.preamble = 'You will not see this in a MIME-aware mail ' \ 'reader.\n' message.epilogue = ' ' # To guarantee the message ends with a newline # First section is simple ASCII data ... m = [] m.append("Bug Report") m.append("==============================") m.append("") if len(comments) > 0: m.append("Comments:") m.append("========") m.append(comments) m.append("") if len(stack_trace) > 0: m.append("Stack Trace:") m.append("===========") m.append(stack_trace) m.append("") msg = MIMEText('\n'.join(m)) message.attach(msg) # Include the log file ... logtext = self.whole_log_text() msg = MIMEText(logtext) msg.add_header('Content-Disposition', 'attachment', filename='logfile.txt') message.attach(msg) # Include the environment variables ... # FIXME: ask the user, maybe? if include_environment: # Transmit the user's environment settings as well. Main purpose is # to work out the user name to help with following up on bug reports # and in future we should probably send less data. entries = [] for key, value in sorted(os.environ.items()): entries.append('%30s : %s\n' % (key, value)) msg = MIMEText(''.join(entries)) msg.add_header('Content-Disposition', 'attachment', filename='environment.txt') message.attach(msg) if include_userdata and len(self.mail_files) != 0: f = StringIO() zf = zipfile.ZipFile(f, 'w') for mf in self.mail_files: mf(zf) zf.close() msg = MIMEApplication(f.getvalue()) msg.add_header('Content-Disposition', 'attachment', filename='userdata.zip') message.attach(msg) return message def send_bug_report(self, smtp_server, fromaddr, toaddrs, ccaddrs, message): """ Send a bug report email. """ try: import smtplib logger.debug("Connecting to: %s" % smtp_server) server = smtplib.SMTP(host=smtp_server) logger.debug("Connected: %s" % server) #server.set_debuglevel(1) server.sendmail(fromaddr, toaddrs + ccaddrs, message.as_string()) server.quit() except Exception as e: logger.exception("Problem sending error report") #### Traits stuff ######################################################### def _get_mail_files(self): return self.application.get_extensions( 'apptools.logger.plugin.mail_files') @on_trait_change('preferences.level_') def _level_changed(self, new): if (new is not None and new is not Undefined and self.handler is not None): root_logger.setLevel(self.preferences.level_) self.handler.setLevel(self.preferences.level_)
class LocalizationZoneModel(BMCSModel): f_t = Float(2.5, MAT=True, auto_set=False, enter_set=True, symbol='$f_t$', unit='MPa', desc='tensile strength') E = Float(34000.0, MAT=True, auto_set=False, enter_set=True, symbol='$E$', unit='MPa', desc='E-modulus') L = Float(300.0, GEO=True, auto_set=False, enter_set=True, symbol='$L$', unit='mm', desc='length') G_f = Float(0.014, MAT=True, auto_set=False, enter_set=True, symbol='$G_\mathrm{F}$', unit='N/mm', desc='fracture energy') A = Float(10.0, CS=True, auto_set=False, enter_set=True, symbol='$A$', unit='$\mathrm{mm}^2$', desc='cross-sectional area') def f(self, w, f_t, G_f): '''Softening law''' return f_t * np.exp(-f_t / G_f * w) def F(self, w, f_t, G_f): '''Integral of the softening law''' return G_f - G_f * np.exp(-f_t / G_f * w) w_ch = Property(Float(depends_on='+MAT')) def _get_w_ch(self): return self.G_f / self.f_t w = Property(Array(np.float, depends_on='+MAT')) @cached_property def _get_w(self): w_max = 5.0 * self.w_ch return np.linspace(0, w_max, 100) def get_response(self): E = self.E A = self.A G_f = self.G_f f_t = self.f_t L = self.L w = self.w eps_el = [0, f_t / E] sig_el = [0, f_t] eps_w = 1 / E * self.f(w, f_t, G_f) + w / L sig_w = self.f(w, f_t, G_f) W_el = [0, f_t**2 / 2 / E * A * L] U_el = [0, f_t**2 / 2 / E * A * L] W_w = 1. / 2. / E * A * L * \ self.f(w, f_t, G_f)**2 + A * self.F(w, f_t, G_f) U_w = 1. / 2. / E * A * L * \ self.f(w, f_t, G_f)**2 + 1. / 2. * \ A * self.f(w, f_t, G_f) * w eps = np.hstack([eps_el, eps_w]) sig = np.hstack([sig_el, sig_w]) W = np.hstack([W_el, W_w]) U = np.hstack([U_el, U_w]) return eps, sig, W, U
class SvgRangeSelectionOverlay(StatusLayer): """ This is a primitive range selection overlay which uses a SVG to define the overlay. TODO: not inherit from StatusLayer, this was a convenience for a quick prototype TODO: use 2 svgs, one which defines the border and does not scale, and the other which defines the fill. """ filename = os.path.join(os.path.dirname(__file__), 'data', 'range_selection.svg') alpha = 0.5 # The axis to which this tool is perpendicular. axis = Enum("index", "value") axis_index = Property(depends_on='axis') # Mapping from screen space to data space. By default, it is just # self.component. plot = Property(depends_on='component') # The mapper (and associated range) that drive this RangeSelectionOverlay. # By default, this is the mapper on self.plot that corresponds to self.axis. mapper = Property(depends_on='plot') # The name of the metadata to look at for dataspace bounds. The metadata # can be either a tuple (dataspace_start, dataspace_end) in "selections" or # a boolean array mask of seleted dataspace points with any other name metadata_name = Str("selections") def overlay(self, component, gc, view_bounds=None, mode="normal"): """ Draws this component overlaid on another component. Overrides AbstractOverlay. """ # Draw the selection coords = self._get_selection_screencoords() if len(coords) == 0: return with gc: gc.set_alpha(self.alpha) plot_width = self.component.width plot_height = self.component.height origin_x = self.component.padding_left origin_y = self.component.padding_top if self.axis == 'index': if isinstance(self.mapper, GridMapper): scale_width = (coords[-1][0] - coords[0][0])/self.doc_width else: scale_width = (coords[0][-1] - coords[0][0])/self.doc_width scale_height = float(plot_height)/self.doc_height gc.translate_ctm(coords[0][0], origin_y + plot_height) else: scale_height = (coords[0][-1] - coords[0][0])/self.doc_height scale_width = float(plot_width)/self.doc_width gc.translate_ctm(origin_x, coords[0][0]) # SVG origin is the upper right with y positive down, so # we need to flip everything gc.scale_ctm(scale_width, -scale_height) self.document.render(gc) self._draw_component(gc, view_bounds, mode) return def _get_selection_screencoords(self): """ Returns a tuple of (x1, x2) screen space coordinates of the start and end selection points. If there is no current selection, then returns None. """ ds = getattr(self.plot, self.axis) selection = ds.metadata[self.metadata_name] # "selections" metadata must be a tuple if self.metadata_name == "selections": if selection is not None and len(selection) == 2: return [self.mapper.map_screen(numpy.array(selection))] else: return [] # All other metadata is interpreted as a mask on dataspace else: ar = numpy.arange(0,len(selection), 1) runs = arg_find_runs(ar[selection]) coords = [] for inds in runs: start = ds._data[ar[selection][inds[0]]] end = ds._data[ar[selection][inds[1]-1]] coords.append(self.map_screen(numpy.array((start, end)))) return coords @cached_property def _get_plot(self): return self.component @cached_property def _get_axis_index(self): if self.axis == 'index': return 0 else: return 1 @cached_property def _get_mapper(self): # If the plot's mapper is a GridMapper, return either its # x mapper or y mapper mapper = getattr(self.plot, self.axis + "_mapper") if isinstance(mapper, GridMapper): if self.axis == 'index': return mapper._xmapper else: return mapper._ymapper else: return mapper