Example #1
0
class TaurusBaseImageItem(TaurusBaseComponent):
    '''A ImageItem that gets its data from a taurus attribute'''

    dataChanged = baseSignal('dataChanged')

    def setModel(self, model):
        # do the standard stuff
        TaurusBaseComponent.setModel(self, model)
        #... and fire a fake event for initialization
        try:
            value = self.getModelObj().read()
            self.fireEvent(self,
                           taurus.core.taurusbasetypes.TaurusEventType.Change,
                           value)
        except:
            pass

    def handleEvent(self, evt_src, evt_type, evt_value):
        if evt_value is None or getattr(evt_value, 'rvalue', None) is None:
            self.debug('Ignoring event from %s' % repr(evt_src))
            return
        v = evt_value.rvalue
        if isinstance(v, Quantity):
            v = v.magnitude
            # TODO: units should be used for setting some title in the colorbar
        try:
            v = self.filterData(v)
        except Exception, e:
            self.info('Ignoring event. Reason: %s', e.message)
            return
        # this is the range of the z axis (color scale)
        lut_range = self.get_lut_range()
        # if the range was not set, make it None (autoscale z axis)
        if lut_range[0] == lut_range[1]:
            lut_range = None
        self.set_data(v, lut_range=lut_range)
        self.dataChanged.emit()
        p = self.plot()

        if p is not None:
            p.update_colormap_axis(self)
            p.replot()
Example #2
0
class TaurusCurveItem(CurveItem, TaurusBaseComponent):
    '''A CurveItem that autoupdates its values & params when x or y components change'''

    dataChanged = baseSignal('dataChanged')

    def __init__(self, curveparam=None, taurusparam=None):
        CurveItem.__init__(self, curveparam=curveparam)
        TaurusBaseComponent.__init__(self, self.__class__.__name__)
        # I need to do this because I am not using the standard model attach
        # mechanism
        self.taurusEvent.connect(self.filterEvent)
        self._xcomp = None
        self._ycomp = None
        if taurusparam is None:
            taurusparam = TaurusCurveParam()
        self.taurusparam = taurusparam

    def setModels(self, x, y):
        # create/get new components
        if x is None:
            newX = None
        else:
            newX = taurus.Attribute(x)
        newY = taurus.Attribute(y)

        # stop listening to previous components (if they are not the same as
        # the new)
        if self._xcomp is not None and self._xcomp is not newX:
            self._xcomp.removeListener(self)
        self._xcomp = newX
        if self._ycomp is not None and self._ycomp is not newY:
            self._ycomp.removeListener(self)
        self._ycomp = newY

        # start listening to new components
        if self._xcomp is not None:
            self._xcomp.addListener(self)
        self._ycomp.addListener(self)
        self.onCurveDataChanged()
        self.taurusparam.xModel = x
        self.taurusparam.yModel = y

    def getModels(self):
        return self.taurusparam.xModel, self.taurusparam.yModel

    def handleEvent(self, evt_src, ect_type, evt_value):
        if evt_value is None or getattr(evt_value, 'rvalue', None) is None:
            self.debug('Ignoring event from %s' % repr(evt_src))
            return
        if evt_src is self._xcomp or evt_src is self._ycomp:
            self.onCurveDataChanged()
            self.dataChanged.emit()

    def onCurveDataChanged(self):
        # TODO: Take units into account for displaying curves, axis, etc.
        try:
            yvalue = self._ycomp.read().rvalue.magnitude
        except:
            yvalue = None

        if yvalue is None:
            return

        # TODO: Take units into account for displaying curves, axis, etc.
        try:
            xvalue = self._xcomp.read().rvalue.magnitude
        except:
            xvalue = None

        if xvalue is None:
            xvalue = numpy.arange(len(yvalue))

        self.set_data(xvalue, yvalue)
        p = self.plot()
        if p is not None:
            p.replot()

    def get_item_parameters(self, itemparams):
        CurveItem.get_item_parameters(self, itemparams)
        itemparams.add("TaurusParam", self, self.taurusparam)

    def updateTaurusParams(self):
        self.taurusparam.update_curve(self)

    def set_item_parameters(self, itemparams):
        CurveItem.set_item_parameters(self, itemparams)
        self.updateTaurusParams()
