예제 #1
0
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
예제 #2
0
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)
예제 #3
0
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'),
    )
예제 #4
0
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
예제 #5
0
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
예제 #6
0
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
예제 #7
0
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)
예제 #8
0
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
예제 #9
0
파일: barplot.py 프로젝트: zijuzhang/chaco
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
예제 #10
0
파일: cell_spec.py 프로젝트: simvisage/bmcs
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)
예제 #11
0
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', {})
예제 #12
0
파일: cell_spec.py 프로젝트: simvisage/bmcs
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]])
예제 #13
0
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:
예제 #14
0
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
예제 #15
0
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__)
예제 #16
0
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
예제 #17
0
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)
예제 #18
0
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
예제 #19
0
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
예제 #20
0
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()
예제 #21
0
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
예제 #22
0
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()
예제 #23
0
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')
예제 #24
0
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
예제 #25
0
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)
예제 #26
0
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()
예제 #27
0
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_)
예제 #29
0
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
예제 #30
0
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