def _initBuffers(self, ntrends):
        """initializes new x and y buffers"""

        self._yBuffer = ArrayBuffer(numpy.zeros(
            (min(128, self._maxBufferSize), ntrends), dtype='d'),
                                    maxSize=self._maxBufferSize)

        self._xBuffer = ArrayBuffer(
            (numpy.zeros(min(128, self._maxBufferSize), dtype='d')),
            maxSize=self._maxBufferSize)
class TaurusTrendSet(PlotDataItem, TaurusBaseComponent):
    """
    A PlotDataItem for displaying trend curve(s) associated to a
    TaurusAttribute. The TaurusTrendSet itself does not contain any data,
    but acts as a manager that dynamically adds/removes curve(s) (other
    PlotDataItems) to its associated plot.

    If the attribute is a scalar, the Trend Set generates only one curve
    representing the evolution of the value of the attribute. If the attribute
    is an array, as many curves as the attribute size are created,
    each representing the evolution of the value of a component of the array.

    When an event is received, all curves belonging to a TaurusTrendSet
    are updated.

    TaurusTrendSet can be considered as a container of (sorted) curves.
    As such, the curves contained by it can be accessed by index::

        ts = TaurusTrendSet('eval:rand(3)')
        # (...) wait for a Taurus Event arriving so that the curves are created
        ncurves = len(ts)  # ncurves will be 3 (assuming the event arrived)
        curve0 = ts[0]     # you can access the curve by index


    Note that internally each curve is a :class:`pyqtgraph.PlotDataItem` (i.e.,
    it is not aware of events by itself, but it relies on the TaurusTrendSet
    object to update its values)
    """

    def __init__(self, *args, **kwargs):
        _ = kwargs.pop("xModel", None)
        yModel = kwargs.pop("yModel", None)
        colors = kwargs.pop("colors", None)
        if colors is None:
            colors = LoopList(CURVE_COLORS)
        name = kwargs.pop("name", None)
        PlotDataItem.__init__(self, x=[], y=[], name=name)
        TaurusBaseComponent.__init__(self, "TaurusBaseComponent")
        self._UImodifiable = False
        self._maxBufferSize = 65536  # (=2**16, i.e., 64K events))
        self._xBuffer = None
        self._yBuffer = None
        self._curveColors = colors
        self._args = args
        self._kwargs = kwargs
        self._curves = []
        self._timer = Qt.QTimer()
        self._timer.timeout.connect(self._forceRead)
        self._legend = None

        # register config properties
        self.setModelInConfig(True)
        self.registerConfigProperty(
            self._getCurvesOpts, self._setCurvesOpts, "opts"
        )
        # TODO: store forceReadPeriod config
        # TODO: store _maxBufferSize config
        if yModel is not None:
            self.setModel(yModel)

    def __repr__(self):
        return "<TrendSet {} ({} items)>".format(self.base_name(), len(self))

    def name(self):
        """Reimplemented from PlotDataItem to avoid having the ts itself added
        to legends.

        .. seealso:: :meth:`basename`
        """
        return None

    def base_name(self):
        """Returns the name of the trendset, which is used as a prefix for
        constructing the associated curves names

        .. seealso:: :meth:`name`
        """
        return PlotDataItem.name(self)

    def __getitem__(self, k):
        return self._curves[k]

    def __len__(self):
        return len(self._curves)

    def __contains__(self, k):
        return k in self._curves

    def setModel(self, name):
        """Reimplemented from :meth:`TaurusBaseComponent.setModel`"""
        TaurusBaseComponent.setModel(self, name)
        # force a read to ensure that the curves are created
        self._forceRead()

    def _initBuffers(self, ntrends):
        """initializes new x and y buffers"""

        self._yBuffer = ArrayBuffer(
            numpy.zeros((min(128, self._maxBufferSize), ntrends), dtype="d"),
            maxSize=self._maxBufferSize,
        )

        self._xBuffer = ArrayBuffer(
            (numpy.zeros(min(128, self._maxBufferSize), dtype="d")),
            maxSize=self._maxBufferSize,
        )

    def _initCurves(self, ntrends):
        """ Initializes new curves """

        # self._removeFromLegend(self._legend)

        # remove previously existing curves from views
        self._updateViewBox(None)

        self._curves = []

        if self._curveColors is None:
            self._curveColors = LoopList(CURVE_COLORS)
            self._curveColors.setCurrentIndex(-1)

        a = self._args
        kw = self._kwargs.copy()

        base_name = (
            self.base_name()
            or taurus.Attribute(self.getModel()).getSimpleName()
        )

        for i in range(ntrends):
            subname = "%s[%i]" % (base_name, i)
            kw["name"] = subname
            curve = TrendCurve(*a, **kw)
            if "pen" not in kw:
                curve.setPen(next(self._curveColors))
            self._curves.append(curve)
        self._updateViewBox(self.getViewBox())

    def _addToLegend(self, legend):
        # ------------------------------------------------------------------
        # In theory, TaurusTrendSet only uses viewBox.addItem to add its
        # sub-curves to the plot. In theory this should not add the curves
        # to the legend, and therefore we should do it here.
        # But somewhere the curves are already being added to the legend, and
        # if we re-add them here we get duplicated legend entries
        # TODO: Find where are the curves being added to the legend
        pass
        # if legend is None:
        #    return
        # for c in self._curves:
        #    legend.addItem(c, c.name())
        # -------------------------------------------------------------------

    def _removeFromLegend(self, legend):
        if legend is None:
            return
        for c in self._curves:
            legend.removeItem(c.name())

    def _updateViewBox(self, viewBox):
        """Add/remove the "extra" curves from the viewbox if needed"""
        for curve in self._curves:
            curve_viewBox = curve.getViewBox()
            if curve_viewBox is not None:
                plotItem = None
                viewWidget = curve_viewBox.getViewWidget()
                if viewWidget is not None:
                    plotItem = viewWidget.getPlotItem()
                curve_viewBox.removeItem(curve)
                if plotItem is not None:
                    plotItem.removeItem(curve)
            if viewBox is not None:
                plotItem = None
                viewWidget = viewBox.getViewWidget()
                if viewWidget is not None:
                    plotItem = viewWidget.getPlotItem()
                if plotItem is not None:
                    curve = ensure_unique_curve_name(curve, plotItem)
                    _cname = curve.name()
                    params = {"all trends": _cname}
                    plotItem.addItem(curve, params=params)

    def _updateBuffers(self, evt_value):
        """Update the x and y buffers with the new data. If the new data is
        not compatible with the existing buffers, the buffers are reset
        """

        # TODO: we use .magnitude below to avoid issue #509 in pint
        # https://github.com/hgrecco/pint/issues/509

        ntrends = numpy.size(evt_value.rvalue.magnitude)

        if not self._isDataCompatible(evt_value, ntrends):
            self._initBuffers(ntrends)
            self._yUnits = evt_value.rvalue.units
            self._initCurves(ntrends)

        try:
            self._yBuffer.append(evt_value.rvalue.to(self._yUnits).magnitude)
        except Exception as e:
            self.warning(
                "Problem updating buffer Y (%s):%s", evt_value.rvalue, e
            )
            evt_value = None

        try:
            self._xBuffer.append(evt_value.time.totime())
        except Exception as e:
            self.warning("Problem updating buffer X (%s):%s", evt_value, e)

        return self._xBuffer.contents(), self._yBuffer.contents()

    def _isDataCompatible(self, evt_value, ntrends):
        """
        Check that the new evt_value is compatible with the current data in the
        buffers. Check shape and unit compatibility.
        """
        if self._xBuffer is None or self._yBuffer is None:
            return False
        rvalue = evt_value.rvalue

        if rvalue.dimensionality != self._yUnits.dimensionality:
            return False

        current_trends = numpy.prod(self._yBuffer.contents().shape[1:])

        if ntrends != current_trends:
            return False

        return True

    def _addData(self, x, y):
        for i, curve in enumerate(self._curves):
            curve.setData(x=x, y=y[:, i])

    def clearBuffer(self):
        """Reset the buffered data"""
        self._initBuffers(len(self._curves))

    def handleEvent(self, evt_src, evt_type, evt_value):
        """Reimplementation of :meth:`TaurusBaseComponent.handleEvent`"""

        # model = evt_src if evt_src is not None else self.getModelObj()

        # TODO: support boolean values from evt_value.rvalue
        if (
            evt_value is None
            or not hasattr(evt_value, "rvalue")
            or evt_value.rvalue is None
        ):
            self.info("Invalid value. Ignoring.")
            return
        else:
            try:
                xValues, yValues = self._updateBuffers(evt_value)
            except Exception:
                # TODO: handle dropped events see: TaurusTrend._onDroppedEvent
                raise

        self._addData(xValues, yValues)

    def parentChanged(self):
        """Reimplementation of :meth:`PlotDataItem.parentChanged` to handle
        the change of the containing viewbox
        """
        PlotDataItem.parentChanged(self)

        self._updateViewBox(self.getViewBox())

        # update legend if needed
        try:
            legend = self.getViewWidget().getPlotItem().legend
        except Exception:
            legend = None
        if legend is not self._legend:
            self._removeFromLegend(self._legend)
            self._addToLegend(legend)
            self._legend = legend

        # Set period from ForcedReadTool (if found)
        try:
            for a in self.getViewBox().menu.actions():
                if isinstance(a, ForcedReadTool) and a.autoconnect():
                    self.setForcedReadPeriod(a.period())
                    break
        except Exception as e:
            self.debug("cannot set period from ForcedReadTool: %r", e)

    @property
    def forcedReadPeriod(self):
        """Returns the forced reading period (in ms). A value <= 0 indicates
        that the forced reading is disabled
        """
        return self._timer.interval()

    def setForcedReadPeriod(self, period):
        """
        Forces periodic reading of the subscribed attribute in order to show
        new points even if no events are received.
        It will create fake events as needed with the read value.
        It will also block the plotting of regular events when period > 0.

        :param period: (int) period in milliseconds. Use period<=0 to stop the
                       forced periodic reading
        """

        # stop the timer and remove the __ONLY_OWN_EVENTS filter
        self._timer.stop()
        filters = self.getEventFilters()
        if self.__ONLY_OWN_EVENTS in filters:
            filters.remove(self.__ONLY_OWN_EVENTS)
            self.setEventFilters(filters)

        # if period is positive, set the filter and start
        if period > 0:
            self.insertEventFilter(self.__ONLY_OWN_EVENTS)
            self._timer.start(period)

    def _forceRead(self, cache=False):
        """Forces a read of the associated attribute.

        :param cache: (bool) If True, the reading will be done with cache=True
                      but the timestamp of the resulting event will be replaced
                      by the current time. If False, no cache will be used at
                      all.
        """
        value = self.getModelValueObj(cache=cache)
        if cache and value is not None:
            value = copy.copy(value)
            value.time = TaurusTimeVal.now()
        self.fireEvent(self, TaurusEventType.Periodic, value)

    def __ONLY_OWN_EVENTS(self, s, t, v):
        """An event filter that rejects all events except those that originate
        from this object
        """
        if s is self:
            return s, t, v
        else:
            return None

    def _getCurvesOpts(self):
        """returns a list of serialized opts (one for each curve)"""
        from taurus.qt.qtgui.tpg import serialize_opts

        return [serialize_opts(copy.copy(c.opts)) for c in self._curves]

    def _setCurvesOpts(self, all_opts):
        """restore options to curves"""
        # If no curves are yet created, force a read to create them
        if not self._curves:
            self._forceRead(cache=True)
        # Check consistency in the number of curves
        if len(self._curves) != len(all_opts):
            self.warning(
                "Cannot apply curve options (mismatch in curves number)"
            )
            return
        from taurus.qt.qtgui.tpg import deserialize_opts

        for c, opts in zip(self._curves, all_opts):
            c.opts = deserialize_opts(opts)

            # This is a workaround for the following pyqtgraph's bug:
            # https://github.com/pyqtgraph/pyqtgraph/issues/531
            if opts["connect"] == "all":
                c.opts["connect"] = "all"
            elif opts["connect"] == "pairs":
                c.opts["connect"] = "pairs"
            elif opts["connect"] == "finite":
                c.opts["connect"] = "finite"

    def getFullModelNames(self):
        """
        Return a tuple of (None, fullmodelname) for API compatibility with
        :class:`TaurusPlotDataItem`.
        """
        return (None, self.getFullModelName())

    def setBufferSize(self, buffer_size):
        """sets the maximum number of points to store per trend curve

        :param buffer_size: (int) max number of points to store per trend curve
        """
        self._maxBufferSize = buffer_size
        try:
            if self._xBuffer is not None:
                # discard oldest data if needed for downsizing
                excess = len(self._xBuffer) - buffer_size
                if excess > 0:
                    self._xBuffer.moveLeft(excess)
                    self._xBuffer.resizeBuffer(buffer_size)
                # resize
                self._xBuffer.setMaxSize(buffer_size)
            if self._yBuffer is not None:
                # discard oldest data if needed for downsizing
                excess = len(self._yBuffer) - buffer_size
                if excess > 0:
                    self._yBuffer.moveLeft(excess)
                    self._yBuffer.resizeBuffer(buffer_size)
                # resize
                self._yBuffer.setMaxSize(buffer_size)
        except ValueError:
            self.info(
                "buffer downsizing  requested."
                + "Current contents will be discarded"
            )
            self.clearBuffer()

    def bufferSize(self):
        """returns the maximum number of points to be stored by the trends"""
        return self._maxBufferSize