Example #3
0
class TaurusTrendItem(CurveItem, TaurusBaseComponent):
    '''A CurveItem that listens to events from a Taurus scalar attribute and appends new values to it'''

    dataChanged = baseSignal('dataChanged')
    scrollRequested = baseSignal('scrollRequested', object, object, object)

    def __init__(self, curveparam=None, taurusparam=None):
        CurveItem.__init__(self, curveparam=curveparam)
        TaurusBaseComponent.__init__(self, self.__class__.__name__)

        self.__xBuffer = None
        self.__yBuffer = None

        self.__timeOffset = None

        if taurusparam is None:
            taurusparam = TaurusTrendParam()
        self.taurusparam = taurusparam
        self.updateTaurusParams()

    def setBufferSize(self, buffersize):
        '''sets the size of the stack.

        :param buffersize: (int) size of the stack
        '''
        self.taurusparam.maxBufferSize = buffersize
        try:
            if self.__xBuffer is not None:
                self.__xBuffer.setMaxSize(buffersize)
            if self.__yBuffer is not None:
                self.__yBuffer.setMaxSize(buffersize)
        except ValueError:
            self.info(
                'buffer downsizing  requested. Current contents will be discarded'
            )
            self.__xBuffer = None
            self.__yBuffer = None

    def setModel(self, model):
        # do the standard stuff
        TaurusBaseComponent.setModel(self, model)
        # update the taurusparam
        self.taurusparam.model = self.getModelName()
        #... and fire a fake event for initialization
        try:
            value = self.getModelObj().read()
            self.fireEvent(self,
                           taurus.core.taurusbasetypes.TaurusEventType.Change,
                           value)
        except:
            pass

    def handleEvent(self, evt_src, evt_type, evt_value):
        if evt_value is None or getattr(evt_value, 'rvalue', None) is None:
            self.debug('Ignoring event from %s' % repr(evt_src))
            return

        plot = self.plot()

        # initialization\
        if self.__xBuffer is None:
            self.__xBuffer = ArrayBuffer(
                numpy.zeros(min(128, self.taurusparam.maxBufferSize),
                            dtype='d'),
                maxSize=self.taurusparam.maxBufferSize)
        if self.__yBuffer is None:
            self.__yBuffer = ArrayBuffer(
                numpy.zeros(min(128, self.taurusparam.maxBufferSize),
                            dtype='d'),
                maxSize=self.taurusparam.maxBufferSize)

        # update x values
        if self.taurusparam.stackMode == 'datetime':
            if self.__timeOffset is None:
                self.__timeOffset = evt_value.time.totime()
                if plot is not None:
                    plot.set_axis_title('bottom', 'Time')
                    plot.set_axis_unit('bottom', '')
            self.__xBuffer.append(evt_value.time.totime())

        elif self.taurusparam.stackMode == 'deltatime':
            try:
                self.__xBuffer.append(evt_value.time.totime() -
                                      self.__timeOffset)
            except TypeError:  # this will happen if self.__timeOffset has not been initialized
                self.__timeOffset = evt_value.time.totime()
                self.__xBuffer.append(0)
                if plot is not None:
                    plot.set_axis_title(
                        'bottom', 'Time since %s' % evt_value.time.isoformat())
                    plot.set_axis_unit('bottom', '')
        else:
            try:
                # +numpy.random.randint(0,4) #for debugging we can put a variable step
                step = 1
                self.__xBuffer.append(self.__xBuffer[-1] + step)
            except IndexError:  # this will happen when the x buffer is empty
                self.__xBuffer.append(0)
                if plot is not None:
                    plot.set_axis_title('bottom', 'Event #')
                    plot.set_axis_unit('bottom', '')

        # update y
        # TODO: Take units into account for displaying curves, axis, etc.
        self.__yBuffer.append(evt_value.rvalue.magnitude)

        # update the plot data
        x, y = self.__xBuffer.contents(), self.__yBuffer.contents()
        self.set_data(x, y)

        # signal data changed and replot
        self.dataChanged.emit()

        if plot is not None:
            value = x[-1]
            axis = self.xAxis()
            xmin, xmax = plot.get_axis_limits(axis)
            if value > xmax or value < xmin:
                self.scrollRequested.emit(plot, axis, value)
            plot.replot()

    def get_item_parameters(self, itemparams):
        CurveItem.get_item_parameters(self, itemparams)
        itemparams.add("TaurusParam", self, self.taurusparam)

    def updateTaurusParams(self):
        self.taurusparam.update_curve(self)

    def set_item_parameters(self, itemparams):
        CurveItem.set_item_parameters(self, itemparams)
        self.updateTaurusParams()
