Exemple #1
0
class MainGroupCti(GroupCti):
    """ Read only config Tree Item that only stores None.
        To be used as a high level group (e.g. the inspector group)
        Is the same as a groupCti but drawn as light text on a dark grey back ground
    """
    _backgroundBrush = QtGui.QBrush(
        QtGui.QColor("#606060"))  # create only once
    _foregroundBrush = QtGui.QBrush(QtGui.QColor(Qt.white))  # create only once
    _font = QtGui.QFont()
    _font.setWeight(QtGui.QFont.Bold)

    def __init__(self, nodeName, defaultData=None):
        """ Constructor. For the parameters see the AbstractCti constructor documentation.
        """
        super(MainGroupCti, self).__init__(nodeName,
                                           defaultData,
                                           expanded=True)  # always expand

    @property
    def font(self):
        """ Returns a font for displaying this item's text in the tree.
        """
        return self._font

    @property
    def backgroundBrush(self):
        """ Returns a (dark gray) brush for drawing the background role in the tree.
        """
        return self._backgroundBrush

    @property
    def foregroundBrush(self):
        """ Returns a (white) brush for drawing the foreground role in the tree.
        """
        return self._foregroundBrush
Exemple #2
0
    def __init__(self, store, parent=None):
        """ Constructor.

            :param store: Underlying data store, must descent from BaseRegistry
            :param parent: Parent widget
        """
        super(BaseRegistryModel, self).__init__(store, parent)
        check_class(store, BaseRegistry)

        self.regularBrush = QtGui.QBrush(QCOLOR_REGULAR)
        self.notImportedBrush = QtGui.QBrush(QCOLOR_NOT_IMPORTED)
        self.errorBrush = QtGui.QBrush(QCOLOR_ERROR)
Exemple #3
0
    def __init__(self, registry, attrNames = ('fullName', ), parent=None):
        """ Constructor.

            :param registry: Underlying registry. Must descent from ClassRegistry
            :param attrNames: List of attributes that will be displayed (def. only the fullName).
            :param parent: Parent widget
        """
        super(RegistryTableModel, self).__init__(parent)
        check_class(registry, ClassRegistry)
        self.registry = registry
        self.attrNames = attrNames

        self.regularBrush = QtGui.QBrush(QCOLOR_REGULAR)
        self.notImportedBrush = QtGui.QBrush(QCOLOR_NOT_IMPORTED)
        self.errorBrush = QtGui.QBrush(QCOLOR_ERROR)
Exemple #4
0
    def createPlotDataItem(self):
        """ Creates a PyQtGraph PlotDataItem from the config values
        """
        antialias = self.antiAliasCti.configValue

        color = self.penColor
        if self.lineCti.configValue:
            pen = QtGui.QPen()
            pen.setCosmetic(True)
            pen.setColor(color)
            pen.setWidthF(self.lineWidthCti.configValue)
            pen.setStyle(self.lineStyleCti.configValue)
            shadowCti = self.lineCti.findByNodePath('shadow')
            shadowPen = shadowCti.createPen(altStyle=pen.style(),
                                            altWidth=2.0 * pen.widthF())
        else:
            pen = None
            shadowPen = None

        drawSymbols = self.symbolCti.configValue
        symbolShape = self.symbolShapeCti.configValue if drawSymbols else None
        symbolSize = self.symbolSizeCti.configValue if drawSymbols else 0.0
        symbolPen = None  # otherwise the symbols will also have dotted/solid line.
        symbolBrush = QtGui.QBrush(color) if drawSymbols else None

        plotDataItem = pg.PlotDataItem(antialias=antialias,
                                       pen=pen,
                                       shadowPen=shadowPen,
                                       symbol=symbolShape,
                                       symbolSize=symbolSize,
                                       symbolPen=symbolPen,
                                       symbolBrush=symbolBrush)
        return plotDataItem
