summaryrefslogtreecommitdiff
path: root/silx/gui/plot/CurvesROIWidget.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/gui/plot/CurvesROIWidget.py')
-rw-r--r--silx/gui/plot/CurvesROIWidget.py1584
1 files changed, 0 insertions, 1584 deletions
diff --git a/silx/gui/plot/CurvesROIWidget.py b/silx/gui/plot/CurvesROIWidget.py
deleted file mode 100644
index 5c9033e..0000000
--- a/silx/gui/plot/CurvesROIWidget.py
+++ /dev/null
@@ -1,1584 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2004-2020 European Synchrotron Radiation Facility
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-# THE SOFTWARE.
-#
-# ###########################################################################*/
-"""
-Widget to handle regions of interest (:class:`ROI`) on curves displayed in a
-:class:`PlotWindow`.
-
-This widget is meant to work with :class:`PlotWindow`.
-"""
-
-__authors__ = ["V.A. Sole", "T. Vincent", "H. Payno"]
-__license__ = "MIT"
-__date__ = "13/03/2018"
-
-from collections import OrderedDict
-import logging
-import os
-import sys
-import functools
-import numpy
-from silx.io import dictdump
-from silx.utils import deprecation
-from silx.utils.weakref import WeakMethodProxy
-from silx.utils.proxy import docstring
-from .. import icons, qt
-from silx.math.combo import min_max
-import weakref
-from silx.gui.widgets.TableWidget import TableWidget
-from . import items
-from .items.roi import _RegionOfInterestBase
-
-
-_logger = logging.getLogger(__name__)
-
-
-class CurvesROIWidget(qt.QWidget):
- """
- Widget displaying a table of ROI information.
-
- Implements also the following behavior:
-
- * if the roiTable has no ROI when showing create the default ICR one
-
- :param parent: See :class:`QWidget`
- :param str name: The title of this widget
- """
-
- sigROIWidgetSignal = qt.Signal(object)
- """Signal of ROIs modifications.
-
- Modification information if given as a dict with an 'event' key
- providing the type of events.
-
- Type of events:
-
- - AddROI, DelROI, LoadROI and ResetROI with keys: 'roilist', 'roidict'
- - selectionChanged with keys: 'row', 'col' 'roi', 'key', 'colheader',
- 'rowheader'
- """
-
- sigROISignal = qt.Signal(object)
-
- def __init__(self, parent=None, name=None, plot=None):
- super(CurvesROIWidget, self).__init__(parent)
- if name is not None:
- self.setWindowTitle(name)
- self.__lastSigROISignal = None
- """Store the last value emitted for the sigRoiSignal. In the case the
- active curve change we need to add this extra step in order to make
- sure we won't send twice the sigROISignal.
- This come from the fact sigROISignal is connected to the
- activeROIChanged signal which is emitted when raw and net counts
- values are changing but are not embed in the sigROISignal.
- """
- assert plot is not None
- self._plotRef = weakref.ref(plot)
- self._showAllMarkers = False
- self.currentROI = None
-
- layout = qt.QVBoxLayout(self)
- layout.setContentsMargins(0, 0, 0, 0)
- layout.setSpacing(0)
-
- self.headerLabel = qt.QLabel(self)
- self.headerLabel.setAlignment(qt.Qt.AlignHCenter)
- self.setHeader()
- layout.addWidget(self.headerLabel)
-
- widgetAllCheckbox = qt.QWidget(parent=self)
- self._showAllCheckBox = qt.QCheckBox("show all ROI",
- parent=widgetAllCheckbox)
- widgetAllCheckbox.setLayout(qt.QHBoxLayout())
- spacer = qt.QWidget(parent=widgetAllCheckbox)
- spacer.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Fixed)
- widgetAllCheckbox.layout().addWidget(spacer)
- widgetAllCheckbox.layout().addWidget(self._showAllCheckBox)
- layout.addWidget(widgetAllCheckbox)
-
- self.roiTable = ROITable(self, plot=plot)
- rheight = self.roiTable.horizontalHeader().sizeHint().height()
- self.roiTable.setMinimumHeight(4 * rheight)
- layout.addWidget(self.roiTable)
- self._roiFileDir = qt.QDir.home().absolutePath()
- self._showAllCheckBox.toggled.connect(self.roiTable.showAllMarkers)
-
- hbox = qt.QWidget(self)
- hboxlayout = qt.QHBoxLayout(hbox)
- hboxlayout.setContentsMargins(0, 0, 0, 0)
- hboxlayout.setSpacing(0)
-
- hboxlayout.addStretch(0)
-
- self.addButton = qt.QPushButton(hbox)
- self.addButton.setText("Add ROI")
- self.addButton.setToolTip('Create a new ROI')
- self.delButton = qt.QPushButton(hbox)
- self.delButton.setText("Delete ROI")
- self.addButton.setToolTip('Remove the selected ROI')
- self.resetButton = qt.QPushButton(hbox)
- self.resetButton.setText("Reset")
- self.addButton.setToolTip('Clear all created ROIs. We only let the '
- 'default ROI')
-
- hboxlayout.addWidget(self.addButton)
- hboxlayout.addWidget(self.delButton)
- hboxlayout.addWidget(self.resetButton)
-
- hboxlayout.addStretch(0)
-
- self.loadButton = qt.QPushButton(hbox)
- self.loadButton.setText("Load")
- self.loadButton.setToolTip('Load ROIs from a .ini file')
- self.saveButton = qt.QPushButton(hbox)
- self.saveButton.setText("Save")
- self.loadButton.setToolTip('Save ROIs to a .ini file')
- hboxlayout.addWidget(self.loadButton)
- hboxlayout.addWidget(self.saveButton)
- layout.setStretchFactor(self.headerLabel, 0)
- layout.setStretchFactor(self.roiTable, 1)
- layout.setStretchFactor(hbox, 0)
-
- layout.addWidget(hbox)
-
- # Signal / Slot connections
- self.addButton.clicked.connect(self._add)
- self.delButton.clicked.connect(self._del)
- self.resetButton.clicked.connect(self._reset)
-
- self.loadButton.clicked.connect(self._load)
- self.saveButton.clicked.connect(self._save)
-
- self.roiTable.activeROIChanged.connect(self._emitCurrentROISignal)
-
- self._isConnected = False # True if connected to plot signals
- self._isInit = False
-
- # expose API
- self.getROIListAndDict = self.roiTable.getROIListAndDict
-
- def getPlotWidget(self):
- """Returns the associated PlotWidget or None
-
- :rtype: Union[~silx.gui.plot.PlotWidget,None]
- """
- return None if self._plotRef is None else self._plotRef()
-
- def showEvent(self, event):
- self._visibilityChangedHandler(visible=True)
- qt.QWidget.showEvent(self, event)
-
- @property
- def roiFileDir(self):
- """The directory from which to load/save ROI from/to files."""
- if not os.path.isdir(self._roiFileDir):
- self._roiFileDir = qt.QDir.home().absolutePath()
- return self._roiFileDir
-
- @roiFileDir.setter
- def roiFileDir(self, roiFileDir):
- self._roiFileDir = str(roiFileDir)
-
- def setRois(self, rois, order=None):
- return self.roiTable.setRois(rois, order)
-
- def getRois(self, order=None):
- return self.roiTable.getRois(order)
-
- def setMiddleROIMarkerFlag(self, flag=True):
- return self.roiTable.setMiddleROIMarkerFlag(flag)
-
- def _add(self):
- """Add button clicked handler"""
- def getNextRoiName():
- rois = self.roiTable.getRois(order=None)
- roisNames = []
- [roisNames.append(roiName) for roiName in rois]
- nrois = len(rois)
- if nrois == 0:
- return "ICR"
- else:
- i = 1
- newroi = "newroi %d" % i
- while newroi in roisNames:
- i += 1
- newroi = "newroi %d" % i
- return newroi
- roi = ROI(name=getNextRoiName())
-
- if roi.getName() == "ICR":
- roi.setType("Default")
- else:
- roi.setType(self.getPlotWidget().getXAxis().getLabel())
-
- xmin, xmax = self.getPlotWidget().getXAxis().getLimits()
- fromdata = xmin + 0.25 * (xmax - xmin)
- todata = xmin + 0.75 * (xmax - xmin)
- if roi.isICR():
- fromdata, dummy0, todata, dummy1 = self._getAllLimits()
- roi.setFrom(fromdata)
- roi.setTo(todata)
- self.roiTable.addRoi(roi)
-
- # back compatibility pymca roi signals
- ddict = {}
- ddict['event'] = "AddROI"
- ddict['roilist'] = self.roiTable.roidict.values()
- ddict['roidict'] = self.roiTable.roidict
- self.sigROIWidgetSignal.emit(ddict)
- # end back compatibility pymca roi signals
-
- def _del(self):
- """Delete button clicked handler"""
- self.roiTable.deleteActiveRoi()
-
- # back compatibility pymca roi signals
- ddict = {}
- ddict['event'] = "DelROI"
- ddict['roilist'] = self.roiTable.roidict.values()
- ddict['roidict'] = self.roiTable.roidict
- self.sigROIWidgetSignal.emit(ddict)
- # end back compatibility pymca roi signals
-
- def _reset(self):
- """Reset button clicked handler"""
- self.roiTable.clear()
- old = self.blockSignals(True) # avoid several sigROISignal emission
- self._add()
- self.blockSignals(old)
-
- # back compatibility pymca roi signals
- ddict = {}
- ddict['event'] = "ResetROI"
- ddict['roilist'] = self.roiTable.roidict.values()
- ddict['roidict'] = self.roiTable.roidict
- self.sigROIWidgetSignal.emit(ddict)
- # end back compatibility pymca roi signals
-
- def _load(self):
- """Load button clicked handler"""
- dialog = qt.QFileDialog(self)
- dialog.setNameFilters(
- ['INI File *.ini', 'JSON File *.json', 'All *.*'])
- dialog.setFileMode(qt.QFileDialog.ExistingFile)
- dialog.setDirectory(self.roiFileDir)
- if not dialog.exec_():
- dialog.close()
- return
-
- # pyflakes bug http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=666494
- outputFile = dialog.selectedFiles()[0]
- dialog.close()
-
- self.roiFileDir = os.path.dirname(outputFile)
- self.roiTable.load(outputFile)
-
- # back compatibility pymca roi signals
- ddict = {}
- ddict['event'] = "LoadROI"
- ddict['roilist'] = self.roiTable.roidict.values()
- ddict['roidict'] = self.roiTable.roidict
- self.sigROIWidgetSignal.emit(ddict)
- # end back compatibility pymca roi signals
-
- def load(self, filename):
- """Load ROI widget information from a file storing a dict of ROI.
-
- :param str filename: The file from which to load ROI
- """
- self.roiTable.load(filename)
-
- def _save(self):
- """Save button clicked handler"""
- dialog = qt.QFileDialog(self)
- dialog.setNameFilters(['INI File *.ini', 'JSON File *.json'])
- dialog.setFileMode(qt.QFileDialog.AnyFile)
- dialog.setAcceptMode(qt.QFileDialog.AcceptSave)
- dialog.setDirectory(self.roiFileDir)
- if not dialog.exec_():
- dialog.close()
- return
-
- outputFile = dialog.selectedFiles()[0]
- extension = '.' + dialog.selectedNameFilter().split('.')[-1]
- dialog.close()
-
- if not outputFile.endswith(extension):
- outputFile += extension
-
- if os.path.exists(outputFile):
- try:
- os.remove(outputFile)
- except IOError:
- msg = qt.QMessageBox(self)
- msg.setIcon(qt.QMessageBox.Critical)
- msg.setText("Input Output Error: %s" % (sys.exc_info()[1]))
- msg.exec_()
- return
- self.roiFileDir = os.path.dirname(outputFile)
- self.save(outputFile)
-
- def save(self, filename):
- """Save current ROIs of the widget as a dict of ROI to a file.
-
- :param str filename: The file to which to save the ROIs
- """
- self.roiTable.save(filename)
-
- def setHeader(self, text='ROIs'):
- """Set the header text of this widget"""
- self.headerLabel.setText("<b>%s<\b>" % text)
-
- @deprecation.deprecated(replacement="calculateRois",
- reason="CamelCase convention",
- since_version="0.7")
- def calculateROIs(self, *args, **kw):
- self.calculateRois(*args, **kw)
-
- def calculateRois(self, roiList=None, roiDict=None):
- """Compute ROI information"""
- return self.roiTable.calculateRois()
-
- def showAllMarkers(self, _show=True):
- self.roiTable.showAllMarkers(_show)
-
- def _getAllLimits(self):
- """Retrieve the limits based on the curves."""
- plot = self.getPlotWidget()
- curves = () if plot is None else plot.getAllCurves()
- if not curves:
- return 1.0, 1.0, 100., 100.
-
- xmin, ymin = None, None
- xmax, ymax = None, None
-
- for curve in curves:
- x = curve.getXData(copy=False)
- y = curve.getYData(copy=False)
- if xmin is None:
- xmin = x.min()
- else:
- xmin = min(xmin, x.min())
- if xmax is None:
- xmax = x.max()
- else:
- xmax = max(xmax, x.max())
- if ymin is None:
- ymin = y.min()
- else:
- ymin = min(ymin, y.min())
- if ymax is None:
- ymax = y.max()
- else:
- ymax = max(ymax, y.max())
-
- return xmin, ymin, xmax, ymax
-
- def showEvent(self, event):
- self._visibilityChangedHandler(visible=True)
- qt.QWidget.showEvent(self, event)
-
- def hideEvent(self, event):
- self._visibilityChangedHandler(visible=False)
- qt.QWidget.hideEvent(self, event)
-
- def _visibilityChangedHandler(self, visible):
- """Handle widget's visibility updates.
-
- It is connected to plot signals only when visible.
- """
- if visible:
- # if no ROI existing yet, add the default one
- if self.roiTable.rowCount() == 0:
- old = self.blockSignals(True) # avoid several sigROISignal emission
- self._add()
- self.blockSignals(old)
- self.calculateRois()
-
- def fillFromROIDict(self, *args, **kwargs):
- self.roiTable.fillFromROIDict(*args, **kwargs)
-
- def _emitCurrentROISignal(self):
- ddict = {}
- ddict['event'] = "currentROISignal"
- if self.roiTable.activeRoi is not None:
- ddict['ROI'] = self.roiTable.activeRoi.toDict()
- ddict['current'] = self.roiTable.activeRoi.getName()
- else:
- ddict['current'] = None
-
- if self.__lastSigROISignal != ddict:
- self.__lastSigROISignal = ddict
- self.sigROISignal.emit(ddict)
-
- @property
- def currentRoi(self):
- return self.roiTable.activeRoi
-
-
-class _FloatItem(qt.QTableWidgetItem):
- """
- Simple QTableWidgetItem overloading the < operator to deal with ordering
- """
- def __init__(self):
- qt.QTableWidgetItem.__init__(self, type=qt.QTableWidgetItem.Type)
-
- def __lt__(self, other):
- if self.text() in ('', ROITable.INFO_NOT_FOUND):
- return False
- if other.text() in ('', ROITable.INFO_NOT_FOUND):
- return True
- return float(self.text()) < float(other.text())
-
-
-class ROITable(TableWidget):
- """Table widget displaying ROI information.
-
- See :class:`QTableWidget` for constructor arguments.
-
- Behavior: listen at the active curve changed only when the widget is
- visible. Otherwise won't compute the row and net counts...
- """
-
- activeROIChanged = qt.Signal()
- """Signal emitted when the active roi changed or when the value of the
- active roi are changing"""
-
- COLUMNS_INDEX = OrderedDict([
- ('ID', 0),
- ('ROI', 1),
- ('Type', 2),
- ('From', 3),
- ('To', 4),
- ('Raw Counts', 5),
- ('Net Counts', 6),
- ('Raw Area', 7),
- ('Net Area', 8),
- ])
-
- COLUMNS = list(COLUMNS_INDEX.keys())
-
- INFO_NOT_FOUND = '????????'
-
- def __init__(self, parent=None, plot=None, rois=None):
- super(ROITable, self).__init__(parent)
- self._showAllMarkers = False
- self._userIsEditingRoi = False
- """bool used to avoid conflict when editing the ROI object"""
- self._isConnected = False
- self._roiToItems = {}
- self._roiDict = {}
- """dict of ROI object. Key is ROi id, value is the ROI object"""
- self._markersHandler = _RoiMarkerManager()
-
- """
- Associate for each marker legend used when the `_showAllMarkers` option
- is active a roi.
- """
- self.setColumnCount(len(self.COLUMNS))
- self.setPlot(plot)
- self.__setTooltip()
- self.setSortingEnabled(True)
- self.itemChanged.connect(self._itemChanged)
-
- @property
- def roidict(self):
- return self._getRoiDict()
-
- @property
- def activeRoi(self):
- return self._markersHandler._activeRoi
-
- def _getRoiDict(self):
- ddict = {}
- for id in self._roiDict:
- ddict[self._roiDict[id].getName()] = self._roiDict[id]
- return ddict
-
- def clear(self):
- """
- .. note:: clear the interface only. keep the roidict...
- """
- self._markersHandler.clear()
- self._roiToItems = {}
- self._roiDict = {}
-
- qt.QTableWidget.clear(self)
- self.setRowCount(0)
- self.setHorizontalHeaderLabels(self.COLUMNS)
- header = self.horizontalHeader()
- if hasattr(header, 'setSectionResizeMode'): # Qt5
- header.setSectionResizeMode(qt.QHeaderView.ResizeToContents)
- else: # Qt4
- header.setResizeMode(qt.QHeaderView.ResizeToContents)
- self.sortByColumn(0, qt.Qt.AscendingOrder)
- self.hideColumn(self.COLUMNS_INDEX['ID'])
-
- def setPlot(self, plot):
- self.clear()
- self.plot = plot
-
- def __setTooltip(self):
- self.horizontalHeaderItem(self.COLUMNS_INDEX['ROI']).setToolTip(
- 'Region of interest identifier')
- self.horizontalHeaderItem(self.COLUMNS_INDEX['Type']).setToolTip(
- 'Type of the ROI')
- self.horizontalHeaderItem(self.COLUMNS_INDEX['From']).setToolTip(
- 'X-value of the min point')
- self.horizontalHeaderItem(self.COLUMNS_INDEX['To']).setToolTip(
- 'X-value of the max point')
- self.horizontalHeaderItem(self.COLUMNS_INDEX['Raw Counts']).setToolTip(
- 'Estimation of the integral between y=0 and the selected curve')
- self.horizontalHeaderItem(self.COLUMNS_INDEX['Net Counts']).setToolTip(
- 'Estimation of the integral between the segment [maxPt, minPt] '
- 'and the selected curve')
-
- def setRois(self, rois, order=None):
- """Set the ROIs by providing a dictionary of ROI information.
-
- The dictionary keys are the ROI names.
- Each value is a sub-dictionary of ROI info with the following fields:
-
- - ``"from"``: x coordinate of the left limit, as a float
- - ``"to"``: x coordinate of the right limit, as a float
- - ``"type"``: type of ROI, as a string (e.g "channels", "energy")
-
-
- :param roidict: Dictionary of ROIs
- :param str order: Field used for ordering the ROIs.
- One of "from", "to", "type".
- None (default) for no ordering, or same order as specified
- in parameter ``roidict`` if provided as an OrderedDict.
- """
- assert order in [None, "from", "to", "type"]
- self.clear()
-
- # backward compatibility since 0.10.0
- if isinstance(rois, dict):
- for roiName, roi in rois.items():
- if isinstance(roi, ROI):
- _roi = roi
- else:
- roi['name'] = roiName
- _roi = ROI._fromDict(roi)
- self.addRoi(_roi)
- else:
- for roi in rois:
- assert isinstance(roi, ROI)
- self.addRoi(roi)
- self._updateMarkers()
-
- def addRoi(self, roi):
- """
-
- :param :class:`ROI` roi: roi to add to the table
- """
- assert isinstance(roi, ROI)
- self._getItem(name='ID', row=None, roi=roi)
- self._roiDict[roi.getID()] = roi
- self._markersHandler.add(roi, _RoiMarkerHandler(roi, self.plot))
- self._updateRoiInfo(roi.getID())
- callback = functools.partial(WeakMethodProxy(self._updateRoiInfo),
- roi.getID())
- roi.sigChanged.connect(callback)
- # set it as the active one
- self.setActiveRoi(roi)
-
- def _getItem(self, name, row, roi):
- if row:
- item = self.item(row, self.COLUMNS_INDEX[name])
- else:
- item = None
- if item:
- return item
- else:
- if name == 'ID':
- assert roi
- if roi.getID() in self._roiToItems:
- return self._roiToItems[roi.getID()]
- else:
- # create a new row
- row = self.rowCount()
- self.setRowCount(self.rowCount() + 1)
- item = qt.QTableWidgetItem(str(roi.getID()),
- type=qt.QTableWidgetItem.Type)
- self._roiToItems[roi.getID()] = item
- elif name == 'ROI':
- item = qt.QTableWidgetItem(roi.getName() if roi else '',
- type=qt.QTableWidgetItem.Type)
- if roi.getName().upper() in ('ICR', 'DEFAULT'):
- item.setFlags(qt.Qt.ItemIsSelectable | qt.Qt.ItemIsEnabled)
- else:
- item.setFlags(qt.Qt.ItemIsSelectable |
- qt.Qt.ItemIsEnabled |
- qt.Qt.ItemIsEditable)
- elif name == 'Type':
- item = qt.QTableWidgetItem(type=qt.QTableWidgetItem.Type)
- item.setFlags((qt.Qt.ItemIsSelectable | qt.Qt.ItemIsEnabled))
- elif name in ('To', 'From'):
- item = _FloatItem()
- if roi.getName().upper() in ('ICR', 'DEFAULT'):
- item.setFlags(qt.Qt.ItemIsSelectable | qt.Qt.ItemIsEnabled)
- else:
- item.setFlags(qt.Qt.ItemIsSelectable |
- qt.Qt.ItemIsEnabled |
- qt.Qt.ItemIsEditable)
- elif name in ('Raw Counts', 'Net Counts', 'Raw Area', 'Net Area'):
- item = _FloatItem()
- item.setFlags((qt.Qt.ItemIsSelectable | qt.Qt.ItemIsEnabled))
- else:
- raise ValueError('item type not recognized')
-
- self.setItem(row, self.COLUMNS_INDEX[name], item)
- return item
-
- def _itemChanged(self, item):
- def getRoi():
- IDItem = self.item(item.row(), self.COLUMNS_INDEX['ID'])
- assert IDItem
- id = int(IDItem.text())
- assert id in self._roiDict
- roi = self._roiDict[id]
- return roi
-
- def signalChanged(roi):
- if self.activeRoi and roi.getID() == self.activeRoi.getID():
- self.activeROIChanged.emit()
-
- self._userIsEditingRoi = True
- if item.column() in (self.COLUMNS_INDEX['To'], self.COLUMNS_INDEX['From']):
- roi = getRoi()
-
- if item.text() not in ('', self.INFO_NOT_FOUND):
- try:
- value = float(item.text())
- except ValueError:
- value = 0
- changed = False
- if item.column() == self.COLUMNS_INDEX['To']:
- if value != roi.getTo():
- roi.setTo(value)
- changed = True
- else:
- assert(item.column() == self.COLUMNS_INDEX['From'])
- if value != roi.getFrom():
- roi.setFrom(value)
- changed = True
- if changed:
- self._updateMarker(roi.getName())
- signalChanged(roi)
-
- if item.column() is self.COLUMNS_INDEX['ROI']:
- roi = getRoi()
- if roi.getName() != item.text():
- roi.setName(item.text())
- self._markersHandler.getMarkerHandler(roi.getID()).updateTexts()
- signalChanged(roi)
-
- self._userIsEditingRoi = False
-
- def deleteActiveRoi(self):
- """
- remove the current active roi
- """
- activeItems = self.selectedItems()
- if len(activeItems) == 0:
- return
- old = self.blockSignals(True) # avoid several emission of sigROISignal
- roiToRm = set()
- for item in activeItems:
- row = item.row()
- itemID = self.item(row, self.COLUMNS_INDEX['ID'])
- roiToRm.add(self._roiDict[int(itemID.text())])
- [self.removeROI(roi) for roi in roiToRm]
- self.blockSignals(old)
- self.setActiveRoi(None)
-
- def removeROI(self, roi):
- """
- remove the requested roi
-
- :param str name: the name of the roi to remove from the table
- """
- if roi and roi.getID() in self._roiToItems:
- item = self._roiToItems[roi.getID()]
- self.removeRow(item.row())
- del self._roiToItems[roi.getID()]
-
- assert roi.getID() in self._roiDict
- del self._roiDict[roi.getID()]
- self._markersHandler.remove(roi)
-
- callback = functools.partial(WeakMethodProxy(self._updateRoiInfo),
- roi.getID())
- roi.sigChanged.connect(callback)
-
- def setActiveRoi(self, roi):
- """
- Define the given roi as the active one.
-
- .. warning:: this roi should already be registred / added to the table
-
- :param :class:`ROI` roi: the roi to defined as active
- """
- if roi is None:
- self.clearSelection()
- self._markersHandler.setActiveRoi(None)
- self.activeROIChanged.emit()
- else:
- assert isinstance(roi, ROI)
- if roi and roi.getID() in self._roiToItems.keys():
- # avoid several call back to setActiveROI
- old = self.blockSignals(True)
- self.selectRow(self._roiToItems[roi.getID()].row())
- self.blockSignals(old)
- self._markersHandler.setActiveRoi(roi)
- self.activeROIChanged.emit()
-
- def _updateRoiInfo(self, roiID):
- if self._userIsEditingRoi is True:
- return
- if roiID not in self._roiDict:
- return
- roi = self._roiDict[roiID]
- if roi.isICR():
- activeCurve = self.plot.getActiveCurve()
- if activeCurve:
- xData = activeCurve.getXData()
- if len(xData) > 0:
- min, max = min_max(xData)
- roi.blockSignals(True)
- roi.setFrom(min)
- roi.setTo(max)
- roi.blockSignals(False)
-
- itemID = self._getItem(name='ID', roi=roi, row=None)
- itemName = self._getItem(name='ROI', row=itemID.row(), roi=roi)
- itemName.setText(roi.getName())
-
- itemType = self._getItem(name='Type', row=itemID.row(), roi=roi)
- itemType.setText(roi.getType() or self.INFO_NOT_FOUND)
-
- itemFrom = self._getItem(name='From', row=itemID.row(), roi=roi)
- fromdata = str(roi.getFrom()) if roi.getFrom() is not None else self.INFO_NOT_FOUND
- itemFrom.setText(fromdata)
-
- itemTo = self._getItem(name='To', row=itemID.row(), roi=roi)
- todata = str(roi.getTo()) if roi.getTo() is not None else self.INFO_NOT_FOUND
- itemTo.setText(todata)
-
- rawCounts, netCounts = roi.computeRawAndNetCounts(
- curve=self.plot.getActiveCurve(just_legend=False))
- itemRawCounts = self._getItem(name='Raw Counts', row=itemID.row(),
- roi=roi)
- rawCounts = str(rawCounts) if rawCounts is not None else self.INFO_NOT_FOUND
- itemRawCounts.setText(rawCounts)
-
- itemNetCounts = self._getItem(name='Net Counts', row=itemID.row(),
- roi=roi)
- netCounts = str(netCounts) if netCounts is not None else self.INFO_NOT_FOUND
- itemNetCounts.setText(netCounts)
-
- rawArea, netArea = roi.computeRawAndNetArea(
- curve=self.plot.getActiveCurve(just_legend=False))
- itemRawArea = self._getItem(name='Raw Area', row=itemID.row(),
- roi=roi)
- rawArea = str(rawArea) if rawArea is not None else self.INFO_NOT_FOUND
- itemRawArea.setText(rawArea)
-
- itemNetArea = self._getItem(name='Net Area', row=itemID.row(),
- roi=roi)
- netArea = str(netArea) if netArea is not None else self.INFO_NOT_FOUND
- itemNetArea.setText(netArea)
-
- if self.activeRoi and roi.getID() == self.activeRoi.getID():
- self.activeROIChanged.emit()
-
- def currentChanged(self, current, previous):
- if previous and current.row() != previous.row() and current.row() >= 0:
- roiItem = self.item(current.row(),
- self.COLUMNS_INDEX['ID'])
-
- assert roiItem
- self.setActiveRoi(self._roiDict[int(roiItem.text())])
- self._markersHandler.updateAllMarkers()
- qt.QTableWidget.currentChanged(self, current, previous)
-
- @deprecation.deprecated(reason="Removed",
- replacement="roidict and roidict.values()",
- since_version="0.10.0")
- def getROIListAndDict(self):
- """
-
- :return: the list of roi objects and the dictionary of roi name to roi
- object.
- """
- roidict = self._roiDict
- return list(roidict.values()), roidict
-
- def calculateRois(self, roiList=None, roiDict=None):
- """
- Update values of all registred rois (raw and net counts in particular)
-
- :param roiList: deprecated parameter
- :param roiDict: deprecated parameter
- """
- if roiDict:
- deprecation.deprecated_warning(name='roiDict', type_='Parameter',
- reason='Unused parameter',
- since_version="0.10.0")
- if roiList:
- deprecation.deprecated_warning(name='roiList', type_='Parameter',
- reason='Unused parameter',
- since_version="0.10.0")
-
- for roiID in self._roiDict:
- self._updateRoiInfo(roiID)
-
- def _updateMarker(self, roiID):
- """Make sure the marker of the given roi name is updated"""
- if self._showAllMarkers or (self.activeRoi
- and self.activeRoi.getName() == roiID):
- self._updateMarkers()
-
- def _updateMarkers(self):
- if self._showAllMarkers is True:
- self._markersHandler.updateMarkers()
- else:
- if not self.activeRoi or not self.plot:
- return
- assert isinstance(self.activeRoi, ROI)
- markerHandler = self._markersHandler.getMarkerHandler(self.activeRoi.getID())
- if markerHandler is not None:
- markerHandler.updateMarkers()
-
- def getRois(self, order):
- """
- Return the currently defined ROIs, as an ordered dict.
-
- The dictionary keys are the ROI names.
- Each value is a :class:`ROI` object..
-
- :param order: Field used for ordering the ROIs.
- One of "from", "to", "type", "netcounts", "rawcounts".
- None (default) to get the same order as displayed in the widget.
- :return: Ordered dictionary of ROI information
- """
-
- if order is None or order.lower() == "none":
- ordered_roilist = list(self._roiDict.values())
- res = OrderedDict([(roi.getName(), self._roiDict[roi.getID()]) for roi in ordered_roilist])
- else:
- assert order in ["from", "to", "type", "netcounts", "rawcounts"]
- ordered_roilist = sorted(self._roiDict.keys(),
- key=lambda roi_id: self._roiDict[roi_id].get(order))
- res = OrderedDict([(roi.getName(), self._roiDict[id]) for id in ordered_roilist])
-
- return res
-
- def save(self, filename):
- """
- Save current ROIs of the widget as a dict of ROI to a file.
-
- :param str filename: The file to which to save the ROIs
- """
- roilist = []
- roidict = {}
- for roiID, roi in self._roiDict.items():
- roilist.append(roi.toDict())
- roidict[roi.getName()] = roi.toDict()
- datadict = {'ROI': {'roilist': roilist, 'roidict': roidict}}
- dictdump.dump(datadict, filename)
-
- def load(self, filename):
- """
- Load ROI widget information from a file storing a dict of ROI.
-
- :param str filename: The file from which to load ROI
- """
- roisDict = dictdump.load(filename)
- rois = []
-
- # Remove rawcounts and netcounts from ROIs
- for roiDict in roisDict['ROI']['roidict'].values():
- roiDict.pop('rawcounts', None)
- roiDict.pop('netcounts', None)
- rois.append(ROI._fromDict(roiDict))
-
- self.setRois(rois)
-
- def showAllMarkers(self, _show=True):
- """
-
- :param bool _show: if true show all the markers of all the ROIs
- boundaries otherwise will only show the one of
- the active ROI.
- """
- self._markersHandler.setShowAllMarkers(_show)
-
- def setMiddleROIMarkerFlag(self, flag=True):
- """
- Activate or deactivate middle marker.
-
- This allows shifting both min and max limits at once, by dragging
- a marker located in the middle.
-
- :param bool flag: True to activate middle ROI marker
- """
- self._markersHandler._middleROIMarkerFlag = flag
-
- def _handleROIMarkerEvent(self, ddict):
- """Handle plot signals related to marker events."""
- if ddict['event'] == 'markerMoved':
- label = ddict['label']
- roiID = self._markersHandler.getRoiID(markerID=label)
- if roiID is not None:
- # avoid several emission of sigROISignal
- old = self.blockSignals(True)
- self._markersHandler.changePosition(markerID=label,
- x=ddict['x'])
- self.blockSignals(old)
- self._updateRoiInfo(roiID)
-
- def showEvent(self, event):
- self._visibilityChangedHandler(visible=True)
- qt.QWidget.showEvent(self, event)
-
- def hideEvent(self, event):
- self._visibilityChangedHandler(visible=False)
- qt.QWidget.hideEvent(self, event)
-
- def _visibilityChangedHandler(self, visible):
- """Handle widget's visibility updates.
-
- It is connected to plot signals only when visible.
- """
- if visible:
- assert self.plot
- if self._isConnected is False:
- self.plot.sigPlotSignal.connect(self._handleROIMarkerEvent)
- self.plot.sigActiveCurveChanged.connect(self._activeCurveChanged)
- self._isConnected = True
- self.calculateRois()
- else:
- if self._isConnected:
- self.plot.sigPlotSignal.disconnect(self._handleROIMarkerEvent)
- self.plot.sigActiveCurveChanged.disconnect(self._activeCurveChanged)
- self._isConnected = False
-
- def _activeCurveChanged(self, curve):
- self.calculateRois()
-
- def setCountsVisible(self, visible):
- """
- Display the columns relative to areas or not
-
- :param bool visible: True if the columns 'Raw Area' and 'Net Area'
- should be visible.
- """
- if visible is True:
- self.showColumn(self.COLUMNS_INDEX['Raw Counts'])
- self.showColumn(self.COLUMNS_INDEX['Net Counts'])
- else:
- self.hideColumn(self.COLUMNS_INDEX['Raw Counts'])
- self.hideColumn(self.COLUMNS_INDEX['Net Counts'])
-
- def setAreaVisible(self, visible):
- """
- Display the columns relative to areas or not
-
- :param bool visible: True if the columns 'Raw Area' and 'Net Area'
- should be visible.
- """
- if visible is True:
- self.showColumn(self.COLUMNS_INDEX['Raw Area'])
- self.showColumn(self.COLUMNS_INDEX['Net Area'])
- else:
- self.hideColumn(self.COLUMNS_INDEX['Raw Area'])
- self.hideColumn(self.COLUMNS_INDEX['Net Area'])
-
- def fillFromROIDict(self, roilist=(), roidict=None, currentroi=None):
- """
- This function API is kept for compatibility.
- But `setRois` should be preferred.
-
- Set the ROIs by providing a list of ROI names and a dictionary
- of ROI information for each ROI.
- The ROI names must match an existing dictionary key.
- The name list is used to provide an order for the ROIs.
- The dictionary's values are sub-dictionaries containing 3
- mandatory fields:
-
- - ``"from"``: x coordinate of the left limit, as a float
- - ``"to"``: x coordinate of the right limit, as a float
- - ``"type"``: type of ROI, as a string (e.g "channels", "energy")
-
- :param roilist: List of ROI names (keys of roidict)
- :type roilist: List
- :param dict roidict: Dict of ROI information
- :param currentroi: Name of the selected ROI or None (no selection)
- """
- if roidict is not None:
- self.setRois(roidict)
- else:
- self.setRois(roilist)
- if currentroi:
- self.setActiveRoi(currentroi)
-
-
-_indexNextROI = 0
-
-
-class ROI(_RegionOfInterestBase):
- """The Region Of Interest is defined by:
-
- - A name
- - A type. The type is the label of the x axis. This can be used to apply or
- not some ROI to a curve and do some post processing.
- - The x coordinate of the left limit (fromdata)
- - The x coordinate of the right limit (todata)
-
- :param str: name of the ROI
- :param fromdata: left limit of the roi
- :param todata: right limit of the roi
- :param type: type of the ROI
- """
-
- sigChanged = qt.Signal()
- """Signal emitted when the ROI is edited"""
-
- def __init__(self, name, fromdata=None, todata=None, type_=None):
- _RegionOfInterestBase.__init__(self)
- self.setName(name)
- global _indexNextROI
- self._id = _indexNextROI
- _indexNextROI += 1
-
- self._fromdata = fromdata
- self._todata = todata
- self._type = type_ or 'Default'
-
- self.sigItemChanged.connect(self.__itemChanged)
-
- def __itemChanged(self, event):
- """Handle name change"""
- if event == items.ItemChangedType.NAME:
- self.sigChanged.emit()
-
- def getID(self):
- """
-
- :return int: the unique ID of the ROI
- """
- return self._id
-
- def setType(self, type_):
- """
-
- :param str type_:
- """
- if self._type != type_:
- self._type = type_
- self.sigChanged.emit()
-
- def getType(self):
- """
-
- :return str: the type of the ROI.
- """
- return self._type
-
- def setFrom(self, frm):
- """
-
- :param frm: set x coordinate of the left limit
- """
- if self._fromdata != frm:
- self._fromdata = frm
- self.sigChanged.emit()
-
- def getFrom(self):
- """
-
- :return: x coordinate of the left limit
- """
- return self._fromdata
-
- def setTo(self, to):
- """
-
- :param to: x coordinate of the right limit
- """
- if self._todata != to:
- self._todata = to
- self.sigChanged.emit()
-
- def getTo(self):
- """
-
- :return: x coordinate of the right limit
- """
- return self._todata
-
- def getMiddle(self):
- """
-
- :return: middle position between 'from' and 'to' values
- """
- return 0.5 * (self.getFrom() + self.getTo())
-
- def toDict(self):
- """
-
- :return: dict containing the roi parameters
- """
- ddict = {
- 'type': self._type,
- 'name': self.getName(),
- 'from': self._fromdata,
- 'to': self._todata,
- }
- if hasattr(self, '_extraInfo'):
- ddict.update(self._extraInfo)
- return ddict
-
- @staticmethod
- def _fromDict(dic):
- assert 'name' in dic
- roi = ROI(name=dic['name'])
- roi._extraInfo = {}
- for key in dic:
- if key == 'from':
- roi.setFrom(dic['from'])
- elif key == 'to':
- roi.setTo(dic['to'])
- elif key == 'type':
- roi.setType(dic['type'])
- else:
- roi._extraInfo[key] = dic[key]
-
- return roi
-
- def isICR(self):
- """
-
- :return: True if the ROI is the `ICR`
- """
- return self.getName() == 'ICR'
-
- def computeRawAndNetCounts(self, curve):
- """Compute the Raw and net counts in the ROI for the given curve.
-
- - Raw count: Points values sum of the curve in the defined Region Of
- Interest.
-
- .. image:: img/rawCounts.png
-
- - Net count: Raw counts minus background
-
- .. image:: img/netCounts.png
-
- :param CurveItem curve:
- :return tuple: rawCount, netCount
- """
- assert isinstance(curve, items.Curve) or curve is None
-
- if curve is None:
- return None, None
-
- x = curve.getXData(copy=False)
- y = curve.getYData(copy=False)
-
- idx = numpy.nonzero((self._fromdata <= x) &
- (x <= self._todata))[0]
- if len(idx):
- xw = x[idx]
- yw = y[idx]
- rawCounts = yw.sum(dtype=numpy.float64)
- deltaX = xw[-1] - xw[0]
- deltaY = yw[-1] - yw[0]
- if deltaX > 0.0:
- slope = (deltaY / deltaX)
- background = yw[0] + slope * (xw - xw[0])
- netCounts = (rawCounts -
- background.sum(dtype=numpy.float64))
- else:
- netCounts = 0.0
- else:
- rawCounts = 0.0
- netCounts = 0.0
- return rawCounts, netCounts
-
- def computeRawAndNetArea(self, curve):
- """Compute the Raw and net counts in the ROI for the given curve.
-
- - Raw area: integral of the curve between the min ROI point and the
- max ROI point to the y = 0 line.
-
- .. image:: img/rawArea.png
-
- - Net area: Raw counts minus background
-
- .. image:: img/netArea.png
-
- :param CurveItem curve:
- :return tuple: rawArea, netArea
- """
- assert isinstance(curve, items.Curve) or curve is None
-
- if curve is None:
- return None, None
-
- x = curve.getXData(copy=False)
- y = curve.getYData(copy=False)
-
- y = y[(x >= self._fromdata) & (x <= self._todata)]
- x = x[(x >= self._fromdata) & (x <= self._todata)]
-
- if x.size == 0:
- return 0.0, 0.0
-
- rawArea = numpy.trapz(y, x=x)
- # to speed up and avoid an intersection calculation we are taking the
- # closest index to the ROI
- closestXLeftIndex = (numpy.abs(x - self.getFrom())).argmin()
- closestXRightIndex = (numpy.abs(x - self.getTo())).argmin()
- yBackground = y[closestXLeftIndex], y[closestXRightIndex]
- background = numpy.trapz(yBackground, x=x)
- netArea = rawArea - background
- return rawArea, netArea
-
- @docstring(_RegionOfInterestBase)
- def contains(self, position):
- return self._fromdata <= position[0] <= self._todata
-
-
-class _RoiMarkerManager(object):
- """
- Deal with all the ROI markers
- """
- def __init__(self):
- self._roiMarkerHandlers = {}
- self._middleROIMarkerFlag = False
- self._showAllMarkers = False
- self._activeRoi = None
-
- def setActiveRoi(self, roi):
- self._activeRoi = roi
- self.updateAllMarkers()
-
- def setShowAllMarkers(self, show):
- if show != self._showAllMarkers:
- self._showAllMarkers = show
- self.updateAllMarkers()
-
- def add(self, roi, markersHandler):
- assert isinstance(roi, ROI)
- assert isinstance(markersHandler, _RoiMarkerHandler)
- if roi.getID() in self._roiMarkerHandlers:
- raise ValueError('roi with the same ID already existing')
- else:
- self._roiMarkerHandlers[roi.getID()] = markersHandler
-
- def getMarkerHandler(self, roiID):
- if roiID in self._roiMarkerHandlers:
- return self._roiMarkerHandlers[roiID]
- else:
- return None
-
- def clear(self):
- roisHandler = list(self._roiMarkerHandlers.values())
- for roiHandler in roisHandler:
- self.remove(roiHandler.roi)
-
- def remove(self, roi):
- if roi is None:
- return
- assert isinstance(roi, ROI)
- if roi.getID() in self._roiMarkerHandlers:
- self._roiMarkerHandlers[roi.getID()].clear()
- del self._roiMarkerHandlers[roi.getID()]
-
- def hasMarker(self, markerID):
- assert type(markerID) is str
- return self.getMarker(markerID) is not None
-
- def changePosition(self, markerID, x):
- markerHandler = self.getMarker(markerID)
- if markerHandler is None:
- raise ValueError('Marker %s not register' % markerID)
- markerHandler.changePosition(markerID=markerID, x=x)
-
- def updateMarker(self, markerID):
- markerHandler = self.getMarker(markerID)
- if markerHandler is None:
- raise ValueError('Marker %s not register' % markerID)
- roiID = self.getRoiID(markerID)
- visible = (self._activeRoi and self._activeRoi.getID() == roiID) or self._showAllMarkers is True
- markerHandler.setVisible(visible)
- markerHandler.updateAllMarkers()
-
- def updateRoiMarkers(self, roiID):
- if roiID in self._roiMarkerHandlers:
- visible = ((self._activeRoi and self._activeRoi.getID() == roiID)
- or self._showAllMarkers is True)
- _roi = self._roiMarkerHandlers[roiID]._roi()
- if _roi and not _roi.isICR():
- self._roiMarkerHandlers[roiID].showMiddleMarker(self._middleROIMarkerFlag)
- self._roiMarkerHandlers[roiID].setVisible(visible)
- self._roiMarkerHandlers[roiID].updateMarkers()
-
- def getMarker(self, markerID):
- assert type(markerID) is str
- for marker in list(self._roiMarkerHandlers.values()):
- if marker.hasMarker(markerID):
- return marker
-
- def updateMarkers(self):
- for markerHandler in list(self._roiMarkerHandlers.values()):
- markerHandler.updateMarkers()
-
- def getRoiID(self, markerID):
- for roiID, markerHandler in self._roiMarkerHandlers.items():
- if markerHandler.hasMarker(markerID):
- return roiID
- return None
-
- def setShowMiddleMarkers(self, show):
- self._middleROIMarkerFlag = show
- self._roiMarkerHandlers.updateAllMarkers()
-
- def updateAllMarkers(self):
- for roiID in self._roiMarkerHandlers:
- self.updateRoiMarkers(roiID)
-
- def getVisibleRois(self):
- res = {}
- for roiID, roiHandler in self._roiMarkerHandlers.items():
- markers = (roiHandler.getMarker('min'), roiHandler.getMarker('max'),
- roiHandler.getMarker('middle'))
- for marker in markers:
- if marker.isVisible():
- if roiID not in res:
- res[roiID] = []
- res[roiID].append(marker)
- return res
-
-
-class _RoiMarkerHandler(object):
- """Used to deal with ROI markers used in ROITable"""
- def __init__(self, roi, plot):
- assert roi and isinstance(roi, ROI)
- assert plot
-
- self._roi = weakref.ref(roi)
- self._plot = weakref.ref(plot)
- self._draggable = False if roi.isICR() else True
- self._color = 'black' if roi.isICR() else 'blue'
- self._displayMidMarker = False
- self._visible = True
-
- @property
- def draggable(self):
- return self._draggable
-
- @property
- def plot(self):
- return self._plot()
-
- def clear(self):
- if self.plot and self.roi:
- self.plot.removeMarker(self._markerID('min'))
- self.plot.removeMarker(self._markerID('max'))
- self.plot.removeMarker(self._markerID('middle'))
-
- @property
- def roi(self):
- return self._roi()
-
- def setVisible(self, visible):
- if visible != self._visible:
- self._visible = visible
- self.updateMarkers()
-
- def showMiddleMarker(self, visible):
- if self.draggable is False and visible is True:
- _logger.warning("ROI is not draggable. Won't display middle marker")
- return
- self._displayMidMarker = visible
- self.getMarker('middle').setVisible(self._displayMidMarker)
-
- def updateMarkers(self):
- if self.roi is None:
- return
- self._updateMinMarkerPos()
- self._updateMaxMarkerPos()
- self._updateMiddleMarkerPos()
-
- def _updateMinMarkerPos(self):
- self.getMarker('min').setPosition(x=self.roi.getFrom(), y=None)
- self.getMarker('min').setVisible(self._visible)
-
- def _updateMaxMarkerPos(self):
- self.getMarker('max').setPosition(x=self.roi.getTo(), y=None)
- self.getMarker('max').setVisible(self._visible)
-
- def _updateMiddleMarkerPos(self):
- self.getMarker('middle').setPosition(x=self.roi.getMiddle(), y=None)
- self.getMarker('middle').setVisible(self._displayMidMarker and self._visible)
-
- def getMarker(self, markerType):
- if self.plot is None:
- return None
- assert markerType in ('min', 'max', 'middle')
- if self.plot._getMarker(self._markerID(markerType)) is None:
- assert self.roi
- if markerType == 'min':
- val = self.roi.getFrom()
- elif markerType == 'max':
- val = self.roi.getTo()
- else:
- val = self.roi.getMiddle()
-
- _color = self._color
- if markerType == 'middle':
- _color = 'yellow'
- self.plot.addXMarker(val,
- legend=self._markerID(markerType),
- text=self.getMarkerName(markerType),
- color=_color,
- draggable=self.draggable)
- return self.plot._getMarker(self._markerID(markerType))
-
- def _markerID(self, markerType):
- assert markerType in ('min', 'max', 'middle')
- assert self.roi
- return '_'.join((str(self.roi.getID()), markerType))
-
- def getMarkerName(self, markerType):
- assert markerType in ('min', 'max', 'middle')
- assert self.roi
- return ' '.join((self.roi.getName(), markerType))
-
- def updateTexts(self):
- self.getMarker('min').setText(self.getMarkerName('min'))
- self.getMarker('max').setText(self.getMarkerName('max'))
- self.getMarker('middle').setText(self.getMarkerName('middle'))
-
- def changePosition(self, markerID, x):
- assert self.hasMarker(markerID)
- markerType = self._getMarkerType(markerID)
- assert markerType is not None
- if self.roi is None:
- return
- if markerType == 'min':
- self.roi.setFrom(x)
- self._updateMiddleMarkerPos()
- elif markerType == 'max':
- self.roi.setTo(x)
- self._updateMiddleMarkerPos()
- else:
- delta = x - 0.5 * (self.roi.getFrom() + self.roi.getTo())
- self.roi.setFrom(self.roi.getFrom() + delta)
- self.roi.setTo(self.roi.getTo() + delta)
- self._updateMinMarkerPos()
- self._updateMaxMarkerPos()
-
- def hasMarker(self, marker):
- return marker in (self._markerID('min'),
- self._markerID('max'),
- self._markerID('middle'))
-
- def _getMarkerType(self, markerID):
- if markerID.endswith('_min'):
- return 'min'
- elif markerID.endswith('_max'):
- return 'max'
- elif markerID.endswith('_middle'):
- return 'middle'
- else:
- return None
-
-
-class CurvesROIDockWidget(qt.QDockWidget):
- """QDockWidget with a :class:`CurvesROIWidget` connected to a PlotWindow.
-
- It makes the link between the :class:`CurvesROIWidget` and the PlotWindow.
-
- :param parent: See :class:`QDockWidget`
- :param plot: :class:`.PlotWindow` instance on which to operate
- :param name: See :class:`QDockWidget`
- """
- sigROISignal = qt.Signal(object)
- """Deprecated signal for backward compatibility with silx < 0.7.
- Prefer connecting directly to :attr:`CurvesRoiWidget.sigRoiSignal`
- """
-
- def __init__(self, parent=None, plot=None, name=None):
- super(CurvesROIDockWidget, self).__init__(name, parent)
-
- assert plot is not None
- self.plot = plot
- self.roiWidget = CurvesROIWidget(self, name, plot=plot)
- """Main widget of type :class:`CurvesROIWidget`"""
-
- # convenience methods to offer a simpler API allowing to ignore
- # the details of the underlying implementation
- # (ALL DEPRECATED)
- self.calculateROIs = self.calculateRois = self.roiWidget.calculateRois
- self.setRois = self.roiWidget.setRois
- self.getRois = self.roiWidget.getRois
-
- self.roiWidget.sigROISignal.connect(self._forwardSigROISignal)
-
- self.layout().setContentsMargins(0, 0, 0, 0)
- self.setWidget(self.roiWidget)
-
- self.setAreaVisible = self.roiWidget.roiTable.setAreaVisible
- self.setCountsVisible = self.roiWidget.roiTable.setCountsVisible
-
- def _forwardSigROISignal(self, ddict):
- # emit deprecated signal for backward compatibility (silx < 0.7)
- self.sigROISignal.emit(ddict)
-
- def toggleViewAction(self):
- """Returns a checkable action that shows or closes this widget.
-
- See :class:`QMainWindow`.
- """
- action = super(CurvesROIDockWidget, self).toggleViewAction()
- action.setIcon(icons.getQIcon('plot-roi'))
- return action
-
- def showEvent(self, event):
- """Make sure this widget is raised when it is shown
- (when it is first created as a tab in PlotWindow or when it is shown
- again after hiding).
- """
- self.raise_()
- qt.QDockWidget.showEvent(self, event)
-
- @property
- def currentROI(self):
- return self.roiWidget.currentRoi