Example #4
0
class TaurusTrend2DItem(XYImageItem, TaurusBaseComponent):
    '''A XYImageItem that is constructed by stacking 1D arrays from events from a Taurus 1D attribute'''

    scrollRequested = baseSignal('scrollRequested', object, object, object)
    dataChanged = baseSignal('dataChanged')

    def __init__(self, param=None, buffersize=512, stackMode='datetime'):
        XYImageItem.__init__(self, numpy.arange(2), numpy.arange(
            2), numpy.zeros((2, 2)), param=param)
        TaurusBaseComponent.__init__(self, self.__class__.__name__)
        self.maxBufferSize = buffersize
        self._yValues = None
        self._xBuffer = None
        self._zBuffer = None
        self.stackMode = stackMode
        self.set_interpolation(INTERP_NEAREST)
        self.__timeOffset = None

    def setBufferSize(self, buffersize):
        '''sets the size of the stack

        :param buffersize: (int) size of the stack
        '''
        self.maxBufferSize = buffersize
        try:
            if self._xBuffer is not None:
                self._xBuffer.setMaxSize(buffersize)
            if self._zBuffer is not None:
                self._zBuffer.setMaxSize(buffersize)
        except ValueError:
            self.info(
                'buffer downsizing  requested. Current contents will be discarded')
            self._xBuffer = None
            self._zBuffer = None

    def setModel(self, model):
        # do the standard stuff
        TaurusBaseComponent.setModel(self, model)
        #... and fire a fake event for initialization
        try:
            value = self.getModelObj().read()
            self.fireEvent(
                self, taurus.core.taurusbasetypes.TaurusEventType.Change, value)
        except:
            pass

    def handleEvent(self, evt_src, evt_type, evt_value):
        if evt_value is None or getattr(evt_value, 'rvalue', None) is None:
            self.debug('Ignoring event from %s' % repr(evt_src))
            return
        plot = self.plot()
        if plot is None:
            return

        # initialization
        ySize = len(evt_value.rvalue)
        if self._yValues is None:
            self._yValues = numpy.arange(ySize, dtype='d')
        if self._xBuffer is None:
            self._xBuffer = ArrayBuffer(numpy.zeros(
                min(128, self.maxBufferSize), dtype='d'), maxSize=self.maxBufferSize)
        if self._zBuffer is None:
            self._zBuffer = ArrayBuffer(numpy.zeros(
                (min(128, self.maxBufferSize), ySize), dtype='d'), maxSize=self.maxBufferSize)
            return

        # check that new data is compatible with previous data
        if ySize != self._yValues.size:
            self.info('Incompatible shape in data from event (orig=%i, current=%i). Ignoring' % (
                self._yValues.size, ySize))
            return

        # update x values
        if self.stackMode == 'datetime':
            x = evt_value.time.totime()
            if self.__timeOffset is None:
                self.__timeOffset = x
                plot.set_axis_title('bottom', 'Time')
                plot.set_axis_unit('bottom', '')

        elif self.stackMode == 'deltatime':
            try:
                x = evt_value.time.totime() - self.__timeOffset
            except TypeError:  # this will happen if self.__timeOffset has not been initialized
                self.__timeOffset = evt_value.time.totime()
                x = 0
                plot.set_axis_title('bottom', 'Time since %s' %
                                    evt_value.time.isoformat())
                plot.set_axis_unit('bottom', '')
        else:
            try:
                # +numpy.random.randint(0,4) #for debugging we can put a variable step
                step = 1
                x = self._xBuffer[-1] + step
            except IndexError:  # this will happen when the x buffer is empty
                x = 0
                plot.set_axis_title('bottom', 'Event #')
                plot.set_axis_unit('bottom', '')

        if len(self._xBuffer) and x <= self._xBuffer[-1]:
            self.info('Ignoring event (non-increasing x value)')
            return
        self._xBuffer.append(x)

        # update z
        rvalue = evt_value.rvalue
        if isinstance(evt_value.rvalue, Quantity):
            rvalue = evt_value.rvalue.magnitude
            # TODO: units should be checked for coherence with previous values
        self._zBuffer.append(rvalue)

        # check if there is enough data to start plotting
        if len(self._xBuffer) < 2:
            self.info('waiting for at least 2 values to start plotting')
            return

        x = self._xBuffer.contents()
        y = self._yValues
        z = self._zBuffer.contents().transpose()

        if x.size == 2:
            plot.set_axis_limits('left', y.min(), y.max())
            # guess the max of the scale allowed by the buffer
            xmax = x[0] + (x[1] - x[0]) * self.maxBufferSize
            plot.set_axis_limits('bottom', x.min(), xmax)

        # update the plot data
        lut_range = self.get_lut_range()  # this is the range of the z axis (color scale)
        if lut_range[0] == lut_range[1]:
            # if the range was not set, make it None (autoscale z axis)
            lut_range = None
        self.set_data(z, lut_range=lut_range)
        self.set_xy(x, y)

        # signal data changed and replot
        self.dataChanged.emit()

        if plot is not None:
            value = x[-1]
            axis = self.xAxis()
            xmin, xmax = plot.get_axis_limits(axis)
            if value > xmax or value < xmin:
                self.scrollRequested.emit(plot, axis, value)
            plot.update_colormap_axis(self)
            plot.replot()