Exemple #5
0
    def _drawContents(self, reason=None, initiator=None):
        """ Draws the plot contents from the sliced array of the collected repo tree item.

            The reason parameter is used to determine if the axes will be reset (the initiator
            parameter is ignored). See AbstractInspector.updateContents for their description.
        """
        self.slicedArray = self.collector.getSlicedArray()

        if not self._hasValidData():
            self._clearContents()
            raise InvalidDataError(
                "No data available or it does not contain real numbers")

        # -- Valid plot data from here on --

        # PyQtGraph doesn't handle masked arrays so we convert the masked values to Nans (missing
        # data values are replaced by NaNs). The PyQtGraph line plot omits the Nans, which is great.
        self.slicedArray.replaceMaskedValueWithNan(
        )  # will convert data to float if int

        self.plotItem.clear()

        # Reset the axes ranges (via the config)
        if (reason == UpdateReason.RTI_CHANGED
                or reason == UpdateReason.COLLECTOR_COMBO_BOX):

            # self.config.yAxisRangeCti.setAutoRangeOn() doesn't work as refreshBlocked is True
            # TODO: can refreshBlocked maybe only block the signals to prevent loops?
            self.config.xAxisRangeCti.autoRangeCti.data = True
            self.config.yAxisRangeCti.autoRangeCti.data = True

        self.titleLabel.setText(
            self.configValue('title').format(**self.collector.rtiInfo))

        connected = np.isfinite(self.slicedArray.data)
        if is_an_array(self.slicedArray.mask):
            connected = np.logical_and(connected, ~self.slicedArray.mask)
        else:
            connected = np.zeros_like(
                self.slicedArray.data) if self.slicedArray.mask else connected

        plotDataItem = self.config.plotDataItemCti.createPlotDataItem()
        plotDataItem.setData(self.slicedArray.data, connect=connected)

        self.plotItem.addItem(plotDataItem)

        if self.config.probeCti.configValue:
            self.probeLabel.setVisible(True)
            self.plotItem.addItem(self.crossLineVerShadow, ignoreBounds=True)
            self.plotItem.addItem(self.crossLineVertical, ignoreBounds=True)
            self.plotItem.addItem(self.probeDataItem, ignoreBounds=True)
            self.probeDataItem.setSymbolBrush(
                QtGui.QBrush(self.config.plotDataItemCti.penColor))
            self.probeDataItem.setSymbolSize(10)
        else:
            self.probeLabel.setVisible(False)

        # Update the config tree from the (possibly) new state of the PgLinePlot1d inspector,
        # e.g. the axis range may have changed while drawing.
        self.config.updateTarget()
