Example #1
0
class SweepDataPlot(GraphicsView):

    GREEN = [0, 204, 153]
    BLUE = [100, 171, 246]
    RED = [221, 61, 53]
    PURPLE = [175, 122, 197]
    ASH = [52, 73, 94]
    GRAY = [178, 186, 187]

    COLORS = [BLUE, RED, GREEN, PURPLE, ASH, GRAY]

    if sys.platform == 'darwin':
        LW = 3
    else:
        LW = 1.5

    def __init__(self):
        GraphicsView.__init__(self)

        # create layout
        self.layout = pg.GraphicsLayout()
        self.layout.setContentsMargins(0, 0, 0, 0)
        self.layout.setSpacing(-1.)
        self.setBackground(None)
        self.setCentralItem(self.layout)

        # create axes and apply formatting
        axisItems = dict()

        for pos in ['bottom', 'left', 'top', 'right']:
            axisItems[pos] = AxisItem(orientation=pos, maxTickLength=-7)

        self.p = PlotItem(axisItems=axisItems)
        self.setTitle('Sweep data', fontScaling=1.3, color='k')
        self.layout.addItem(self.p)

        self.p.vb.setBackgroundColor('w')
        self.p.setContentsMargins(10, 10, 10, 10)

        for pos in ['bottom', 'left', 'top', 'right']:
            ax = self.p.getAxis(pos)
            ax.setZValue(0)  # draw on top of patch
            ax.setVisible(True)  # make all axes visible
            ax.setPen(width=self.LW * 2 / 3,
                      color=0.5)  # grey spines and ticks
            try:
                ax.setTextPen('k')  # black text
            except AttributeError:
                pass
            ax.setStyle(autoExpandTextSpace=True, tickTextOffset=4)

        self.p.getAxis('top').setTicks([])
        self.p.getAxis('top').setHeight(0)
        self.p.getAxis('right').setTicks([])

        self.x_axis = self.p.getAxis('bottom')
        self.y_axis = self.p.getAxis('left')

        self.x_axis.setLabel('Voltage', units='V', color='k', size='12pt')
        self.y_axis.setLabel('Current', units='A', color='k', size='12pt')
        self.y_axis.setStyle(tickTextWidth=35)

        # set auto range and mouse panning / zooming
        self.p.enableAutoRange(x=True, y=True)
        self.p.setLimits(xMin=-1e20, xMax=1e20, yMin=-1e20, yMax=1e20)

        def suggestPadding(axis):
            length = self.p.vb.width() if axis == 0 else self.p.vb.height()
            if length > 0:
                if axis == 0:
                    padding = 0
                else:
                    padding = np.clip(1. / (length**0.5), 0.02, 0.1)
            else:
                padding = 0.02
            return padding

        self.p.vb.suggestPadding = suggestPadding

        # set default ranges to start
        self.p.setXRange(-10, 10)
        self.p.setYRange(-10, 10)

        # add legend
        self.legend = LegendItem(brush=fn.mkBrush(255, 255, 255, 150),
                                 labelTextColor='k',
                                 offset=(20, -20))
        self.legend.setParentItem(self.p.vb)

    def clear(self):
        self.p.clear()  # clear current plot
        self.legend.clear()  # clear current legend

    def plot(self, sweep_data):
        self.clear()

        xdata = sweep_data.get_column(0)
        xdata_title = sweep_data.titles[0]
        ydata = sweep_data.values()[1:]

        # format plot according to sweep type
        unit = xdata_title.unit if xdata_title.has_unit() else 'a.u.'
        self.x_axis.setLabel(xdata_title.name, unit=unit)
        self.y_axis.setLabel('Current', unit='A')

        if sweep_data.params['sweep_type'] == 'transfer':
            self.setTitle('Transfer curve')
            self.p.setLogMode(x=False, y=True)
            self.legend.setOffset((20, -20))  # legend in bottom-left corner
            ydata = [np.abs(y) for y in ydata]

        elif sweep_data.params['sweep_type'] == 'output':
            self.setTitle('Output curve')
            self.p.setLogMode(x=False, y=False)
            self.legend.setOffset((-20, 20))  # legend in top-right corner
            ydata = [np.abs(y) for y in ydata]

        else:
            self.setTitle('Sweep curve')
            self.p.setLogMode(x=False, y=False)
            ydata = [np.abs(y) for y in ydata]

        # plot data
        self.lines = []
        for y, c in zip(ydata, itertools.cycle(self.COLORS)):
            p = self.p.plot(xdata, y, pen=fn.mkPen(color=c, width=self.LW))
            self.lines.append(p)

        # add legend
        for l, t in zip(self.lines, sweep_data.column_names[1:]):
            self.legend.addItem(l, str(t))

        self.p.autoRange()

    def setTitle(self, text, fontScaling=None, color=None, font=None):
        # work around pyqtplot which forces the title to be HTML
        if text is None:
            self.p.setTitle(None)  # clears title and hides title column
        else:
            self.p.setTitle(
                '')  # makes title column visible, sets placeholder text
            self.p.titleLabel.item.setPlainText(
                text)  # replace HTML with plain text

        if color is not None:
            color = fn.mkColor(color)
            self.p.titleLabel.item.setDefaultTextColor(color)

        if font is not None:
            self.p.titleLabel.item.setFont(font)

        if fontScaling is not None:
            font = self.p.titleLabel.item.font()
            defaultFontSize = QtWidgets.QLabel('test').font().pointSize()
            fontSize = round(defaultFontSize * fontScaling, 1)
            font.setPointSize(fontSize)
            self.p.titleLabel.item.setFont(font)