Example #5
0
class QTaurusBaseListener(TaurusListener):
    """Base class for QObjects listening to taurus events.

    .. note::
           :meth:`getSignaller` is now unused and deprecated. This is because
           `taurusEvent` is implemented using :func:`baseSignal`, that doesn't
           require the class to inherit from QObject.
    """

    taurusEvent = baseSignal('taurusEvent', object, object, object)

    def __init__(self, name=None, parent=None):
        if name is None:
            name = self.__class__.__name__
        super(QTaurusBaseListener, self).__init__(name, parent=parent)
        self._eventFilters = []
        self.taurusEvent.connect(self.filterEvent)

    #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-
    # Event handling chain
    #-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-~-

    def eventReceived(self, evt_src, evt_type, evt_value):
        """The basic implementation of the event handling chain is as
        follows:

            - eventReceived just calls :meth:`fireEvent` which emits a "taurusEvent"
              PyQt signal that is connected (by :meth:`preAttach`) to the
              :meth:`filterEvent` method.
            - After filtering, :meth:`handleEvent` is invoked with the resulting
              filtered event

        .. note::
            in the earlier steps of the chain (i.e., in :meth:`eventReceived`/:meth:`fireEvent`),
            the code is executed in a Python thread, while from eventFilter
            ahead, the code is executed in a Qt thread.
            When writing widgets, one should normally work on the Qt thread
            (i.e. reimplementing :meth:`handleEvent`)

        :param evt_src: (object) object that triggered the event
        :param evt_type: (taurus.core.taurusbasetypes.TaurusEventType) type of event
        :param evt_value: (object) event value
        """
        self.fireEvent(evt_src, evt_type, evt_value)

    def fireEvent(self, evt_src=None, evt_type=None, evt_value=None):
        """Emits a "taurusEvent" signal.
        It is unlikely that you may need to reimplement this method in subclasses.
        Consider reimplementing :meth:`eventReceived` or :meth:`handleEvent`
        instead depending on whether you need to execute code in the python
        or Qt threads, respectively

        :param evt_src: (object or None) object that triggered the event
        :param evt_type: (taurus.core.taurusbasetypes.TaurusEventType or None) type of event
        :param evt_value: (object or None) event value
        """
        try:
            self.taurusEvent.emit(evt_src, evt_type, evt_value)
        except:
            pass

    def filterEvent(self, evt_src=-1, evt_type=-1, evt_value=-1):
        """The event is processed by each and all filters in strict order
        unless one of them returns None (in which case the event is discarded)

        :param evt_src: (object) object that triggered the event
        :param evt_type: (taurus.core.taurusbasetypes.TaurusEventType) type of event
        :param evt_value: (object) event value
        """
        r = evt_src, evt_type, evt_value

        if r == (-1, -1, -1):
            # @todo In an ideal world the signature of this method should be
            # (evt_src, evt_type, evt_value). However there's a bug in PyQt:
            # If a signal is disconnected between the moment it is emitted and
            # the moment the slot is called, then the slot is called without
            # parameters (!?). We added this default values to detect if
            # this is the case without printing an error message each time.
            # If this gets fixed, we should remove this line.
            return

        for f in self._eventFilters:
            r = f(*r)
            if r is None:
                return
        self.handleEvent(*r)

    def handleEvent(self, evt_src, evt_type, evt_value):
        """Event handling. Default implementation does nothing.
        Reimplement as necessary

        :param evt_src: (object or None) object that triggered the event
        :param evt_type: (taurus.core.taurusbasetypes.TaurusEventType or None) type of event
        :param evt_value: (object or None) event value
        """
        pass

    def setEventFilters(self, filters=None):
        """sets the taurus event filters list.
        The filters are run in order, using each output to feed the next filter.
        A filter must be a function that accepts 3 arguments ``(evt_src, evt_type, evt_value)``
        If the event is to be ignored, the filter must return None.
        If the event is  not to be ignored, filter must return a
        ``(evt_src, evt_type, evt_value)`` tuple which may (or not) differ from the input.

        For a library of common filters, see taurus/core/util/eventfilters.py

        :param filters: (sequence) a sequence of filters

        See also: insertEventFilter
        """
        if filters is None:
            filters = []
        self._eventFilters = list(filters)

    def getEventFilters(self):
        """Returns the list of event filters for this widget

        :return: (sequence<callable>) the event filters
        """
        return self._eventFilters

    def insertEventFilter(self, filter, index=-1):
        """insert a filter in a given position

        :param filter: (callable(evt_src, evt_type, evt_value)) a filter
        :param index: (int) index to place the filter (default = -1 meaning place at the end)

        See also: setEventFilters
        """
        self._eventFilters.insert(index, filter)

    @deprecation_decorator(rel='4.0')
    def getSignaller(self):
        return self