Beispiel #3
0
    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()
Beispiel #4
0
    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()
Beispiel #5
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()
Beispiel #6
0
def empty_data(nb_points):
    return ArrayBuffer(numpy.full(nb_points, numpy.nan))
Beispiel #7
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()
Beispiel #8
0
class TaurusTrend2DScanItem(TaurusTrend2DItem):
    _xDataKey = 'point_nb'

    def __init__(self, channelKey, xDataKey, door, param=None, buffersize=512):
        TaurusTrend2DItem.__init__(self,
                                   param=param,
                                   buffersize=buffersize,
                                   stackMode=None)
        self._channelKey = channelKey
        self._xDataKey = xDataKey
        self.connectWithQDoor(door)

    def scanDataReceived(self, packet):
        '''
        packet is a dict with {type:str, "data":object} and the accepted types are: data_desc, record_data, record_end
        and the data objects are: seq<ColumnDesc.Todict()>, record.data dict and dict , respectively
        '''
        if packet is None:
            self.debug('Ignoring empty scan data packet')
            return
        id, packet = packet
        pcktype = packet.get("type", "__UNKNOWN_PCK_TYPE__")
        if pcktype == "data_desc":
            self._dataDescReceived(packet["data"])
        elif pcktype == "record_data":
            self._scanLineReceived(packet["data"])
        elif pcktype == "record_end":
            pass
        else:
            self.debug("Ignoring packet of type %s" % repr(pcktype))

    def clearTrend(self):
        self._yValues = None
        self._xBuffer = None
        self._zBuffer = None

    def _dataDescReceived(self, datadesc):
        '''prepares the plot according to the info in the datadesc dictionary'''
        self.clearTrend()
        # decide which data to use for x
        if self._xDataKey is None or self._xDataKey == "<mov>":
            self._autoXDataKey = datadesc['ref_moveables'][0]
        elif self._xDataKey == "<idx>":
            self._autoXDataKey = 'point_nb'
        else:
            self._autoXDataKey = self._xDataKey
        # set the x axis
        columndesc = datadesc.get('column_desc', [])
        xinfo = {'min_value': None, 'max_value': None}
        for e in columndesc:
            if e['label'] == self._autoXDataKey:
                xinfo = e
                break
        plot = self.plot()
        plot.set_axis_title('bottom', self._autoXDataKey)
        xmin, xmax = xinfo.get('min_value'), xinfo.get('max_value')
        if xmin is None or xmax is None:
            pass  # @todo: autoscale if any limit is unknown
        else:
            plot.set_axis_limits('bottom', xmin, xmax)

    def _scanLineReceived(self, recordData):
        '''Receives a recordData dictionary and updates the curves associated to it

        .. seealso:: <Sardana>/MacroServer/scan/scandata.py:Record.data

        '''
        # obtain the x value
        try:
            xval = recordData[self._autoXDataKey]
        except KeyError:
            self.warning(
                'Cannot find data "%s" in the current scan record. Ignoring',
                self._autoXDataKey)
            return
        if not numpy.isscalar(xval):
            self.warning(
                'Data for "%s" is of type "%s". Cannot use it for the X values. Ignoring',
                self._autoXDataKey, type(xval))
            return
        # obtain y value
        try:
            chval = recordData[self._channelKey]
        except KeyError:
            self.warning(
                'Cannot find data "%s" in the current scan record. Ignoring',
                self._channelKey)
        if chval.shape != self._yValues.shape:
            self.warning('Incompatible shape of "%s" (%s). Ignoring',
                         self._channelKey, repr(chval.shape))
            return

        # initialization
        if self._yValues is None:
            self._yValues = numpy.arange(chval.size, dtype='d')
        if self._xBuffer is None:
            self._xBuffer = ArrayBuffer(numpy.zeros(min(
                16, self.maxBufferSize),
                                                    dtype='d'),
                                        maxSize=self.maxBufferSize)
        if self._zBuffer is None:
            self._zBuffer = ArrayBuffer(numpy.zeros(
                (min(16, self.maxBufferSize), chval.size), dtype='d'),
                                        maxSize=self.maxBufferSize)

        # update x
        self._xBuffer.append(xval)
        # update z
        self._zBuffer.append(chval)

        # 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()

        # 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()
        plot = self.plot()
        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()

    def connectWithQDoor(self, doorname):
        '''connects this TaurusTrend2DScanItem to a QDoor

        :param doorname: (str) the QDoor name
        '''
        qdoor = taurus.Device(doorname)
        qdoor.recordDataUpdated.connect(self.scanDataReceived)

    def getModel(self):
        return self.__model

    def setModel(self, model):
        self.__model = model
