Source code for lavuelib.levelsGroupBox

# Copyright (C) 2017  DESY, Christoph Rosemann, Notkestr. 85, D-22607 Hamburg
#
# lavue is an image viewing program for photon science imaging detectors.
# Its usual application is as a live viewer using hidra as data source.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation in  version 2
# of the License.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor,
# Boston, MA  02110-1301, USA.
#
# Authors:
#     Christoph Rosemann <christoph.rosemann@desy.de>
#     Jan Kotanski <jan.kotanski@desy.de>
#

""" level widget """

from .qtuic import uic
import pyqtgraph as _pg
from pyqtgraph import QtCore
from .histogramWidget import HistogramHLUTWidget
from . import messageBox
from . import gradientDialog

try:
    from pyqtgraph import QtWidgets
except Exception:
    from pyqtgraph import QtGui as QtWidgets


# from .histogramWidget import HistogramHLUTItem
import math
import os
import logging


_VMAJOR, _VMINOR, _VPATCH = _pg.__version__.split(".")[:3] \
    if _pg.__version__ else ("0", "9", "0")
try:
    _NPATCH = int(_VPATCH)
except Exception:
    _NPATCH = 0
_PQGVER = int(_VMAJOR) * 1000 + int(_VMINOR) * 100 + _NPATCH


_formclass, _baseclass = uic.loadUiType(
    os.path.join(os.path.dirname(os.path.abspath(__file__)),
                 "ui", "LevelsGroupBox.ui"))


logger = logging.getLogger("lavue")