Example #6
0
class TaurusBaseImageItem(TaurusBaseComponent):
    '''A ImageItem that gets its data from a taurus attribute'''

    dataChanged = baseSignal('dataChanged')

    def setModel(self, model):
        # do the standard stuff
        TaurusBaseComponent.setModel(self, model)
        #... and fire a fake event for initialization
        try:
            value = self.getModelObj().read()
            self.fireEvent(self,
                           taurus.core.taurusbasetypes.TaurusEventType.Change,
                           value)
        except:
            pass

    def handleEvent(self, evt_src, evt_type, evt_value):
        if evt_value is None or getattr(evt_value, 'rvalue', None) is None:
            self.debug('Ignoring event from %s' % repr(evt_src))
            return
        v = evt_value.rvalue
        if isinstance(v, Quantity):
            v = v.magnitude
            # TODO: units should be used for setting some title in the colorbar
        try:
            v = self.filterData(v)
        except Exception as e:
            self.info('Ignoring event. Reason: %s', e.message)
            return
        # this is the range of the z axis (color scale)
        lut_range = self.get_lut_range()
        # if the range was not set, make it None (autoscale z axis)
        if lut_range[0] == lut_range[1]:
            lut_range = None
        self.set_data(v, lut_range=lut_range)
        self.dataChanged.emit()
        p = self.plot()

        if p is not None:
            p.update_colormap_axis(self)
            p.replot()

    def filterData(self, data):
        '''Reimplement this method if you want to pre-process
        the data that will be passed to set_data.

        It should return something acceptable by :meth:`setData`
        and raise an exception if the data cannot be processed.

        This default implementation casts array types not
        supported by guiqwt to numpy.int32

        See:
          - http://code.google.com/p/guiqwt/issues/detail?id=44 and
          - https://sourceforge.net/tracker/?func=detail&atid=484769&aid=3603991&group_id=57612
          - https://sourceforge.net/p/tauruslib/tickets/33/
        '''
        try:
            dtype = data.dtype
            v = data
        except:
            v = numpy.array(data)  # note that this is potentially expensive
            dtype = v.dtype

        if dtype not in (float, numpy.double, numpy.int32, numpy.uint16,
                         numpy.int16, numpy.uint8, numpy.int8, bool):
            # note: numpy.uint32 was not included because of
            # https://sourceforge.net/p/tauruslib/tickets/33/
            try:
                self.debug('casting to numpy.int32')
                v = numpy.int32(v)
            except OverflowError:
                raise OverflowError(
                    "type %s not supported by guiqwt and cannot be casted to int32"
                    % repr(v.dtype))

        return v