# 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
from pyqtgraph import QtCore
import os
import logging
try:
from pyqtgraph import QtWidgets
except Exception:
from pyqtgraph import QtGui as QtWidgets
_formclass, _baseclass = uic.loadUiType(
os.path.join(os.path.dirname(os.path.abspath(__file__)),
"ui", "ChannelGroupBox.ui"))
logger = logging.getLogger("lavue")
[docs]class ChannelGroupBox(QtWidgets.QWidget):
"""
Set minimum and maximum displayed values and its color.
"""
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) color channel changed signal
channelChanged = QtCore.pyqtSignal()
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) rgb color channel changed signal
rgbChanged = QtCore.pyqtSignal(bool)
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`) levels shown
self.__levels = True
#: (:obj: `bool`) colors to be shown
self.__colors = True
#: (:obj: `bool`) rgb flag
self.__rgb = False
#: (:obj: `int`) red channel
self.__rindex = 0
#: (:obj: `int`) green channel
self.__gindex = 1
#: (:obj: `int`) blue channel
self.__bindex = 2
#: (:obj: `int`) default red channel
self.__defrindex = 0
#: (:obj: `int`) default green channel
self.__defgindex = 1
#: (:obj: `int`) default blue channel
self.__defbindex = 2
#: (:obj: `int`) default color channel
self.__defaultchannel = 0
#: (:obj: `int`) current color channel
self.__colorchannel = 0
#: (:obj: `int`) number of color channels
self.__numberofchannels = 0
#: (:class:`lavuelib.settings.Settings`) settings
self.__settings = settings
#: (:obj:`bool`) expert mode
self.__expertmode = expertmode
#: (:obj:`dict`) channellabels
self.__channellabels = {}
#: (:obj:`bool`) connected flag
self.__connected = False
self.__connectChannels()
self.setNumberOfChannels(-1)
self.__ui.rComboBox.currentIndexChanged.connect(
self._onRChannelChanged)
self.__ui.gComboBox.currentIndexChanged.connect(
self._onGChannelChanged)
self.__ui.bComboBox.currentIndexChanged.connect(
self._onBChannelChanged)
[docs] def setDefaultColorChannel(self, channel):
""" sets default color channel
:param channel: default color channel(s)
:type channel: :obj:`int` or :obj:`int`
"""
if isinstance(channel, list):
self.__defaultchannel = -1
if len(channel) > 0:
self.__defrindex = channel[0]
if len(channel) > 1:
self.__defgindex = channel[1]
else:
self.__defgindex = -1
if len(channel) > 2:
self.__defbindex = channel[2]
else:
self.__defbindex = -1
else:
self.__defaultchannel = channel
[docs] def changeView(self, showlevels=None):
""" shows or hides the histogram widget
:param showlevels: if levels should be shown
:type showlevels: :obj:`bool`
"""
if showlevels is True and self.__levels is False:
if self.__colors:
self.__ui.channelGroupBox.show()
self.__ui.channelComboBox.show()
else:
self.__ui.channelGroupBox.hide()
self.__ui.channelComboBox.hide()
self.__ui.rLabel.hide()
self.__ui.gLabel.hide()
self.__ui.bLabel.hide()
self.__ui.rComboBox.hide()
self.__ui.gComboBox.hide()
self.__ui.bComboBox.hide()
elif showlevels is False and self.__levels is True:
self.__ui.channelGroupBox.hide()
self.__ui.channelComboBox.hide()
self.__ui.rLabel.show()
self.__ui.gLabel.show()
self.__ui.bLabel.show()
self.__ui.rComboBox.show()
self.__ui.gComboBox.show()
self.__ui.bComboBox.show()
if showlevels is not None:
self.__levels = showlevels
if not self.__levels:
self.hide()
else:
self.show()
@QtCore.pyqtSlot()
def _setWidgetChannel(self):
""" update channel comboboxs and sets color channel
"""
channel = self.__ui.channelHorizontalSlider.value()
if self.__colorchannel != channel:
self.__ui.channelComboBox.setCurrentIndex(channel)
self.setChannel(channel)
self.__ui.channelComboBox.currentIndexChanged.connect(
self.setChannel)
@QtCore.pyqtSlot()
def _skipChannels(self):
""" disconnects channel combobox
"""
self.__ui.channelComboBox.currentIndexChanged.disconnect(
self.setChannel)
@QtCore.pyqtSlot()
def _setChannelTips(self):
""" update channel comboboxes
"""
channel = self.__ui.channelHorizontalSlider.value()
if self.__colorchannel != channel:
self.__ui.channelComboBox.setCurrentIndex(channel)
[docs] @QtCore.pyqtSlot(int)
def setChannel(self, channel, force=False):
""" sets color channel
:param channel: color channel
:type channel: :obj:`int`
:param force: force change
:type force: :obj:`bool`
"""
if self.__colorchannel != channel or force:
if channel >= 0 and channel <= self.__numberofchannels + 2:
if self.__numberofchannels and \
channel == self.__numberofchannels + 2:
self.__colorchannel = channel
self.showGradient(False)
self.rgbChanged.emit(True)
elif self.__colorchannel == self.__numberofchannels + 2:
self.__colorchannel = channel
self.showGradient(True)
self.rgbChanged.emit(False)
else:
self.__colorchannel = channel
if force:
self.showGradient(True)
self.channelChanged.emit()
self.__ui.channelHorizontalSlider.setValue(channel)
[docs] def showGradient(self, status=True):
""" resets color channel
:param status: show gradient flag
:type status: :obj:`bool`
"""
if status:
self.__ui.rLabel.hide()
self.__ui.gLabel.hide()
self.__ui.bLabel.hide()
self.__ui.rComboBox.hide()
self.__ui.gComboBox.hide()
self.__ui.bComboBox.hide()
else:
self.__ui.rLabel.show()
self.__ui.gLabel.show()
self.__ui.bLabel.show()
self.__ui.rComboBox.show()
self.__ui.gComboBox.show()
self.__ui.bComboBox.show()
@QtCore.pyqtSlot(int)
def _onRChannelChanged(self, index):
""" set red channel
"""
if index < self.__numberofchannels:
self.__rindex = index
else:
self.__rindex = -1
if self.__connected:
self.channelChanged.emit()
@QtCore.pyqtSlot(int)
def _onGChannelChanged(self, index):
""" set green channel
"""
if index < self.__numberofchannels:
self.__gindex = index
else:
self.__gindex = -1
if self.__connected:
self.channelChanged.emit()
@QtCore.pyqtSlot(int)
def _onBChannelChanged(self, index):
""" set blue channel
"""
if index < self.__numberofchannels:
self.__bindex = index
else:
self.__bindex = -1
if self.__connected:
self.channelChanged.emit()
[docs] def updateChannelLabels(self, chlabels=None):
""" update channel labels
:param chlabels: dictionary with channel labels
:type chlabels: :obj:`dict` <:obj:`int` :obj:`str`>
"""
if isinstance(chlabels, dict):
for ky, vl in chlabels.items():
if not vl:
if ky in self.__channellabels.keys():
self.__channellabels.pop(ky)
else:
try:
self.__channellabels[int(ky)] = vl
self.setChannelItemText(int(ky), vl)
except Exception as e:
logger.warning(str(e))
# print(str(e))
else:
self.__channellabels = {}
self.__numberofchannels = 0
self.__ui.channelGroupBox.hide()
self.__ui.channelComboBox.hide()
self.__colors = False
[docs] def channelLabels(self):
""" provides channel labels
:returns: list of channel labels
:rtype: :obj:`list` <:obj:`str`>
"""
return [
(self.__channellabels[i]
if i in self.__channellabels else "")
for i in range(len(self.__channellabels.keys()))]
[docs] def setChannelItemText(self, iid, text):
""" sets channel item text
:param iid: label id
:type iid: :obj:`int`
:param iid: label text
:type iid: :obj:`str`
"""
self.__ui.channelComboBox.setItemText(
iid + 1, text)
self.__ui.channelComboBox.setItemData(
iid + 1, text, QtCore.Qt.ToolTipRole)
[docs] def updateRChannel(self):
""" update red channel
"""
current = self.__ui.rComboBox.currentIndex()
if self.__rindex != self.__numberofchannels:
if self.__rindex != current:
self.__ui.rComboBox.setCurrentIndex(self.__rindex)
if self.__rindex == -1:
self.__ui.rComboBox.setCurrentIndex(self.__numberofchannels)
[docs] def updateGChannel(self):
""" update green channel
"""
current = self.__ui.gComboBox.currentIndex()
if self.__gindex != self.__numberofchannels:
if self.__gindex != current:
self.__ui.gComboBox.setCurrentIndex(self.__gindex)
if self.__gindex == -1:
self.__ui.gComboBox.setCurrentIndex(self.__numberofchannels)
[docs] def updateBChannel(self):
""" update blue channel
"""
current = self.__ui.bComboBox.currentIndex()
if self.__bindex != self.__numberofchannels:
if self.__bindex != current:
self.__ui.bComboBox.setCurrentIndex(self.__bindex)
if self.__bindex == -1:
self.__ui.bComboBox.setCurrentIndex(self.__numberofchannels)
[docs] def rgbchannels(self):
return (self.__rindex, self.__gindex, self.__bindex)
@QtCore.pyqtSlot()
def _lowerchannelpushed(self):
""" select the one-lower channel
"""
channel = self.__ui.channelHorizontalSlider.value()
channel -= 1
if channel >= 0:
self.__ui.channelHorizontalSlider.setValue(channel)
@QtCore.pyqtSlot()
def _higherchannelpushed(self):
""" select the one-higher channel
"""
channel = self.__ui.channelHorizontalSlider.value()
channel += 1
if channel <= self.__numberofchannels + 2:
self.__ui.channelHorizontalSlider.setValue(channel)
[docs] def colorChannel(self):
""" provides color channel
:returns: color channel
:rtype: :obj:`int`
"""
return self.__colorchannel
[docs] def channelLabel(self):
""" provides color channel label
:returns: color channel label
:rtype: :obj:`str`
"""
channel = self.__colorchannel
if channel == -1 or self.__numberofchannels + 2 == channel:
label = ",".join([str(ch) for ch in self.rgbchannels()])
elif channel == -2 or self.__numberofchannels + 1 == channel:
label = 'mean'
elif channel == 0:
if self.__numberofchannels:
label = 'sum'
else:
label = ""
else:
label = str(channel - 1)
return label
def __connectChannels(self):
""" connects channel signals
"""
self.__ui.channelComboBox.currentIndexChanged.connect(
self.setChannel)
self.__ui.channelHorizontalSlider.sliderReleased.connect(
self._setWidgetChannel)
self.__ui.channelHorizontalSlider.sliderPressed.connect(
self._skipChannels)
self.__ui.channelHorizontalSlider.valueChanged.connect(
self._setChannelTips)
self.__ui.lowerchannelPushButton.clicked.connect(
self._lowerchannelpushed)
self.__ui.higherchannelPushButton.clicked.connect(
self._higherchannelpushed)
self.__connected = True
def __disconnectChannels(self):
""" connects channel signals
"""
self.__ui.channelComboBox.currentIndexChanged.disconnect(
self.setChannel)
self.__ui.channelHorizontalSlider.sliderReleased.disconnect(
self._setWidgetChannel)
self.__ui.channelHorizontalSlider.sliderPressed.disconnect(
self._skipChannels)
self.__ui.channelHorizontalSlider.valueChanged.disconnect(
self._setChannelTips)
self.__ui.lowerchannelPushButton.clicked.disconnect(
self._lowerchannelpushed)
self.__ui.higherchannelPushButton.clicked.disconnect(
self._higherchannelpushed)
self.__connected = False
[docs] def setNumberOfChannels(self, number):
""" sets maximum number of color channel
:param number: number of color channel
:type number: :obj:`int`
"""
if number != self.__numberofchannels:
self.__disconnectChannels()
self.__numberofchannels = int(max(number, 0))
if self.__numberofchannels > 0:
for i in reversed(
range(0, self.__ui.channelComboBox.count())):
self.__ui.channelComboBox.removeItem(i)
self.__ui.channelComboBox.addItem("sum")
# self.__ui.channelComboBox.setSizeAdjustPolicy(
# QtWidgets.QComboBox.AdjustToMinimumContentsLength)
self.__ui.channelGroupBox.show()
self.__ui.channelComboBox.show()
self.__ui.channelComboBox.setSizeAdjustPolicy(
QtWidgets.QComboBox.AdjustToContents)
self.__ui.channelComboBox.addItems(
["channel %s" % (ch)
for ch in range(self.__numberofchannels)])
for ky, vl in self.__channellabels.items():
if vl and ky < self.__numberofchannels:
self.setChannelItemText(ky, vl)
self.__ui.channelComboBox.addItem("mean")
self.__ui.channelComboBox.addItem("RGB")
self.__colors = True
for i in reversed(
range(0, self.__ui.rComboBox.count())):
self.__ui.rComboBox.removeItem(i)
for i in reversed(
range(0, self.__ui.gComboBox.count())):
self.__ui.gComboBox.removeItem(i)
for i in reversed(
range(0, self.__ui.bComboBox.count())):
self.__ui.bComboBox.removeItem(i)
self.__ui.rComboBox.addItems(
["%s" % (ch)
for ch in range(self.__numberofchannels)])
self.__ui.bComboBox.addItems(
["%s" % (ch)
for ch in range(self.__numberofchannels)])
self.__ui.gComboBox.addItems(
["%s" % (ch)
for ch in range(self.__numberofchannels)])
self.__ui.rComboBox.addItem("None")
self.__ui.bComboBox.addItem("None")
self.__ui.gComboBox.addItem("None")
if self.__numberofchannels > self.__defrindex:
self.__rindex = self.__defrindex
else:
self.__rindex = 0
if self.__numberofchannels > self.__defgindex:
self.__gindex = self.__defgindex
elif self.__numberofchannels > 1:
self.__gindex = 1
else:
self.__gindex = -1
if self.__numberofchannels > self.__defbindex:
self.__bindex = self.__defbindex
elif self.__numberofchannels > 2:
self.__bindex = 2
else:
self.__bindex = -1
self.__ui.channelHorizontalSlider.setMaximum(
self.__numberofchannels + 2)
self.updateRChannel()
self.updateGChannel()
self.updateBChannel()
else:
self.__ui.channelGroupBox.hide()
self.__ui.channelComboBox.hide()
self.__colors = False
channel = 0
if self.__defaultchannel >= 0:
if self.__numberofchannels + 2 > self.__defaultchannel:
channel = self.__defaultchannel
else:
if self.__numberofchannels + 2 > - self.__defaultchannel:
channel = self.__numberofchannels + 3 \
+ self.__defaultchannel
self.__ui.channelHorizontalSlider.setValue(channel)
self.__ui.channelComboBox.setCurrentIndex(channel)
self.setChannel(channel, True)
self.__connectChannels()
self.channelChanged.emit()