Beispiel #9
0
    def _scanLineReceived(self, recordData):
        '''Receives a recordData dictionary and updates the curves associated to it

        .. seealso:: <Sardana>/MacroServer/scan/scandata.py:Record.data

        '''
        # obtain the x value
        try:
            xval = recordData[self._autoXDataKey]
        except KeyError:
            self.warning(
                'Cannot find data "%s" in the current scan record. Ignoring',
                self._autoXDataKey)
            return
        if not numpy.isscalar(xval):
            self.warning(
                'Data for "%s" is of type "%s". Cannot use it for the X values. Ignoring',
                self._autoXDataKey, type(xval))
            return
        # obtain y value
        try:
            chval = recordData[self._channelKey]
        except KeyError:
            self.warning(
                'Cannot find data "%s" in the current scan record. Ignoring',
                self._channelKey)
        if chval.shape != self._yValues.shape:
            self.warning('Incompatible shape of "%s" (%s). Ignoring',
                         self._channelKey, repr(chval.shape))
            return

        # initialization
        if self._yValues is None:
            self._yValues = numpy.arange(chval.size, dtype='d')
        if self._xBuffer is None:
            self._xBuffer = ArrayBuffer(numpy.zeros(min(
                16, self.maxBufferSize),
                                                    dtype='d'),
                                        maxSize=self.maxBufferSize)
        if self._zBuffer is None:
            self._zBuffer = ArrayBuffer(numpy.zeros(
                (min(16, self.maxBufferSize), chval.size), dtype='d'),
                                        maxSize=self.maxBufferSize)

        # update x
        self._xBuffer.append(xval)
        # update z
        self._zBuffer.append(chval)

        # 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()

        # 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()
        plot = self.plot()
        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()