Exemple #6
0
    def mouseMoved(self, viewPos):
        """ Updates the probe text with the values under the cursor.
            Draws a vertical line and a symbol at the position of the probe.
        """
        try:
            check_class(viewPos, QtCore.QPointF)
            show_data_point = False  # shows the data point as a circle in the cross hair plots
            self.crossPlotRow, self.crossPlotCol = None, None

            self.probeLabel.setText(
                "<span style='color: #808080'>No data at cursor</span>")
            self.crossLineHorizontal.setVisible(False)
            self.crossLineVertical.setVisible(False)
            self.crossLineHorShadow.setVisible(False)
            self.crossLineVerShadow.setVisible(False)

            self.horCrossPlotItem.clear()
            self.verCrossPlotItem.clear()

            if self.slicedArray is not None and self.viewBox.sceneBoundingRect(
            ).contains(viewPos):

                # Calculate the row and column at the cursor.
                scenePos = self.viewBox.mapSceneToView(viewPos)
                row, col = round(scenePos.y()), round(scenePos.x())
                row, col = int(row), int(col)  # Needed in Python 2
                nRows, nCols = self.slicedArray.shape

                if (0 <= row < nRows) and (0 <= col < nCols):
                    self.viewBox.setCursor(Qt.CrossCursor)

                    self.crossPlotRow, self.crossPlotCol = row, col
                    index = tuple([row, col])
                    valueStr = to_string(self.slicedArray.data[index],
                                         masked=self.slicedArray.maskAt(index),
                                         maskFormat='&lt;masked&gt;')

                    txt = "({}, {}) = ({:d}, {:d}) {} {} = {}".format(
                        self.collector.rtiInfo['x-dim'],
                        self.collector.rtiInfo['y-dim'], col, row, RIGHT_ARROW,
                        self.collector.rtiInfo['name'], valueStr)
                    self.probeLabel.setText(txt)

                    # Show cross section at the cursor pos in the line plots
                    if self.config.horCrossPlotCti.configValue:
                        self.crossLineHorShadow.setVisible(True)
                        self.crossLineHorizontal.setVisible(True)
                        self.crossLineHorShadow.setPos(row)
                        self.crossLineHorizontal.setPos(row)

                        # Line plot of cross section row.
                        # First determine which points are connected or separated by masks/nans.
                        rowData = self.slicedArray.data[row, :]
                        connected = np.isfinite(rowData)
                        if is_an_array(self.slicedArray.mask):
                            connected = np.logical_and(
                                connected, ~self.slicedArray.mask[row, :])
                        else:
                            connected = (np.zeros_like(rowData) if
                                         self.slicedArray.mask else connected)

                        # Replace mask by Nans. Only doing when not showing lines to hack around PyQtGraph issue 1057
                        # See comment in PgLinePlot1d._drawContents for a more detailed explanation
                        # TODO: reuse imageItem data array when this hack is no longer necessary
                        if not self.config.crossPenCti.lineCti.configValue:
                            rowData = replaceMaskedValueWithFloat(
                                rowData,
                                np.logical_not(connected),
                                np.nan,
                                copyOnReplace=True)

                        # Replace infinite value with nans because PyQtGraph can't handle them
                        rowData = replaceMaskedValueWithFloat(
                            rowData,
                            np.isinf(rowData),
                            np.nan,
                            copyOnReplace=True)

                        horPlotDataItem = self.config.crossPenCti.createPlotDataItem(
                        )
                        # TODO: try to use connect='finite' when the hack above is no longer necessary. In that case
                        # test with array_masked test data
                        horPlotDataItem.setData(rowData, connect=connected)
                        self.horCrossPlotItem.addItem(horPlotDataItem)

                        # Vertical line in hor-cross plot
                        crossLineShadow90 = pg.InfiniteLine(
                            angle=90, movable=False, pen=self.crossShadowPen)
                        crossLineShadow90.setPos(col)
                        self.horCrossPlotItem.addItem(crossLineShadow90,
                                                      ignoreBounds=True)
                        crossLine90 = pg.InfiniteLine(angle=90,
                                                      movable=False,
                                                      pen=self.crossPen)
                        crossLine90.setPos(col)
                        self.horCrossPlotItem.addItem(crossLine90,
                                                      ignoreBounds=True)

                        if show_data_point:
                            crossPoint90 = pg.PlotDataItem(
                                symbolPen=self.crossPen)
                            crossPoint90.setSymbolBrush(
                                QtGui.QBrush(self.config.crossPenCti.penColor))
                            crossPoint90.setSymbolSize(10)
                            crossPoint90.setData((col, ), (rowData[col], ))
                            self.horCrossPlotItem.addItem(crossPoint90,
                                                          ignoreBounds=True)

                        self.config.horCrossPlotRangeCti.updateTarget(
                        )  # update auto range
                        del rowData  # defensive programming

                    if self.config.verCrossPlotCti.configValue:
                        self.crossLineVerShadow.setVisible(True)
                        self.crossLineVertical.setVisible(True)
                        self.crossLineVerShadow.setPos(col)
                        self.crossLineVertical.setPos(col)

                        # Line plot of cross section row.
                        # First determine which points are connected or separated by masks/nans.
                        colData = self.slicedArray.data[:, col]
                        connected = np.isfinite(colData)
                        if is_an_array(self.slicedArray.mask):
                            connected = np.logical_and(
                                connected, ~self.slicedArray.mask[:, col])
                        else:
                            connected = (np.zeros_like(colData) if
                                         self.slicedArray.mask else connected)

                        # Replace mask by Nans. Only doing when not showing lines to hack around PyQtGraph issue 1057
                        # See comment in PgLinePlot1d._drawContents for a more detailed explanation
                        if not self.config.crossPenCti.lineCti.configValue:
                            colData = replaceMaskedValueWithFloat(
                                colData,
                                np.logical_not(connected),
                                np.nan,
                                copyOnReplace=True)

                        # Replace infinite value with nans because PyQtGraph can't handle them
                        colData = replaceMaskedValueWithFloat(
                            colData,
                            np.isinf(colData),
                            np.nan,
                            copyOnReplace=True)

                        verPlotDataItem = self.config.crossPenCti.createPlotDataItem(
                        )
                        verPlotDataItem.setData(colData,
                                                np.arange(nRows),
                                                connect=connected)
                        self.verCrossPlotItem.addItem(verPlotDataItem)

                        # Horizontal line in ver-cross plot
                        crossLineShadow0 = pg.InfiniteLine(
                            angle=0, movable=False, pen=self.crossShadowPen)
                        crossLineShadow0.setPos(row)
                        self.verCrossPlotItem.addItem(crossLineShadow0,
                                                      ignoreBounds=True)
                        crossLine0 = pg.InfiniteLine(angle=0,
                                                     movable=False,
                                                     pen=self.crossPen)
                        crossLine0.setPos(row)
                        self.verCrossPlotItem.addItem(crossLine0,
                                                      ignoreBounds=True)

                        if show_data_point:
                            crossPoint0 = pg.PlotDataItem(
                                symbolPen=self.crossPen)
                            crossPoint0.setSymbolBrush(
                                QtGui.QBrush(self.config.crossPenCti.penColor))
                            crossPoint0.setSymbolSize(10)
                            crossPoint0.setData((colData[row], ), (row, ))
                            self.verCrossPlotItem.addItem(crossPoint0,
                                                          ignoreBounds=True)

                        self.config.verCrossPlotRangeCti.updateTarget(
                        )  # update auto range
                        del colData  # defensive programming

        except Exception as ex:
            # In contrast to _drawContents, this function is a slot and thus must not throw
            # exceptions. The exception is logged. Perhaps we should clear the cross plots, but
            # this could, in turn, raise exceptions.
            if DEBUGGING:
                raise
            else:
                logger.exception(ex)
