def calcPgImagePlot2dDataRange(pgImagePlot2d, percentage, crossPlot): """ Calculates the range from the inspectors' sliced array. Discards percentage of the minimum and percentage of the maximum values of the inspector.slicedArray :param pgImagePlot2d: the range methods will work on (the sliced array) of this inspector. :param percentage: percentage that will be discarded. :param crossPlot: if None, the range will be calculated from the entire sliced array, if "horizontal" or "vertical" the range will be calculated from the data under the horizontal or vertical cross hairs. If the cursor is outside the image, there is no valid data under the cross-hair and the range will be determined from the sliced array as a fall back. """ check_class(pgImagePlot2d.slicedArray, ArrayWithMask) # sanity check if crossPlot is None: array = pgImagePlot2d.slicedArray # the whole image elif crossPlot == 'horizontal': if pgImagePlot2d.crossPlotRow is not None: array = pgImagePlot2d.slicedArray.asMaskedArray()[pgImagePlot2d.crossPlotRow, :] else: array = pgImagePlot2d.slicedArray # fall back on complete sliced array elif crossPlot == 'vertical': if pgImagePlot2d.crossPlotCol is not None: array = pgImagePlot2d.slicedArray.asMaskedArray()[:, pgImagePlot2d.crossPlotCol] else: array = pgImagePlot2d.slicedArray # fall back on complete sliced array else: raise ValueError("crossPlot must be: None, 'horizontal' or 'vertical', got: {}" .format(crossPlot)) return maskedNanPercentile(array, (percentage, 100-percentage) )
def maskedEqual(array, missingValue): """ Mask an array where equal to a given (missing)value. Unfortunately ma.masked_equal does not work with structured arrays. See: https://mail.scipy.org/pipermail/numpy-discussion/2011-July/057669.html If the data is a structured array the mask is applied for every field (i.e. forming a logical-and). Otherwise ma.masked_equal is called. """ if array_is_structured(array): # Enforce the array to be masked if not isinstance(array, ma.MaskedArray): array = ma.MaskedArray(array) # Set the mask separately per field for nr, field in enumerate(array.dtype.names): if hasattr(missingValue, '__len__'): fieldMissingValue = missingValue[nr] else: fieldMissingValue = missingValue array[field] = ma.masked_equal(array[field], fieldMissingValue) check_class(array, ma.MaskedArray) # post-condition check return array else: # masked_equal works with missing is None result = ma.masked_equal(array, missingValue, copy=False) check_class(result, ma.MaskedArray) # post-condition check return result
def tryImportRegItem(self, regItem): """ Tries to import a registry item (plugin) """ check_class(regItem, self.store.ITEM_CLASS) logger.debug("Importing {}...".format(regItem.name)) regItem.tryImportClass() self.emitDataChanged(regItem)
def __init__(self, ncDim, nodeName, fileName=''): """ Constructor """ super(NcdfDimensionRti, self).__init__(nodeName, fileName=fileName) check_class(ncDim, Dimension) self._ncDim = ncDim
def __init__(self, h5Group, nodeName, fileName=''): """ Constructor """ super(H5pyGroupRti, self).__init__(nodeName, fileName=fileName) check_class(h5Group, h5py.Group, allow_none=True) self._h5Group = h5Group
def __init__(self, ndFrame=None, nodeName='', fileName='', standAlone=True, iconColor=_defaultIconColor): """ Constructor The NDFrame is not part of Pandas' documented API, although it mentions this inheritance. Therefore it is not checked the ndFrame is actually of type NDFrame. :param ndFrame: the underlying pandas object. May be undefined (None) :type ndFrame: pandas.core.generic.NDFrame :param standAlone: True if the NDFrame is a stand-alone object, False if it is part of another higher-dimensional, NDFrame. This influences the array. Furthermore, if standAlone is True the index of the NDFrame will be included when the children are fetched and included in the tree (as a PandasIndexRti) """ super(AbstractPandasNDFrameRti, self).__init__(nodeName=nodeName, fileName=fileName) check_class(ndFrame, NDFrame, allow_none=True) self._ndFrame = ndFrame self._iconColor = iconColor self._standAlone = standAlone
def __init__(self, model=None, onlyShowImported=False, parent=None): """ Constructor :param model: a RegistryTableModel that maps the regItems :param onlyShowImported: If True, regItems that are not (successfully) imported are filtered from the table. :param parent: the parent widget """ super(RegistryTableView, self).__init__(parent) self._onlyShowImported = onlyShowImported if model is not None: check_class(model, (RegistryTableModel, RegistryTableProxyModel)) self.setModel(model) else: assert False, "not yet implemented" #self.setHorizontalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) #self.setVerticalScrollMode(QtWidgets.QAbstractItemView.ScrollPerPixel) self.verticalHeader().hide() self.setAlternatingRowColors(True) self.setShowGrid(False) self.setSortingEnabled(True) self.setTabKeyNavigation(False) self.setSelectionMode(QtWidgets.QAbstractItemView.SingleSelection) self.setSelectionBehavior(QtWidgets.QAbstractItemView.SelectRows) self.setEditTriggers(QtWidgets.QAbstractItemView.NoEditTriggers) self.setWordWrap(True) tableHeader = self.horizontalHeader() tableHeader.setDefaultAlignment(Qt.AlignVCenter | Qt.AlignLeft) tableHeader.setSectionResizeMode(QtWidgets.QHeaderView.Interactive) # don't set to stretch tableHeader.setStretchLastSection(True)
def __init__(self, viewBox, axisNumber, autoRangeFunctions=None, nodeName='range', expanded=True): """ Constructor. The target axis is specified by viewBox and axisNumber (0 for x-axis, 1 for y-axis) If given, autoRangeFunctions must be a (label to function) dictionary that will be used to populate the (auto range) method ChoiceCti. If not give, the there will not be a method choice and the autorange implemented by PyQtGraph will be used. """ if autoRangeFunctions is None: autoRangeFunctions = {self.PYQT_RANGE: partial(viewBoxAxisRange, viewBox, axisNumber)} super(PgAxisRangeCti, self).__init__(autoRangeFunctions=autoRangeFunctions, nodeName=nodeName, expanded=expanded) check_class(viewBox, pg.ViewBox) assert axisNumber in (X_AXIS, Y_AXIS), "axisNumber must be 0 or 1" self.viewBox = viewBox self.axisNumber = axisNumber # Autorange must be disabled as not to interfere with this class. # Note that autorange of ArgosPgPlotItem is set to False by default. axisAutoRange = self.viewBox.autoRangeEnabled()[axisNumber] assert axisAutoRange is False, \ "Autorange is {!r} for axis {} of {}".format(axisAutoRange, axisNumber, self.nodePath) # Connect signals self.viewBox.sigRangeChangedManually.connect(self.setAutoRangeOff) self.viewBox.sigRangeChanged.connect(self.refreshMinMax)
def __init__(self, textInspector, nodeName): """ Constructor :param textInspector: the TextInspector widget that is being configured :param nodeName: node name """ super(TextInspectorCti, self).__init__(nodeName) check_class(textInspector, TextInspector) self.textInspector = textInspector Opt = QtGui.QTextOption self.wordWrapCti = self.insertChild( ChoiceCti('word wrap', displayValues=[ 'No wrapping', 'Word boundaries', 'Anywhere', 'Boundaries or anywhere' ], configValues=[ Opt.NoWrap, Opt.WordWrap, Opt.WrapAnywhere, Opt.WrapAtWordBoundaryOrAnywhere ])) self.encodingCti = self.insertChild( ChoiceCti( 'encoding', editable=True, configValues=['utf-8', 'ascii', 'latin-1', 'windows-1252'])) self.fontCti = self.insertChild( FontCti(self.textInspector.editor, "font", defaultData=QtGui.QFont(MONO_FONT, FONT_SIZE)))
def __init__(self, h5Dataset, nodeName, fileName=''): """ Constructor. The name of the field must be given to the nodeName parameter. """ super(H5pyFieldRti, self).__init__(nodeName, fileName=fileName) check_class(h5Dataset, h5py.Dataset) self._h5Dataset = h5Dataset
def mask(self, mask): """ The mask values. Must be an array or a boolean scalar.""" check_class(mask, (np.ndarray, bool, np.bool_)) if isinstance(mask, (bool, np.bool_)): self._mask = bool(mask) else: self._mask = mask
def __init__(self, h5Dataset, nodeName, fileName=''): """ Constructor """ super(H5pyDatasetRti, self).__init__(nodeName, fileName=fileName) check_class(h5Dataset, h5py.Dataset) self._h5Dataset = h5Dataset self._isStructured = bool(self._h5Dataset.dtype.names)
def __init__(self, legend, autoRangeFunctions=None, nodeName='color range', expanded=True): """ Constructor. The target axis is specified by viewBox and axisNumber (0 for x-axis, 1 for y-axis) If given, autoRangeFunctions must be a (label to function) dictionary that will be used to populate the (auto range) method ChoiceCti. If not give, the there will not be a method choice and the autorange implemented by PyQtGraph will be used. """ super(PgColorLegendCti, self).__init__(autoRangeFunctions=autoRangeFunctions, nodeName=nodeName, expanded=True, paddingDefault=0) check_class(legend, ColorLegendItem) self.legend = legend self.paddingCti.defaultData = 0 # Connect signals self.legend.sigLevelsChanged.connect(self.setAutoRangeOff) self.legend.sigLevelsChanged.connect(self.refreshMinMax)
def __init__(self, ncGroup, nodeName, fileName=''): """ Constructor """ super(NcdfGroupRti, self).__init__(nodeName, fileName=fileName) check_class(ncGroup, Dataset, allow_none=True) self._ncGroup = ncGroup
def __init__(self, ncVar, nodeName, fileName=''): """ Constructor. The name of the field must be given to the nodeName parameter. """ super(NcdfFieldRti, self).__init__(nodeName, fileName=fileName) check_class(ncVar, Variable) self._ncVar = ncVar
def __init__(self, viewBox, nodeName="lock aspect ratio", defaultData=False, expanded=False): """ Constructor. The target axis is specified by viewBox and axisNumber (0 for x-axis, 1 for y-axis) """ super(PgAspectRatioCti, self).__init__(nodeName, defaultData=defaultData, expanded=expanded) check_class(viewBox, pg.ViewBox) self.aspectRatioCti = self.insertChild(FloatCti("ratio", 1.0, minValue=0.0)) self.viewBox = viewBox
def __init__(self, viewBox, axisNumber, nodeName='flipped', defaultData=False ): """ Constructor. The target axis is specified by viewBox and axisNumber (0 for x-axis, 1 for y-axis) """ super(PgAxisFlipCti, self).__init__(nodeName, defaultData=defaultData) check_class(viewBox, pg.ViewBox) assert axisNumber in (X_AXIS, Y_AXIS), "axisNumber must be 0 or 1" self.viewBox = viewBox self.axisNumber = axisNumber
def insertItem(self, item, row): """ Insert an item in the store at a certain row. """ check_class(item, self.store.ITEM_CLASS) logger.info("Inserting {!r} at row {}".format(item, row, self)) self.beginInsertRows(QtCore.QModelIndex(), row, row) try: self.store.items.insert(row, item) finally: self.endInsertRows()
def __init__(self, name='', absClassName='', pythonPath='', iconColor=ICON_COLOR_UNDEF, globs=''): """ Constructor. See the ClassRegItem class doc string for the parameter help. """ super(RtiRegItem, self).__init__(name=name, absClassName=absClassName, pythonPath=pythonPath) check_class(globs, str) assert is_a_color_str(iconColor), \ "Icon color for {} is not a color string: {!r}".format(self, iconColor) self._data['iconColor'] = iconColor self._data['globs'] = globs
def removeItem(self, regItem): """ Removes a ClassRegItem object to the registry. Will raise a KeyError if the regItem is not registered. """ check_class(regItem, ClassRegItem) logger.info("Removing {!r} containing {}".format(regItem.identifier, regItem.fullClassName)) del self._index[regItem.identifier] idx = self._items.index(regItem) del self._items[idx]
def __init__(self, nodeName, defaultData=0, configValues=None, displayValues=None, editable=False, insertPolicy=QtWidgets.QComboBox.InsertAtBottom, completer=NOT_SPECIFIED): """ Constructor. The defaultData is an integers that is used to store the currentIndex. The displayValues parameter must be a list of strings, which will be displayed in the combo box. The configValues should be a list of the same size with the configValues that each 'choice' represents, e.g. choice 'dashed' maps to configValue Qt.DashLine. If displayValues is None, the configValues are used as displayValues. The defaultData can be set to a negative value, e.g. use -1 to select the last item by default. However, it will be converted to a positive value in the constructor. :param defaultData: the default index in the combobox that is used for editing :param configValues: list of configValue items to populate the combobox with :param displayValues: list of possible displayValue items. If None the config values are used as display values as well. :param editable: if True the combobox will be editable :param insertPolicy: determines where user-inserted items should appear in the combobox must be of type QtWidgets.QComboBox.InsertPolicy :param completer: a QCompleter that will be used by the combobox for auto-completion. If NotSpecified a default completer is used, i.e. case-insensitive auto-completion. Use None, 0 or False to disable auto-completion. For the parameters see the AbstractCti constructor documentation. """ check_class(insertPolicy, QtWidgets.QComboBox.InsertPolicy) if completer: # completer may be False check_class(completer, QtWidgets.QCompleter) self.editable = editable self.insertPolicy = insertPolicy self.completer = completer self._configValues = [] if configValues is None else configValues if displayValues is None: self._displayValues = copy.copy(self._configValues) else: assert not editable, "No separate displayValues may be set if the CTI is editable" self._displayValues = displayValues assert len(self._configValues) == len(self._displayValues),\ "If set, configValues must have the same length as displayValues." self._defaultConfigValues = copy.copy(self._configValues) # Set after self._displayValues are defined. The parent constructor call _enforceDataType super(ChoiceCti, self).__init__(nodeName, defaultData)
def __init__(self, store, parent=None): """ Constructor. :param store: Underlying data store, must descent from BaseItemStore :param parent: Parent widget """ super(BaseTableModel, self).__init__(parent) check_class(store, BaseItemStore) self._store = store self._fieldNames = self._store.fieldNames self._fieldLabels = self.store.fieldLabels
def __init__(self, colorLegendItem, nodeName='show histogram', defaultData=True): """ Constructor. The target axis is specified by viewBox and axisNumber (0 for x-axis, 1 for y-axis) """ super(PgShowHistCti, self).__init__(nodeName, defaultData=defaultData) check_class(colorLegendItem, ColorLegendItem) self.colorLegendItem = colorLegendItem
def __init__(self, gradientEditorItem, nodeName="color scale", defaultData=-1): """ Constructor. The gradientEditorItem must be a PyQtGraph.GradientEditorItem. The configValues are taken from pyqtgraph.graphicsItems.GradientEditorItem.Gradients which is an OrderedDict of color scales. By default the last item from this list is chosen, which is they 'grey' color scale. """ super(PgGradientEditorItemCti, self).__init__(nodeName, defaultData=defaultData, configValues=list(GRADIENTS.keys())) check_class(gradientEditorItem, pg.GradientEditorItem) self.gradientEditorItem = gradientEditorItem
def __init__(self, exdirDataset, nodeName='', fileName='', iconColor=ICON_COLOR_UNDEF): """ Constructor """ super(ExdirScalarRti, self).__init__(nodeName=nodeName, fileName=fileName, iconColor=iconColor) check_class(exdirDataset, exdir.Dataset) self._exdirDataset = exdirDataset
def __init__(self, plotItem, nodeName="grid", defaultData=True, expanded=False): """ Constructor. The target axis is specified by viewBox and axisNumber (0 for x-axis, 1 for y-axis) """ super(PgGridCti, self).__init__(nodeName, defaultData=defaultData, expanded=expanded) check_class(plotItem, pg.PlotItem) self.plotItem = plotItem self.xGridCti = self.insertChild(BoolCti('x-axis', defaultData)) self.yGridCti = self.insertChild(BoolCti('y-axis', defaultData)) self.alphaCti = self.insertChild(FloatCti('alpha', 0.20, minValue=0.0, maxValue=1.0, stepSize=0.01, decimals=2))
def __init__(self, h5Dataset, nodeName='', fileName='', iconColor=ICON_COLOR_UNDEF): """ Constructor """ super(H5pyScalarRti, self).__init__(nodeName=nodeName, fileName=fileName, iconColor=iconColor) check_class(h5Dataset, h5py.Dataset) self._h5Dataset = h5Dataset
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)
def setRti(self, rti): """ Updates the current VisItem from the contents of the repo tree item. Is a slot but the signal is usually connected to the Collector, which then calls this function directly. """ check_class(rti, BaseRti) #assert rti.isSliceable, "RTI must be sliceable" # TODO: maybe later self._rti = rti self._updateWidgets() self._updateRtiInfo()
def setRti(self, rti): """ Updates the current VisItem from the contents of the repo tree item. Is a slot but the signal is usually connected to the Collector, which then calls this function directly. """ check_class(rti, BaseRti) #assert rti.isSliceable, "RTI must be sliceable" # TODO: maybe later self._rti = rti self._updateWidgets() self._updateRtiInfo()
def _registerExtension(self, extension, rtiRegItem): """ Links an file name extension to a repository tree item. """ check_is_a_string(extension) check_class(rtiRegItem, RtiRegItem) logger.debug(" Registering extension {!r} for {}".format(extension, rtiRegItem)) # TODO: type checking if extension in self._extensionMap: logger.info("Overriding extension {!r}: old={}, new={}" .format(extension, self._extensionMap[extension], rtiRegItem)) self._extensionMap[extension] = rtiRegItem
def __init__(self, exdirGroup, nodeName, fileName='', iconColor=ICON_COLOR_UNDEF): """ Constructor """ super(ExdirGroupRti, self).__init__(nodeName, fileName=fileName, iconColor=iconColor) check_class(exdirGroup, exdir.Group, allow_none=True) self._exdirGroup = exdirGroup
def __init__(self, ncVar, nodeName, fileName=''): """ Constructor """ super(NcdfVariableRti, self).__init__(nodeName, fileName=fileName) check_class(ncVar, Variable) self._ncVar = ncVar try: self._isStructured = bool(self._ncVar.dtype.names) except (AttributeError, KeyError): # If dtype is a string instead of an numpy dtype, netCDF4 raises a KeyError # or AttributeError, depending on its version. self._isStructured = False
def registerItem(self, regItem): """ Adds a ClassRegItem object to the registry. """ check_class(regItem, ClassRegItem) if regItem.identifier in self._index: oldRegItem = self._index[regItem.identifier] logger.warn("Class key {!r} already registered as {}. Removing old regItem." .format(regItem.identifier, oldRegItem.fullClassName)) self.removeItem(oldRegItem) logger.info("Registering {!r} with {}".format(regItem.identifier, regItem.fullClassName)) self._items.append(regItem) self._index[regItem.identifier] = regItem
def fillValuesToNan(masked_array): """ Replaces the fill_values of the masked array by NaNs If the array is None or it does not contain floating point values, it cannot contain NaNs. In that case the original array is returned. """ if masked_array is not None and masked_array.dtype.kind == 'f': check_class(masked_array, ma.masked_array) logger.debug("Replacing fill_values by NaNs") masked_array[:] = ma.filled(masked_array, np.nan) masked_array.set_fill_value(np.nan) else: return masked_array
def __init__(self, h5Group, nodeName, fileName='', iconColor=ICON_COLOR_UNDEF): """ Constructor """ super(H5pyGroupRti, self).__init__(nodeName, fileName=fileName, iconColor=iconColor) check_class(h5Group, h5py.Group, allow_none=True) self._h5Group = h5Group
def __init__(self, viewBox, axisNumber, nodeName='flipped', defaultData=False): """ Constructor. The target axis is specified by viewBox and axisNumber (0 for x-axis, 1 for y-axis) """ super(PgAxisFlipCti, self).__init__(nodeName, defaultData=defaultData) check_class(viewBox, pg.ViewBox) assert axisNumber in (X_AXIS, Y_AXIS), "axisNumber must be 0 or 1" self.viewBox = viewBox self.axisNumber = axisNumber
def __init__(self, h5Dataset, nodeName, fileName='', iconColor=ICON_COLOR_UNDEF): """ Constructor """ super(H5pyDatasetRti, self).__init__(nodeName, fileName=fileName, iconColor=iconColor) check_class(h5Dataset, h5py.Dataset) self._h5Dataset = h5Dataset self._isStructured = bool(self._h5Dataset.dtype.names)
def __init__(self, nodeName, defaultData=0, configValues=None, displayValues=None, editable=False, insertPolicy=QtWidgets.QComboBox.InsertAtBottom, completer=NOT_SPECIFIED): """ Constructor. The defaultData is an integers that is used to store the currentIndex. The displayValues parameter must be a list of strings, which will be displayed in the combo box. The configValues should be a list of the same size with the configValues that each 'choice' represents, e.g. choice 'dashed' maps to configValue Qt.DashLine. If displayValues is None, the configValues are used as displayValues. The defaultData can be set to a negative value, e.g. use -1 to select the last item by default. However, it will be converted to a positive value in the constructor. :param defaultData: the default index in the combobox that is used for editing :param configValues: list of configValue items to populate the combobox with :param displayValues: list of possible displayValue items. If None the config values are used as display values as well. :param editable: if True the combobox will be editable :param insertPolicy: determines where user-inserted items should appear in the combobox must be of type QtWidgets.QComboBox.InsertPolicy :param completer: a QCompleter that will be used by the combobox for auto-completion. If NotSpecified a default completer is used, i.e. case-insensitive auto-completion. Use None, 0 or False to disable auto-completion. For the parameters see the AbstractCti constructor documentation. """ check_class(insertPolicy, QtWidgets.QComboBox.InsertPolicy) if completer: # completer may be False check_class(completer, QtWidgets.QCompleter) self.editable = editable self.insertPolicy = insertPolicy self.completer = completer self._configValues = [] if configValues is None else configValues if displayValues is None: self._displayValues = copy.copy(self._configValues) else: assert not editable, "No separate displayValues may be set if the CTI is editable" self._displayValues = displayValues assert len(self._configValues) == len(self._displayValues),\ "If set, configValues must have the same length as displayValues." self._defaultConfigValues = copy.copy(self._configValues) # Set after self._displayValues are defined. The parent constructor call _enforceDataType super(ChoiceCti, self).__init__(nodeName, defaultData)
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)
def __init__(self, nodeName, fileName=''): """ Constructor :param nodeName: name of this node (used to construct the node path). :param fileName: absolute path to the file where the data of this RTI originates. """ super(BaseRti, self).__init__(nodeName=nodeName) self._isOpen = False self._exception = None # Any exception that may occur when opening this item. check_class(fileName, six.string_types, allow_none=True) if fileName: fileName = os.path.abspath(fileName) self._fileName = fileName
def __init__(self, histLutItem, autoRangeFunctions=None, nodeName='color range', expanded=True): """ Constructor. The target axis is specified by viewBox and axisNumber (0 for x-axis, 1 for y-axis) If given, autoRangeFunctions must be a (label to function) dictionary that will be used to populate the (auto range) method ChoiceCti. If not give, the there will not be a method choice and the autorange implemented by PyQtGraph will be used. """ super(PgHistLutColorRangeCti, self).__init__(autoRangeFunctions=autoRangeFunctions, nodeName=nodeName, expanded=expanded) check_class(histLutItem, HistogramLUTItem) self.histLutItem = histLutItem # Connect signals self.histLutItem.sigLevelsChanged.connect(self.setAutoRangeOff) self.histLutItem.sigLevelsChanged.connect(self.refreshMinMax)
def __init__(self, pgLinePlot1d, nodeName): """ Constructor Maintains a link to the target pgLinePlot1d inspector, so that changes in the configuration can be applied to the target by simply calling the apply method. Vice versa, it can connect signals to the target. """ super(PgLinePlot1dCti, self).__init__(nodeName=nodeName) check_class(pgLinePlot1d, PgLinePlot1d) self.pgLinePlot1d = pgLinePlot1d self.insertChild(ChoiceCti('title', 0, editable=True, configValues=["{base-name} -- {name} {slices}", "{name} {slices}", "{path} {slices}"])) #### Axes #### plotItem = self.pgLinePlot1d.plotItem viewBox = plotItem.getViewBox() self.insertChild(PgGridCti(plotItem)) self.xAxisCti = self.insertChild(PgAxisCti('x-axis')) self.xAxisCti.insertChild(PgAxisLabelCti(plotItem, 'bottom', self.pgLinePlot1d.collector, defaultData=1, configValues=[PgAxisLabelCti.NO_LABEL, "{x-dim} [index]"])) # No logarithmic X-Axis as long as abcissa is not yet implemented. #xAxisCti.insertChild(PgAxisLogModeCti(imagePlotItem, X_AXIS)) self.xAxisRangeCti = self.xAxisCti.insertChild(PgAxisRangeCti(viewBox, X_AXIS)) self.yAxisCti = self.insertChild(PgAxisCti('y-axis')) self.yAxisCti.insertChild(PgAxisLabelCti(plotItem, 'left', self.pgLinePlot1d.collector, defaultData=1, configValues=[PgAxisLabelCti.NO_LABEL, "{name} {unit}", "{path} {unit}", "{name}", "{path}", "{raw-unit}"])) self.yLogCti = self.yAxisCti.insertChild(PgAxisLogModeCti(plotItem, Y_AXIS)) rangeFunctions = defaultAutoRangeMethods(self.pgLinePlot1d, {PgAxisRangeCti.PYQT_RANGE: partial(viewBoxAxisRange, viewBox, Y_AXIS)}) self.yAxisRangeCti = self.yAxisCti.insertChild(PgAxisRangeCti(viewBox, Y_AXIS, rangeFunctions)) #### Pen #### self.plotDataItemCti = self.insertChild(PgPlotDataItemCti()) self.probeCti = self.insertChild(BoolCti('show probe', True)) # Connect signals self.pgLinePlot1d.plotItem.sigAxisReset.connect(self.setAutoRangeOn)
def createFromMaskedArray(cls, masked_arr): """ Creates an ArrayWithMak :param masked_arr: a numpy MaskedArray or numpy array :return: ArrayWithMask """ if isinstance(masked_arr, ArrayWithMask): return masked_arr check_class(masked_arr, (np.ndarray, ma.MaskedArray)) # A MaskedConstant (i.e. masked) is a special case of MaskedArray. It does not seem to have # a fill_value so we use None to use the default. # https://docs.scipy.org/doc/numpy/reference/maskedarray.baseclass.html#numpy.ma.masked fill_value = getattr(masked_arr, 'fill_value', None) return cls(masked_arr.data, masked_arr.mask, fill_value)
def __init__(self, data, mask, fill_value): """ Constructor :param data: :param mask: array with mask or single boolean for the complete mask :param fill_value: """ check_is_an_array(data) check_class(mask, (np.ndarray, bool, np.bool_)) # Init fields self._data = None self._mask = None self._fill_value= None # Use setters self.data = data self.mask = mask self.fill_value= fill_value
def __init__(self, ndFrame=None, nodeName='', fileName='', standAlone=True, iconColor=_defaultIconColor): """ Constructor The NDFrame is not part of Pandas' documented API, although it mentions this inheritance. Therefore it is not checked the ndFrame is actually of type NDFrame. :param ndFrame: the underlying pandas object. May be undefined (None) :type ndFrame: pandas.core.generic.NDFrame :param standAlone: True if the NDFrame is a stand-alone object, False if it is part of another higher-dimensional, NDFrame. This influences the array. Furthermore, if standAlone is True the index of the NDFrame will be included when the children are fetched and included in the tree (as a PandasIndexRti) """ super(AbstractPandasNDFrameRti, self).__init__(nodeName=nodeName, fileName=fileName) check_class(ndFrame, NDFrame, allow_none=True) self._ndFrame = ndFrame self._iconColor = iconColor self._standAlone = standAlone
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) self.crossLineVerShadow.setVisible(False) self.crossLineVertical.setVisible(False) self.probeLabel.setText("") self.probeDataItem.clear() if (self._hasValidData() and self.config.probeCti.configValue and self.viewBox.sceneBoundingRect().contains(viewPos)): scenePos = self.viewBox.mapSceneToView(viewPos) index = int(scenePos.x()) data = self.slicedArray.data if not 0 <= index < len(data): txt = "<span style='color: grey'>no data at cursor</span>" self.probeLabel.setText(txt) else: valueStr = to_string(data[index], masked=self.slicedArray.maskAt(index), maskFormat='<masked>') self.probeLabel.setText("pos = {!r}, value = {}".format(index, valueStr)) if np.isfinite(data[index]): self.crossLineVerShadow.setVisible(True) self.crossLineVerShadow.setPos(index) self.crossLineVertical.setVisible(True) self.crossLineVertical.setPos(index) if data[index] > 0 or self.config.yLogCti.configValue == False: self.probeDataItem.setData((index,), (data[index],)) 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)
def _openResources(self): """ Uses numpy.load to open the underlying file """ dct = np.load(self._fileName, allow_pickle=ALLOW_PICKLE) check_class(dct, NpzFile) self._dictionary = dct
def setInspectorById(self, identifier): """ Sets the central inspector widget given a inspector ID. If identifier is None, the inspector will be unset. Otherwise it will lookup the inspector class in the registry. It will raise a KeyError if the ID is not found there. It will do an import of the inspector code if it's loaded for the first time. If the the inspector class cannot be imported a warning is logged and the inspector is unset. NOTE: does not draw the new inspector, this is the responsibility of the caller. Also, the corresponding action is not triggered. Emits the sigInspectorChanged(self.inspectorRegItem) """ logger.info("Setting inspector: {}".format(identifier)) # Use the identifier to find a registered inspector and set self.inspectorRegItem. # Then create an inspector object from it. oldInspectorRegItem = self.inspectorRegItem oldInspector = self.inspector if not identifier: inspector = None self._inspectorRegItem = None else: inspectorRegistry = self.argosApplication.inspectorRegistry inspectorRegItem = inspectorRegistry.getItemById(identifier) self._inspectorRegItem = inspectorRegItem if inspectorRegItem is None: inspector = None else: try: inspector = inspectorRegItem.create(self.collector, tryImport=True) except ImportError as ex: # Only log the error. No dialog box or user interaction here because this # function may be called at startup. logger.exception("Clearing inspector. Unable to create {!r} because {}" .format(inspectorRegItem.identifier, ex)) inspector = None self.getInspectorActionById(identifier).setEnabled(False) if DEBUGGING: raise ###################### # Set self.inspector # ###################### check_class(inspector, AbstractInspector, allow_none=True) logger.debug("Disabling updates.") self.setUpdatesEnabled(False) try: centralLayout = self.centralWidget().layout() # Delete old inspector if oldInspector is None: # can be None at start-up oldConfigPosition = None # Last top level element in the config tree. else: self._updateNonDefaultsForInspector(oldInspectorRegItem, oldInspector) # Remove old inspector configuration from tree oldConfigPosition = oldInspector.config.childNumber() configPath = oldInspector.config.nodePath _, oldConfigIndex = self._configTreeModel.findItemAndIndexPath(configPath)[-1] self._configTreeModel.deleteItemAtIndex(oldConfigIndex) oldInspector.finalize() # TODO: before removing config centralLayout.removeWidget(oldInspector) oldInspector.deleteLater() # Set new inspector self._inspector = inspector # Update collector widgets and the config tree oldBlockState = self.collector.blockSignals(True) try: if self.inspector is None: self.collector.clearAndSetComboBoxes([]) else: # Add and apply config values to the inspector key = self.inspectorRegItem.identifier nonDefaults = self._inspectorsNonDefaults.get(key, {}) logger.debug("Setting non defaults: {}".format(nonDefaults)) self.inspector.config.setValuesFromDict(nonDefaults) self._configTreeModel.insertItem(self.inspector.config, oldConfigPosition) self.configWidget.configTreeView.expandBranch() self.collector.clearAndSetComboBoxes(self.inspector.axesNames()) centralLayout.addWidget(self.inspector) finally: self.collector.blockSignals(oldBlockState) finally: logger.debug("Enabling updates.") self.setUpdatesEnabled(True) self.updateWindowTitle() logger.debug("Emitting sigInspectorChanged({})".format(self.inspectorRegItem)) self.sigInspectorChanged.emit(self.inspectorRegItem)
def __init__(self, pgImagePlot2d, nodeName): """ Constructor Maintains a link to the target pgImagePlot2d inspector, so that changes in the configuration can be applied to the target by simply calling the apply method. Vice versa, it can connect signals to the target. """ super(PgImagePlot2dCti, self).__init__(nodeName) check_class(pgImagePlot2d, PgImagePlot2d) self.pgImagePlot2d = pgImagePlot2d imagePlotItem = self.pgImagePlot2d.imagePlotItem viewBox = imagePlotItem.getViewBox() self.insertChild(ChoiceCti('title', 0, editable=True, configValues=["{base-name} -- {name} {slices}", "{name} {slices}", "{path} {slices}"])) #### Axes #### self.aspectLockedCti = self.insertChild(PgAspectRatioCti(viewBox)) self.xAxisCti = self.insertChild(PgAxisCti('x-axis')) #xAxisCti.insertChild(PgAxisShowCti(imagePlotItem, 'bottom')) # disabled, seems broken self.xAxisCti.insertChild(PgAxisLabelCti(imagePlotItem, 'bottom', self.pgImagePlot2d.collector, defaultData=1, configValues=[PgAxisLabelCti.NO_LABEL, "{x-dim} [index]"])) self.xFlippedCti = self.xAxisCti.insertChild(PgAxisFlipCti(viewBox, X_AXIS)) self.xAxisRangeCti = self.xAxisCti.insertChild(PgAxisRangeCti(viewBox, X_AXIS)) self.yAxisCti = self.insertChild(PgAxisCti('y-axis')) #yAxisCti.insertChild(PgAxisShowCti(imagePlotItem, 'left')) # disabled, seems broken self.yAxisCti.insertChild(PgAxisLabelCti(imagePlotItem, 'left', self.pgImagePlot2d.collector, defaultData=1, configValues=[PgAxisLabelCti.NO_LABEL, "{y-dim} [index]"])) self.yFlippedCti = self.yAxisCti.insertChild(PgAxisFlipCti(viewBox, Y_AXIS)) self.yAxisRangeCti = self.yAxisCti.insertChild(PgAxisRangeCti(viewBox, Y_AXIS)) #### Color scale #### colorAutoRangeFunctions = defaultAutoRangeMethods(self.pgImagePlot2d) self.histColorRangeCti = self.insertChild( PgHistLutColorRangeCti(pgImagePlot2d.histLutItem, colorAutoRangeFunctions, nodeName="color range")) histViewBox = pgImagePlot2d.histLutItem.vb histViewBox.enableAutoRange(Y_AXIS, False) rangeFunctions = defaultAutoRangeMethods(self.pgImagePlot2d, {PgAxisRangeCti.PYQT_RANGE: partial(viewBoxAxisRange, histViewBox, Y_AXIS)}) self.histRangeCti = self.insertChild( PgAxisRangeCti(histViewBox, Y_AXIS, nodeName='histogram range', autoRangeFunctions=rangeFunctions)) self.insertChild(PgGradientEditorItemCti(self.pgImagePlot2d.histLutItem.gradient)) # Probe and cross-hair plots self.probeCti = self.insertChild(BoolCti('show probe', True)) self.crossPlotGroupCti = self.insertChild(BoolGroupCti('cross-hair', expanded=False)) self.crossPenCti = self.crossPlotGroupCti.insertChild(PgPlotDataItemCti(expanded=False)) #self.crossLinesCti = self.crossPlotGroupCti.insertChild(PgPlotDataItemCti('cross pen', # expanded=False)) self.horCrossPlotCti = self.crossPlotGroupCti.insertChild(BoolCti('horizontal', False, expanded=False)) self.horCrossPlotCti.insertChild(PgGridCti(pgImagePlot2d.horCrossPlotItem)) self.horCrossPlotRangeCti = self.horCrossPlotCti.insertChild(PgAxisRangeCti( self.pgImagePlot2d.horCrossPlotItem.getViewBox(), Y_AXIS, nodeName="data range", autoRangeFunctions = crossPlotAutoRangeMethods(self.pgImagePlot2d, "horizontal"))) self.verCrossPlotCti = self.crossPlotGroupCti.insertChild(BoolCti('vertical', False, expanded=False)) self.verCrossPlotCti.insertChild(PgGridCti(pgImagePlot2d.verCrossPlotItem)) self.verCrossPlotRangeCti = self.verCrossPlotCti.insertChild(PgAxisRangeCti( self.pgImagePlot2d.verCrossPlotItem.getViewBox(), X_AXIS, nodeName="data range", autoRangeFunctions = crossPlotAutoRangeMethods(self.pgImagePlot2d, "vertical"))) # Connect signals self.pgImagePlot2d.imagePlotItem.sigAxisReset.connect(self.setImagePlotAutoRangeOn) self.pgImagePlot2d.horCrossPlotItem.sigAxisReset.connect(self.setHorCrossPlotAutoRangeOn) self.pgImagePlot2d.verCrossPlotItem.sigAxisReset.connect(self.setVerCrossPlotAutoRangeOn) # Also update axis auto range tree items when linked axes are resized horCrossViewBox = self.pgImagePlot2d.horCrossPlotItem.getViewBox() horCrossViewBox.sigRangeChangedManually.connect(self.xAxisRangeCti.setAutoRangeOff) verCrossViewBox = self.pgImagePlot2d.verCrossPlotItem.getViewBox() verCrossViewBox.sigRangeChangedManually.connect(self.yAxisRangeCti.setAutoRangeOff)
def setModel(self, model): """ Sets the model. Checks that the model is a """ check_class(model, BaseTreeModel) super(ArgosTreeView, self).setModel(model)
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._hasValidData() and self.slicedArray is not None and self.viewBox.sceneBoundingRect().contains(viewPos)): # Calculate the row and column at the cursor. We use math.floor because the pixel # corners of the image lie at integer values (and not the centers of the pixels). scenePos = self.viewBox.mapSceneToView(viewPos) row, col = math.floor(scenePos.y()), math.floor(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[index], masked=self.slicedArray.maskAt(index), maskFormat='<masked>') txt = "pos = ({:d}, {:d}), value = {}".format(row, col, 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 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() 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 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)
def __init__(self, h5Dataset, nodeName='', fileName=''): """ Constructor """ super(H5pyScalarRti, self).__init__(nodeName = nodeName, fileName=fileName) check_class(h5Dataset, h5py.Dataset) self._h5Dataset = h5Dataset