Beispiel #10
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'):
        """
        :param param: param to be passed to XYImageItem constructor
        :param buffersize: (int) size of the stack
        :param stackMode: (str) can be 'datetime', 'timedelta' or 'event'
        """
        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

        # Config properties
        self.registerConfigProperty(self.get_lut_range, self.set_lut_range,
                                    'lut_range')
        self.registerConfigProperty(self._get_interpolation_cfg,
                                    self._set_interpolation_cfg,
                                    'interpolation')
        self.registerConfigProperty(self.get_color_map_name,
                                    self.set_color_map, 'color_map')

    def _get_interpolation_cfg(self):
        ret = self.get_interpolation()
        if len(ret) == 2:
            ret = (ret[0], len(ret[1]))
        return ret

    def _set_interpolation_cfg(self, interpolate_cfg):
        self.set_interpolation(*interpolate_cfg)

    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', '')
        elif self.stackMode == 'event':
            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', '')
        else:
            raise ValueError('Unsupported stack mode %s' % self.stackMode)

        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()

        # Use previous LUT range (z axis range), or set to None (autoscale)
        # if it is uninitialized
        lut_range = self.get_lut_range()
        if lut_range[0] == lut_range[1]:
            lut_range = None

        # update the plot data
        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()
Beispiel #11
0
    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', '')
        elif self.stackMode == 'event':
            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', '')
        else:
            raise ValueError('Unsupported stack mode %s' % self.stackMode)

        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()

        # Use previous LUT range (z axis range), or set to None (autoscale)
        # if it is uninitialized
        lut_range = self.get_lut_range()
        if lut_range[0] == lut_range[1]:
            lut_range = None

        # update the plot data
        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()