Exemple #7
0
    def _drawContents(self, reason=None, initiator=None):
        """ Draws the plot contents from the sliced array of the collected repo tree item.

            The reason parameter is used to determine if the axes will be reset (the initiator
            parameter is ignored). See AbstractInspector.updateContents for their description.
        """

        # If auto-reset is true, reset config complete or partially, depending on the mode.
        if self._resetRequired(reason, initiator):
            self.resetConfig()

        self.slicedArray = self.collector.getSlicedArray()

        slicedArray = self.collector.getSlicedArray()
        if slicedArray is None:
            self._clearContents()
            raise InvalidDataError()  # Don't show message, to common.
        elif not array_has_real_numbers(slicedArray.data):
            self._clearContents()
            raise InvalidDataError("Selected item contains {} data.".format(
                array_kind_label(slicedArray.data)))
        else:
            self.slicedArray = slicedArray

        # -- Valid plot data from here on --

        numElem = np.prod(self.slicedArray.data.shape)
        if numElem == 0:
            self.sigShowMessage.emit(
                "Current slice is empty.")  # Not expected to happen.
        elif numElem == 1:
            self.sigShowMessage.emit(
                "Current slice contains only a single data point.")

        # PyQtGraph doesn't handle masked arrays so we convert the masked values to Nans (missing
        # data values are replaced by NaNs). The PyQtGraph line plot omits the Nans, which is great.
        # Update: in newer version of Qt the Nans are no longer printed, see PyQtGraph issue 1057,
        # https://github.com/pyqtgraph/pyqtgraph/issues/1057
        # When showing lines we therefore don't replace the Nans and let the setData connect parameter be responsible
        # for omitting the masked data. When showing only symbols the masked values are replaced. When both symbols
        # wnd lines are shown the resulting plot is incorrect as the masked values are not replaced and thus displayed
        # as point. This is unfortunate but can't be helped until the issue is resolved in PyQtGraph.
        if not self.config.plotDataItemCti.lineCti.configValue:
            self.slicedArray.replaceMaskedValueWithNan(
            )  # will convert data to float if int

        self.plotItem.clear()

        self.titleLabel.setText(
            self.configValue('title').format(**self.collector.rtiInfo))

        connected = np.isfinite(self.slicedArray.data)
        if is_an_array(self.slicedArray.mask):
            connected = np.logical_and(connected, ~self.slicedArray.mask)
        else:
            connected = np.zeros_like(
                self.slicedArray.data) if self.slicedArray.mask else connected

        plotDataItem = self.config.plotDataItemCti.createPlotDataItem()
        plotDataItem.setData(self.slicedArray.data, connect=connected)

        if plotDataItem.opts['pen'] is None and plotDataItem.opts[
                'symbol'] is None:
            self.sigShowMessage.emit(
                "The 'line' and 'symbol' config options are both unchecked!")

        self.plotItem.addItem(plotDataItem)

        if self.config.probeCti.configValue:
            self.probeLabel.setVisible(True)
            self.plotItem.addItem(self.crossLineVerShadow, ignoreBounds=True)
            self.plotItem.addItem(self.crossLineVertical, ignoreBounds=True)
            self.plotItem.addItem(self.probeDataItem, ignoreBounds=True)
            self.probeDataItem.setSymbolBrush(
                QtGui.QBrush(self.config.plotDataItemCti.penColor))
            self.probeDataItem.setSymbolSize(10)
        else:
            self.probeLabel.setVisible(False)

        self.plotItem.setRectangleZoomOn(self.config.zoomModeCti.configValue)

        self.config.updateTarget()