lavuelib.imageWidget — LaVue 2.76.0 documentation

# 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, QtGui

    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")

    class ImageWidget(QtGui.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`
            """
            QtGui.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
            #: (: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()

            #: (:class:`pyqtgraph.PlotWidget`) right 1D plot widget
            self.__rightplot = memoExportDialog.MemoPlotWidget(self)

            self.__ui.twoDVerticalLayout.addWidget(self.__displaywidget)
            self.__ui.oneDBottomVerticalLayout.addWidget(self.__bottomplot)

            self.__ui.oneDRightHorizontalLayout.addWidget(self.__rightplot)

            sizePolicy = QtGui.QSizePolicy(QtGui.QSizePolicy.Preferred,
                                           QtGui.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()

        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)

        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)

        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))

        @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))

        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))

        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()

        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)

        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()

        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)

        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)

        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()

        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

        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

        def removebottomplot(self, plot):
            """ removes bottom plot

            :param plot: right plot item
            :type plot: :class:`pyqtgraph.PlotItem`
            """
            self.__bottomplot.removeItem(plot)

        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)

        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()

        @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()

        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()

        @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)

        @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)

        @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)

        def currentTool(self):
            """ provides the current tool

            :returns: current tool name
            :rtype: :obj:`str`
            """
            return str(self.__ui.toolComboBox.currentText())

        @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
        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)

        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()

        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

        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()

        def plot(self, array, rawarray=None, imagename=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`
            """
            if array is None:
                return
            if rawarray is None:
                rawarray = array
            barrays = None
            self.__data = array
            self.__rawdata = rawarray
            self.__imagename = imagename
            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)
            if self.__currenttool:
                self.__currenttool.afterplot()

        def updateImage(self, array=None, rawarray=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`
            """
            self.__displaywidget.updateImage(
                array if array is not None else self.__data,
                rawarray if rawarray is not None else self.__rawdata)

        @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)

        @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)

        @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)

        @QtCore.pyqtSlot(float)
        def setMinLevel(self, level=None):
            """ sets minimum intensity level

            :param level: minimum intensity
            :type level: :obj:`float`
            """
            self.__displaywidget.setDisplayMinLevel(level)

        @QtCore.pyqtSlot(float)
        def setMaxLevel(self, level=None):
            """ sets maximum intensity level

            :param level: maximum intensity
            :type level: :obj:`float`
            """
            self.__displaywidget.setDisplayMaxLevel(level)

        @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)

        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()

        def calcROIsums(self):
            """ calculates all roi sums

            :returns: sum roi value, roi id
            :rtype: :obj:`list` < float >
            """
            return self.__displaywidget.extension('rois').calcROIsums()

        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)

        @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)

        @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)

        @QtCore.pyqtSlot()
        def setTicks(self):
            """ launch axes widget

            :returns: apply status
            :rtype: :obj:`bool`
            """
            self.__displaywidget.setTicks()
            self.emitTCC()

        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()

        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
        @QtCore.pyqtSlot()
        def emitTCC(self):
            """emits toolConfigurationChanged
            """
            self.toolConfigurationChanged.emit()

        @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()

        @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)

        def emitReplotImage(self, autorange=True):
            """emits replotImage
            """
            self.replotImage.emit(autorange)

        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)

        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)

        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)

        def setScalingType(self, scalingtype):
            """ sets intensity scaling types

            :param scalingtype: intensity scaling type
            :type scalingtype: :obj:`str`
            """
            self.__displaywidget.setScalingType(scalingtype)

        def setDoBkgSubtraction(self, state):
            """ sets do background subtraction flag

            :param status: do background subtraction flag
            :type status: :obj:`bool`
            """
            self.__displaywidget.setDoBkgSubtraction(state)

        def setDoBFSubtraction(self, state):
            """ sets do brightfield subtraction flag

            :param status: do brightfield subtraction flag
            :type status: :obj:`bool`
            """
            self.__displaywidget.setDoBFSubtraction(state)

        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

        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

        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

        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

        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))

        @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

        @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

        def currentIntensity(self):
            """ provides intensity for current mouse position

            :returns: x position, y position, pixel intensity
            :rtype: (float, float, float)
            """
            return self.__displaywidget.currentIntensity()

        def scalingLabel(self):
            """ provides scaling label

            :returns:  scaling label
            :rtype: str
            """
            return self.__displaywidget.scalingLabel()

        def scaling(self):
            """ provides scaling type

            :returns:  scaling type
            :rtype: str
            """
            return self.__displaywidget.scaling()

        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)

        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)

        def axesunits(self):
            """ return axes units

            :returns: x,y units
            :rtype: (:obj:`str`, :obj:`str`)
            """
            return self.__displaywidget.axesunits()

        def axestext(self):
            """ return axes text

            :returns: x,y text
            :rtype: (:obj:`str`, :obj:`str`)
            """
            return self.__displaywidget.axestext()

        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()

        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()

        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()

        def currentROI(self):
            """ provides current roi id

            :return: roi id
            :rtype: :obj:`int`
            """
            return self.__displaywidget.extension('rois').currentROI()

        def currentCut(self):
            """ provides current cut id

            :return: cut id
            :rtype: :obj:`int`
            """
            return self.__displaywidget.extension('cuts').currentCut()

        def changeROIRegion(self):
            """ changes the current roi region
            """
            return self.__displaywidget.extension('rois').changeROIRegion()

        def changeMeshRegion(self):
            """ changes the current roi region
            """
            return self.__displaywidget.extension('mesh').changeROIRegion()

        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)

        def rawData(self):
            """ provides the raw data

            :returns: current raw data
            :rtype: :class:`numpy.ndarray`
            """
            return self.__rawdata

        def currentData(self):
            """ provides the data

            :returns: current data
            :rtype: :class:`numpy.ndarray`
            """
            return self.__data

        def autoRange(self):
            """ sets auto range
            """
            self.__displaywidget.autoRange()

        @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)

        @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)

        @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)

        @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)

        @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)

        def setDoubleClickLock(self, status=True):
            """ sets double click lock

            :param status: status flag
            :type status: :obj:`bool`
            """
            self.__displaywidget.setDoubleClickLock(status)

        @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()

        @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))

        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()

        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 ""

        @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()

        @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()

        @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()

        @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()

        @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()

        @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()

        @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)

        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)

        def setViewRange(self, rangelist):
            """ set view range values

            :param rangelist: xmin,ymin,xsize,ysize
            :type rangelist: :obj:`str`
            """
            self.__displaywidget.setViewRange(rangelist)

        def viewRange(self):
            """ get view range values

            :returns: xmin,ymin,xsize,ysize
            :rtype rangelist: :obj:`str`
            """
            return self.__displaywidget.viewRange()

        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)

        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)

        def rgb(self):
            """ gets RGB on/off

            :returns: True for on and False for off
            :rtype: :obj:`bool`
            """
            return self.__displaywidget.rgb()

        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)

        def gradientColors(self):
            """ gets gradientcolors on/off

            :returns: True for on and False for off
            :rtype: :obj:`bool`
            """
            return self.__displaywidget.gradientColors()

        def applyMask(self):
            """ provides apply mask flag

            :returns: True for apply mask
            :rtype: :obj:`bool`
            """
            return self.__applymask

        def setApplyMask(self, applymask=True):
            """ sets apply mask flag

            :params applymask: True for apply mask
            :type applymask: :obj:`bool`
            """
            self.__applymask = applymask

        def maskValue(self):
            """ provides high mask value

            :returns: high mask value
            :rtype: :obj:`float`
            """
            return self.__maskvalue

        def setMaskValue(self, maskvalue):
            """ sets high mask value

            :params applymask: high mask value
            :type applymask: :obj:`float`
            """
            self.__maskvalue = maskvalue

        def maskIndices(self):
            """ provides mask image indices

            :returns: mask image indices
            :rtype: :class:`numpy.ndarray`
            """
            return self.__maskindices

        def setMaskIndices(self, maskindices):
            """ sets mask image indices

            :params maskindices: mask image indices
            :type maskindices: :class:`numpy.ndarray`
            """
            self.__maskindices = maskindices

        def maskValueIndices(self):
            """ provides mask image value indices

            :returns: mask image indices
            :rtype: :class:`numpy.ndarray`
            """
            return self.__maskValueIndices

        def setMaskValueIndices(self, maskindices):
            """ sets mask image indices

            :params maskindices: mask image  value indices
            :type maskindices: :class:`numpy.ndarray`
            """
            self.__maskValueIndices = maskindices

        def rangeWindowEnabled(self):
            """ provide info if range window enabled

            :returns: range window enabled
            :rtype: :obj:`bool`
            """
            return self.__displaywidget.rangeWindowEnabled()

        def imageName(self):
            """ provide the current image name

            :returns: image name
            :rtype: :obj:`str`
            """
            return self.__imagename

        def rangeWindowScale(self):
            """ provide info range window scale

            :returns: range window scale
            :rtype: :obj:`float`
            """
            return self.__displaywidget.rangeWindowScale()

        def setLevelMode(self, levelmode=True):
            """ sets levelmode

            :param levelmode: level mode, i.e. `mono` or `rgba`
            :type levelmode: :obj:`str`
            """
            self.__displaywidget.setLevelMode(levelmode)

        def levelMode(self):
            """ gets level mode

            :returns: level mode, i.e. `mono` or `rgba`
            :rtype: :obj:`str`
            """
            return self.__displaywidget.levelMode()