Beispiel #12
0
    def _scanLineReceived(self, recordData):
        '''Receives a recordData dictionary and updates the curves associated to it

        .. seealso:: <Sardana>/MacroServer/scan/scandata.py:Record.data

        '''
        # obtain the x value
        try:
            xval = recordData[self._autoXDataKey]
        except KeyError:
            self.warning(
                'Cannot find data "%s" in the current scan record. Ignoring', self._autoXDataKey)
            return
        if not numpy.isscalar(xval):
            self.warning('Data for "%s" is of type "%s". Cannot use it for the X values. Ignoring',
                         self._autoXDataKey, type(xval))
            return
        # obtain y value
        try:
            chval = recordData[self._channelKey]
        except KeyError:
            self.warning(
                'Cannot find data "%s" in the current scan record. Ignoring', self._channelKey)
        if chval.shape != self._yValues.shape:
            self.warning('Incompatible shape of "%s" (%s). Ignoring',
                         self._channelKey, repr(chval.shape))
            return

        # initialization
        if self._yValues is None:
            self._yValues = numpy.arange(chval.size, dtype='d')
        if self._xBuffer is None:
            self._xBuffer = ArrayBuffer(numpy.zeros(
                min(16, self.maxBufferSize), dtype='d'), maxSize=self.maxBufferSize)
        if self._zBuffer is None:
            self._zBuffer = ArrayBuffer(numpy.zeros(
                (min(16, self.maxBufferSize), chval.size), dtype='d'), maxSize=self.maxBufferSize)

        # update x
        self._xBuffer.append(xval)
        # update z
        self._zBuffer.append(chval)

        # 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()

        # 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()
        plot = self.plot()
        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()