class ExtendedPlotItem(PlotItem):
    def __init__(self, *args, **kwargs):
        """
        Create a new PlotItem, same as a base plotitem, but with a few
        extra pieces of functionality.
          - Keep track of images and allow proxying
          - Use a custom view box, such that we can do common tasks
        """
        if 'viewBox' not in kwargs:
            vb = CustomViewBox()
            kwargs['viewBox'] = vb
        super().__init__(*args, **kwargs)

        # Keep track of context menus for items in this plot
        self.itemMenus = {}

    def addItem(self, item, *args, **kwargs):
        super().addItem(item, *args, **kwargs)
        if isinstance(item, ImageItem):
            # addItem does not keep track of images, let's add it ourselves
            self.dataItems.append(item)

    def plot(self, *args, **kargs):
        """
        Reimplements PlotItem.plot to use ExtendedPlotDataItems.
        Add and return a new plot.
        See :func:`PlotDataItem.__init__ <pyqtgraph.PlotDataItem.__init__>` for data arguments

        Extra allowed arguments are:
            clear    - clear all plots before displaying new data
            params   - meta-parameters to associate with this data
        """
        clear = kargs.get('clear', False)
        params = kargs.get('params', None)

        if clear:
            self.clear()

        item = ExtendedPlotDataItem(*args, **kargs)

        if params is None:
            params = {}
        self.addItem(item, params=params)

        return item

    def makeTracesDifferent(self, saturation=0.8, value=0.9, items=None):
        """
        Color each of the traces in a plot a different color
        """
        if items is None:
            items = self.listDataItems()
        items = [x for x in items if isinstance(x, PlotDataItem)]
        ntraces = len(items)

        for i, trace in enumerate(items):
            color = colorsys.hsv_to_rgb(i / ntraces, saturation, value)
            color = tuple(int(c * 255) for c in color)
            trace.setPen(*color)

    def addLegend(self, size=None, offset=(30, 30)):
        """
        Reimplement addLegend to check if legend already exists.
        The default one should do this, but doesn't
        seem to work on our extended version for some reason?
        """
        if self.legend is None:
            self.legend = LegendItem(size, offset)
            self.legend.setParentItem(self.vb)
        return self.legend
Example #3
0
class Network(Base, metaclass=config.Singleton):
    """ Network performance measure """
    def __init__(self, base):
        super(Network, self).__init__(base)
        self._out_val = None
        self._inc_val = None

        self._plot()
        self._clear()

    def _plot(self):
        """ Network performance graph """
        setConfigOption('background', '#FF000000')
        _graph = PlotWidget()
        _graph.setMenuEnabled(enableMenu=False)
        _graph.setMouseEnabled(x=False, y=False)
        _graph.hideButtons()

        self._out_curve = _graph.getPlotItem().plot()
        self._inc_curve = _graph.getPlotItem().plot()

        self._legend = LegendItem(offset=(50, 10))
        self._legend.setParentItem(_graph.getPlotItem())
        self._legend.addItem(self._out_curve, self._lang.NetworkGraphOutgoing)
        self._legend.addItem(self._inc_curve, self._lang.NetworkGraphIncoming)

        self._base.net_perf_lyt.addWidget(_graph)

        self._menu_popup = QMenu()
        _action = QAction(QIcon('icon/reload.png'), self._lang.PopupReload,
                          self._base.net_perf_box)
        _action.triggered.connect(self.reload)
        self._menu_popup.addAction(_action)

        self._base.net_perf_box.setContextMenuPolicy(Qt.CustomContextMenu)
        self._base.net_perf_box.customContextMenuRequested.connect(
            self._popup_show)

    def _set_value(self):
        """ Set send and receive value for the network performance curves """
        self._out_curve.setData(
            self._out_val,
            pen=mkPen(width=self._sw_config.net_perf_out_wd,
                      color=QColor(self._sw_config.net_perf_out_cl)))
        self._inc_curve.setData(
            self._inc_val,
            pen=mkPen(width=self._sw_config.net_perf_inc_wd,
                      color=QColor(self._sw_config.net_perf_inc_cl)))

    async def _load(self):
        """ Load the network performance measure - coroutine """
        if not self._sw_config.net_perf:
            return self._event.set()
        _old_out = 0
        _old_inc = 0
        while not self._event.is_set():
            _rev_out = net_io_counters().bytes_sent
            _rev_int = net_io_counters().bytes_recv
            if _old_out and _old_inc:
                self._out_val.append(_rev_out - _old_out)
                self._inc_val.append(_rev_int - _old_inc)
            self._set_value()
            _old_out = _rev_out
            _old_inc = _rev_int
            await sleep(self._sw_config.net_perf_tmo)

    def _popup_show(self, coordinate):
        """ Right-click popup menu in the graph area """
        self._menu_popup.exec_(self._base.net_perf_box.mapToGlobal(coordinate))

    def _clear(self):
        """ Clear the entire graph """
        self._out_val = deque([0] * self._sw_config.net_perf_len,
                              maxlen=self._sw_config.net_perf_len)
        self._inc_val = deque([0] * self._sw_config.net_perf_len,
                              maxlen=self._sw_config.net_perf_len)
        self._set_value()