Source code for lavuelib.imageWidget
# 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>
#
""" image widget """
from .qtuic import uic
import pyqtgraph as _pg
from pyqtgraph import QtCore
try:
from pyqtgraph import QtWidgets
except Exception:
from pyqtgraph import QtGui as QtWidgets
import re
import os
import json
import numpy as np
import logging
from . import imageDisplayWidget
from . import displayExtensions
from . import messageBox
from . import imageSource as isr
from . import toolWidget
from . import memoExportDialog
from . import sardanaUtils
from .sardanaUtils import debugmethod
# _VMAJOR, _VMINOR, _VPATCH = _pg.__version__.split(".") \
# if _pg.__version__ else ("0", "9", "0")
_formclass, _baseclass = uic.loadUiType(
os.path.join(os.path.dirname(os.path.abspath(__file__)),
"ui", "ImageWidget.ui"))
logger = logging.getLogger("lavue")
[docs]class ImageWidget(QtWidgets.QWidget):
"""
The part of the GUI that incorporates the image view.
"""
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) current tool changed signal
currentToolChanged = QtCore.pyqtSignal(str)
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) roi number changed signal
roiNumberChanged = QtCore.pyqtSignal(int)
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) roi coordinate changed signal
roiCoordsChanged = QtCore.pyqtSignal()
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) mesh coordinate changed signal
meshCoordsChanged = QtCore.pyqtSignal()
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) roi Line Edit changed signal
roiLineEditChanged = QtCore.pyqtSignal()
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) roi aliases changed signal
roiAliasesChanged = QtCore.pyqtSignal(str)
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) roi value changed signal
roiValueChanged = QtCore.pyqtSignal(str, int, str)
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) cut number changed signal
cutNumberChanged = QtCore.pyqtSignal(int)
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) cut coordinate changed signal
cutCoordsChanged = QtCore.pyqtSignal()
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) image plotted signal
imagePlotted = QtCore.pyqtSignal()
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) replot image signal
replotImage = QtCore.pyqtSignal(bool)
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) sardana enabled signal
sardanaEnabled = QtCore.pyqtSignal(bool)
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) aspect locked toggled signal
aspectLockedToggled = QtCore.pyqtSignal(bool)
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) apply tips changed signal
applyTipsChanged = QtCore.pyqtSignal(int)
#: (:class:`pyqtgraph.QtCore.pyqtSignal`)
# mouse image position changed signal
mouseImagePositionChanged = QtCore.pyqtSignal()
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) mouse double clicked
mouseImageDoubleClicked = QtCore.pyqtSignal(float, float)
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) mouse single clicked
mouseImageSingleClicked = QtCore.pyqtSignal(float, float)
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) geometry changed
geometryChanged = QtCore.pyqtSignal()
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) freeze clicked signal
freezeBottomPlotClicked = QtCore.pyqtSignal()
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) clear clicked signal
clearBottomPlotClicked = QtCore.pyqtSignal()
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) scales changed signal
scalesChanged = QtCore.pyqtSignal()
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) colors changed signal
colorsChanged = QtCore.pyqtSignal(str)
#: (:class:`pyqtgraph.QtCore.pyqtSignal`) tool configuration changed signal
toolConfigurationChanged = QtCore.pyqtSignal()
def __init__(self, parent=None, tooltypes=None, settings=None):
""" constructor
:param parent: parent object
:type parent: :class:`pyqtgraph.QtCore.QObject`
:param tooltypes: tool class names
:type tooltypes: :obj:`list` <:obj:`str`>
:param settings: lavue configuration settings
:type settings: :class:`lavuelib.settings.Settings`
"""
QtWidgets.QWidget.__init__(self, parent)
#: (:obj:`list` < :obj:`str` > ) tool class names
self.__tooltypes = tooltypes or []
#: (:obj:`list` < :obj:`str` > ) tool names
self.__toolnames = []
#: (:obj:`dict` < :obj:`str`,
#: :class:`lavuelib.toolWidget.BaseToolWidget` > )
#: tool names
self.__toolwidgets = {}
#: (:class:`pyqtgraph.QtCore.QMutex`) mutex lock for CB
self.__mutex = QtCore.QMutex()
#: (:class:`lavuelib.settings.Settings`) settings
self.__settings = settings
#: (:class:`lavuelib.controllerClient.ControllerClient`)
#: tango controller client
self.__tangoclient = None
#: (obj`list`) collection of last writing rois
self.__lastrois = []
#: (obj`list`) collection of last writing rois values
self.__lastroisvalues = []
#: (obj`list`) collection of last writing rois parameters
self.__lastroisparams = tuple()
#: (obj`str`) last text
self.__lasttext = ""
#: (obj`str`) roi labels
self.roilabels = ""
#: (:class:`lavuelib.toolWidget.BaseToolWidget`) current tool
self.__currenttool = None
#: (:class:`numpy.ndarray`) data to displayed in 2d widget
self.__data = None
#: (:class:`numpy.ndarray`) raw data to cut plots
self.__rawdata = None
#: (:obj:`bool`) apply mask
self.__applymask = False
#: (:class:`numpy.ndarray`) mask image indices
self.__maskindices = None
#: (:class:`numpy.ndarray`) mask image value indices
self.__maskvalueindices = None
#: (:obj:`float`) file name
self.__maskvalue = None
#: (:class:`numpy.ndarray`) overflow image value indices
self.__overflowvalueindices = None
#: (:obj:`float`) file name
self.__overflowvalue = None
#: (:obj:`str`) image name
self.__imagename = None
#: ( ( :obj:`bool`, :obj:`bool`,:obj:`bool`) )
# selected (transpose, leftright-flip, updown-flip )
self.__selectedtrans = (False, False, False)
#: (:class:`Ui_ImageWidget') ui_imagewidget object from qtdesigner
self.__ui = _formclass()
self.__ui.setupUi(self)
#: (:class:`lavuelib.imageDisplayWidget.ImageDisplayWidget`)
#: 2D image display widget
self.__displaywidget = imageDisplayWidget.ImageDisplayWidget(
parent=self)
self.__displaywidget.addExtensions(
[
displayExtensions.ROIExtension,
displayExtensions.CutExtension,
displayExtensions.LockerExtension,
displayExtensions.CenterExtension,
displayExtensions.MarkExtension,
displayExtensions.TrackingExtension,
displayExtensions.MeshExtension,
displayExtensions.MaximaExtension,
displayExtensions.VHBoundsExtension,
displayExtensions.RegionsExtension,
]
)
#: (:class:`pyqtgraph.PlotWidget`) bottom 1D plot widget
self.__bottomplot = memoExportDialog.MemoPlotWidget(self)
self.__bottomplot.addLegend()
self.__bottomplot.plotItem.legend.hide()
self.__bottomplot.getViewBox().menu.ctrl[0].invertCheck.hide()
self.__bottomplot.getViewBox().menu.ctrl[0].mouseCheck.hide()
self.__bottomplot.getViewBox().menu.ctrl[0].linkCombo.hide()
self.__bottomplot.getViewBox().menu.ctrl[0].autoPanCheck.hide()
self.__bottomplot.getViewBox().menu.ctrl[0].visibleOnlyCheck.hide()
self.__bottomplot.getViewBox().menu.ctrl[0].label.hide()
self.__bottomplot.getViewBox().menu.ctrl[1].mouseCheck.hide()
self.__bottomplot.getViewBox().menu.ctrl[1].linkCombo.hide()
self.__bottomplot.getViewBox().menu.ctrl[1].autoPanCheck.hide()
self.__bottomplot.getViewBox().menu.ctrl[1].visibleOnlyCheck.hide()
self.__bottomplot.getViewBox().menu.ctrl[1].label.hide()
#: (:class:`pyqtgraph.PlotWidget`) right 1D plot widget
self.__rightplot = memoExportDialog.MemoPlotWidget(self)
self.__rightplot.getViewBox().menu.ctrl[0].mouseCheck.hide()
self.__rightplot.getViewBox().menu.ctrl[0].linkCombo.hide()
self.__rightplot.getViewBox().menu.ctrl[0].autoPanCheck.hide()
self.__rightplot.getViewBox().menu.ctrl[0].visibleOnlyCheck.hide()
self.__rightplot.getViewBox().menu.ctrl[0].label.hide()
self.__rightplot.getViewBox().menu.ctrl[1].invertCheck.hide()
self.__rightplot.getViewBox().menu.ctrl[1].mouseCheck.hide()
self.__rightplot.getViewBox().menu.ctrl[1].linkCombo.hide()
self.__rightplot.getViewBox().menu.ctrl[1].autoPanCheck.hide()
self.__rightplot.getViewBox().menu.ctrl[1].visibleOnlyCheck.hide()
self.__rightplot.getViewBox().menu.ctrl[1].label.hide()
self.__ui.twoDVerticalLayout.addWidget(self.__displaywidget)
self.__ui.oneDBottomVerticalLayout.addWidget(self.__bottomplot)
self.__ui.oneDRightHorizontalLayout.addWidget(self.__rightplot)
sizePolicy = QtWidgets.QSizePolicy(QtWidgets.QSizePolicy.Preferred,
QtWidgets.QSizePolicy.Preferred)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(15)
sizePolicy.setHeightForWidth(
self.__displaywidget.sizePolicy().hasHeightForWidth())
self.__displaywidget.setSizePolicy(sizePolicy)
self.__ui.upperPlotWidget.setSizePolicy(sizePolicy)
self.__ui.rgbtoolComboBox.hide()
# if _VMAJOR == '0' and int(_VMINOR) < 10 and int(_VPATCH) < 9:
# self.__bottomplot.setMinimumSize(QtCore.QSize(0, 170))
self.__addToolWidgets()
self.__ui.plotSplitter.setStretchFactor(0, 50)
self.__ui.plotSplitter.setStretchFactor(1, 1)
self.__ui.toolSplitter.setStretchFactor(0, 2000)
self.__ui.toolSplitter.setStretchFactor(1, 1)
self.__displaywidget.extension('cuts').cutCoordsChanged.connect(
self.emitCutCoordsChanged)
self.__ui.toolComboBox.currentIndexChanged.connect(
self.showCurrentTool)
self.__displaywidget.aspectLockedToggled.connect(
self.emitAspectLockedToggled)
self.__displaywidget.mouseImagePositionChanged.connect(
self._emitMouseImagePositionChanged)
self.__displaywidget.mouseImageDoubleClicked.connect(
self._emitMouseImageDoubleClicked)
self.__displaywidget.mouseImageSingleClicked.connect(
self._emitMouseImageSingleClicked)
self.__bottomplot.freezeClicked.connect(
self._emitFreezeBottomPlotClicked)
self.__bottomplot.clearClicked.connect(
self._emitClearBottomPlotClicked)
self.__sardana = None
self.__connectsplitters()
self.roiLineEditChanged.emit()
[docs] def updateToolComboBox(self, toolnames, name=None):
""" set tool by changing combobox
:param toolnames: tool names
:type toolnames: :obj:`list` < :obj:`str` >
:param index: combobox index
:type index: :obj:`int`
"""
self.__ui.toolComboBox.currentIndexChanged.disconnect(
self.showCurrentTool)
if toolnames and name and name not in toolnames:
toolnames = list(toolnames)
toolnames.append(name)
toolnames = [sr for sr in toolnames if sr in self.__toolnames]
if not toolnames:
toolnames = self.__toolnames
name = name or str(self.__ui.toolComboBox.currentText())
self.__ui.toolComboBox.clear()
self.__ui.toolComboBox.addItems(toolnames)
if self.__ui.toolComboBox.count() == 0:
self.__ui.toolComboBox.addItems(self.__toolnames)
index = self.__ui.toolComboBox.findText(name)
if index == -1:
index = 0
self.__ui.toolComboBox.setCurrentIndex(index)
self.__ui.toolComboBox.currentIndexChanged.connect(
self.showCurrentTool)
[docs] def writeAttribute(self, name, value):
""" writes attribute value of device
:param name: attribute name
:type name: :obj:`str`
:param value: attribute value
:type value: :obj:`any`
"""
if self.__tangoclient:
self.__tangoclient.writeAttribute(name, value)
[docs] def writeDetectorAttributes(self):
""" write detector settings from ai object
"""
self.writeAttribute("BeamCenterX",
float(self.__settings.centerx))
self.writeAttribute("BeamCenterY",
float(self.__settings.centery))
self.writeAttribute("Energy", float(self.__settings.energy))
self.writeAttribute("DetectorDistance",
float(self.__settings.detdistance))
self.writeAttribute("PixelSizeX",
float(self.__settings.pixelsizex))
self.writeAttribute("PixelSizeY",
float(self.__settings.pixelsizey))
[docs] @QtCore.pyqtSlot()
def writeDetectorROIsAttribute(self):
""" writes DetectorROIsattribute value of device
"""
if self.__tangoclient:
rois = {}
slabel = re.split(';|,| |\n', str(self.roilabels))
slabel = [lb for lb in slabel if lb]
if len(slabel) == 0:
slabel = ["__null__"]
rid = 0
lastcrdlist = None
toadd = []
lastalias = None
roicoords = self.__displaywidget.extension('rois').roiCoords()
self.__lastrois = list(roicoords)
for alias in slabel:
if alias not in toadd:
rois[alias] = []
lastcrdlist = rois[alias]
if rid < len(roicoords):
lastcrdlist.append(roicoords[rid])
rid += 1
if alias not in toadd:
toadd.append(alias)
if not lastcrdlist:
if alias in rois.keys():
rois.pop(alias)
toadd.append(alias)
lastalias = alias
if rid > 0:
while rid < len(roicoords):
lastcrdlist.append(roicoords[rid])
rid += 1
if not lastcrdlist:
if lastalias in rois.keys():
rois.pop(lastalias)
toadd.append(lastalias)
self.__tangoclient.writeAttribute("DetectorROIs", json.dumps(rois))
[docs] def writeDetectorROIsValuesAttribute(self, rvalues):
""" writes DetectorROIsValuesattribute of device
:param rvalues: list of roi values
:type rvalues: `obj`list < :obj:`float`>
"""
if self.__tangoclient:
rois = {}
slabel = re.split(';|,| |\n', str(self.roilabels))
slabel = [lb for lb in slabel if lb]
if len(slabel) == 0:
slabel = ["__null__"]
rid = 0
lastcrdlist = None
toadd = []
lastalias = None
if rvalues is None:
self.__tangoclient.writeAttribute(
"DetectorROIsValues", json.dumps({}))
return
self.__lastroisvalues = rvalues
for alias in slabel:
if alias not in toadd:
rois[alias] = []
lastcrdlist = rois[alias]
if rid < len(rvalues):
lastcrdlist.append(
np.asscalar(rvalues[rid])
if hasattr(rvalues[rid], "item")
else rvalues[rid]
)
rid += 1
if alias not in toadd:
toadd.append(alias)
if not lastcrdlist:
if alias in rois.keys():
rois.pop(alias)
toadd.append(alias)
lastalias = alias
if rid > 0:
while rid < len(rvalues):
lastcrdlist.append(
np.asscalar(rvalues[rid])
if hasattr(rvalues[rid], "item")
else rvalues[rid]
)
rid += 1
if not lastcrdlist:
if lastalias in rois.keys():
rois.pop(lastalias)
toadd.append(lastalias)
self.__tangoclient.writeAttribute(
"DetectorROIsValues", json.dumps(rois))
[docs] def setTangoClient(self, tangoclient):
""" sets tango client
:param tangoclient: attribute name
:type tangoclient:
:class:`lavuelib.controllerClient.ControllerClient`
"""
self.__tangoclient = tangoclient
def __connectsplitters(self):
""" connects splitters signals
"""
self.__ui.lowerPlotSplitter.splitterMoved.connect(
self._moveUpperPlotSplitter)
self.__ui.upperPlotSplitter.splitterMoved.connect(
self._moveLowerPlotSplitter)
def __disconnectsplitters(self):
""" disconnects splitters signals
"""
try:
self.__ui.lowerPlotSplitter.splitterMoved.disconnect(
self._moveUpperPlotSplitter)
except Exception as e:
# print(str(e))
logger.warning(str(e))
try:
self.__ui.upperPlotSplitter.splitterMoved.disconnect(
self._moveLowerPlotSplitter)
except Exception as e:
# print(str(e))
logger.warning(str(e))
@QtCore.pyqtSlot(int, int)
def _moveLowerPlotSplitter(self, pos, index):
""" moves the lower plot splitter
"""
self.__disconnectsplitters()
self.__ui.lowerPlotSplitter.moveSplitter(pos, index)
self.__connectsplitters()
@QtCore.pyqtSlot(int, int)
def _moveUpperPlotSplitter(self, pos, index):
""" moves the upper plot splitter
"""
self.__disconnectsplitters()
self.__ui.upperPlotSplitter.moveSplitter(pos, index)
self.__connectsplitters()
[docs] def onedbottomplot(self, clear=False, name=None):
""" creates 1d bottom plot
:param clear: clear flag
:type clear: :obj:`bool`
:returns: 1d bottom plot
:rtype: :class:`pyqtgraph.PlotDataItem`
"""
return self.__bottomplot.plot(clear=clear, name=name)
[docs] def onedshowlegend(self, show=True):
""" shows/hides 1d bottom plot legend
:param status: show flag
:type status: :obj:`bool`
:returns: 1d bottom plot
:rtype: :class:`pyqtgraph.PlotDataItem`
"""
if show:
self.__bottomplot.plotItem.legend.show()
else:
legend = self.__bottomplot.plotItem.legend
its = [it[1].text for it in legend.items]
for it in its:
legend.removeItem(it)
legend.hide()
[docs] def bottomplotShowMenu(self, freeze=False, clear=False):
""" shows freeze or/and clean action in the menu
:param freeze: freeze show status
:type freeze: :obj:`bool`
:param freeze: clean show status
:type freeze: :obj:`bool`
"""
return self.__bottomplot.showMenu(freeze, clear)
[docs] def bottomplotStretch(self, stretch=1):
""" stretches the bottom plot
:param stretch: stretch factor
:type stretch: :obj:`int`
"""
self.__ui.plotSplitter.setStretchFactor(1, stretch)
if stretch >= 1000:
self.__ui.plotSplitter.setStretchFactor(0, 0)
self.__ui.plotSplitter.setSizes([0, 50])
else:
self.__ui.plotSplitter.setStretchFactor(0, 1)
[docs] def onedrightplot(self, clear=False):
""" creates 1d right plot
:param clear: clear flag
:type clear: :obj:`bool`
:returns: 1d right plot
:rtype: :class:`pyqtgraph.PlotDataItem`
"""
return self.__rightplot.plot()
[docs] def onedbarbottomplot(self):
""" creates 1d bottom bar plot
:returns: 1d bottom bar plot
:rtype: :class:`pyqtgraph.BarGraphItem`
"""
bg = _pg.BarGraphItem(x=[0], y0=[0.], y1=[0.], width=[1.], brush='b')
self.__bottomplot.addItem(bg)
return bg
[docs] def onedbarrightplot(self):
""" creates 1d right bar plot
:returns: 1d right bar plot
:rtype: :class:`pyqtgraph.BarGraphItem`
"""
bg = _pg.BarGraphItem(x0=[0.], x1=[0.], y=[0.], height=[1.], brush='b')
self.__rightplot.addItem(bg)
return bg
[docs] def removebottomplot(self, plot):
""" removes bottom plot
:param plot: right plot item
:type plot: :class:`pyqtgraph.PlotItem`
"""
self.__bottomplot.removeItem(plot)
[docs] def removerightplot(self, plot):
""" removes right plot
:param plot: right plot item
:type plot: :class:`pyqtgraph.PlotItem`
"""
self.__rightplot.removeItem(plot)
def __addToolWidgets(self):
""" add tool subwidgets into grid layout
"""
for tt in self.__tooltypes:
twg = getattr(toolWidget, tt)(self)
self.__toolwidgets[twg.name] = twg
self.__toolnames.append(twg.name)
self.__ui.toolComboBox.addItem(twg.name)
self.__ui.toolVerticalLayout.addWidget(twg)
[docs] def settings(self):
""" provides settings
:returns: setting object
:rtype: :class:`lavuelib.settings.Settings`
"""
return self.__settings
@debugmethod
def __connecttool(self):
""" connect current tool widget
"""
if self.__currenttool:
for signal, slot in self.__currenttool.signal2slot:
if isinstance(signal, str):
signal = getattr(self, signal)
if isinstance(slot, str):
slot = getattr(self, slot)
signal.connect(slot)
self.__currenttool.activate()
[docs] @debugmethod
def disconnecttool(self):
""" disconnect current tool widget
"""
if self.__currenttool:
for signal, slot in self.__currenttool.signal2slot:
if isinstance(signal, str):
signal = getattr(self, signal)
if isinstance(slot, str):
slot = getattr(self, slot)
try:
signal.disconnect(slot)
except Exception as e:
# print(str(e))
logger.warning(str(e))
self.__currenttool.deactivate()
[docs] def updateMetaData(self, axisscales=None, axislabels=None,
rescale=False):
""" update Metadata informations
:param axisscales: [xstart, ystart, xscale, yscale]
:type axisscales:
[:obj:`float`, :obj:`float`, :obj:`float`, :obj:`float`]
:param axislabels: [xtext, ytext, xunits, yunits]
:type axislabels:
[:obj:`float`, :obj:`float`, :obj:`float`, :obj:`float`]
:param rescale: rescale or select range window
:type rescale: :obj:`True`
"""
self.__displaywidget.updateMetaData(axisscales, axislabels,
rescale)
self.scalesChanged.emit()
[docs] @QtCore.pyqtSlot(int)
def updateROIs(self, rid, coords=None):
""" update ROIs
:param rid: roi id
:type rid: :obj:`int`
:param coords: roi coordinates
:type coords: :obj:`list`
< [:obj:`float`, :obj:`float`, :obj:`float`, :obj:`float`] >
"""
self.__displaywidget.extension('rois').updateROIs(rid, coords)
self.applyTipsChanged.emit(rid)
self.roiCoordsChanged.emit()
self.roiNumberChanged.emit(rid)
[docs] @QtCore.pyqtSlot(int)
def updateRegions(self, points=None, rid=None):
""" update Ranges
:param points: roi coordinates
:type points: :obj:`list` < :obj:`list` < :obj:`list`
< (:obj:`float`, :obj:`float`) > > >
:param rid: region id
:type rid: :obj:`int`
"""
self.__displaywidget.extension('regions').updateRegions(points, rid)
# self.applyTipsChanged.emit(rid)
# self.roiCoordsChanged.emit()
# self.roiNumberChanged.emit(rid)
[docs] @QtCore.pyqtSlot(int)
def updateCuts(self, cid, coords=None):
""" update Cuts
:param cid: cut id
:type cid: :obj:`int`
:param coords: cut coordinates and width
:type coords: :obj:`list`
< [float, float, float, float, float] >
"""
self.__displaywidget.extension('cuts').updateCuts(cid, coords)
self.cutCoordsChanged.emit()
self.cutNumberChanged.emit(cid)
[docs] def currentTool(self):
""" provides the current tool
:returns: current tool name
:rtype: :obj:`str`
"""
return str(self.__ui.toolComboBox.currentText())
[docs] @debugmethod
@QtCore.pyqtSlot()
def showCurrentTool(self):
""" shows the current tool
"""
with QtCore.QMutexLocker(self.__mutex):
text = self.__ui.toolComboBox.currentText()
stwg = None
self.__ui.toolComboBox.show()
for nm, twg in self.__toolwidgets.items():
if text == nm:
stwg = twg
else:
twg.hide()
self.disconnecttool()
self.__currenttool = stwg
if stwg is not None:
stwg.show()
self.updateinfowidgets(stwg.parameters)
self.__connecttool()
self.currentToolChanged.emit(text)
# @debugmethod
[docs] def showTool(self, text):
""" shows the current tool
"""
stwg = None
for nm, twg in self.__toolwidgets.items():
if text == nm:
stwg = twg
else:
twg.hide()
self.disconnecttool()
self.__currenttool = stwg
if stwg is not None:
stwg.show()
self.updateinfowidgets(stwg.parameters)
self.__connecttool()
self.currentToolChanged.emit(text)
[docs] def updateinfowidgets(self, parameters):
self.__displaywidget.setSubWidgets(parameters)
self.__updateinfowidgets(parameters)
def __updateinfowidgets(self, parameters):
""" update info widgets
:param parameters: tool parameters
:type parameters: :class:`lavuelib.toolWidget.ToolParameters`
"""
if parameters.infolabel is None:
self.__ui.infoLabel.hide()
else:
self.__ui.infoLabel.setText(parameters.infolabel)
if parameters.infotips is not None:
self.__ui.infoLabel.setToolTip(parameters.infotips)
self.__ui.infoLabel.show()
if parameters.infolineedit is None:
self.__ui.infoLineEdit.hide()
else:
self.__ui.infoLineEdit.setText(parameters.infolineedit)
if parameters.infotips is not None:
self.__ui.infoLineEdit.setToolTip(parameters.infotips)
self.__ui.infoLineEdit.show()
if parameters.bottomplot is True:
self.__bottomplot.show()
self.__ui.oneDBottomWidget.show()
self.__ui.lowerPlotWidget.show()
elif parameters.bottomplot is False:
self.__bottomplot.hide()
self.__ui.oneDBottomWidget.hide()
self.__ui.lowerPlotWidget.hide()
if parameters.rightplot is True:
self.__rightplot.show()
self.__ui.cornerWidget.show()
self.__ui.oneDRightWidget.show()
smin, smax = self.__ui.upperPlotSplitter.getRange(1)
self._moveUpperPlotSplitter(int((smax-smin)*4/5.), 1)
self._moveLowerPlotSplitter(int((smax-smin)*4/5.), 1)
elif parameters.rightplot is False:
self.__rightplot.hide()
self.__ui.cornerWidget.hide()
self.__ui.oneDRightWidget.hide()
[docs] def setTransformations(self, transpose, leftrightflip, updownflip,
orgtranspose, orgleftrightflip, orgupdownflip):
""" sets coordinate transformations
:param transpose: transpose coordinates flag
:type transpose: :obj:`bool`
:param leftrightflip: left-right flip coordinates flag
:type leftrightflip: :obj:`bool`
:param updownflip: up-down flip coordinates flag
:type updownflip: :obj:`bool`
:param orgtranspose: selected transpose coordinates flag
:type orgtranspose: :obj:`bool`
:param orgleftrightflip: selected left-right flip coordinates flag
:type orgleftrightflip: :obj:`bool`
:param orgupdownflip: selected up-down flip coordinates flag
:type orgupdownflip: :obj:`bool`
"""
oldtrans, oldleftright, oldupdown, _ = \
self.__displaywidget.transformations()
if oldleftright != leftrightflip:
if hasattr(self.__bottomplot.getViewBox(), "invertX"):
self.__bottomplot.getViewBox().invertX(leftrightflip)
else:
""" version 0.9.10 without invertX """
# workaround for a bug in old pyqtgraph versions: stretch 0.10
self.__bottomplot.getViewBox().sigXRangeChanged.emit(
self.__bottomplot.getViewBox(),
tuple(self.__bottomplot.getViewBox().state['viewRange'][0]))
self.__bottomplot.getViewBox().sigYRangeChanged.emit(
self.__bottomplot.getViewBox(),
tuple(self.__bottomplot.getViewBox().state['viewRange'][1]))
if oldupdown != updownflip:
self.__rightplot.getViewBox().invertY(updownflip)
# workaround for a bug in old pyqtgraph versions: stretch 0.9.10
self.__rightplot.getViewBox().sigXRangeChanged.emit(
self.__rightplot.getViewBox(),
tuple(self.__rightplot.getViewBox().state['viewRange'][0]))
self.__rightplot.getViewBox().sigYRangeChanged.emit(
self.__rightplot.getViewBox(),
tuple(self.__rightplot.getViewBox().state['viewRange'][1]))
self.__selectedtrans = (orgtranspose, orgleftrightflip, orgupdownflip)
self.__displaywidget.setTransformations(
transpose, leftrightflip, updownflip,
orgtranspose)
self.scalesChanged.emit()
if self.__tangoclient:
if self.__selectedtrans != self.__lastroisparams or \
self.__lastkeepcoords != self.__settings.keepcoords:
if not self.__settings.keepcoords:
selectedtrans = self.__selectedtrans
else:
selectedtrans = (False, False, False)
pars = {
"transpose": selectedtrans[0],
"flip-left-right": selectedtrans[1],
"flip-up-down": selectedtrans[2]
}
lpars = [tr for tr in sorted(pars.keys()) if pars[tr]]
self.__tangoclient.writeAttribute(
"DetectorROIsParams", json.dumps(lpars))
self.__lastroisparams = self.__selectedtrans
self.__lastkeepcoords = self.__settings.keepcoords
[docs] def transformations(self):
""" povides coordinates transformations
:returns: transpose, leftrightflip, updownflip flags,
original transpose
:rtype: (:obj:`bool`, :obj:`bool`, :obj:`bool`, :obj:`bool`)
"""
return self.__displaywidget.transformations()
[docs] def plot(self, array, rawarray=None, imagename=None, maskarray=None):
""" plots the image
:param array: 2d image array
:type array: :class:`numpy.ndarray`
:param rawarray: 2d raw image array
:type rawarray: :class:`numpy.ndarray`
:param imagename: image name
:type imagename: :obj:`str`
:param maskarray: 2d color mask image array
:type maskarray: :class:`numpy.ndarray`
"""
if array is None:
return
if rawarray is None:
rawarray = array
barrays = None
self.__data = array
self.__rawdata = rawarray
self.__imagename = imagename
self.__maskdata = maskarray
if self.__currenttool:
barrays = self.__currenttool.beforeplot(array, rawarray)
self.__displaywidget.updateImage(
barrays[0] if barrays is not None else self.__data,
barrays[1] if barrays is not None else self.__rawdata,
None if barrays is not None else self.__maskdata)
if self.__currenttool:
self.__currenttool.afterplot()
[docs] def updateImage(self, array=None, rawarray=None, maskarray=None):
""" update the image
:param array: 2d image array
:type array: :class:`numpy.ndarray`
:param rawarray: 2d raw image array
:type rawarray: :class:`numpy.ndarray`
:param maskarray: 2d color mask image array
:type maskarray: :class:`numpy.ndarray`
"""
self.__displaywidget.updateImage(
array if array is not None else self.__data,
rawarray if rawarray is not None else self.__rawdata,
maskarray if maskarray is not None else self.__maskdata)
[docs] @QtCore.pyqtSlot(int)
def setAutoLevels(self, autolevels):
""" sets auto levels
:param autolevels: 2: auto levels enabled 1: with autofactor
:type autolevels: :obj:'int`
"""
self.__displaywidget.setAutoLevels(autolevels)
[docs] @QtCore.pyqtSlot(int)
def setAutoDownSample(self, autodownsample):
""" sets auto down sample
:param autolevels: auto down sample enabled
:type autolevels: :obj:`bool`
"""
self.__displaywidget.setAutoDownSample(autodownsample)
[docs] @QtCore.pyqtSlot(float)
def setChannelLevels(self, levels=None):
""" sets minimum intensity levels
:param levels: channel intensity levels
:type levels: :obj:`list` < (:obj`float`:, :obj`float`:)>
"""
self.__displaywidget.setDisplayChannelLevels(levels)
[docs] @QtCore.pyqtSlot(float)
def setMinLevel(self, level=None):
""" sets minimum intensity level
:param level: minimum intensity
:type level: :obj:`float`
"""
self.__displaywidget.setDisplayMinLevel(level)
[docs] @QtCore.pyqtSlot(float)
def setMaxLevel(self, level=None):
""" sets maximum intensity level
:param level: maximum intensity
:type level: :obj:`float`
"""
self.__displaywidget.setDisplayMaxLevel(level)
[docs] @QtCore.pyqtSlot(str)
def setDisplayedText(self, text=None):
""" sets displayed info text and recalculates the current roi sum
:param text: text to display
:type text: :obj:`str`
"""
currentroi = None
sroiVal = ""
if text is not None:
self.__lasttext = text
else:
text = self.__lasttext
if self.__displaywidget.extension('rois').isROIsEnabled():
if self.__settings.showallrois:
currentroi = self.currentROI()
roiVals = self.__displaywidget.extension('rois').calcROIsums()
if roiVals is not None:
sroiVal = " / ".join(
[(("%g" % roiv) if roiv is not None else "?")
for roiv in roiVals])
if self.__settings.sendrois:
if self.__lastroisvalues != roiVals:
self.writeDetectorROIsValuesAttribute(roiVals)
else:
roiVal, currentroi = self.__displaywidget.\
extension('rois').calcROIsum()
if roiVal is not None:
sroiVal = "%.4f" % roiVal
if self.__settings.sendrois:
if self.__lastroisvalues != [roiVal]:
self.writeDetectorROIsValuesAttribute([roiVal])
if currentroi is not None:
self.roiValueChanged.emit(text, currentroi, sroiVal)
else:
self.__ui.infoLineEdit.setText(text)
[docs] def calcROIsum(self):
"""calculates the current roi sum
:returns: sum roi value, roi id
:rtype: (:obj:`str`, :obj:`int`)
"""
return self.__displaywidget.extension('rois').calcROIsum()
[docs] def calcROIsums(self):
""" calculates all roi sums
:returns: sum roi value, roi id
:rtype: :obj:`list` < float >
"""
return self.__displaywidget.extension('rois').calcROIsums()
[docs] def setExtensionsRefreshTime(self, refreshtime):
""" set display extension refresh time
:param refreshtime: refresh time in seconds
:type refreshtime: :obj:`float`
"""
for name in self.__displaywidget.extensions():
self.__displaywidget.extension(name).setRefreshTime(
refreshtime)
[docs] @QtCore.pyqtSlot(str)
def updateDisplayedText(self, text):
""" sets displayed info text
:param text: text to display
:type text: :obj:`str`
"""
self.__ui.infoLineEdit.setText(text)
[docs] @QtCore.pyqtSlot(str)
def updateDisplayedTextTip(self, text):
""" sets displayed info text tup
:param text: tip text to display
:type text: :obj:`str`
"""
self.__ui.infoLineEdit.setToolTip(text)
[docs] @QtCore.pyqtSlot()
def setTicks(self):
""" launch axes widget
:returns: apply status
:rtype: :obj:`bool`
"""
self.__displaywidget.setTicks()
self.emitTCC()
[docs] def updateTicks(self, record):
""" update Ticks values
:param record: dict record with the tick parameters:
"position" : [x, y]
"scale" : [sx, sy]
"xtext" : xlabel
"ytext" : ylabel
"xunits" : xunits
"yunits" : yunits
:type record: :obj:`dict`<:obj:`str`, `any`>
"""
self.__displaywidget.updateTicks(record)
self.emitTCC()
[docs] def image(self, iid=0):
""" provides imageItem object
:param iid: image id
:type iid: :obj:`int`
:returns: image object
:rtype: :class:`pyqtgraph.ImageItem`
"""
return self.__displaywidget.image(iid)
# @debugmethod
[docs] @QtCore.pyqtSlot()
def emitTCC(self):
"""emits toolConfigurationChanged
"""
self.toolConfigurationChanged.emit()
[docs] @QtCore.pyqtSlot()
def emitCutCoordsChanged(self):
"""emits cutCoordsChanged
"""
self.cutCoordsChanged.emit()
@QtCore.pyqtSlot()
def _emitFreezeBottomPlotClicked(self):
"""emits freezeBottomPlotClicked
"""
self.freezeBottomPlotClicked.emit()
@QtCore.pyqtSlot()
def _emitClearBottomPlotClicked(self):
"""emits clearBottomPlotClicked
"""
self.clearBottomPlotClicked.emit()
[docs] @QtCore.pyqtSlot(bool)
def emitAspectLockedToggled(self, status):
"""emits aspectLockedToggled
:param status: current state
:type status: :obj:`bool`
"""
self.aspectLockedToggled.emit(status)
@QtCore.pyqtSlot()
def _emitMouseImagePositionChanged(self):
"""emits mouseImagePositionChanged
"""
if self.__displaywidget.extension('rois').isROIsEnabled():
if self.__lastrois != self.__displaywidget.\
extension('rois').roiCoords():
self.writeDetectorROIsAttribute()
self.mouseImagePositionChanged.emit()
@QtCore.pyqtSlot(float, float)
def _emitMouseImageDoubleClicked(self, x, y):
"""emits mouseImageDoubleClicked
:param x: x pixel coordinate
:type x: :obj:`float`
:param y: y pixel coordinate
:type y: :obj:`float`
"""
self.mouseImageDoubleClicked.emit(x, y)
@QtCore.pyqtSlot(float, float)
def _emitMouseImageSingleClicked(self, x, y):
"""emits mouseImageSingleClicked
:param x: x pixel coordinate
:type x: :obj:`float`
:param y: y pixel coordinate
:type y: :obj:`float`
"""
self.mouseImageSingleClicked.emit(x, y)
[docs] def emitReplotImage(self, autorange=True):
"""emits replotImage
"""
self.replotImage.emit(autorange)
[docs] def setAspectLocked(self, status):
"""sets aspectLocked
:param status: state to set
:type status: :obj:`bool`
:returns: old state
:rtype: :obj:`bool`
"""
return self.__displaywidget.setAspectLocked(status)
[docs] def setStatsWOScaling(self, status):
""" sets statistics without scaling flag
:param status: statistics without scaling flag
:type status: :obj:`bool`
:returns: change status
:rtype: :obj:`bool`
"""
return self.__displaywidget.setStatsWOScaling(status)
[docs] def setColors(self, colors):
""" sets item colors
:param colors: json list of roi colors
:type colors: :obj:`str`
"""
for name in self.__displaywidget.extensions():
if hasattr(self.__displaywidget.extension(name), "setColors"):
self.__displaywidget.extension(name).setColors(colors)
self.colorsChanged.emit(colors)
[docs] def setOverflowColor(self, color):
""" sets item color
:param color: json list of overflow color
:type color: :obj:`str`
"""
self.__displaywidget.setOverflowColor(color)
[docs] def setScalingType(self, scalingtype):
""" sets intensity scaling types
:param scalingtype: intensity scaling type
:type scalingtype: :obj:`str`
"""
self.__displaywidget.setScalingType(scalingtype)
[docs] def setDoBkgSubtraction(self, state):
""" sets do background subtraction flag
:param status: do background subtraction flag
:type status: :obj:`bool`
"""
self.__displaywidget.setDoBkgSubtraction(state)
[docs] def setDoBFSubtraction(self, state):
""" sets do brightfield subtraction flag
:param status: do brightfield subtraction flag
:type status: :obj:`bool`
"""
self.__displaywidget.setDoBFSubtraction(state)
[docs] def setSardanaUtils(self, sardana):
""" sets sardana utils
:param sardana: sardana utils
:type sardana: :class:`lavuelib.sardanaUtils.SardanaUtils`
"""
if sardana:
self.sardanaEnabled.emit(True)
else:
self.sardanaEnabled.emit(False)
self.__sardana = sardana
[docs] def getDoor(self):
""" runs macro
:param command: command list
:type command: :obj:`list` <:obj:`str`>
:return: macro runned
:rtype: :obj:`bool`
"""
dp = None
if isr.TANGO:
if not self.__settings.doorname:
self.__settings.doorname = self.__sardana.getDeviceName("Door")
try:
dp = self.__sardana.openProxy(str(self.__settings.doorname))
dp.ping()
except Exception as e:
# print(str(e))
logger.warning(str(e))
dp = None
return dp
[docs] def getElementNames(self, listattr, typefilter=None):
""" provides experimental Channels
:param listattr: pool attribute with list
:type listattr: :obj:`str`
:param typefilter: pool attribute with list
:type typefilter: :obj:`list` <:obj:`str`>
:returns: names from given pool listattr
:rtype: :obj:`list` <:obj:`str`>
"""
elements = None
if isr.TANGO and self.__sardana:
if not self.__settings.doorname:
self.__settings.doorname = self.__sardana.getDeviceName("Door")
try:
elements = self.__sardana.getElementNames(
self.__settings.doorname,
listattr, typefilter)
except Exception as e:
# print(str(e))
logger.warning(str(e))
return elements
[docs] def runMacro(self, command):
""" runs macro
:param command: command list
:type command: :obj:`list` <:obj:`str`>
:return: macro runned
:rtype: :obj:`bool`
"""
if isr.TANGO:
if not self.__settings.doorname:
self.__settings.doorname = self.__sardana.getDeviceName("Door")
try:
_, warn = self.__sardana.runMacro(
str(self.__settings.doorname), command, wait=False)
if warn:
logger.warning("ImageWidget.runMacro %s" % str(warn))
# print("Warning: %s" % str(warn))
msg = str(warn)
messageBox.MessageBox.warning(
self, "lavue: Errors in running macro: %s" % command,
msg, str(warn))
except Exception:
import traceback
value = traceback.format_exc()
text = messageBox.MessageBox.getText(
"lavue: Errors in running macro: %s" % command)
messageBox.MessageBox.warning(
self, "lavue: Errors in running macro: %s" % command,
text, str(value))
return False
return True
return False
[docs] def showDoorError(self):
""" show door error
"""
if isr.TANGO:
if not self.__settings.doorname:
self.__settings.doorname = self.__sardana.getDeviceName("Door")
try:
warn = self.__sardana.getError(str(self.__settings.doorname))
if warn:
# print("Warning: %s" % str(warn))
logger.warning(str(warn))
msg = str(warn)
messageBox.MessageBox.warning(
self, "lavue: Errors in running macro ",
msg, str(warn))
except Exception:
import traceback
value = traceback.format_exc()
text = messageBox.MessageBox.getText(
"lavue: Errors in running macro")
messageBox.MessageBox.warning(
self, "lavue: Errors in running macro",
text, str(value))
[docs] @QtCore.pyqtSlot(str, int)
def applyROIs(self, rlabel, roispin):
""" saves ROIs in sardana and add them to the measurement group
:param rlabel: rois aliases separated by space
:type rlabel: :obj:`str`
:param roispin: the current number of rois
:type roispin: :obj:`int`
"""
if isr.TANGO:
if self.__settings.sardana:
if not self.__settings.doorname:
self.__settings.doorname = self.__sardana.getDeviceName(
"Door")
try:
rois = json.loads(self.__sardana.getScanEnv(
str(self.__settings.doorname), ["DetectorROIs"]))
except Exception:
import traceback
value = traceback.format_exc()
text = messageBox.MessageBox.getText(
"Problems in connecting to Door or MacroServer")
messageBox.MessageBox.warning(
self,
"lavue: Error in connecting to Door or MacroServer",
text, str(value))
return
else:
rois = {}
slabel = re.split(';|,| |\n', str(rlabel))
slabel = [lb for lb in slabel if lb]
rid = 0
lastcrdlist = None
toremove = []
toadd = []
if "DetectorROIs" not in rois or not isinstance(
rois["DetectorROIs"], dict):
rois["DetectorROIs"] = {}
lastalias = None
roicoords = self.__displaywidget.extension('rois').roiCoords()
for alias in slabel:
if alias not in toadd:
rois["DetectorROIs"][alias] = []
lastcrdlist = rois["DetectorROIs"][alias]
if rid < len(roicoords):
lastcrdlist.append(roicoords[rid])
rid += 1
if alias not in toadd:
toadd.append(alias)
if not lastcrdlist:
if alias in rois["DetectorROIs"].keys():
rois["DetectorROIs"].pop(alias)
if roispin >= 0:
toadd.append(alias)
else:
toremove.append(alias)
lastalias = alias
if rid > 0:
while rid < len(roicoords):
lastcrdlist.append(roicoords[rid])
rid += 1
if not lastcrdlist:
if lastalias in rois["DetectorROIs"].keys():
rois["DetectorROIs"].pop(lastalias)
if roispin >= 0:
toadd.append(lastalias)
else:
toremove.append(lastalias)
if not self.__settings.keepcoords:
selectedtrans = self.__selectedtrans
else:
selectedtrans = (False, False, False)
pars = {
"transpose": selectedtrans[0],
"flip-left-right": selectedtrans[1],
"flip-up-down": selectedtrans[2]
}
lpars = [tr for tr in sorted(pars.keys()) if pars[tr]]
rois["DetectorROIsParams"] = lpars
rois["DetectorROIsOrder"] = slabel
if self.__settings.sardana:
self.__sardana.setScanEnv(
str(self.__settings.doorname), json.dumps(rois))
warns = []
if self.__settings.addrois:
try:
for alias in toadd:
_, warn = self.__sardana.runMacro(
str(self.__settings.doorname),
["nxsadd", alias])
if warn:
warns.extend(list(warn))
# print("Warning: %s" % str(warn))
logger.warning(str(warn))
for alias in toremove:
_, warn = self.__sardana.runMacro(
str(self.__settings.doorname),
["nxsrm", alias])
if warn:
warns.extend(list(warn))
# print("Warning: %s" % str(warn))
logger.warning(str(warn))
if warns:
msg = "\n".join(set(warns))
messageBox.MessageBox.warning(
self,
"lavue: Errors in setting Measurement group",
msg, str(warns))
except Exception:
import traceback
value = traceback.format_exc()
text = messageBox.MessageBox.getText(
"Problems in setting Measurement group")
messageBox.MessageBox.warning(
self, "lavue: Error in Setting Measurement group",
text, str(value))
if self.__settings.analysisdevice:
flatrois = self._flattenROIs(roicoords)
try:
adp = sardanaUtils.SardanaUtils.openProxy(
str(self.__settings.analysisdevice))
adp.RoIs = flatrois
except Exception:
import traceback
value = traceback.format_exc()
text = messageBox.MessageBox.getText(
"Problems in setting RoIs for Analysis device")
messageBox.MessageBox.warning(
self, "lavue: Error in Setting Rois",
text, str(value))
else:
# print("Connection error")
logger.error("ImageWidget.applyROI: Connection error")
def _flattenROIs(self, roicoords):
""" calculate source rois coordinates from lavue rois coordinates
:param roicoords: lavue rois coordinates
:type roicoords: :obj:`list`
< [:obj:`float`, :obj:`float`, :obj:`float`, :obj:`float`] >
:returns: detector rois coordinates
:rtype: :obj:`list` < :obj:`float` >
"""
flatrois = []
if hasattr(self.__rawdata, "shape"):
sh = self.__rawdata.shape
else:
sh = (0, 0)
for crds in roicoords:
if self.__settings.keepcoords:
trans, leftright, updown, _ = \
self.__displaywidget.transformations()
flatrois.extend(
[crds[1], crds[3] + 1, crds[0], crds[2] + 1])
else:
trans, leftright, updown = self.__selectedtrans
if not trans and not leftright and not updown:
flatrois.extend(
[crds[1], crds[3] + 1,
crds[0], crds[2] + 1])
elif trans and not leftright and not updown:
flatrois.extend(
[crds[0], crds[2] + 1,
crds[1], crds[3] + 1])
###
elif not trans and leftright and not updown:
flatrois.extend(
[crds[1], crds[3] + 1,
sh[0] - crds[2] - 1, sh[0] - crds[0]])
elif trans and leftright and not updown:
flatrois.extend(
[sh[0] - crds[2] - 1, sh[0] - crds[0],
crds[1], crds[3] + 1])
###
elif not trans and not leftright and updown:
flatrois.extend(
[sh[1] - crds[3] - 1, sh[1] - crds[1],
crds[0], crds[2] + 1])
elif trans and not leftright and updown:
flatrois.extend(
[crds[0], crds[2] + 1,
sh[1] - crds[3] - 1, sh[1] - crds[1]])
###
elif not trans and leftright and updown:
flatrois.extend(
[sh[1] - crds[3] - 1, sh[1] - crds[1],
sh[0] - crds[2] - 1, sh[0] - crds[0]])
elif trans and leftright and updown:
flatrois.extend(
[sh[0] - crds[2] - 1, sh[0] - crds[0],
sh[1] - crds[3] - 1, sh[1] - crds[1]])
else:
raise Exception("Dead end")
flatrois = [max(cr, 0) for cr in flatrois]
if trans:
sha, shb = sh
else:
shb, sha = sh
for i in range(len(flatrois) // 4):
flatrois[4 * i] = min(flatrois[4 * i], sha)
flatrois[4 * i + 1] = min(flatrois[4 * i + 1], sha)
flatrois[4 * i + 2] = min(flatrois[4 * i + 2], shb)
flatrois[4 * i + 3] = min(flatrois[4 * i + 3], shb)
return flatrois
[docs] @QtCore.pyqtSlot(str)
def fetchROIs(self, rlabel):
""" loads ROIs from sardana
:param rlabel: rois aliases separated by space
:type rlabel: :obj:`str`
"""
if isr.TANGO:
if self.__settings.sardana:
if not self.__settings.doorname:
self.__settings.doorname = self.__sardana.getDeviceName(
"Door")
try:
rois = json.loads(self.__sardana.getScanEnv(
str(self.__settings.doorname),
["DetectorROIs", "DetectorROIsOrder"]))
except Exception:
import traceback
value = traceback.format_exc()
text = messageBox.MessageBox.getText(
"Problems in connecting to Door or MacroServer")
messageBox.MessageBox.warning(
self,
"lavue: Error in connecting to Door or MacroServer",
text, str(value))
return
if self.__settings.orderrois and "DetectorROIsOrder" in rois \
and isinstance(rois["DetectorROIsOrder"], list):
slabel = rois["DetectorROIsOrder"]
else:
slabel = re.split(';|,| |\n', str(rlabel))
slabel = [lb for lb in slabel if lb]
detrois = {}
if "DetectorROIs" in rois and isinstance(
rois["DetectorROIs"], dict):
detrois = rois["DetectorROIs"]
if slabel:
detrois = dict(
(k, v) for k, v in detrois.items() if k in slabel)
coords = []
aliases = []
if slabel:
for i, lb in enumerate(slabel):
if lb in detrois.keys():
if len(set(slabel[i:])) == 1:
v = detrois.pop(lb)
if isinstance(v, list):
for cr in v:
if isinstance(cr, list):
coords.append(cr)
aliases.append(lb)
break
else:
v = detrois[lb]
if isinstance(v, list) and v:
cr = v[0]
if isinstance(cr, list):
coords.append(cr)
aliases.append(lb)
detrois[lb] = v[1:]
if not detrois[lb]:
detrois.pop(lb)
for k, v in detrois.items():
if isinstance(v, list):
for cr in v:
if isinstance(cr, list):
coords.append(cr)
aliases.append(k)
slabel = []
for i, al in enumerate(aliases):
if len(set(aliases[i:])) == 1:
slabel.append(al)
break
else:
slabel.append(al)
self.roiAliasesChanged.emit(" ".join(slabel))
self.updateROIs(len(coords), coords)
elif self.__settings.analysisdevice:
try:
adp = sardanaUtils.SardanaUtils.openProxy(
str(self.__settings.analysisdevice))
flatrois = adp.RoIs
coords = self._fromFlatROIs(flatrois)
self.updateROIs(len(coords), coords)
except Exception:
import traceback
value = traceback.format_exc()
text = messageBox.MessageBox.getText(
"Problems in setting RoIs for Analysis device")
messageBox.MessageBox.warning(
self, "lavue: Error in Setting Rois",
text, str(value))
else:
# print("Connection error")
logger.error("ImageWidget.fetchROIs: Connection error")
def _fromFlatROIs(self, flatrois):
""" calculate lavue rois coordinates from source rois coordinates
:param roicoords: lavue rois coordinates
:type roicoords: :obj:`list` < :obj:`float` >
:returns: detector rois coordinates
:rtype: :obj:`list`
< [:obj:`float`, :obj:`float`, :obj:`float`, :obj:`float`] >
"""
coords = []
if hasattr(self.__rawdata, "shape"):
sh = self.__rawdata.shape
else:
sh = (0, 0)
for crds in zip(flatrois[::4], flatrois[1::4],
flatrois[2::4], flatrois[3::4]):
if self.__settings.keepcoords:
trans, leftright, updown, _ = \
self.__displaywidget.transformations()
coords.append([crds[2], crds[0], crds[3] - 1, crds[1] - 1])
else:
trans, leftright, updown = self.__selectedtrans
if not trans and not leftright and not updown:
coords.append([crds[2], crds[0], crds[3] - 1, crds[1] - 1])
elif trans and not leftright and not updown:
coords.append([crds[0], crds[2], crds[1] - 1, crds[3] - 1])
###
elif not trans and leftright and not updown:
coords.append([sh[0] - crds[3], crds[0],
sh[0] - crds[2] - 1, crds[1] - 1])
elif trans and leftright and not updown:
coords.append([sh[0] - crds[1], crds[2],
sh[0] - crds[0] - 1, crds[3] - 1])
elif not trans and not leftright and updown:
coords.append([crds[2], sh[1] - crds[1],
crds[3] - 1, sh[1] - crds[0] - 1])
elif trans and not leftright and updown:
coords.append([crds[0], sh[1] - crds[3],
crds[1] - 1, sh[1] - crds[2] - 1])
###
elif not trans and leftright and updown:
coords.append([sh[0] - crds[3], sh[1] - crds[1],
sh[0] - crds[2] - 1, sh[1] - crds[0] - 1])
elif trans and leftright and updown:
coords.append([sh[0] - crds[1], sh[1] - crds[3],
sh[0] - crds[0] - 1, sh[1] - crds[2] - 1])
else:
raise Exception("Dead end")
return coords
[docs] def currentIntensity(self):
""" provides intensity for current mouse position
:returns: x position, y position, pixel intensity
:rtype: (float, float, float)
"""
return self.__displaywidget.currentIntensity()
[docs] def scalingLabel(self):
""" provides scaling label
:returns: scaling label
:rtype: str
"""
return self.__displaywidget.scalingLabel()
[docs] def scaling(self):
""" provides scaling type
:returns: scaling type
:rtype: str
"""
return self.__displaywidget.scaling()
[docs] def scaledxy(self, x, y, useraxes=True):
""" provides scaled x,y positions
:param x: x pixel coordinate
:type x: :obj:`float`
:param y: y pixel coordinate
:type y: :obj:`float`
:param useraxes: use user scaling
:type useraxes: :obj:`bool`
:returns: scaled x,y position
:rtype: (:obj:`float`, :obj:`float`)
"""
return self.__displaywidget.scaledxy(x, y, useraxes)
[docs] def scale(self, useraxes=True, noNone=False):
""" provides scale and position of the axes
:param useraxes: use user scaling
:type useraxes: :obj:`bool`
:param noNone: return values without None
:type noNone: :obj:`bool`
:rtype: [int, int, int, int]
:returns: [posx, posy, scalex, scaley]
"""
return self.__displaywidget.scale(useraxes, noNone)
[docs] def axesunits(self):
""" return axes units
:returns: x,y units
:rtype: (:obj:`str`, :obj:`str`)
"""
return self.__displaywidget.axesunits()
[docs] def axestext(self):
""" return axes text
:returns: x,y text
:rtype: (:obj:`str`, :obj:`str`)
"""
return self.__displaywidget.axestext()
[docs] def roiCoords(self):
""" provides rois coordinates
:return: rois coordinates
:rtype: :obj:`list`
< [:obj:`float`, :obj:`float`, :obj:`float`, :obj:`float`] >
"""
return self.__displaywidget.extension('rois').roiCoords()
[docs] def meshCoords(self):
""" provides rois coordinates
:return: rois coordinates
:rtype: :obj:`list`
< [:obj:`float`, :obj:`float`, :obj:`float`, :obj:`float`] >
"""
return self.__displaywidget.extension('mesh').roiCoords()
[docs] def cutCoords(self):
""" provides cuts coordinates
:return: cuts coordinates
:rtype: :obj:`list`
< [:obj:`float`, :obj:`float`, :obj:`float`, :obj:`float`] >
"""
return self.__displaywidget.extension('cuts').cutCoords()
[docs] def currentROI(self):
""" provides current roi id
:return: roi id
:rtype: :obj:`int`
"""
return self.__displaywidget.extension('rois').currentROI()
[docs] def currentCut(self):
""" provides current cut id
:return: cut id
:rtype: :obj:`int`
"""
return self.__displaywidget.extension('cuts').currentCut()
[docs] def changeROIRegion(self):
""" changes the current roi region
"""
return self.__displaywidget.extension('rois').changeROIRegion()
[docs] def changeMeshRegion(self):
""" changes the current roi region
"""
return self.__displaywidget.extension('mesh').changeROIRegion()
[docs] def cutData(self, cid=None):
""" provides the current cut data
:param cid: cut id
:type cid: :obj:`int`
:returns: current cut data
:rtype: :class:`numpy.ndarray`
"""
return self.__displaywidget.extension('cuts').cutData(cid)
[docs] def rawData(self):
""" provides the raw data
:returns: current raw data
:rtype: :class:`numpy.ndarray`
"""
return self.__rawdata
[docs] def currentData(self):
""" provides the data
:returns: current data
:rtype: :class:`numpy.ndarray`
"""
return self.__data
[docs] @QtCore.pyqtSlot(float, float)
def updateHBounds(self, xdata1, xdata2):
""" updates the vertical bounds
:param xdata1: first x-pixel position
:type xdata1: :obj:`float`
:param xdata2: second x-pixel position
:type xdata2: :obj:`float`
"""
self.__displaywidget.extension('vhbounds').updateHBounds(
xdata1, xdata2)
[docs] @QtCore.pyqtSlot(float, float)
def updateVBounds(self, ydata1, ydata2):
""" updates the vertical bounds
:param ydata1: first x-pixel position
:type ydata1: :obj:`float`
:param ydata2: second x-pixel position
:type ydata2: :obj:`float`
"""
self.__displaywidget.extension('vhbounds').updateVBounds(
ydata1, ydata2)
[docs] @QtCore.pyqtSlot(float, float)
def updateCenter(self, xdata, ydata):
""" updates the image center
:param xdata: x pixel position
:type xdata: :obj:`float`
:param ydata: y-pixel position
:type ydata: :obj:`float`
"""
self.__displaywidget.extension('center').updateCenter(xdata, ydata)
[docs] @QtCore.pyqtSlot(float, float)
def updatePositionMark(self, xdata, ydata, scaled=False):
""" updates the position mark
:param xdata: x pixel position
:type xdata: :obj:`float`
:param ydata: y-pixel position
:type ydata: :obj:`float`
:param scaled: scaled flag
:type scaled: :obj:`bool`
"""
self.__displaywidget.extension('mark').updatePositionMark(
xdata, ydata, scaled)
[docs] @QtCore.pyqtSlot(float, float)
def updatePositionTrackingMark(self, xdata, ydata, scaled=False):
""" updates the position tracking mark
:param xdata: x pixel position
:type xdata: :obj:`float`
:param ydata: y-pixel position
:type ydata: :obj:`float`
:param scaled: scaled flag
:type scaled: :obj:`bool`
"""
self.__displaywidget.extension('tracking').updatePositionMark(
xdata, ydata, scaled)
[docs] def setDoubleClickLock(self, status=True):
""" sets double click lock
:param status: status flag
:type status: :obj:`bool`
"""
self.__displaywidget.setDoubleClickLock(status)
[docs] @debugmethod
def setTool(self, tool):
""" sets tool from string
:param tool: tool name
:type tool: :obj:`str`
"""
index = self.__ui.toolComboBox.findText(tool)
if index != -1:
self.__ui.toolComboBox.setCurrentIndex(index)
self.showCurrentTool()
[docs] @debugmethod
def setToolConfiguration(self, config):
""" sets tool configuration from JSON dictionary
:param config: JSON dictionary with tool configuration
:type config: :obj:`str`
"""
if self.__currenttool is not None:
try:
self.__currenttool.configure(config)
except Exception as e:
logger.warning(str(e))
[docs] def toolConfiguration(self):
""" provides tool configuration
:returns: JSON dictionary with tool configuration
:rtype: :obj:`str`
"""
if self.__currenttool is not None:
return self.__currenttool.configuration()
[docs] def tool(self):
""" provices tool from string
:param tool: tool name
:type tool: :obj:`str`
"""
if self.__currenttool is not None:
return self.__currenttool.alias
else:
return ""
[docs] @QtCore.pyqtSlot(float)
def updateEnergy(self, energy):
""" updates the beam energy
:param energy: beam energy
:type energy: :obj:`float`
"""
if self.__settings.energy != energy:
self.__settings.energy = energy
self.__settings.updateAISettings()
self.mouseImagePositionChanged.emit()
self.geometryChanged.emit()
[docs] @QtCore.pyqtSlot(float)
def updateDetectorDistance(self, distance):
""" updates the detector distance
:param distance: detector distance
:type distance: :obj:`float`
"""
if self.__settings.detdistance != distance:
self.__settings.detdistance = distance
self.__settings.updateAISettings()
self.mouseImagePositionChanged.emit()
self.geometryChanged.emit()
[docs] @QtCore.pyqtSlot(float)
def updateBeamCenterX(self, x):
""" updates the beam center x
:param x: beam center x
:type x: :obj:`float`
"""
if self.__settings.centerx != x:
self.__settings.centerx = x
self.__settings.updateAISettings()
self.updateCenter(
self.__settings.centerx, self.__settings.centery)
self.mouseImagePositionChanged.emit()
self.geometryChanged.emit()
[docs] @QtCore.pyqtSlot(float)
def updateBeamCenterY(self, y):
""" updates the beam center y
:param y: beam center y
:type y: :obj:`float`
"""
if self.__settings.centery != y:
self.__settings.centery = y
self.__settings.updateAISettings()
self.updateCenter(
self.__settings.centerx, self.__settings.centery)
self.mouseImagePositionChanged.emit()
self.geometryChanged.emit()
[docs] @QtCore.pyqtSlot(float)
def updatePixelSizeX(self, x):
""" updates the pixel x-size
:param x: pixel x-size
:type x: :obj:`float`
"""
if self.__settings.pixelsizex != x:
self.__settings.pixelsizex = x
self.__settings.updateAISettings()
self.mouseImagePositionChanged.emit()
self.geometryChanged.emit()
[docs] @QtCore.pyqtSlot(float)
def updatePixelSizeY(self, y):
""" updates the pixel y-size
:param y: pixel y-size
:type y: :obj:`float`
"""
if self.__settings.pixelsizey != y:
self.__settings.pixelsizey = y
self.__settings.updateAISettings()
self.mouseImagePositionChanged.emit()
self.geometryChanged.emit()
[docs] @QtCore.pyqtSlot(str)
def updateDetectorROIs(self, rois):
""" updates the detector ROIs
:param distance: json dictionary with detector ROIs
:type distance: :obj:`str`
"""
detrois = json.loads(str(rois))
coords = []
aliases = []
found = set()
llabels = str(self.roilabels).split(" ")
for k in llabels:
if k in detrois.keys():
v = detrois[k]
if k not in found and isinstance(v, list):
found.add(k)
for cr in v:
if isinstance(cr, list):
coords.append(cr)
aliases.append(k)
for k, v in detrois.items():
if isinstance(v, list) and k not in found:
for cr in v:
if isinstance(cr, list):
coords.append(cr)
aliases.append(k)
slabel = []
for i, al in enumerate(aliases):
if len(set(aliases[i:])) == 1:
slabel.append(al)
break
else:
slabel.append(al)
if len(slabel) == 1 and slabel[0] == "__null__":
slabel = []
# print(slabel)
if self.roilabels != " ".join(slabel):
self.roilabels = " ".join(slabel)
self.roiAliasesChanged.emit(self.roilabels)
oldcoords = self.__displaywidget.extension('rois').roiCoords()
# print("UPDATE %s" % str(coords))
if oldcoords != coords:
self.updateROIs(len(coords), coords)
[docs] def setToolScale(self, position=None, scale=None):
""" get axes parameters
:param position: start position of axes
:type position: [:obj:`float`, :obj:`float`]
:param scale: scale axes
:type scale: [:obj:`float`, :obj:`float`]
"""
return self.__displaywidget.setToolScale(position, scale)
[docs] def setViewRange(self, rangelist):
""" set view range values
:param rangelist: xmin,ymin,xsize,ysize
:type rangelist: :obj:`str`
"""
self.__displaywidget.setViewRange(rangelist)
[docs] def viewRange(self):
""" get view range values
:returns: xmin,ymin,xsize,ysize
:rtype rangelist: :obj:`str`
"""
return self.__displaywidget.viewRange()
[docs] def setMaximaPos(self, positionlist, offset=None):
"""
sets maxima postions
:param positionlist: [(x1, y1), ... , (xn, yn)]
:type positionlist: :obj:`list` < (float, float) >
:param offset: offset of position
:type offset: [ :obj:`float`, :obj:`float`]
"""
return self.__displaywidget.extension('maxima').\
setMaximaPos(positionlist, offset)
[docs] def setrgb(self, status=True):
""" sets RGB on/off
:param status: True for on and False for off
:type status: :obj:`bool`
"""
# self.setTool("Intensity")
self.__displaywidget.setrgb(status)
[docs] def rgb(self):
""" gets RGB on/off
:returns: True for on and False for off
:rtype: :obj:`bool`
"""
return self.__displaywidget.rgb()
[docs] def setGradientColors(self, status=True):
""" sets gradientcolors on/off
:param status: True for on and False for off
:type status: :obj:`bool`
"""
# self.setTool("Intensity")
self.__displaywidget.setGradientColors(status)
[docs] def gradientColors(self):
""" gets gradientcolors on/off
:returns: True for on and False for off
:rtype: :obj:`bool`
"""
return self.__displaywidget.gradientColors()
[docs] def applyMask(self):
""" provides apply mask flag
:returns: True for apply mask
:rtype: :obj:`bool`
"""
return self.__applymask
[docs] def setApplyMask(self, applymask=True):
""" sets apply mask flag
:params applymask: True for apply mask
:type applymask: :obj:`bool`
"""
self.__applymask = applymask
[docs] def maskValue(self):
""" provides high mask value
:returns: high mask value
:rtype: :obj:`float`
"""
return self.__maskvalue
[docs] def setMaskValue(self, maskvalue):
""" sets high mask value
:params maskvalue: high mask value
:type maskvalue: :obj:`float`
"""
self.__maskvalue = maskvalue
[docs] def overflowValue(self):
""" provides overflow value
:returns: overflow value
:rtype: :obj:`float`
"""
return self.__overflowvalue
[docs] def setOverflowValue(self, overflowvalue):
""" sets overflow value
:params overflowvalue: overflow value
:type overflowvalue: :obj:`float`
"""
self.__overflowvalue = overflowvalue
[docs] def overflowValueIndices(self):
""" provides overflow image value indices
:returns: overflow image indices
:rtype: :class:`numpy.ndarray`
"""
return self.__overflowValueIndices
[docs] def setOverflowValueIndices(self, overflowindices):
""" sets overflow image indices
:params overflowindices: overflow image value indices
:type overflowindices: :class:`numpy.ndarray`
"""
self.__overflowValueIndices = overflowindices
[docs] def maskIndices(self):
""" provides mask image indices
:returns: mask image indices
:rtype: :class:`numpy.ndarray`
"""
return self.__maskindices
[docs] def setMaskIndices(self, maskindices):
""" sets mask image indices
:params maskindices: mask image indices
:type maskindices: :class:`numpy.ndarray`
"""
self.__maskindices = maskindices
[docs] def maskValueIndices(self):
""" provides mask image value indices
:returns: mask image indices
:rtype: :class:`numpy.ndarray`
"""
return self.__maskValueIndices
[docs] def setMaskValueIndices(self, maskindices):
""" sets mask image indices
:params maskindices: mask image value indices
:type maskindices: :class:`numpy.ndarray`
"""
self.__maskValueIndices = maskindices
[docs] def rangeWindowEnabled(self):
""" provide info if range window enabled
:returns: range window enabled
:rtype: :obj:`bool`
"""
return self.__displaywidget.rangeWindowEnabled()
[docs] def imageName(self):
""" provide the current image name
:returns: image name
:rtype: :obj:`str`
"""
return self.__imagename
[docs] def rangeWindowScale(self):
""" provide info range window scale
:returns: range window scale
:rtype: :obj:`float`
"""
return self.__displaywidget.rangeWindowScale()
[docs] def setLevelMode(self, levelmode=True):
""" sets levelmode
:param levelmode: level mode, i.e. `mono` or `rgba`
:type levelmode: :obj:`str`
"""
self.__displaywidget.setLevelMode(levelmode)
[docs] def levelMode(self):
""" gets level mode
:returns: level mode, i.e. `mono` or `rgba`
:rtype: :obj:`str`
"""
return self.__displaywidget.levelMode()