Beispiel #13
0
class TaurusTrend2DScanItem(TaurusTrend2DItem):
    _xDataKey = 'point_nb'

    def __init__(self, channelKey, xDataKey, door, param=None, buffersize=512):
        TaurusTrend2DItem.__init__(
            self, param=param, buffersize=buffersize, stackMode=None)
        self._channelKey = channelKey
        self._xDataKey = xDataKey
        self.connectWithQDoor(door)

    def scanDataReceived(self, packet):
        '''
        packet is a dict with {type:str, "data":object} and the accepted types are: data_desc, record_data, record_end
        and the data objects are: seq<ColumnDesc.Todict()>, record.data dict and dict , respectively
        '''
        if packet is None:
            self.debug('Ignoring empty scan data packet')
            return
        id, packet = packet
        pcktype = packet.get("type", "__UNKNOWN_PCK_TYPE__")
        if pcktype == "data_desc":
            self._dataDescReceived(packet["data"])
        elif pcktype == "record_data":
            self._scanLineReceived(packet["data"])
        elif pcktype == "record_end":
            pass
        else:
            self.debug("Ignoring packet of type %s" % repr(pcktype))

    def clearTrend(self):
        self._yValues = None
        self._xBuffer = None
        self._zBuffer = None

    def _dataDescReceived(self, datadesc):
        '''prepares the plot according to the info in the datadesc dictionary'''
        self.clearTrend()
        # decide which data to use for x
        if self._xDataKey is None or self._xDataKey == "<mov>":
            self._autoXDataKey = datadesc['ref_moveables'][0]
        elif self._xDataKey == "<idx>":
            self._autoXDataKey = 'point_nb'
        else:
            self._autoXDataKey = self._xDataKey
        # set the x axis
        columndesc = datadesc.get('column_desc', [])
        xinfo = {'min_value': None, 'max_value': None}
        for e in columndesc:
            if e['label'] == self._autoXDataKey:
                xinfo = e
                break
        plot = self.plot()
        plot.set_axis_title('bottom', self._autoXDataKey)
        xmin, xmax = xinfo.get('min_value'), xinfo.get('max_value')
        if xmin is None or xmax is None:
            pass  # @todo: autoscale if any limit is unknown
        else:
            plot.set_axis_limits('bottom', xmin, xmax)

    def _scanLineReceived(self, recordData):
        '''Receives a recordData dictionary and updates the curves associated to it

        .. seealso:: <Sardana>/MacroServer/scan/scandata.py:Record.data

        '''
        # obtain the x value
        try:
            xval = recordData[self._autoXDataKey]
        except KeyError:
            self.warning(
                'Cannot find data "%s" in the current scan record. Ignoring', self._autoXDataKey)
            return
        if not numpy.isscalar(xval):
            self.warning('Data for "%s" is of type "%s". Cannot use it for the X values. Ignoring',
                         self._autoXDataKey, type(xval))
            return
        # obtain y value
        try:
            chval = recordData[self._channelKey]
        except KeyError:
            self.warning(
                'Cannot find data "%s" in the current scan record. Ignoring', self._channelKey)
        if chval.shape != self._yValues.shape:
            self.warning('Incompatible shape of "%s" (%s). Ignoring',
                         self._channelKey, repr(chval.shape))
            return

        # initialization
        if self._yValues is None:
            self._yValues = numpy.arange(chval.size, dtype='d')
        if self._xBuffer is None:
            self._xBuffer = ArrayBuffer(numpy.zeros(
                min(16, self.maxBufferSize), dtype='d'), maxSize=self.maxBufferSize)
        if self._zBuffer is None:
            self._zBuffer = ArrayBuffer(numpy.zeros(
                (min(16, self.maxBufferSize), chval.size), dtype='d'), maxSize=self.maxBufferSize)

        # update x
        self._xBuffer.append(xval)
        # update z
        self._zBuffer.append(chval)

        # 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()

        # 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()
        plot = self.plot()
        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()

    def connectWithQDoor(self, doorname):
        '''connects this TaurusTrend2DScanItem to a QDoor

        :param doorname: (str) the QDoor name
        '''
        qdoor = taurus.Device(doorname)
        qdoor.recordDataUpdated.connect(self.scanDataReceived)

    def getModel(self):
        return self.__model

    def setModel(self, model):
        self.__model = model
Beispiel #14
0
    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()
Beispiel #15
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()
Beispiel #16
0
class TaurusTrend2DItem(XYImageItem, TaurusBaseComponent):
    '''A XYImageItem that is constructed by stacking 1D arrays from events from a Taurus 1D attribute'''
    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._signalGen = Qt.QObject()
        self.maxBufferSize = buffersize
        self._yValues = None
        self._xBuffer = None
        self._zBuffer = None
        self.stackMode = stackMode
        self.set_interpolation(INTERP_NEAREST)
        self.__timeOffset = None

    def getSignaller(self):
        '''reimplemented from TaurusBaseComponent because TaurusImageItem is
        not (and cannot be) a QObject'''
        return self._signalGen

    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.getSignaller().emit(Qt.SIGNAL('dataChanged'))

        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.getSignaller().emit(Qt.SIGNAL('scrollRequested'), plot,
                                         axis, value)
            plot.update_colormap_axis(self)
            plot.replot()