[docs]class LevelsGroupBox(QtWidgets.QWidget): """ Set minimum and maximum displayed values and its color. """ #: (:class:`pyqtgraph.QtCore.pyqtSignal`) minimum level changed signal minLevelChanged = QtCore.pyqtSignal(float) #: (:class:`pyqtgraph.QtCore.pyqtSignal`) maximum level changed signal maxLevelChanged = QtCore.pyqtSignal(float) #: (:class:`pyqtgraph.QtCore.pyqtSignal`) channel levels changed signal channelLevelsChanged = QtCore.pyqtSignal() #: (:class:`pyqtgraph.QtCore.pyqtSignal`) automatic levels changed signal autoLevelsChanged = QtCore.pyqtSignal(int) #: (:class:`pyqtgraph.QtCore.pyqtSignal`) levels changed signal levelsChanged = QtCore.pyqtSignal() #: (:class:`pyqtgraph.QtCore.pyqtSignal`) store settings requested storeSettingsRequested = QtCore.pyqtSignal() #: (:class:`pyqtgraph.QtCore.pyqtSignal`) gradient changed signal gradientChanged = QtCore.pyqtSignal() def __init__(self, parent=None, settings=None, expertmode=False): """ constructor :param parent: parent object :type parent: :class:`pyqtgraph.QtCore.QObject` :param settings: lavue configuration settings :type settings: :class:`lavuelib.settings.Settings` :param expertmode: expert mode flag :type expertmode: :obj:`bool` """ QtWidgets.QGroupBox.__init__(self, parent) #: (:class:`Ui_LevelsGroupBox') ui_groupbox object from qtdesigner self.__ui = _formclass() self.__ui.setupUi(self) #: (:obj: `bool`) auto levels enabled self.__auto = True #: (:obj: `bool`) histogram shown self.__histo = True #: (:obj: `bool`) levels shown self.__levels = True #: (:obj: `str`) scale label self.__scaling = "sqrt" #: (:class:`lavuelib.settings.Settings`) settings self.__settings = settings #: (:obj:`bool`) expert mode self.__expertmode = expertmode #: (:obj:`list` <:obj:`int`>) rgb channel indexes self.__rgbchannels = (-1, -1, -1) #: (:obj:`dict` < :obj:`str`, :obj:`dict` < :obj:`str`,`any`> > # custom gradients self.__customgradients = self.__settings.customGradients() for name, gradient in self.__customgradients.items(): _pg.graphicsItems.GradientEditorItem.Gradients[name] = gradient self._addGradientItem(name) #: (:obj: `float`) minimum intensity level value self.__minval = 0.2 #: (:obj: `float`) maximum intensity level value self.__maxval = 1.1 #: (:obj: `float`) minimum maximum intensity level value # for separete channels self.__channels = None #: (:obj: `int`) channel to display self.__dchl = 0 #: (:obj:`bool`) gradient colors flag self.__gradientcolors = False self.__ui.minDoubleSpinBox.setMinimum(-10e20) self.__ui.minDoubleSpinBox.setMaximum(10e20) self.__ui.maxDoubleSpinBox.setMinimum(-10e20) self.__ui.maxDoubleSpinBox.setMaximum(10e20) #: (:class: `lavuelib.histogramWidget.HistogramHLUTWidget`) #: intensity histogram widget for name in _pg.graphicsItems.GradientEditorItem.\ Gradients.keys(): self._addGradientItem(name) self.__histograms = [ HistogramHLUTWidget(bins='auto', step='auto', expertmode=expertmode), HistogramHLUTWidget(bins='auto', step='auto', expertmode=expertmode), HistogramHLUTWidget(bins='auto', step='auto', expertmode=expertmode) ] self.__histogram = self.__histograms[0] self.__histconnect = [False, False, False] self.__onLevelsSlots = [ self._onLevelsChanged, self._onLevelsChanged1, self._onLevelsChanged2 ] self.__changeGradientSlots = [ self._changeGradient0, self._changeGradient1, self._changeGradient2 ] self.__saveGradientSlots = [ self._saveGradient, self._saveGradient1, self._saveGradient2 ] self.__removeGradientSlots = [ self._removeGradient, self._removeGradient1, self._removeGradient2 ] self.__rgbstatus = False self.__ui.histogramLayout.addWidget(self.__histograms[0]) self.__ui.histogramLayout.addWidget(self.__histograms[1]) self.__ui.histogramLayout.addWidget(self.__histograms[2]) self.__ui.gradientComboBox.currentIndexChanged.connect( self._updateGradient) self.__ui.binsComboBox.currentIndexChanged.connect(self.setBins) self.__hideControls() self.__ui.autoLevelsCheckBox.stateChanged.connect( self._onAutoLevelsChanged) self.__ui.stepLineEdit.textChanged.connect( self._onStepChanged) self.__ui.monoRadioButton.clicked.connect( self._monoLevelMode) self.__ui.redRadioButton.clicked.connect( self._redLevelMode) self.__ui.blueRadioButton.clicked.connect( self._blueLevelMode) self.__ui.greenRadioButton.clicked.connect( self._greenLevelMode) self.__ui.autofactorLineEdit.textChanged.connect( self._onAutoFactorChanged) self.__connectHistogram() self.__histograms[0].show() self.__histograms[1].hide() self.__histograms[2].hide() # print(self.__histogram.isVisible()) self.updateLevels(0.1, 1.0) self.__connectMinMax() self.__levelmode = "mono" def _updateLevelLabels(self, dchl=None): """ update level labels :param dchl: channel id :type dchl: :obj:`int` """ if dchl is None: dchl = self.__dchl if _PQGVER >= 1100: if dchl == 1: self.__ui.minLabel.setText("Red Min. value:") self.__ui.maxLabel.setText("Red Max. value:") elif dchl == 2: self.__ui.minLabel.setText("Green Min. value:") self.__ui.maxLabel.setText("Green Max. value:") elif dchl == 3: self.__ui.minLabel.setText("Blue Min. value:") self.__ui.maxLabel.setText("Blue Max. value:") else: self.__ui.minLabel.setText("Minimum value:") self.__ui.maxLabel.setText("Maximum value:") @QtCore.pyqtSlot(bool) def _monoLevelMode(self, status): """ update to the mono level mode :param status: button status :type status: :obj:`bool` """ if self.__gradientcolors: if status: self.__dchl = 0 self._updateLevelLabels() if not self.__histo: self.__histogram.switchLevelMode('mono') self.__levelmode = "mono" self.updateLevels(self.__minval, self.__maxval) if self.__histogram: self.__histogram.switchLevelMode('mono') elif _PQGVER >= 1100: if status: self.__dchl = 0 # if not self.__ui.gradientLabel.isVisible(): self._updateLevelLabels() if not self.__histo: self.__histogram.switchLevelMode('mono') self.__levelmode = "mono" self.updateLevels(self.__minval, self.__maxval) if self.__histogram: self.__histogram.switchLevelMode('mono') self.__histogram.gradient.hide() @QtCore.pyqtSlot(bool) def _redLevelMode(self, status): """ update to the red level mode :param status: button status :type status: :obj:`bool` """ if self.__gradientcolors: if status: self.__dchl = 1 self._updateLevelLabels() if not self.__histo: self.__histogram.switchLevelMode('mono') self.__levelmode = "rgba" if self.__channels is not None: while len(self.__channels) < 1: self.__channels.append( (self.__minval, self.__maxval)) self.updateLevels(None, None, self.__channels) if self.__histogram: self.__histogram.switchLevelMode('mono') self.setGradient(self.__histograms[0].gradient.name, 0) elif _PQGVER >= 1100: if status: self.__dchl = 1 # if self.__ui.gradientLabel.isVisible(): self._updateLevelLabels() if not self.__histo: self.__histogram.switchLevelMode('rgba') self.__levelmode = "rgba" if self.__channels is not None: while len(self.__channels) < 1: self.__channels.append( (self.__minval, self.__maxval)) self.updateLevels(None, None, self.__channels) if self.__histogram: self.__histogram.switchLevelMode('rgba') @QtCore.pyqtSlot(bool) def _greenLevelMode(self, status): """ update to the green level mode :param status: button status :type status: :obj:`bool` """ if self.__gradientcolors: if status: self.__dchl = 2 self._updateLevelLabels() if not self.__histo: self.__histogram.switchLevelMode('mono') self.__levelmode = "rgba" if self.__channels is not None: while len(self.__channels) < 2: self.__channels.append( (self.__minval, self.__maxval)) self.updateLevels(None, None, self.__channels) if self.__histogram: self.__histogram.switchLevelMode('mono') self.setGradient(self.__histograms[1].gradient.name, 1) elif _PQGVER >= 1100: if status: self.__dchl = 2 self._updateLevelLabels() # if self.__ui.gradientLabel.isVisible(): if not self.__histo: self.__histogram.switchLevelMode('rgba') self.__levelmode = "rgba" if self.__channels is not None: while len(self.__channels) < 2: self.__channels.append( (self.__minval, self.__maxval)) self.updateLevels(None, None, self.__channels) if self.__histogram: self.__histogram.switchLevelMode('rgba') @QtCore.pyqtSlot(bool) def _blueLevelMode(self, status): """ update to the blue level mode :param status: button status :type status: :obj:`bool` """ if self.__gradientcolors: if status: self.__dchl = 3 self._updateLevelLabels() if not self.__histo: self.__histogram.switchLevelMode('mono') self.__levelmode = "rgba" if self.__channels is not None: while len(self.__channels) < 3: self.__channels.append( (self.__minval, self.__maxval)) self.updateLevels(None, None, self.__channels) if self.__histogram: self.__histogram.switchLevelMode('mono') self.setGradient(self.__histograms[2].gradient.name, 2) elif _PQGVER >= 1100: if status: self.__dchl = 3 # if self.__ui.gradientLabel.isVisible(): self._updateLevelLabels() if not self.__histo: self.__histogram.switchLevelMode('rgba') self.__levelmode = "rgba" if self.__channels is not None: while len(self.__channels) < 3: self.__channels.append( (self.__minval, self.__maxval)) self.updateLevels(None, None, self.__channels) if self.__histogram: self.__histogram.switchLevelMode('rgba') def __connectHistogram(self, iid=0): """ create histogram object and connect its signals :param iid: image id :type iid: :obj:`int` """ if not self.__histconnect[iid]: self.__histograms[iid].item.sigLevelsChanged.connect( self.__onLevelsSlots[iid]) self.__histograms[iid].sigNameChanged.connect( self.__changeGradientSlots[iid]) self.__histograms[iid].saveGradientRequested.connect( self.__saveGradientSlots[iid]) self.__histograms[iid].removeGradientRequested.connect( self.__removeGradientSlots[iid]) self.__histconnect[iid] = True # else: # print("WARN: trying to connect HIS", iid) def __disconnectHistogram(self, iid=0): """ remove histogram object and disconnect its signals :param iid: image id :type iid: :obj:`int` """ if self.__histconnect[iid]: self.__histograms[iid].item.sigLevelsChanged.disconnect( self.__onLevelsSlots[iid]) self.__histograms[iid].sigNameChanged.disconnect( self.__changeGradientSlots[iid]) self.__histograms[iid].saveGradientRequested.disconnect( self.__saveGradientSlots[iid]) self.__histograms[iid].removeGradientRequested.disconnect( self.__removeGradientSlots[iid]) self.__histconnect[iid] = False # else: # print("WARN: trying to disconnect HIS", iid) def __connectHistograms(self): """ create histogram object and connect its signals """ for iid in range(3): if self.__histograms[iid].isVisible(): self.__connectHistogram(iid) def __disconnectHistograms(self): """ remove histogram object and disconnect its signals """ for iid in range(3): if self.__histograms[iid].isVisible(): self.__disconnectHistogram(iid)
[docs] def updateCustomGradients(self, gradients): self.__customgradients = dict(gradients) for name, gradient in self.__customgradients.items(): _pg.graphicsItems.GradientEditorItem.Gradients[name] = gradient self._addGradientItem(name) for iid in range(3): self.__histograms[iid].resetGradient() self._updateGradient()
def __connectMinMax(self): """ connects mix/max spinboxes """ self.__ui.minDoubleSpinBox.valueChanged.connect(self._onMinMaxChanged) self.__ui.maxDoubleSpinBox.valueChanged.connect(self._onMinMaxChanged) def __disconnectMinMax(self): """ disconnects mix/max spinboxes """ self.__ui.minDoubleSpinBox.valueChanged.disconnect( self._onMinMaxChanged) self.__ui.maxDoubleSpinBox.valueChanged.disconnect( self._onMinMaxChanged) @QtCore.pyqtSlot(float) def _onMinMaxChanged(self, _): """ sets region of the histograms """ if not self.__auto: try: self.__disconnectMinMax() if self.__histo: self.__disconnectHistogram() self._checkAndEmit() if self.__histo: lowlim = self.__ui.minDoubleSpinBox.value() uplim = self.__ui.maxDoubleSpinBox.value() if self.__gradientcolors and self.__rgbstatus: for iid in range(3): if not self.__dchl or iid + 1 == self.__dchl: self.__histograms[iid].region.setRegion( [lowlim, uplim]) else: if self.__dchl == 0: self.__histogram.region.setRegion([lowlim, uplim]) else: if hasattr(self.__histogram, "regions"): self.__histogram.regions[self.__dchl].\ setRegion([lowlim, uplim]) finally: self.__connectMinMax() if self.__histo: self.__connectHistogram() else: lowlim = self.__ui.minDoubleSpinBox.value() uplim = self.__ui.maxDoubleSpinBox.value() if self.__dchl == 0: self.minLevelChanged.emit(lowlim) self.maxLevelChanged.emit(uplim) else: if self.__channels is None: self.__channels = [] while len(self.__channels) < self.__dchl: self.__channels.append((lowlim, uplim)) self.__channels[self.__dchl - 1] = (lowlim, uplim) self.channelLevelsChanged.emit()
[docs] def changeView(self, showhistogram=None, showlevels=None, showadd=None): """ shows or hides the histogram widget :param showhistogram: if histogram should be shown :type showhistogram: :obj:`bool` :param showlevels: if levels should be shown :type showlevels: :obj:`bool` :param showadd: if additional histogram should be shown :type showadd: :obj:`bool` """ if showhistogram is True and self.__histo is False: if showhistogram is not None: self.__histo = showhistogram self.showHistograms(True) elif showhistogram is False and self.__histo is True: if showhistogram is not None: self.__histo = showhistogram self.showHistograms(False) if showadd is True: self.__ui.binsComboBox.show() self.__ui.binsLabel.show() self.__ui.stepLineEdit.show() self.__ui.stepLabel.show() elif showadd is False: self.__ui.binsComboBox.hide() self.__ui.binsLabel.hide() self.__ui.stepLineEdit.hide() self.__ui.stepLabel.hide() if showlevels is True and self.__levels is False: self.__ui.gradientLabel.show() self.__ui.gradientComboBox.show() self.__ui.scalingLabel.show() self.__ui.autoLevelsCheckBox.show() self.__ui.autofactorLineEdit.show() self.__ui.autofactorLabel.show() self.__ui.maxDoubleSpinBox.show() self.__ui.maxLabel.show() self.__ui.minDoubleSpinBox.show() self.__ui.minLabel.show() self.__ui.scalingLabel.show() elif showlevels is False and self.__levels is True: self.__ui.gradientLabel.hide() self.__ui.gradientComboBox.hide() self.__ui.scalingLabel.hide() self.__ui.autoLevelsCheckBox.hide() self.__ui.autofactorLineEdit.hide() self.__ui.autofactorLabel.hide() self.__ui.maxDoubleSpinBox.hide() self.__ui.maxLabel.hide() self.__ui.minDoubleSpinBox.hide() self.__ui.minLabel.hide() self.__ui.scalingLabel.hide() if showlevels is not None: self.__levels = showlevels if not self.__histo and not self.__levels: self.hide() else: self.show()
[docs] def isAutoLevel(self): """ returns if automatics levels are enabled :returns: if automatics levels are enabled :rtype: :obj:`bool` """ return self.__auto
[docs] @QtCore.pyqtSlot(int) def setAutoLevels(self, auto): """enables or disables automatic levels :param auto: if automatics levels to be set :type auto: :obj:`bool` or :obj:`int` """ self.__ui.autoLevelsCheckBox.setChecked( 2 if auto else 0) self._onAutoLevelsChanged(auto)
@QtCore.pyqtSlot(str) def _onStepChanged(self, step): """sets step for automatic levels :param step: if automatics levels to be set :type step: :obj:`str` """ try: fstep = float(step) if fstep <= 0: fstep = None self.__ui.stepLineEdit.setText("") for histogram in self.__histograms: histogram.setStep(fstep) except Exception: for histogram in self.__histograms: histogram.setStep(None) self.__ui.stepLineEdit.setText("") self.levelsChanged.emit() @QtCore.pyqtSlot(str) def _onAutoFactorChanged(self, factor): """sets factor for automatic levels :param factor: if automatics levels to be set :type factor: :obj:`str` """ try: ffactor = float(factor) if ffactor < 0: ffactor = 0 self.__ui.autofactorLineEdit.setText("0") elif ffactor > 100: ffactor = 100 self.__ui.autofactorLineEdit.setText("100") for histogram in self.__histograms: histogram.setAutoFactor(ffactor) self.autoLevelsChanged.emit(1) except Exception: for histogram in self.__histograms: histogram.setAutoFactor(None) self.autoLevelsChanged.emit(2 if self.__auto else 0) self.levelsChanged.emit() @QtCore.pyqtSlot(int) def _onAutoLevelsChanged(self, auto): """enables or disables automatic levels :param auto: if automatics levels to be set :type auto: :obj:`bool` or :obj:`int` """ if auto: self.__auto = True self.__hideControls() factor = str(self.__ui.autofactorLineEdit.text()) try: ffactor = float(factor) if ffactor < 0: ffactor = 0 self.__ui.autofactorLineEdit.setText("0") elif ffactor > 100: ffactor = 100 self.__ui.autofactorLineEdit.setText("100") for histogram in self.__histograms: histogram.setAutoFactor(ffactor) except Exception: for histogram in self.__histograms: histogram.setAutoFactor(None) self.autoLevelsChanged.emit(2) else: for histogram in self.__histograms: histogram.setAutoFactor(None) self.__auto = False self.__showControls() self.autoLevelsChanged.emit(0) self._checkAndEmit() self.levelsChanged.emit() @QtCore.pyqtSlot(object) def _onLevelsChanged1(self, histogram=None): """ set min/max level spinboxes according to histogram :param histogram: intensity histogram object :type histogram: :class: `lavuelib.histogramWidget.HistogramHLUTWidget` """ if histogram is None: histogram = self.__histograms[1] self._onLevelsChanged(histogram) @QtCore.pyqtSlot(object) def _onLevelsChanged2(self, histogram=None): """ set min/max level spinboxes according to histogram :param histogram: intensity histogram object :type histogram: :class: `lavuelib.histogramWidget.HistogramHLUTWidget` """ if histogram is None: histogram = self.__histograms[2] self._onLevelsChanged(histogram) @QtCore.pyqtSlot(object) def _onLevelsChanged(self, histogram=None): """ set min/max level spinboxes according to histogram :param histogram: intensity histogram object :type histogram: :class: `lavuelib.histogramWidget.HistogramHLUTWidget` """ if histogram is None: histogram = self.__histogram levels = histogram.region.getRegion() lowlim = self.__minval uplim = self.__maxval changed = False try: # self.__disconnectMinMax() # self.__disconnectHistogram() if levels[0] != lowlim or levels[1] != uplim: lowlim = levels[0] uplim = levels[1] # refresh widget changed = True self.__minval = levels[0] self.__maxval = levels[1] if self.__dchl == 0: self.__ui.minDoubleSpinBox.setValue(levels[0]) self.__ui.maxDoubleSpinBox.setValue(levels[1]) if hasattr(self.__histogram, "regions") and \ self.__channels is not None: while len(self.__channels) < 3: self.__channels.append((self.__minval, self.__maxval)) added = True for i in range(1, 4): if self.__gradientcolors and self.__rgbstatus: levels = self.__histograms[i - 1].region.getRegion() else: levels = histogram.regions[i].getRegion() lowlim = self.__channels[i - 1][0] uplim = self.__channels[i - 1][1] added = False if levels[0] != lowlim or levels[1] != uplim or added: lowlim = levels[0] uplim = levels[1] # refresh widget changed = True self.__channels[i - 1] = levels[0], levels[1] if self.__dchl > 0: self.__ui.minDoubleSpinBox.setValue( self.__channels[self.__dchl - 1][0]) self.__ui.maxDoubleSpinBox.setValue( self.__channels[self.__dchl - 1][1]) finally: # self.__connectMinMax() # self.__connectHistogram() pass if not self.__auto and changed: self._checkLevels() self._emitLevels() @QtCore.pyqtSlot() def _checkLevels(self): """ checks if the minimum value is actually smaller than the maximum """ minval = self.__ui.minDoubleSpinBox.value() maxval = self.__ui.maxDoubleSpinBox.value() if maxval >= 10e+20: maxval = self.__maxval if minval >= 10e+20: minval = self.__minval if maxval - minval < 0: if minval >= 0.0: minval = maxval else: maxval = minval self.__ui.minDoubleSpinBox.setValue(minval) self.__ui.maxDoubleSpinBox.setValue(maxval) @QtCore.pyqtSlot() def _emitLevels(self): """ checks if the minimum value is actually smaller than the maximum """ self.minLevelChanged.emit(self.__minval) self.maxLevelChanged.emit(self.__maxval) self.channelLevelsChanged.emit() @QtCore.pyqtSlot() def _updateAndEmit(self): lowlim = self.__ui.minDoubleSpinBox.value() uplim = self.__ui.maxDoubleSpinBox.value() if self.__dchl == 0: self.__minval = lowlim self.__maxval = uplim self.minLevelChanged.emit(self.__minval) self.maxLevelChanged.emit(self.__maxval) else: if self.__channels is None: self.__channels = [] while len(self.__channels) < self.__dchl: self.__channels.append((lowlim, uplim)) self.__channels[self.__dchl - 1] = (lowlim, uplim) self.channelLevelsChanged.emit() @QtCore.pyqtSlot() def _checkAndEmit(self): """ checks if the minimum value is actually smaller than the maximum """ self._checkLevels() self._updateAndEmit()
[docs] def updateLevels(self, lowlim, uplim, channels=None, signals=True, force=False): """ set min/max level spinboxes and histogram from the parameters :param lowlim: minimum intensity value :type lowlim: :obj:`float` :param uplim: maximum intensity value :type uplim: :obj:`float` :param signal: dont disconnect signals :type signal: :obj:`bool` :param force: force update histogram :type force: :obj:`bool` """ try: if not signals: self.__disconnectMinMax() if lowlim is not None: self.__minval = lowlim if self.__dchl == 0: self.__ui.minDoubleSpinBox.setValue(lowlim) else: lowlim = self.__minval if uplim is not None: self.__maxval = uplim if self.__dchl == 0: self.__ui.maxDoubleSpinBox.setValue(uplim) else: uplim = self.__maxval if channels is not None: self.__channels = channels if self.__dchl != 0 and len(self.__channels) >= self.__dchl: ch = self.__channels[self.__dchl - 1] if ch is not None: chl, chh = ch if chl is not None: self.__ui.minDoubleSpinBox.setValue(chl) if chh is not None: self.__ui.maxDoubleSpinBox.setValue(chh) finally: if not signals: self.__connectMinMax() if self.__histo and (self.__auto or force): levels = self.__histogram.region.getRegion() update = False try: if self.__histo: if self.__gradientcolors and self.__rgbstatus: self.__disconnectHistograms() else: self.__disconnectHistogram() if levels[0] != lowlim or levels[1] != uplim or force: if self.__histo: self.__histogram.region.setRegion([lowlim, uplim]) if hasattr(self.__histogram, "regions"): if channels is not None and self.__histo: for i, ch in enumerate(self.__channels): if ch is not None: lowlim, uplim = ch if lowlim is not None and uplim is not None: if self.__gradientcolors and \ self.__rgbstatus: levels = self.__histograms[i].region\ .getRegion() if levels[0] != lowlim \ or levels[1] != uplim or force: self.__histograms[i].region.\ setRegion([lowlim, uplim]) else: levels = self.__histogram.\ regions[i + 1].getRegion() if levels[0] != lowlim \ or levels[1] != uplim or force: self.__histogram.regions[i + 1].\ setRegion([lowlim, uplim]) finally: if self.__histo: if self.__gradientcolors and self.__rgbstatus: self.__connectHistograms() else: self.__connectHistogram() if update: self._onLevelsChanged() self._emitLevels()
[docs] def updateAutoLevels(self, lowlim, uplim, channels=None): """ set min/max level spinboxes and histogram from the parameters :param lowlim: minimum intensity value :type lowlim: :obj:`float` :param uplim: maximum intensity value :type uplim: :obj:`float` """ if channels is not None: self.__channels = channels try: factor = str(self.__ui.autofactorLineEdit.text()) float(factor) channels = None llim = None ulim = None if self.__histo: llim, ulim = self.__histogram.getFactorRegion() if self.__gradientcolors and self.__rgbstatus: channels = [(llim, ulim)] channels.append(self.__histograms[1].getFactorRegion()) channels.append(self.__histograms[2].getFactorRegion()) else: channels = self.__histogram.getChannelFactorRegion() if channels is not None: self.__channels = channels if llim is not None and ulim is not None: self.updateLevels(llim, ulim) self.minLevelChanged.emit(llim) self.maxLevelChanged.emit(ulim) else: self.updateLevels(lowlim, uplim, channels) if channels is not None: self.channelLevelsChanged.emit() except Exception: self.updateLevels(lowlim, uplim, channels)
def __hideControls(self): """ disables spinboxes """ self.__ui.minDoubleSpinBox.setEnabled(False) self.__ui.maxDoubleSpinBox.setEnabled(False) self.__ui.autofactorLineEdit.setEnabled(True) self.__ui.autofactorLabel.setEnabled(True) def __showControls(self): """ enables spinboxes """ self.__ui.minDoubleSpinBox.setEnabled(True) self.__ui.maxDoubleSpinBox.setEnabled(True) self.__ui.autofactorLineEdit.setEnabled(False) self.__ui.autofactorLabel.setEnabled(False) def _tologscale(self, lowlim, uplim): """ change scaling to log scale :param lowlim: minimum intensity value :type lowlim: :obj:`float` :param uplim: maximum intensity value :type uplim: :obj:`float` :returns: (minimum intensity value, maximum intensity value) in log scale :rtype: (:obj:`float`, :obj:`float`) """ if self.__scaling == "linear": lowlim = math.log10( lowlim or 10e-3) if lowlim > 0 else -2 uplim = math.log10( uplim or 10e-3) if uplim > 0 else -2 elif self.__scaling == "sqrt": lowlim = math.log10( lowlim * lowlim or 10e-3) if lowlim > 0 else -2 uplim = math.log10( uplim * uplim or 10e-3) if uplim > 0 else -2 return lowlim, uplim def _tolinearscale(self, lowlim, uplim): """ change scaling to linear scale :param lowlim: minimum intensity value :type lowlim: :obj:`float` :param uplim: maximum intensity value :type uplim: :obj:`float` :returns: (minimum intensity value, maximum intensity value) in linear scale :rtype: (:obj:`float`, :obj:`float`) """ if self.__scaling == "log": lowlim = math.pow(10, lowlim) uplim = math.pow(10, uplim) elif self.__scaling == "sqrt": lowlim = lowlim * lowlim uplim = uplim * uplim return lowlim, uplim def _tosqrtscale(self, lowlim, uplim): """ change scaling to sqrt scale :param lowlim: minimum intensity value :type lowlim: :obj:`float` :param uplim: maximum intensity value :type uplim: :obj:`float` :returns: (minimum intensity value, maximum intensity value) in sqrt scale :rtype: (:obj:`float`, :obj:`float`) """ if self.__scaling == "linear": lowlim = math.sqrt(max(lowlim, 0)) uplim = math.sqrt(max(uplim, 0)) elif self.__scaling == "log": lowlim = math.sqrt(max(math.pow(10, lowlim), 0)) uplim = math.sqrt(max(math.pow(10, uplim), 0)) return lowlim, uplim
[docs] @QtCore.pyqtSlot(str) def setRGBChannels(self, rgbchannels): """ rgb channel indexes :param rgbchannels: rgb channel indexes :type rgbchannels: :obj:`tuple` <:obj:`int`> """ if self.__rgbchannels != rgbchannels: self.__rgbchannels = rgbchannels self.showHistograms(self.__rgbstatus)
[docs] @QtCore.pyqtSlot(str) def setScalingLabel(self, scalingtype): """ sets scaling label :param scalingtype: scaling type, i.e. log, linear, sqrt :type scalingtype: :obj:`str` """ lowlim = float(self.__ui.minDoubleSpinBox.value()) uplim = float(self.__ui.maxDoubleSpinBox.value()) scalefun = { "log": self._tologscale, "linear": self._tolinearscale, "sqrt": self._tosqrtscale, } scalelabel = { "log": "log scale!", "linear": "linear scale!", "sqrt": "sqrt scale!", } if scalingtype in scalefun.keys(): sfun = scalefun[scalingtype] slabel = scalelabel[scalingtype] if scalingtype != self.__scaling: self.__ui.scalingLabel.setText(slabel) if not self.__auto: lowlim, uplim = sfun(lowlim, uplim) self.__minval, self.__maxval = sfun( self.__minval, self.__maxval) if self.__channels: for i, ch in enumerate(self.__channels): if ch is not None: if ch[0] is not None and ch[1] is not None: self.__channels[i] = sfun(ch[0], ch[1]) # self.__ui.minDoubleSpinBox.setValue(lowlim) # self.__ui.maxDoubleSpinBox.setValue(uplim) if scalingtype != self.__scaling: self.__scaling = scalingtype if not self.__auto: if self.__histo: try: self.__disconnectHistogram() self.__histogram.region.setRegion( [self.__minval, self.__maxval]) if hasattr(self.__histogram, "regions"): if self.__channels is not None: for i, ch in enumerate(self.__channels): if ch is not None: lowlim, uplim = ch if lowlim is not None \ and uplim is not None: self.__histogram.regions[i + 1].\ setRegion([lowlim, uplim]) finally: self.__connectHistogram() self.updateLevels( self.__minval, self.__maxval, self.__channels, signals=False) # self._onLevelsChanged() else: self.__ui.minDoubleSpinBox.setValue(lowlim) self.__ui.maxDoubleSpinBox.setValue(uplim)
[docs] @QtCore.pyqtSlot(bool) def setrgb(self, status=True): """ sets RGB on/off :param status: True for on and False for off :type status: :obj:`bool` """ self.__histogram.setRGB(status and not self.__gradientcolors) if status and self.__dchl and not self.__gradientcolors: mode = 'rgba' lmode = 'rgba' dchl = self.__dchl elif status and self.__dchl and self.__gradientcolors: mode = 'mono' lmode = 'rgba' dchl = self.__dchl else: mode = 'mono' lmode = 'mono' dchl = 0 switch = False if self.__rgbstatus != status: switch = True self.__rgbstatus = status self.__histogram.switchLevelMode(mode) self.__levelmode = lmode self._updateLevelLabels(dchl) self.showGradient(not status or self.__gradientcolors) self.showHistograms(status) if _PQGVER >= 1100 or self.__gradientcolors: self.showChannels(status) if switch: self.gradientChanged.emit()
[docs] def showHistograms(self, status=True): """ show/hide gradient widget :param status: show gradient flag :type status: :obj:`bool` """ if self.__gradientcolors and status and self.__histo: for iid in range(0, 3): if self.__rgbchannels[iid] != -1: if not self.__histograms[iid].isVisible(): self.__connectHistogram(iid) self.__histograms[iid].show() self.__histograms[iid].fillHistogram(True) else: if self.__histograms[iid].isVisible(): self.__histograms[iid].hide() self.__histograms[iid].fillHistogram(False) self.__disconnectHistogram(iid) else: if self.__histo: if not self.__histograms[0].isVisible(): self.__connectHistogram() self.__histograms[0].show() self.__histograms[0].fillHistogram(True) else: if self.__histograms[0].isVisible(): self.__histograms[0].hide() self.__histograms[0].fillHistogram(False) self.__disconnectHistogram() if self.__histograms[1].isVisible(): self.__histograms[1].hide() self.__histograms[1].fillHistogram(False) self.__disconnectHistogram(1) if self.__histograms[2].isVisible(): self.__histograms[2].hide() self.__histograms[2].fillHistogram(False) self.__disconnectHistogram(2)
[docs] def showGradient(self, status=True): """ show/hide gradient widget :param status: show gradient flag :type status: :obj:`bool` """ if status and not self.__ui.gradientComboBox.isVisible(): self.__ui.gradientComboBox.show() self.__ui.gradientLabel.show() self.__histogram.gradient.show() # self.setGradient(self.gradient()) self._updateGradient() elif not status and self.__ui.gradientComboBox.isVisible(): self.__ui.gradientComboBox.hide() self.__ui.gradientLabel.hide() self.__histogram.gradient.hide()
[docs] def showChannels(self, status=True): """ show/hide channel widget :param status: show channel flag :type status: :obj:`bool` """ if status: self.__ui.channelWidget.show() else: self.__ui.channelWidget.hide()
# self.__ui.monoRadioButton.setChecked(2)
[docs] @QtCore.pyqtSlot(int) def setBins(self, index): """ sets bins edges algorithm for histogram :param index: bins edges algorithm index for histogram :type index: :obj:`int` """ for histogram in self.__histograms: histogram.setBins( self.__ui.binsComboBox.itemText(index)) self.levelsChanged.emit()
[docs] def gradient(self): """ provides the current color gradient :returns: gradient name :rtype: :obj:`str` """ if self.__gradientcolors and self.__rgbstatus: return str(";".join( [his.gradient.name for his in self.__histograms])) else: return str(self.__ui.gradientComboBox.currentText())
@QtCore.pyqtSlot() def _saveGradient1(self): """ saves the current gradient """ self._saveGradient(1) @QtCore.pyqtSlot() def _saveGradient2(self): """ saves the current gradient """ self._saveGradient(2) @QtCore.pyqtSlot() def _saveGradient(self, iid=0): """ saves the current gradient :param iid: image id :type iid: :obj:`int` """ graddlg = gradientDialog.GradientDialog() graddlg.protectednames = list( set(_pg.graphicsItems.GradientEditorItem.Gradients.keys()) - set(self.__customgradients.keys()) ) graddlg.createGUI() if graddlg.exec_(): if graddlg.name: name = graddlg.name gradient = self.__histograms[iid].gradient.getCurrentGradient() self.__customgradients[name] = gradient _pg.graphicsItems.GradientEditorItem.Gradients[name] = gradient self._addGradientItem(name) for i, histogram in enumerate(self.__histograms): lname = histogram.gradient.name histogram.resetGradient() if iid == i: lname = name self.setGradient(lname, i) self._updateGradient() self.__settings.setCustomGradients(self.__customgradients) self.storeSettingsRequested.emit() @QtCore.pyqtSlot() def _removeGradient1(self): """ removes the current gradient """ self._removeGradient(1) @QtCore.pyqtSlot() def _removeGradient2(self): """ removes the current gradient """ self._removeGradient(2) @QtCore.pyqtSlot() def _removeGradient(self, iid=0): """ removes the current gradient :param iid: image id :type iid: :obj:`int` """ name = self.__histograms[iid].gradient.name if name in self.__customgradients: if QtWidgets.QMessageBox.question( self, "Removing Label", 'Would you like to remove "%s"" ?' % (name), QtWidgets.QMessageBox.Yes | QtWidgets.QMessageBox.No, QtWidgets.QMessageBox.Yes) == QtWidgets.QMessageBox.No: return False self.__customgradients.pop(name) _pg.graphicsItems.GradientEditorItem.Gradients.pop(name) self._removeGradientItem(name) for i, histogram in enumerate(self.__histograms): lname = histogram.gradient.name histogram.resetGradient() if name != lname: self.setGradient(lname, i) self.__settings.setCustomGradients(self.__customgradients) self.storeSettingsRequested.emit() else: messageBox.MessageBox.warning( self, "Gradient: '%s' cannot be removed" % str(name), None, None) def _addGradientItem(self, name): """ sets gradient :param name gradient name :type name: :obj:`str` """ cid = self.__ui.gradientComboBox.findText(name) if cid < 0: self.__ui.gradientComboBox.addItem(name) def _removeGradientItem(self, name): """ removes gradient :param name gradient name :type name: :obj:`str` """ cid = self.__ui.gradientComboBox.findText(name) if cid > -1: self.__ui.gradientComboBox.removeItem(cid) self._updateGradient(0) else: logger.error( "_removeGradientItem: " "Error in _removeGradientItem for %s" % name) # print("Error %s" % name)
[docs] def setGradient(self, name, iid=None): """ sets gradient :param name gradient name :type name: :obj:`str` :param iid: image id :type iid: :obj:`int` """ if iid is not None: self.__histograms[iid].setGradientByName(name) self.__changeGradientSlots[iid](name) else: names = name.split(";") for i, nm in enumerate(names): if i > 2: break self.__histograms[i].setGradientByName(nm) if i + 1 == self.__dchl: self.__changeGradientSlots[i](nm)
@QtCore.pyqtSlot(int) def _updateGradient(self, index=-1): """ updates gradient in the intensity histogram :param index: gradient index :type index: :obj:`int` """ if index == -1: name = self.__ui.gradientComboBox.currentText() index = self.__ui.gradientComboBox.findText(name) # if self.__gradientcolors and self.__rgbstatus: if self.__gradientcolors and self.__rgbstatus: if not self.__dchl: for iid in range(3): self.__histograms[iid].setGradientByName( self.__ui.gradientComboBox.itemText(index)) else: self.__histograms[self.__dchl - 1].setGradientByName( self.__ui.gradientComboBox.itemText(index)) else: self.__histogram.setGradientByName( self.__ui.gradientComboBox.itemText(index)) self.gradientChanged.emit() @QtCore.pyqtSlot(str) def _changeGradient0(self, name): """ updates the gradient combobox :param name: gradient name :type name: :obj:`str` """ self._changeGradient(name, 0) @QtCore.pyqtSlot(str) def _changeGradient1(self, name): """ updates the gradient combobox :param name: gradient name :type name: :obj:`str` """ self._changeGradient(name, 1) @QtCore.pyqtSlot(str) def _changeGradient2(self, name): """ updates the gradient combobox :param name: gradient name :type name: :obj:`str` """ self._changeGradient(name, 2) @QtCore.pyqtSlot(str) def _changeGradient(self, name, iid=0): """ updates the gradient combobox :param name: gradient name :type name: :obj:`str` :param iid: image id :type iid: :obj:`int` """ text = self.__ui.gradientComboBox.currentText() if text != name: cid = self.__ui.gradientComboBox.findText(name) if self.__gradientcolors and self.__rgbstatus: self.__updateRadio(iid + 1) if cid > -1: if self.__gradientcolors and self.__rgbstatus or iid == 0: self.__ui.gradientComboBox.setCurrentIndex(cid) else: logger.error( "LevelsGroupBox.changeGradient: " "Error in _changeGradient for %s" % name) # print("Error %s" % name) self.gradientChanged.emit()
[docs] def updateHistoImage(self, autoLevel=None): """ executes imageChanged of histogram with the givel autoLevel :param autoLevel: if automatics levels to be set :type autoLevel: :obj:`bool` """ auto = autoLevel if autoLevel is not None else self.__auto if self.__gradientcolors and self.__rgbstatus: for histogram in self.__histograms: histogram.imageChanged(autoLevel=auto) else: self.__histogram.imageChanged(autoLevel=auto)
[docs] def setImageItem(self, image, iid=0): """ sets histogram image :param image: histogram image :type image: :class:`pyqtgraph.graphicsItems.ImageItem.ImageItem` :param iid: image id :type iid: :obj:`int` """ self.__histograms[iid].setImageItem(image)
[docs] def levels(self): """ provides levels from configuration string :returns: configuration string: lowlim,uplim or lowlim,uplim;c1l,c1u;c2l,c2u;c3l,c3u :rtype: :obj:`str` """ lowlim = self.__minval uplim = self.__maxval main = "%s,%s" % (lowlim, uplim) chl = "" chw = "" if self.__channels: chl = ";".join( ["%s,%s" % (ch[0], ch[1]) for ch in self.__channels]) chl = ";%s" % chl if self.__dchl in [1, 2, 3]: chw = ";%s" % ({1: "red", 2: "green", 3: "blue"}[self.__dchl]) return "%s%s%s" % (main, chl, chw)
[docs] def channelLevels(self): """ provides levels from configuration string :returns: channel levels :rtype: :obj:`str` """ if self.__channels is not None: return list(self.__channels)
[docs] def setLevels(self, cnflevels): """ set levels from configuration string :param cnflevels: configuration string: lowlim,uplim or lowlim,uplim;lowred,upred;lowgreen,upgreen;lowblue,upblue :type cnflevels: :obj:`str` """ dchl = 0 channels = None if cnflevels: clst = cnflevels.split(";") if clst[-1].startswith("r") or clst[-1].startswith("R"): dchl = 1 clst.pop() elif clst[-1].startswith("g") or clst[-1].startswith("G"): dchl = 2 clst.pop() elif clst[-1].startswith("b") or clst[-1].startswith("B"): dchl = 3 clst.pop() channels = [] if clst: self.__ui.autoLevelsCheckBox.setChecked(False) self._onAutoLevelsChanged(0) for ch in clst[1:]: llst = ch.split(",") lmin = None lmax = None try: smin = llst[0] if smin.startswith("m"): smin = "-" + smin[1:] lmin = float(smin) except Exception as e: logger.warning(str(e)) # print(str(e)) try: smax = llst[1] if smax.startswith("m"): smax = "-" + smax[1:] lmax = float(smax) except Exception as e: logger.warning(str(e)) channels.append((lmin, lmax)) llst = clst[0].split(",") lmin = None lmax = None try: smin = llst[0] if smin.startswith("m"): smin = "-" + smin[1:] lmin = float(smin) except Exception: pass try: smax = llst[1] if smax.startswith("m"): smax = "-" + smax[1:] lmax = float(smax) except Exception: pass self.updateLevels(lmin, lmax, channels, force=True) self.__updateRadio(dchl)
def __updateRadio(self, dchl=None): """ update RGB radio button position :param dchl: channel id :type dchl: :obj:`int` """ if dchl is None: dchl = self.__dchl if dchl == 0 and not self.__ui.monoRadioButton.isChecked(): self.__ui.monoRadioButton.click() elif dchl == 1 and not self.__ui.redRadioButton.isChecked(): self.__ui.redRadioButton.click() elif dchl == 2 and not self.__ui.greenRadioButton.isChecked(): self.__ui.greenRadioButton.click() elif dchl == 3 and not self.__ui.blueRadioButton.isChecked(): self.__ui.blueRadioButton.click()
[docs] def autoFactor(self): """ provides factor for automatic levels :returns: factor for automatic levels :rtype: :obj:`str` """ return str(self.__ui.autofactorLineEdit.text())
[docs] def setAutoFactor(self, factor): """ sets factor for automatic levels :param factor: factor for automatic levels :type factor: :obj:`str` """ self.__ui.autofactorLineEdit.setText(factor) if factor: self.setAutoLevels(2) self._onAutoFactorChanged(factor)
[docs] def levelMode(self): """ return historgram level mode :return: level mode :rtype: :obj:`str` """ # if self.__histogram and self.__histo: # return self.__histogram.levelMode return self.__levelmode
[docs] def setGradientColors(self, status=True): """ sets gradientcolors on/off :param status: True for on and False for off :type status: :obj:`bool` """ if self.__gradientcolors != status: self.__gradientcolors = status self.__updateRadio(0) self.showGradient( not self.__rgbstatus or self.__gradientcolors) self.showHistograms(self.__rgbstatus)
[docs] def gradientColors(self): """ gets gradientcolors on/off :returns: True for on and False for off :rtype: :obj:`bool` """ return self.__gradientcolors