lavuelib.toolWidget — LaVue 2.76.0 documentation

# Source code for lavuelib.toolWidget

    # 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:
    #     Jan Kotanski <jan.kotanski@desy.de>
    #     Christoph Rosemann <christoph.rosemann@desy.de>
    #

    """ image widget """

    from .qtuic import uic

    import os
    import re
    import math
    import sys
    import time
    import numpy as np
    import scipy.optimize
    import scipy.interpolate
    import pyqtgraph as _pg
    import logging
    import random
    import json
    from pyqtgraph import QtCore, QtGui, functions
    from enum import Enum

    from . import geometryDialog
    from . import rangeDialog
    from . import diffRangeDialog
    from . import takeMotorsDialog
    from . import intervalsDialog
    from . import motorWatchThread
    from . import edDictDialog
    from . import edListDialog
    from . import commandThread
    from .sardanaUtils import debugmethod, numpyEncoder

    try:
        try:
            import tango
        except ImportError:
            import PyTango as tango
        #: (:obj:`bool`) tango imported
        TANGO = True
    except ImportError:
        #: (:obj:`bool`) tango imported
        TANGO = False

    try:
        import pyFAI
        #: (:obj:`bool`) pyFAI imported
        PYFAI = True
    except ImportError:
        #: (:obj:`bool`) pyFAI imported
        PYFAI = False

    if sys.version_info > (3,):
        long = int

    _intensityformclass, _intensitybaseclass = uic.loadUiType(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     "ui", "IntensityToolWidget.ui"))

    _roiformclass, _roibaseclass = uic.loadUiType(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     "ui", "ROIToolWidget.ui"))

    _cutformclass, _cutbaseclass = uic.loadUiType(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     "ui", "LineCutToolWidget.ui"))

    _angleqformclass, _angleqbaseclass = uic.loadUiType(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     "ui", "AngleQToolWidget.ui"))

    _diffractogramformclass, _diffractogrambaseclass = uic.loadUiType(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     "ui", "DiffractogramToolWidget.ui"))

    _maximaformclass, _maximabaseclass = uic.loadUiType(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     "ui", "MaximaToolWidget.ui"))

    _motorsformclass, _motorsbaseclass = uic.loadUiType(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     "ui", "MotorsToolWidget.ui"))

    _meshformclass, _meshbaseclass = uic.loadUiType(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     "ui", "MeshToolWidget.ui"))

    _onedformclass, _onedbaseclass = uic.loadUiType(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     "ui", "OneDToolWidget.ui"))

    _projectionformclass, _projectionbaseclass = uic.loadUiType(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     "ui", "ProjectionToolWidget.ui"))

    _parametersformclass, _parametersbaseclass = uic.loadUiType(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     "ui", "ParametersToolWidget.ui"))

    _qroiprojformclass, _qroiprojbaseclass = uic.loadUiType(
        os.path.join(os.path.dirname(os.path.abspath(__file__)),
                     "ui", "QROIProjToolWidget.ui"))

    __all__ = [
        'IntensityToolWidget',
        'ROIToolWidget',
        'LineCutToolWidget',
        'AngleQToolWidget',
        'MotorsToolWidget',
        'MeshToolWidget',
        'OneDToolWidget',
        'ProjectionToolWidget',
        'MaximaToolWidget',
        'ParametersToolWidget',
        'DiffractogramToolWidget',
        'QROIProjToolWidget',
        'twproperties',
    ]

    logger = logging.getLogger("lavue")

    class Converters(object):

        """ set of converters
        """

        @classmethod
        def toBool(cls, value):
            """ converts to bool

            :param value: variable to convert
            :type value: any
            :returns: result in bool type
            :rtype: :obj:`bool`
            """
            if type(value).__name__ == 'str' or type(value).__name__ == 'unicode':
                lvalue = value.strip().lower()
                if lvalue == 'false' or lvalue == '0':
                    return False
                else:
                    return True
            elif value:
                return True
            return False

    class ToolParameters(object):
        """ tool parameters
        """
        def __init__(self):
            """ constructor

            """
            #: (:obj:`bool`) lines enabled
            # self.lines = False
            #: (:obj:`bool`) rois enabled
            self.rois = False
            #: (:obj:`bool`) cuts enabled
            self.cuts = False
            #: (:obj:`bool`) mesh enabled
            self.mesh = False
            #: (:obj:`bool`) axes scale enabled
            self.scale = False
            #: (:obj:`bool`) tool axes scale enabled
            self.toolscale = False
            #: (:obj:`bool`) bottom 1d plot enabled
            self.bottomplot = False
            #: (:obj:`bool`) right 1d plot enabled
            self.rightplot = False
            #: (:obj:`bool`) cross hair locker enabled
            self.crosshairlocker = False
            #: (:obj:`bool`) center lines enabled
            self.centerlines = False
            #: (:obj:`bool`) mark position lines enabled
            self.marklines = False
            #: (:obj:`bool`) tracking position lines enabled
            self.trackinglines = False
            #: (:obj:`str`) infolineedit text
            self.infolineedit = None
            #: (:obj:`str`) infolabel text
            self.infolabel = None
            #: (:obj:`str`) infolabel text
            self.infotips = None
            #: (:obj:`str`) show maxima
            self.maxima = False
            #: (:obj:`bool`) vertical and horizontal bounds
            self.vhbounds = False
            #: (:obj:`bool`) angle range enabled
            self.regions = False

    class ToolBaseWidget(QtGui.QWidget):
        """ tool widget
        """

        #: (:obj:`str`) tool name
        name = "None"
        #: (:obj:`str`) tool name alias
        alias = "none"
        #: (:obj:`tuple` <:obj:`str`>) capitalized required packages
        requires = ()

        def __init__(self, parent=None):
            """ constructor

            :param parent: parent object
            :type parent: :class:`pyqtgraph.QtCore.QObject`
            """
            QtGui.QWidget.__init__(self, parent)
            #: (:class:`pyqtgraph.QtCore.QObject`) mainwidget
            self._mainwidget = parent
            #: (:class:`Ui_ToolBaseWidget')
            #:     ui_toolwidget object from qtdesigner
            self._ui = None
            #: (:class:`ToolParameters`) tool parameters
            self.parameters = ToolParameters()

            #: (:obj:`list` < [:class:`pyqtgraph.QtCore.pyqtSignal`, :obj:`str`] >)
            #: list of [signal, slot] object to connect
            self.signal2slot = []

        def activate(self):
            """ activates tool widget
            """

        def deactivate(self):
            """ deactivates tool widget
            """

        def afterplot(self):
            """ command after plot
            """

        def beforeplot(self, array, rawarray):
            """ command  before plot

            :param array: 2d image array
            :type array: :class:`numpy.ndarray`
            :param rawarray: 2d raw image array
            :type rawarray: :class:`numpy.ndarray`
            :return: 2d image array and raw image
            :rtype: (:class:`numpy.ndarray`, :class:`numpy.ndarray`)
            """

        def configure(self, configuration):
            """ set configuration for the current tool

            :param configuration: configuration string
            :type configuration: :obj:`str`
            """

        def configuration(self):
            """ provides configuration for the current tool

            :returns configuration: configuration string
            :rtype configuration: :obj:`str`
            """
            return ""

    class IntensityToolWidget(ToolBaseWidget):
        """ intensity tool widget
        """

        #: (:obj:`str`) tool name
        name = "Intensity"
        #: (:obj:`str`) tool name alias
        alias = "intensity"
        #: (:obj:`tuple` <:obj:`str`>) capitalized required packages
        requires = ()

        def __init__(self, parent=None):
            """ constructor

            :param parent: parent object
            :type parent: :class:`pyqtgraph.QtCore.QObject`
            """
            ToolBaseWidget.__init__(self, parent)

            #: (:class:`Ui_IntensityToolWidget')
            #:        ui_toolwidget object from qtdesigner
            self.__ui = _intensityformclass()
            self.__ui.setupUi(self)

            #: (:class:`lavuelib.settings.Settings`) configuration settings
            self.__settings = self._mainwidget.settings()

            self.parameters.scale = True
            self.parameters.crosshairlocker = True
            self.parameters.infolineedit = ""
            self.parameters.infotips = \
                "coordinate info display for the mouse pointer"
            self.__ui.crosshairCheckBox.setChecked(self.__settings.crosshairlocker)
            self._updateCrossHairLocker(self.__settings.crosshairlocker)

            #: (:obj:`list` < [:class:`pyqtgraph.QtCore.pyqtSignal`, :obj:`str`] >)
            #: list of [signal, slot] object to connect
            self.signal2slot = [
                [self.__ui.axesPushButton.clicked, self._mainwidget.setTicks],
                [self.__ui.crosshairCheckBox.stateChanged,
                 self._mainwidget.emitTCC],
                [self.__ui.crosshairCheckBox.stateChanged,
                 self._updateCrossHairLocker],
                [self._mainwidget.mouseImagePositionChanged, self._message]
            ]

        @QtCore.pyqtSlot(int)
        def _updateCrossHairLocker(self, status):
            self.parameters.crosshairlocker = bool(status)
            self._mainwidget.updateinfowidgets(self.parameters)

        @QtCore.pyqtSlot()
        def _message(self):
            """ provides intensity message
            """
            x, y, intensity = self._mainwidget.currentIntensity()[:3]
            if isinstance(intensity, float) and np.isnan(intensity):
                intensity = 0
            ilabel = self._mainwidget.scalingLabel()
            txdata, tydata = self._mainwidget.scaledxy(x, y)
            xunits, yunits = self._mainwidget.axesunits()
            if isinstance(intensity, np.ndarray) and \
               intensity.size <= 3:
                itn = [0 if (isinstance(it, float) and np.isnan(it))
                       else it for it in intensity]
                if len(itn) >= 3:
                    if txdata is not None:
                        message = "x = %f%s, y = %f%s, " \
                                  "%s = (%.2f, %.2f, %.2f)" % (
                                      txdata,
                                      (" %s" % xunits) if xunits else "",
                                      tydata,
                                      (" %s" % yunits) if yunits else "",
                                      ilabel,
                                      itn[0], itn[1], itn[2])
                    else:
                        message = "x = %i%s, y = %i%s, " \
                                  "%s = (%.2f, %.2f, %.2f)" % (
                                      x,
                                      (" %s" % xunits) if xunits else "",
                                      y,
                                      (" %s" % yunits) if yunits else "",
                                      ilabel,
                                      itn[0], itn[1], itn[2])
                    self._mainwidget.setDisplayedText(message)
            else:
                if txdata is not None:
                    message = "x = %f%s, y = %f%s, %s = %.2f" % (
                        txdata,
                        (" %s" % xunits) if xunits else "",
                        tydata,
                        (" %s" % yunits) if yunits else "",
                        ilabel,
                        intensity
                    )
                else:
                    message = "x = %i%s, y = %i%s, %s = %.2f" % (
                        x,
                        (" %s" % xunits) if xunits else "",
                        y,
                        (" %s" % yunits) if yunits else "",
                        ilabel,
                        intensity)
                self._mainwidget.setDisplayedText(message)

        def afterplot(self):
            """ command after plot
            """
            if self.__settings.sendresults:
                self.__sendresults()

        def configure(self, configuration):
            """ set configuration for the current tool

            :param configuration: configuration string
            :type configuration: :obj:`str`
            """
            if configuration:
                cnf = json.loads(configuration)
                if "crosshair_locker" in cnf.keys():
                    crosshairlocker = cnf["crosshair_locker"]
                    self.__ui.crosshairCheckBox.setChecked(crosshairlocker)
                pars = ["position", "scale",
                        "xtext", "ytext", "xunits", "yunits"]
                if any(par in cnf.keys() for par in pars):
                    self._mainwidget.updateTicks(cnf)

        def configuration(self):
            """ provides configuration for the current tool

            :returns configuration: configuration string
            :rtype configuration: :obj:`str`
            """
            cnf = {}
            cnf["crosshair_locker"] = self.__ui.crosshairCheckBox.isChecked()
            xpos, ypos, xsc, ysc = self._mainwidget.scale()
            cnf["xunits"], cnf["yunits"] = self._mainwidget.axesunits()
            cnf["xtext"], cnf["ytext"] = self._mainwidget.axestext()
            cnf["position"] = [xpos, ypos]
            cnf["scale"] = [xsc, ysc]
            return json.dumps(cnf, cls=numpyEncoder)

        def __sendresults(self):
            """ send results to LavueController
            """
            x, y, intensity = self._mainwidget.currentIntensity()[:3]
            if isinstance(intensity, float) and np.isnan(intensity):
                intensity = 0
            scaling = self._mainwidget.scalingLabel()
            sx, sy = self._mainwidget.scaledxy(x, y)
            xunits, yunits = self._mainwidget.axesunits()
            results = {"tool": self.alias}
            if isinstance(intensity, np.ndarray):
                intensity = [0 if (isinstance(it, float) and np.isnan(it))
                             else float(it) for it in intensity]
                results["intensity"] = intensity
            else:
                results["intensity"] = float(intensity)
            results["imagename"] = self._mainwidget.imageName()
            results["timestamp"] = time.time()
            results["pixel"] = [float(x), float(y)]
            results["scaled_coordiantes"] = [float(sx), float(sy)]
            results["coordiantes_units"] = [xunits, yunits]
            results["intensity_scaling"] = scaling
            self._mainwidget.writeAttribute(
                "ToolResults", json.dumps(results, cls=numpyEncoder))

    class MotorsToolWidget(ToolBaseWidget):
        """ motors tool widget
        """

        #: (:obj:`str`) tool name
        name = "MoveMotors"
        #: (:obj:`str`) tool name alias
        alias = "movemotors"
        #: (:obj:`tuple` <:obj:`str`>) capitalized required packages
        requires = ("TANGO",)

        def __init__(self, parent=None):
            """ constructor

            :param parent: parent object
            :type parent: :class:`pyqtgraph.QtCore.QObject`
            """
            ToolBaseWidget.__init__(self, parent)

            #: (:obj:`str`) x-motor name
            self.__xmotorname = ""
            #: (:obj:`str`) y-motor name
            self.__ymotorname = ""
            #: (:obj:`str`) x final position
            self.__xfinal = None
            #: (:obj:`str`) y final position
            self.__yfinal = None
            #: (:obj:`str`) state of x-motor
            self.__statex = None
            #: (:obj:`str`) state of y-motor
            self.__statey = None
            #: (:class:`tango.DeviceProxy`) x-motor device
            self.__xmotordevice = None
            #: (:class:`tango.DeviceProxy`) y-motor device
            self.__ymotordevice = None
            #: (:class:`lavuelib.motorWatchThread.motorWatchThread`) motor watcher
            self.__motorWatcher = None
            #: (:obj:`bool`) is moving
            self.__moving = False
            #: (:obj:`bool`) is tracking
            self.__tracking = False

            #: (:class:`Ui_MotorsToolWidget')
            #:        ui_toolwidget object from qtdesigner
            self.__ui = _motorsformclass()
            self.__ui.setupUi(self)
            self.__ui.xcurLineEdit.hide()
            self.__ui.ycurLineEdit.hide()

            #: (:obj:`bool`) position lines enabled
            self.parameters.scale = True
            self.parameters.marklines = True
            self.parameters.trackinglines = False
            self.parameters.infolineedit = ""
            self.parameters.infotips = \
                "coordinate info display for the mouse pointer"

            #: (:obj:`list` < [:class:`pyqtgraph.QtCore.pyqtSignal`, :obj:`str`] >)
            #: list of [signal, slot] object to connect
            self.signal2slot = [
                [self.__ui.axesPushButton.clicked, self._mainwidget.setTicks],
                [self.__ui.takePushButton.clicked, self._setMotors],
                [self.__ui.movePushButton.clicked, self._moveStopMotors],
                [self.__ui.trackPushButton.clicked, self._trackStopMotors],
                [self._mainwidget.mouseImageDoubleClicked, self._updateFinal],
                [self._mainwidget.mouseImagePositionChanged, self._message],
                [self.__ui.xLineEdit.textEdited, self._getFinal],
                [self.__ui.xLineEdit.textChanged, self._mainwidget.emitTCC],
                [self.__ui.yLineEdit.textEdited, self._getFinal],
                [self.__ui.yLineEdit.textChanged, self._mainwidget.emitTCC],
            ]

        # @debugmethod
        def configure(self, configuration):
            """ set configuration for the current tool

            :param configuration: configuration string
            :type configuration: :obj:`str`
            """
            if configuration:
                cnf = json.loads(configuration)
                pars = ["position", "scale",
                        "xtext", "ytext", "xunits", "yunits"]
                if any(par in cnf.keys() for par in pars):
                    self._mainwidget.updateTicks(cnf)
                if "motors" in cnf.keys():
                    try:
                        motorname = cnf["motors"][0]
                        motordevice = tango.DeviceProxy(motorname)
                        for attr in ["state", "position"]:
                            if not hasattr(motordevice, attr):
                                raise Exception("Missing %s" % attr)
                        self.__xmotorname = motorname
                        self.__xmotordevice = motordevice
                    except Exception as e:
                        logger.warning(str(e))
                    try:
                        motorname = cnf["motors"][1]
                        motordevice = tango.DeviceProxy(motorname)
                        for attr in ["state", "position"]:
                            if not hasattr(motordevice, attr):
                                raise Exception("Missing %s" % attr)
                        self.__ymotorname = motorname
                        self.__ymotordevice = motordevice
                    except Exception as e:
                        logger.warning(str(e))
                if "x_position" in cnf.keys():
                    try:
                        self.__ui.xLineEdit.setText(str(cnf["x_position"]))
                    except Exception:
                        pass
                if "y_position" in cnf.keys():
                    try:
                        self.__ui.yLineEdit.setText(str(cnf["y_position"]))
                    except Exception:
                        pass
                if "track" in cnf.keys():
                    if cnf["track"]:
                        if str(self.__ui.trackePushButton.text()) == "Track":
                            self._trackStopMotors()
                if "untrack" in cnf.keys():
                    if cnf["untrack"]:
                        if str(self.__ui.trackePushButton.text()) == "Untrack":
                            self._trackStopMotors()
                if "move" in cnf.keys():
                    if cnf["move"]:
                        if str(self.__ui.movePushButton.text()) == "Move":
                            self._moveStopMotors()
                if "stop" in cnf.keys():
                    if cnf["stop"]:
                        if str(self.__ui.movePushButton.text()) == "Stop":
                            self._moveStopMotors()

        # @debugmethod
        def configuration(self):
            """ provides configuration for the current tool

            :returns configuration: configuration string
            :rtype configuration: :obj:`str`
            """
            cnf = {}
            xpos, ypos, xsc, ysc = self._mainwidget.scale()
            cnf["xunits"], cnf["yunits"] = self._mainwidget.axesunits()
            cnf["xtext"], cnf["ytext"] = self._mainwidget.axestext()
            cnf["position"] = [xpos, ypos]
            cnf["scale"] = [xsc, ysc]
            try:
                cnf["x_position"] = float(self.__ui.xLineEdit.text())
            except Exception:
                cnf["x_position"] = self.__ui.xLineEdit.text()
            try:
                cnf["y_position"] = float(self.__ui.yLineEdit.text())
            except Exception:
                cnf["y_position"] = self.__ui.yLineEdit.text()
            cnf["motors"] = [self.__xmotorname, self.__ymotorname]
            if str(self.__ui.movePushButton.text()) == "Move":
                cnf["motor_state"] = "ON"
            else:
                cnf["motor_state"] = "MOVING"
            if self.__tracking:
                cnf["tracking"] = True
            else:
                cnf["tracking"] = False

            return json.dumps(cnf, cls=numpyEncoder)

        def activate(self):
            """ activates tool widget
            """
            if self.__xfinal is not None and self.__yfinal is not None:
                self._mainwidget.updatePositionMark(
                    self.__xfinal, self.__yfinal, True)

        @QtCore.pyqtSlot(float, float)
        def _updateFinal(self, xdata, ydata):
            """ updates the final motors position

            :param xdata: x pixel position
            :type xdata: :obj:`float`
            :param ydata: y-pixel position
            :type ydata: :obj:`float`
            """
            if not self.__moving:
                x, y = self._mainwidget.scaledxy(float(xdata), float(ydata))
                if x is not None:
                    self.__xfinal = x
                    self.__yfinal = y
                else:
                    self.__xfinal = float(xdata)
                    self.__yfinal = float(ydata)

                self.__ui.xLineEdit.setText(str(self.__xfinal))
                self.__ui.yLineEdit.setText(str(self.__yfinal))
                self.__ui.movePushButton.setToolTip(
                    "Move to x- and y-motors to (%s, %s)"
                    % (self.__xfinal, self.__yfinal))

        @QtCore.pyqtSlot()
        def _moveStopMotors(self):
            """ move or stop motors depending on movePushButton
            """
            if str(self.__ui.movePushButton.text()) == "Move":
                self.__moveMotors()
            else:
                self.__stopMotors()
            self._mainwidget.emitTCC()

        @QtCore.pyqtSlot()
        def _trackStopMotors(self):
            """ move or stop motors depending on movePushButton
            """
            if str(self.__ui.trackPushButton.text()) == "Track":
                self.__trackMotors()
            else:
                self.__hideMotors()
            self._mainwidget.emitTCC()

        @QtCore.pyqtSlot()
        def _finished(self):
            """ stop motors
            """
            self.__stopMotors()
            self._mainwidget.emitTCC()

        def __stopMotors(self):
            """ move motors

            :returns: motors stopped
            :rtype: :obj:`bool`
            """
            try:
                if hasattr(self.__xmotordevice, "stop"):
                    self.__xmotordevice.stop()
                elif hasattr(self.__xmotordevice, "StopMove"):
                    self.__xmotordevice.StopMove()
                else:
                    return False
                if hasattr(self.__ymotordevice, "stop"):
                    self.__ymotordevice.stop()
                elif hasattr(self.__ymotordevice, "StopMove"):
                    self.__ymotordevice.StopMove()
                else:
                    return False
            except Exception as e:
                logger.warning(str(e))
                # print(str(e))
            if self.__motorWatcher:
                self.__motorWatcher.motorStatusSignal.disconnect(self._showMotors)
                self.__motorWatcher.watchingFinished.disconnect(self._finished)
                self.__motorWatcher.stop()
                self.__motorWatcher.wait()
                self.__motorWatcher = None
            if not self.__tracking:
                self.parameters.trackinglines = False
                self._mainwidget.updateinfowidgets(self.parameters)

            self._mainwidget.updateinfowidgets(self.parameters)
            self._mainwidget.setDoubleClickLock(False)
            self.__ui.movePushButton.setText("Move")
            self.__ui.xcurLineEdit.hide()
            self.__ui.ycurLineEdit.hide()
            self.__ui.takePushButton.show()
            self.__ui.axesPushButton.show()
            self.__ui.trackPushButton.show()
            self.__moving = False
            self.__ui.xLineEdit.setReadOnly(False)
            self.__ui.yLineEdit.setReadOnly(False)
            self.__ui.xcurLineEdit.setStyleSheet(
                "color: black; background-color: #90EE90;")
            self.__ui.ycurLineEdit.setStyleSheet(
                "color: black; background-color: #90EE90;")
            if self.__tracking:
                self.__trackMotors()
            return True

        def __hideMotors(self, changestatus=True):
            """ move motors

            :returns: motors stopped
            :rtype: :obj:`bool`
            """
            if self.__motorWatcher:
                self.__motorWatcher.motorStatusSignal.disconnect(self._showMotors)
                # self.__motorWatcher.watchingFinished.disconnect(self._hide)
                self.__motorWatcher.stop()
                self.__motorWatcher.wait()
                self.__motorWatcher = None
            self.parameters.trackinglines = False
            self._mainwidget.updateinfowidgets(self.parameters)
            # self._mainwidget.setDoubleClickLock(False)
            self.__ui.trackPushButton.setText("Track")
            self.__ui.xcurLineEdit.hide()
            self.__ui.ycurLineEdit.hide()
            self.__ui.takePushButton.show()
            self.__ui.axesPushButton.show()
            self.__ui.trackPushButton.show()
            if changestatus:
                self.__tracking = False
            # self.__ui.xLineEdit.setReadOnly(False)
            # self.__ui.yLineEdit.setReadOnly(False)
            self.__ui.xcurLineEdit.setStyleSheet(
                "color: black; background-color: #90EE90;")
            self.__ui.ycurLineEdit.setStyleSheet(
                "color: black; background-color: #90EE90;")
            return True

        @QtCore.pyqtSlot()
        def _getFinal(self):
            """ update final positions
            """
            try:
                self.__xfinal = float(self.__ui.xLineEdit.text())
            except Exception:
                self.__ui.xLineEdit.setFocus()
                return False
            try:
                self.__yfinal = float(self.__ui.yLineEdit.text())
            except Exception:
                self.__ui.yLineEdit.setFocus()
                return False
            self._mainwidget.updatePositionMark(
                self.__xfinal, self.__yfinal, True)

        def __moveMotors(self):
            """ move motors

            :returns: motors started
            :rtype: :obj:`bool`
            """

            self._getFinal()

            if self.__xmotordevice is None or self.__ymotordevice is None:
                if not self._setMotors():
                    return False
            if str(self.__xmotordevice.state) != "ON" \
               and str(self.__ymotordevice.state) != "ON":
                try:
                    self.__xmotordevice.position = self.__xfinal
                    self.__ymotordevice.position = self.__yfinal
                except Exception as e:
                    logger.warning(str(e))
                    # print(str(e))
                    return False
            else:
                return False
            # print("%s %s" % (self.__xfinal, self.__yfinal))
            if self.__tracking:
                self.__hideMotors(False)
            self.parameters.trackinglines = True
            self._mainwidget.updateinfowidgets(self.parameters)
            self.__motorWatcher = motorWatchThread.MotorWatchThread(
                self.__xmotordevice, self.__ymotordevice)
            self.__motorWatcher.motorStatusSignal.connect(self._showMotors)
            self.__motorWatcher.watchingFinished.connect(self._finished)
            self.__motorWatcher.start()
            self._mainwidget.setDoubleClickLock(True)
            self.__ui.movePushButton.setText("Stop")
            self.__ui.xcurLineEdit.show()
            self.__ui.ycurLineEdit.show()
            self.__ui.takePushButton.hide()
            self.__ui.axesPushButton.hide()
            self.__ui.trackPushButton.hide()
            self.__ui.xLineEdit.setReadOnly(True)
            self.__ui.yLineEdit.setReadOnly(True)
            self.__moving = True
            self.__statex = None
            self.__statey = None
            return True

        def __trackMotors(self):
            """ track motors

            :returns: motors tracked
            :rtype: :obj:`bool`
            """

            if self.__xmotordevice is None or self.__ymotordevice is None:
                if not self._setMotors():
                    return False
            try:
                self.parameters.trackinglines = True
                self._mainwidget.updateinfowidgets(self.parameters)
            except Exception as e:
                logger.warning(str(e))
                # print(str(e))
                return False

            # print("%s %s" % (self.__xfinal, self.__yfinal))
            self.__motorWatcher = motorWatchThread.MotorWatchThread(
                self.__xmotordevice, self.__ymotordevice)
            self.__motorWatcher.motorStatusSignal.connect(self._showMotors)
            # self.__motorWatcher.watchingFinished.connect(self._hide)
            self.__motorWatcher.start()
            self.__ui.trackPushButton.setText("Untrack")
            self.__ui.xcurLineEdit.show()
            self.__ui.ycurLineEdit.show()
            self.__ui.takePushButton.hide()
            self.__ui.axesPushButton.hide()
            # self.__ui.trackPushButton.hide()
            # self.__ui.xLineEdit.setReadOnly(True)
            # self.__ui.yLineEdit.setReadOnly(True)
            self.__tracking = True
            return True

        @QtCore.pyqtSlot(float, str, float, str)
        def _showMotors(self, positionx, statex, positiony, statey):
            """ shows motors positions and states
            """
            # print("%s %s %s %s" % (positionx, statex, positiony, statey))
            self._mainwidget.updatePositionTrackingMark(
                positionx, positiony, True)

            self.__ui.xcurLineEdit.setText(str(positionx))
            self.__ui.ycurLineEdit.setText(str(positiony))
            if self.__statex != statex:
                self.__statex = statex
                if statex == "MOVING":
                    self.__ui.xcurLineEdit.setStyleSheet(
                        "color: black; background-color: #ADD8E6;")
                else:
                    self.__ui.xcurLineEdit.setStyleSheet(
                        "color: black; background-color: #90EE90;")
            if self.__statey != statey:
                self.__statey = statey
                if statey == "MOVING":
                    self.__ui.ycurLineEdit.setStyleSheet(
                        "color: black; background-color: #ADD8E6;")
                else:
                    self.__ui.ycurLineEdit.setStyleSheet(
                        "color: black; background-color: #90EE90;")

        @QtCore.pyqtSlot()
        def _setMotors(self):
            """ launches motors widget

            :returns: apply status
            :rtype: :obj:`bool`
            """

            motors = self._mainwidget.getElementNames("MotorList")
            cnfdlg = takeMotorsDialog.TakeMotorsDialog()
            if motors is not None:
                cnfdlg.motortips = motors
            cnfdlg.xmotorname = self.__xmotorname
            cnfdlg.ymotorname = self.__ymotorname
            cnfdlg.createGUI()
            if cnfdlg.exec_():
                self.__xmotorname = cnfdlg.xmotorname
                self.__ymotorname = cnfdlg.ymotorname
                self.__xmotordevice = cnfdlg.xmotordevice
                self.__ymotordevice = cnfdlg.ymotordevice
                self.__ui.takePushButton.setToolTip(
                    "x-motor: %s\ny-motor: %s" % (
                        self.__xmotorname, self.__ymotorname))
                self.__ui.xLabel.setToolTip(
                    "x-motor position (%s)" % self.__xmotorname)
                self.__ui.xLineEdit.setToolTip(
                    "final x-motor position (%s)" % self.__xmotorname)
                self.__ui.xcurLineEdit.setToolTip(
                    "current x-motor position (%s)" % self.__xmotorname)
                self.__ui.yLabel.setToolTip(
                    "y-motor position (%s)" % self.__ymotorname)
                self.__ui.yLineEdit.setToolTip(
                    "final y-motor position (%s)" % self.__ymotorname)
                self.__ui.ycurLineEdit.setToolTip(
                    "current y-motor position (%s)" % self.__ymotorname)
                return True
                self._mainwidget.emitTCC()
            return False

        @QtCore.pyqtSlot()
        def _message(self):
            """ provides intensity message
            """
            x, y, intensity = self._mainwidget.currentIntensity()[:3]
            if isinstance(intensity, float) and np.isnan(intensity):
                intensity = 0
            ilabel = self._mainwidget.scalingLabel()
            txdata, tydata = self._mainwidget.scaledxy(x, y)
            xunits, yunits = self._mainwidget.axesunits()
            if isinstance(intensity, np.ndarray) and \
               intensity.size <= 3:
                itn = [0 if (isinstance(it, float) and np.isnan(it))
                       else it for it in intensity]
                if len(itn) >= 3:
                    if txdata is not None:
                        message = "x = %f%s, y = %f%s, " \
                                  "%s = (%.2f, %.2f, %.2f)" % (
                                      txdata,
                                      (" %s" % xunits) if xunits else "",
                                      tydata,
                                      (" %s" % yunits) if yunits else "",
                                      ilabel,
                                      itn[0], itn[1], itn[2])
                    else:
                        message = "x = %i%s, y = %i%s, " \
                                  "%s = (%.2f, %.2f, %.2f)" % (
                                      x,
                                      (" %s" % xunits) if xunits else "",
                                      y,
                                      (" %s" % yunits) if yunits else "",
                                      ilabel,
                                      itn[0], itn[1], itn[2])
                    self._mainwidget.setDisplayedText(message)
            else:
                if txdata is not None:
                    message = "x = %f%s, y = %f%s, %s = %.2f" % (
                        txdata,
                        (" %s" % xunits) if xunits else "",
                        tydata,
                        (" %s" % yunits) if yunits else "",
                        ilabel,
                        intensity
                    )
                else:
                    message = "x = %i%s, y = %i%s, %s = %.2f" % (
                        x,
                        (" %s" % xunits) if xunits else "",
                        y,
                        (" %s" % yunits) if yunits else "",
                        ilabel,
                        intensity)
                self._mainwidget.setDisplayedText(message)

    class WD(Enum):
        """ Enum with Parameter Widget positions
        """
        label = 0
        read = 1
        write = 2
        button = 3

    class ParametersToolWidget(ToolBaseWidget):
        """ motors tool widget
        """
        #: (:obj:`str`) tool name
        name = "Parameters"
        #: (:obj:`str`) tool name alias
        alias = "parameters"
        #: (:obj:`tuple` <:obj:`str`>) capitalized required packages
        requires = ("TANGO",)

        #: (:obj:`dict` <:obj:`str` , :obj:`type` or :obj:`types.MethodType` >) \
        #:      map of type : converting function
        convert = {"float16": float, "float32": float, "float64": float,
                   "float": float, "int64": long, "int32": int,
                   "int16": int, "int8": int, "int": int, "uint64": long,
                   "uint32": long, "uint16": int,
                   "uint8": int, "uint": int, "string": str, "str": str,
                   "bool": Converters.toBool}

        def __init__(self, parent=None):
            """ constructor

            :param parent: parent object
            :type parent: :class:`pyqtgraph.QtCore.QObject`
            """
            ToolBaseWidget.__init__(self, parent)

            #: (:class:`lavuelib.settings.Settings`) configuration settings
            self.__settings = self._mainwidget.settings()

            # #: (:obj:`list` <str, str, str>)
            #        detector parameters (label, devicename, type)
            self.__detparams = []
            #: (:obj:`list` <:class:`tango.DeviceProxy`>)  attribute proxies
            self.__aproxies = []
            #: (:obj:`list` <:list:`any`>)  attribute values
            self.__avalues = []
            #: (:class:`lavuelib.motorWatchThread.attributeWatchThread`)
            #               attribute watcher
            self.__attrWatcher = None
            #: (:obj:`bool`) is watching
            self.__watching = False
            #: (:obj:`list` <:obj:`list` <:class:`pyqtgraph.QtCore.QObject`> >)
            # widgets
            self.__widgets = []

            #: (:class:`Ui_ParametersToolWidget')
            #:        ui_toolwidget object from qtdesigner
            self.__ui = _parametersformclass()
            self.__ui.setupUi(self)

            self.parameters.infolineedit = ""
            self.parameters.infotips = \
                "coordinate info display for the mouse pointer"

            #: (:obj:`list` < [:class:`pyqtgraph.QtCore.pyqtSignal`, :obj:`str`] >)
            #: list of [signal, slot] object to connect
            self.signal2slot = [
                [self.__ui.setupPushButton.clicked, self._setParameters],
                [self._mainwidget.mouseImagePositionChanged, self._message],
             ]

            #: (:class:`pyqtgraph.QtCore.QSignalMapper`) apply mapper
            self.__applymapper = QtCore.QSignalMapper(self)
            self.__applymapper.mapped.connect(self._applypars)

        @debugmethod
        def configure(self, configuration):
            """ set configuration for the current tool

            :param configuration: configuration string
            :type configuration: :obj:`str`
            """
            if configuration:
                cnf = json.loads(configuration)
                if "tango_det_attrs" in cnf.keys():
                    record = cnf["tango_det_attrs"]
                    if self.__settings.tangodetattrs != \
                       str(json.dumps(record, cls=numpyEncoder)):
                        self.__settings.tangodetattrs = \
                            str(json.dumps(record, cls=numpyEncoder))
                    self.__updateParams()

        @debugmethod
        def configuration(self):
            """ provides configuration for the current tool

            :returns configuration: configuration string
            :rtype configuration: :obj:`str`
            """
            cnf = {}
            try:
                cnf["tango_det_attrs"] = json.loads(self.__settings.tangodetattrs)
            except Exception as e:
                logger.warning(str(e))
            return json.dumps(cnf, cls=numpyEncoder)

        def _applypars(self, wid):
            """ apply the parameter with the given widget id

            :param wid: widget id
            :type wid: :obj:`int`
            """
            txt = str(self.__widgets[wid][WD.write.value].text() or "")
            tp = self.__detparams[wid][2]
            frm = self.__detparams[wid][4]
            ap = self.__aproxies[wid]
            if frm == "SPECTRUM":
                try:
                    vl = json.loads(txt)
                except Exception as e:
                    try:
                        vl = txt.split()
                        if tp and tp in self.convert.keys():
                            vl = [self.convert[tp](v) for v in vl]
                    except Exception as e2:
                        logger.warning(str(e))
                        logger.warning(str(e2))
                        vl = txt
            elif frm == "IMAGE":
                try:
                    vl = json.loads(txt)
                except Exception as e:
                    logger.warning(str(e))
                    vl = txt
            else:
                try:
                    if tp and tp in self.convert.keys():
                        vl = self.convert[tp](txt)
                    else:
                        vl = txt
                except Exception as e:
                    logger.warning(str(e))
                    vl = txt
            try:
                ap.write(vl)
            except Exception as e:
                logger.warning(str(e))

        @debugmethod
        def activate(self):
            """ activates tool widget
            """
            record = json.loads(str(self.__settings.tangodetattrs or "{}").strip())
            if not isinstance(record, dict):
                record = {}
            self.deactivate()
            for lb in sorted(record.keys()):
                try:
                    dv = record[lb]
                    ap = tango.AttributeProxy(dv)
                    unit = ""
                    frm = ""
                    try:
                        cap = ap.get_config()
                        unit = cap.unit or ""
                        vl = ap.read().value
                        tp = type(vl).__name__
                        if tp == "ndarray":
                            tp = str(vl.dtype)
                        frm = str(cap.data_format)
                    except Exception as e:
                        logger.warning(str(e))
                        vl = None
                        tp = ""
                except Exception as e:
                    logger.warning(str(e))
                    dv = None
                if dv is not None:
                    self.__aproxies.append(ap)
                    self.__detparams.append([lb, dv, tp, unit, frm])
                    self.__avalues.append(vl)
            self.__updateWidgets()
            self.__attrWatcher = motorWatchThread.AttributeWatchThread(
                self.__aproxies, self.__settings.toolpollinginterval)
            self.__attrWatcher.attrValuesSignal.connect(self._showValues)
            self.__attrWatcher.start()
            while not self.__attrWatcher.isWatching():
                QtCore.QCoreApplication.processEvents()
                time.sleep(0.1)

        def __updateWidgets(self):
            """ add widgets
            """
            layout = self.__ui.parGridLayout

            while len(self.__detparams) > len(self.__widgets):
                self.__widgets.append(
                    [
                        QtGui.QLabel(parent=self._mainwidget),
                        QtGui.QLineEdit(parent=self._mainwidget),
                        QtGui.QLineEdit(parent=self._mainwidget),
                        QtGui.QPushButton("Apply", parent=self._mainwidget),
                    ]
                )
                last = len(self.__widgets)
                self.__widgets[-1][WD.read.value].setReadOnly(True)
                self.__widgets[-1][WD.read.value].setStyleSheet(
                    "color: black; background-color: #90EE90;")
                self.__widgets[-1][WD.read.value].setAlignment(
                    QtCore.Qt.AlignCenter)
                self.__widgets[-1][WD.write.value].setAlignment(
                    QtCore.Qt.AlignRight)
                for i, w in enumerate(self.__widgets[-1]):
                    layout.addWidget(w, last, i)
                self.__widgets[-1][WD.button.value].clicked.connect(
                    self.__applymapper.map)
                self.__applymapper.setMapping(
                    self.__widgets[-1][WD.button.value], last - 1)
                # self.__widgets[-1][WD.read.value].setMaximumWidth(200)
            while len(self.__detparams) < len(self.__widgets):
                wds = self.__widgets.pop()
                self.__applymapper.removeMappings(wds[WD.button.value])
                for w in wds:
                    w.hide()
                    layout.removeWidget(w)
            for i, pars in enumerate(self.__detparams):
                self.__widgets[i][WD.label.value].setText("%s:" % pars[0])
                self.__widgets[i][WD.label.value].setToolTip("%s" % pars[1])
                self.__widgets[i][WD.write.value].setText("")
                self.__widgets[i][WD.write.value].setToolTip("%s" % pars[1])
                self.__widgets[i][WD.write.button.value].setToolTip(
                    "Write the new value of %s" % pars[0])
                if self.__avalues[i] is None:
                    vl = ""
                else:
                    vl = str(self.__avalues[i])
                if pars[3]:
                    vl = "%s %s" % (vl, pars[3])
                self.__widgets[i][WD.read.value].setToolTip(vl)
                self.__widgets[i][WD.read.value].setText(vl)

        @debugmethod
        def deactivate(self):
            """ activates tool widget
            """
            if self.__attrWatcher:
                self.__attrWatcher.attrValuesSignal.disconnect(self._showValues)
                # self.__attrWatcher.watchingFinished.disconnect(self._finished)
                logger.debug("STOPING %s" % str(self.__attrWatcher))
                self.__attrWatcher.stop()
                logger.debug("WAITING  for %s" % str(self.__attrWatcher))
                self.__attrWatcher.wait()
                logger.debug("REMOVING  for %s" % str(self.__attrWatcher))
                self.__attrWatcher = None
            self.__aproxies = []
            self.__detparams = []
            self.__avalues = []

        @QtCore.pyqtSlot(str)
        def _showValues(self, values):
            """ show values
            """
            vls = json.loads(values)
            for i, pars in enumerate(self.__widgets):
                if i < len(vls):
                    vl = str(vls[i])
                    if self.__detparams[i][3]:
                        vl = "%s %s" % (vl, self.__detparams[i][3])
                    self.__widgets[i][WD.read.value].setText(vl)
                    self.__widgets[i][WD.read.value].setToolTip(vl)

        @QtCore.pyqtSlot()
        def _setParameters(self):
            """ launches parameters widget

            :returns: apply status
            :rtype: :obj:`bool`
            """
            record = json.loads(str(self.__settings.tangodetattrs or "{}").strip())
            if not isinstance(record, dict):
                record = {}
            dform = edDictDialog.EdDictDialog(self)
            dform.record = record
            dform.title = "Tango Detector Attributes"
            dform.createGUI()
            dform.exec_()
            if dform.dirty:
                for key in list(record.keys()):
                    if not str(key).strip():
                        record.pop(key)
                if self.__settings.tangodetattrs != \
                   str(json.dumps(record, cls=numpyEncoder)):
                    self.__settings.tangodetattrs = \
                        str(json.dumps(record, cls=numpyEncoder))
                    self.__updateParams()

        def __updateParams(self):
            """ update parameters
            """
            self.deactivate()
            self.activate()
            self._mainwidget.emitTCC()

        @QtCore.pyqtSlot()
        def _message(self):
            """ provides intensity message
            """
            _, _, intensity, x, y = self._mainwidget.currentIntensity()
            if isinstance(intensity, float) and np.isnan(intensity):
                intensity = 0
            if isinstance(intensity, np.ndarray):
                intensity = np.nansum(
                    [0 if (isinstance(it, float) and np.isnan(it))
                     else it for it in intensity])
            ilabel = self._mainwidget.scalingLabel()
            txdata, tydata = self._mainwidget.scaledxy(x, y)
            xunits, yunits = self._mainwidget.axesunits()
            if txdata is not None:
                message = "x = %f%s, y = %f%s, %s = %.2f" % (
                    txdata,
                    (" %s" % xunits) if xunits else "",
                    tydata,
                    (" %s" % yunits) if yunits else "",
                    ilabel,
                    intensity
                )
            else:
                message = "x = %f%s, y = %f%s, %s = %.2f" % (
                    x,
                    (" %s" % xunits) if xunits else "",
                    y,
                    (" %s" % yunits) if yunits else "",
                    ilabel,
                    intensity)
            self._mainwidget.setDisplayedText(message)

    class MeshToolWidget(ToolBaseWidget):
        """ mesh tool widget
        """
        #: (:class:`pyqtgraph.QtCore.pyqtSignal`) roi info Changed signal
        roiInfoChanged = QtCore.pyqtSignal(str)

        #: (:obj:`str`) tool name
        name = "MeshScan"
        #: (:obj:`str`) tool name alias
        alias = "meshscan"
        #: (:obj:`tuple` <:obj:`str`>) capitalized required packages
        requires = ("TANGO",)

        def __init__(self, parent=None):
            """ constructor

            :param parent: parent object
            :type parent: :class:`pyqtgraph.QtCore.QObject`
            """
            ToolBaseWidget.__init__(self, parent)

            #: (:obj:`str`) x-motor name
            self.__xmotorname = ""
            #: (:obj:`str`) y-motor name
            self.__ymotorname = ""
            #: (:obj:`str`) state of x-motor
            self.__statex = None
            #: (:obj:`str`) state of y-motor
            self.__statey = None
            #: (:class:`tango.DeviceProxy`) x-motor device
            self.__xmotordevice = None
            #: (:class:`tango.DeviceProxy`) y-motor device
            self.__ymotordevice = None
            #: (:class:`tango.DeviceProxy`) door server
            self.__door = None
            #: (:class:`lavuelib.motorWatchThread.motorWatchThread`) motor watcher
            self.__motorWatcher = None
            #: (:obj:`bool`) is moving
            self.__moving = False

            #: (:obj:`int`) number of x intervals
            self.__xintervals = 2
            #: (:obj:`int`) number of y intervals
            self.__yintervals = 3
            #: (:obj:`float`) integration time in seconds
            self.__itime = 0.1

            #: (:class:`Ui_MeshToolWidget')
            #:        ui_toolwidget object from qtdesigner
            self.__ui = _meshformclass()
            self.__ui.setupUi(self)
            self.__showLabels()

            self.parameters.scale = True
            self.parameters.rois = False
            self.parameters.mesh = True
            self.parameters.infolineedit = ""
            self.parameters.infolabel = "[x1, y1, x2, y2]: "
            self.parameters.infotips = \
                "coordinate info display for the mouse pointer"

            #: (:obj:`list` < [:class:`pyqtgraph.QtCore.pyqtSignal`, :obj:`str`] >)
            #: list of [signal, slot] object to connect
            self.signal2slot = [
                [self.__ui.axesPushButton.clicked, self._mainwidget.setTicks],
                [self.__ui.takePushButton.clicked, self._setMotors],
                [self.__ui.intervalsPushButton.clicked, self._setIntervals],
                [self.__ui.scanPushButton.clicked, self._scanStopMotors],
                [self.roiInfoChanged, self._mainwidget.updateDisplayedText],
                [self._mainwidget.roiValueChanged, self.updateROIDisplayText],
                [self._mainwidget.mouseImagePositionChanged, self._message]
            ]

        # @debugmethod
        def configure(self, configuration):
            """ set configuration for the current tool

            :param configuration: configuration string
            :type configuration: :obj:`str`
            """
            if configuration:
                cnf = json.loads(configuration)
                pars = ["position", "scale",
                        "xtext", "ytext", "xunits", "yunits"]
                if any(par in cnf.keys() for par in pars):
                    self._mainwidget.updateTicks(cnf)
                if "motors" in cnf.keys():
                    try:
                        motorname = cnf["motors"][0]
                        motordevice = tango.DeviceProxy(motorname)
                        for attr in ["state", "position"]:
                            if not hasattr(motordevice, attr):
                                raise Exception("Missing %s" % attr)
                        self.__xmotorname = motorname
                        self.__xmotordevice = motordevice
                    except Exception as e:
                        logger.warning(str(e))
                    try:
                        motorname = cnf["motors"][1]
                        motordevice = tango.DeviceProxy(motorname)
                        for attr in ["state", "position"]:
                            if not hasattr(motordevice, attr):
                                raise Exception("Missing %s" % attr)
                        self.__ymotorname = motorname
                        self.__ymotordevice = motordevice
                    except Exception as e:
                        logger.warning(str(e))
                pars = ["x_intervals", "y_intervals", "interval_time"]
                if any(par in cnf.keys() for par in pars):
                    if "x_intervals" in cnf.keys():
                        try:
                            self.__xintervals = int(cnf["x_intervals"])
                        except Exception:
                            pass
                    if "y_intervals" in cnf.keys():
                        try:
                            self.__yintervals = int(cnf["y_intervals"])
                        except Exception:
                            pass
                    if "interval_time" in cnf.keys():
                        try:
                            self.__itime = float(cnf["interval_time"])
                        except Exception:
                            pass
                    self._updateIntervals()
                if "scan" in cnf.keys():
                    if cnf["scan"]:
                        if str(self.__ui.scanPushButton.text()) == "Scan":
                            self._scanStopMotors()
                if "stop" in cnf.keys():
                    if cnf["stop"]:
                        if str(self.__ui.scanPushButton.text()) == "Stop":
                            self._scanStopMotors()

        # @debugmethod
        def configuration(self):
            """ provides configuration for the current tool

            :returns configuration: configuration string
            :rtype configuration: :obj:`str`
            """
            cnf = {}
            xpos, ypos, xsc, ysc = self._mainwidget.scale()
            cnf["xunits"], cnf["yunits"] = self._mainwidget.axesunits()
            cnf["xtext"], cnf["ytext"] = self._mainwidget.axestext()
            cnf["position"] = [xpos, ypos]
            cnf["scale"] = [xsc, ysc]
            cnf["x_intervals"] = self.__xintervals
            cnf["y_intervals"] = self.__yintervals
            cnf["interval_time"] = self.__itime
            cnf["motors"] = [self.__xmotorname, self.__ymotorname]
            if str(self.__ui.scanPushButton.text()) == "Scan":
                cnf["motor_state"] = "ON"
            else:
                cnf["motor_state"] = "MOVING"

            return json.dumps(cnf, cls=numpyEncoder)

        def activate(self):
            """ activates tool widget
            """
            self._mainwidget.changeMeshRegion()
            # self._mainwidget.updateROIs(1)

        def deactivate(self):
            """ deactivates tool widget
            """
            self._mainwidget.meshCoordsChanged.emit()

        @QtCore.pyqtSlot(str, int, str)
        def updateROIDisplayText(self, text, currentroi, roiVal):
            """ updates ROI display text

            :param text: standard display text
            :type text: :obj:`str`
            :param currentroi: current roi label
            :type currentroi: :obj:`str`
            :param text: roi sum value
            :type text: :obj:`str`
            """

            if "/" in roiVal:
                sroiv = " / ".split(roiVal)
                roiVal = sroiv[currentroi] if len(sroiv) > currentroi else ""
            roilabel = "roi [%s]" % (currentroi + 1)

            self.roiInfoChanged.emit("%s, %s = %s" % (text, roilabel, roiVal))

        @QtCore.pyqtSlot()
        def _scanStopMotors(self):
            """ starts or stops scan
            """
            if str(self.__ui.scanPushButton.text()) == "Scan":
                self.__startScan()
            else:
                self.__stopScan()
            self._mainwidget.emitTCC()

        @QtCore.pyqtSlot()
        def _finished(self):
            """ stops mesh scan without stopping the macro
            """
            self.__stopScan(stopmacro=False)
            self._mainwidget.emitTCC()

        def __stopScan(self, stopmacro=True):
            """ stops mesh scan
            :param stopmacro: call stopmacro
            :type stopmacro: :obj:`bool`
            :returns: motors stopped
            :rtype: :obj:`bool`
            """

            if stopmacro:
                try:
                    self.__door.StopMacro()
                except Exception as e:
                    logger.warning(str(e))
                    # print(str(e))

            if self.__motorWatcher:
                self.__motorWatcher.motorStatusSignal.disconnect(self._showMotors)
                self.__motorWatcher.watchingFinished.disconnect(self._finished)
                self.__motorWatcher.stop()
                self.__motorWatcher.wait()
                self.__motorWatcher = None
            self.__showLabels()
            self.__moving = False
            self.__ui.xcurLineEdit.setStyleSheet(
                "color: black; background-color: #90EE90;")
            self.__ui.ycurLineEdit.setStyleSheet(
                "color: black; background-color: #90EE90;")
            self._mainwidget.showDoorError()
            return True

        def __showLabels(self):
            """ shows GUI labels
            """
            self.__ui.scanPushButton.setText("Scan")
            self.__ui.xcurLineEdit.hide()
            self.__ui.ycurLineEdit.hide()
            self.__ui.takePushButton.show()
            self.__ui.axesPushButton.show()
            self.__ui.intervalsPushButton.show()
            self.__ui.xLabel.setText("X: %s" % (self.__xintervals))
            self.__ui.yLabel.setText("Y: %s" % (self.__yintervals))
            self.__ui.timeLabel.setText("dT: %ss" % str(self.__itime))
            self.__ui.timeLabel.show()

        def __hideLabels(self):
            """ hides GUI labels
            """
            self.__ui.scanPushButton.setText("Stop")
            self.__ui.xcurLineEdit.show()
            self.__ui.ycurLineEdit.show()
            self.__ui.takePushButton.hide()
            self.__ui.axesPushButton.hide()
            self.__ui.intervalsPushButton.hide()
            self.__ui.xLabel.setText("X: %s" % (self.__xintervals))
            self.__ui.yLabel.setText("Y: %s" % (self.__yintervals))
            self.__ui.timeLabel.setText("dT: %ss" % str(self.__itime))
            self.__ui.timeLabel.show()

        def __startScan(self):
            """ start scan

            :returns: motors started
            :rtype: :obj:`bool`
            """
            coords = self._mainwidget.meshCoords()
            if 0 < len(coords):
                curcoords = coords[0]
            else:
                return False

            if self.__xmotordevice is None or self.__ymotordevice is None:
                if not self._setMotors():
                    return False

            macrocommand = []
            macrocommand.append("mesh")
            macrocommand.append(str(self.__xmotorname))
            macrocommand.append(str(float(curcoords[0])))
            macrocommand.append(str(float(curcoords[2])))
            macrocommand.append(str(self.__xintervals))
            macrocommand.append(str(self.__ymotorname))
            macrocommand.append(str(float(curcoords[1])))
            macrocommand.append(str(float(curcoords[3])))
            macrocommand.append(str(self.__yintervals))
            macrocommand.append(str(self.__itime))
            macrocommand.append("True")
            self.__door = self._mainwidget.getDoor()
            if self.__door is None:
                logger.error(
                    "MeshToolWidget.__startScan: Cannot access Door device")
                return False

            if not self._mainwidget.runMacro(macrocommand):
                logger.error(
                    "MeshToolWidget.__startScan: Cannot in running %s"
                    % macrocommand)
                return False

            self.__motorWatcher = motorWatchThread.MotorWatchThread(
                self.__xmotordevice, self.__ymotordevice, self.__door)
            self.__motorWatcher.motorStatusSignal.connect(self._showMotors)
            self.__motorWatcher.watchingFinished.connect(self._finished)
            self.__motorWatcher.start()
            self.__hideLabels()
            self.__moving = True
            self.__statex = None
            self.__statey = None
            return True

        @QtCore.pyqtSlot(float, str, float, str)
        def _showMotors(self, positionx, statex, positiony, statey):
            """ shows motors positions and states
            """
            # print("%s %s %s %s" % (positionx, statex, positiony, statey))
            self.__ui.xcurLineEdit.setText(str(positionx))
            self.__ui.ycurLineEdit.setText(str(positiony))
            if self.__statex != statex:
                self.__statex = statex
                if statex == "MOVING":
                    self.__ui.xcurLineEdit.setStyleSheet(
                        "color: black; background-color: #ADD8E6;")
                else:
                    self.__ui.xcurLineEdit.setStyleSheet(
                        "color: black; background-color: #90EE90;")
            if self.__statey != statey:
                self.__statey = statey
                if statey == "MOVING":
                    self.__ui.ycurLineEdit.setStyleSheet(
                        "color: black; background-color: #ADD8E6;")
                else:
                    self.__ui.ycurLineEdit.setStyleSheet(
                        "color: black; background-color: #90EE90;")

        @QtCore.pyqtSlot()
        def _setMotors(self):
            """ launches motors widget

            :returns: apply status
            :rtype: :obj:`bool`
            """
            motors = self._mainwidget.getElementNames("MotorList")
            cnfdlg = takeMotorsDialog.TakeMotorsDialog()
            if motors is not None:
                cnfdlg.motortips = motors
            cnfdlg.title = "Motor aliases"
            cnfdlg.xmotorname = self.__xmotorname
            cnfdlg.ymotorname = self.__ymotorname
            cnfdlg.createGUI()
            if cnfdlg.exec_():
                self.__xmotorname = cnfdlg.xmotorname
                self.__ymotorname = cnfdlg.ymotorname
                self.__xmotordevice = cnfdlg.xmotordevice
                self.__ymotordevice = cnfdlg.ymotordevice
                self.__ui.takePushButton.setToolTip(
                    "x-motor: %s\ny-motor: %s" % (
                        self.__xmotorname, self.__ymotorname))
                self.__ui.xLabel.setToolTip(
                    "x-motor interval number (%s)" % self.__xmotorname)
                self.__ui.xcurLineEdit.setToolTip(
                    "current x-motor position (%s)" % self.__xmotorname)
                self.__ui.yLabel.setToolTip(
                    "y-motor interval number (%s)" % self.__ymotorname)
                self.__ui.ycurLineEdit.setToolTip(
                    "current y-motor position (%s)" % self.__ymotorname)
                self.__showLabels()
                self._mainwidget.emitTCC()
                return True
            return False

        @QtCore.pyqtSlot()
        def _setIntervals(self):
            """ launches motors widget

            :returns: apply status
            :rtype: :obj:`bool`
            """
            cnfdlg = intervalsDialog.IntervalsDialog()
            cnfdlg.xintervals = self.__xintervals
            cnfdlg.yintervals = self.__yintervals
            cnfdlg.itime = self.__itime
            cnfdlg.createGUI()
            if cnfdlg.exec_():
                self.__xintervals = cnfdlg.xintervals
                self.__yintervals = cnfdlg.yintervals
                self.__itime = cnfdlg.itime
                self._updateIntervals()
                return True
            return False

        def _updateIntervals(self):
            """ update interval informations
            """
            self.__ui.intervalsPushButton.setToolTip(
                "x-intervals:%s\ny-intervals:%s\nintegration time:%s" % (
                    self.__xintervals, self.__yintervals, self.__itime))
            self.__showLabels()
            self._mainwidget.emitTCC()

        @QtCore.pyqtSlot()
        def _message(self):
            """ provides mesh message
            """
            message = ""
            coords = self._mainwidget.meshCoords()
            if 0 < len(coords):
                message = "%s" % coords[0]
            self._mainwidget.setDisplayedText(message)

    class ROIToolWidget(ToolBaseWidget):
        """ roi tool widget
        """
        #: (:class:`pyqtgraph.QtCore.pyqtSignal`) apply ROI pressed signal
        applyROIPressed = QtCore.pyqtSignal(str, int)
        #: (:class:`pyqtgraph.QtCore.pyqtSignal`) fetch ROI pressed signal
        fetchROIPressed = QtCore.pyqtSignal(str)
        #: (:class:`pyqtgraph.QtCore.pyqtSignal`) roi info Changed signal
        roiInfoChanged = QtCore.pyqtSignal(str)

        #: (:obj:`str`) tool name
        name = "ROI"
        #: (:obj:`str`) tool name alias
        alias = "roi"
        #: (:obj:`tuple` <:obj:`str`>) capitalized required packages
        requires = ()

        def __init__(self, parent=None):
            """ constructor

            :param parent: parent object
            :type parent: :class:`pyqtgraph.QtCore.QObject`
            """
            ToolBaseWidget.__init__(self, parent)

            #: (:class:`Ui_ROIToolWidget') ui_toolwidget object from qtdesigner
            self.__ui = _roiformclass()
            self.__ui.setupUi(self)

            #: (:obj:`list`< :obj:`str`>) sardana aliases
            self.__aliases = []
            #: (:obj:`int`) ROI label length
            self.__textlength = 0

            self.parameters.rois = True
            self.parameters.infolineedit = ""
            self.parameters.infolabel = "[x1, y1, x2, y2], sum: "
            self.parameters.infotips = \
                "coordinate info display for the mouse pointer"

            #: (:class:`lavuelib.settings.Settings`) configuration settings
            self.__settings = self._mainwidget.settings()

            self._updateApplyButton()
            #: (:obj:`list` < [:class:`pyqtgraph.QtCore.pyqtSignal`, :obj:`str`] >)
            #: list of [signal, slot] object to connect
            self.signal2slot = [
                [self.__ui.applyROIPushButton.clicked, self._emitApplyROIPressed],
                [self.__ui.fetchROIPushButton.clicked, self._emitFetchROIPressed],
                [self.applyROIPressed, self._mainwidget.applyROIs],
                [self.fetchROIPressed, self._mainwidget.fetchROIs],
                [self.roiInfoChanged, self._mainwidget.updateDisplayedText],
                [self.__ui.labelROILineEdit.textChanged,
                 self._updateApplyButton],
                [self.__ui.roiSpinBox.valueChanged, self._mainwidget.updateROIs],
                [self.__ui.roiSpinBox.valueChanged, self._mainwidget.emitTCC],
                [self.__ui.roiSpinBox.valueChanged,
                 self._mainwidget.writeDetectorROIsAttribute],
                [self.__ui.labelROILineEdit.textEdited, self._writeDetectorROIs],
                [self.__ui.labelROILineEdit.textEdited, self._mainwidget.emitTCC],
                [self._mainwidget.roiLineEditChanged, self._updateApplyButton],
                [self._mainwidget.roiAliasesChanged, self.updateROILineEdit],
                [self._mainwidget.roiValueChanged, self.updateROIDisplayText],
                [self._mainwidget.roiNumberChanged, self.setROIsNumber],
                # [self._mainwidget.sardanaEnabled, self.updateROIButton],
                [self._mainwidget.mouseImagePositionChanged, self._message],
            ]

        def configure(self, configuration):
            """ set configuration for the current tool

            :param configuration: configuration string
            :type configuration: :obj:`str`
            """
            if configuration:
                cnf = json.loads(configuration)
                if "aliases" in cnf.keys():
                    aliases = cnf["aliases"]
                    if isinstance(aliases, list):
                        aliases = " ".join(aliases)
                    self.__ui.labelROILineEdit.setText(aliases)
                if "rois_number" in cnf.keys():
                    try:
                        self.__ui.roiSpinBox.setValue(int(cnf["rois_number"]))
                    except Exception as e:
                        logger.warning(str(e))
                        # print(str(e))
                if "rois_coords" in cnf.keys():
                    self._mainwidget.updateROIs(
                        len(cnf["rois_coords"]), cnf["rois_coords"])
                if "apply" in cnf.keys():
                    if cnf["apply"]:
                        self._emitApplyROIPressed()
                if "fetch" in cnf.keys():
                    if cnf["fetch"]:
                        self._emitFetchROIPressed()

        def configuration(self):
            """ provides configuration for the current tool

            :returns configuration: configuration string
            :rtype configuration: :obj:`str`
            """
            cnf = {}
            cnf["aliases"] = str(self.__ui.labelROILineEdit.text()).split(" ")
            cnf["rois_number"] = self.__ui.roiSpinBox.value()
            cnf["rois_coords"] = self._mainwidget.roiCoords()
            return json.dumps(cnf, cls=numpyEncoder)

        def activate(self):
            """ activates tool widget
            """
            self._mainwidget.changeROIRegion()
            self.setROIsNumber(len(self._mainwidget.roiCoords()))
            self.__aliases = self._mainwidget.getElementNames("ExpChannelList")
            self.updateROILineEdit(self._mainwidget.roilabels)
            self.__updateCompleter()
            # self.updateROIButton(
            #     self.__settings.sardana or bool(self.__settings.analysisdevice))

        def __updateCompleter(self):
            """ updates the labelROI help
            """
            text = str(self.__ui.labelROILineEdit.text())
            sttext = text.strip()
            sptext = sttext.split()
            stext = ""
            if text.endswith(" "):
                stext = sttext
            elif len(sptext) > 1:
                stext = " ".join(sptext[:-1])

            if stext:
                if self.__aliases:
                    hints = ["%s %s" % (stext, al) for al in self.__aliases]
                else:
                    hints = [stext]
            else:
                hints = self.__aliases or []
            completer = QtGui.QCompleter(hints, self)
            self.__ui.labelROILineEdit.setCompleter(completer)

        def deactivate(self):
            """ deactivates tool widget
            """
            self._mainwidget.roiCoordsChanged.emit()

        @QtCore.pyqtSlot()
        def _writeDetectorROIs(self):
            """ writes Detector rois and updates roi labels
            """
            self._mainwidget.roilabels = str(self.__ui.labelROILineEdit.text())
            self._mainwidget.writeDetectorROIsAttribute()

        @QtCore.pyqtSlot()
        def _message(self):
            """ provides roi message
            """
            message = ""
            current = self._mainwidget.currentROI()
            coords = self._mainwidget.roiCoords()
            if current > -1 and current < len(coords):
                message = "%s" % coords[current]
            self._mainwidget.setDisplayedText(message)

        @QtCore.pyqtSlot()
        def _emitApplyROIPressed(self):
            """ emits applyROIPressed signal"""

            text = str(self.__ui.labelROILineEdit.text()).strip()
            if self.__settings.singlerois:
                slabel = re.split(';|,| |\n', str(text))
                slabel = [lb for lb in slabel if lb]
                lsl = len(slabel)
                while lsl < len(self._mainwidget.roiCoords()):
                    lsl += 1
                    slabel.append("roi%s" % lsl)

                self.__ui.labelROILineEdit.setText(" ".join(slabel))
                self._updateApplyButton()
                text = str(self.__ui.labelROILineEdit.text()).strip()
            if not text:
                self.__ui.labelROILineEdit.setText("rois")
                self._updateApplyButton()
                text = str(self.__ui.labelROILineEdit.text()).strip()
            roispin = int(self.__ui.roiSpinBox.value())
            self.applyROIPressed.emit(text, roispin)

        @QtCore.pyqtSlot()
        def _emitFetchROIPressed(self):
            """ emits fetchROIPressed signal"""
            text = str(self.__ui.labelROILineEdit.text())
            self.fetchROIPressed.emit(text)

        @QtCore.pyqtSlot(str)
        @QtCore.pyqtSlot()
        def _updateApplyButton(self):
            """ updates applied button"""
            stext = str(self.__ui.labelROILineEdit.text())
            self._mainwidget.roilabels = stext
            currentlength = len(stext)
            # if not stext.strip():
            # self.__ui.applyROIPushButton.setEnabled(False)
            self.__updateCompleter()
            # else:
            #    # self.__ui.applyROIPushButton.setEnabled(True)
            if stext.endswith(" ") or currentlength < self.__textlength:
                self.__updateCompleter()
            self.__textlength = currentlength

        @QtCore.pyqtSlot(str)
        def updateROILineEdit(self, text):
            """ updates ROI line edit text

            :param text: text to update
            :type text: :obj:`str`
            """

            if not self.__ui.labelROILineEdit.hasFocus():
                self.__ui.labelROILineEdit.setText(text)
                self._updateApplyButton()

        @QtCore.pyqtSlot(bool)
        def updateROIButton(self, enabled):
            """ enables/disables ROI buttons

            :param enable: buttons enabled
            :type enable: :obj:`bool`
            """
            # self.__ui.applyROIPushButton.setEnabled(enabled)
            # self.__ui.fetchROIPushButton.setEnabled(enabled)

        @QtCore.pyqtSlot(int)
        def updateApplyTips(self, rid):
            """ updates apply tips

            :param rid: current roi id
            :type rid: :obj:`int`
            """
            if rid < 0:
                self.__ui.applyROIPushButton.setToolTip(
                    "remove ROI aliases from the Door environment"
                    " as well as from Active MntGrp")
            else:
                self.__ui.applyROIPushButton.setToolTip(
                    "add ROI aliases to the Door environment "
                    "as well as to Active MntGrp")

        @QtCore.pyqtSlot(str, int, str)
        def updateROIDisplayText(self, text, currentroi, roiVal):
            """ updates ROI display text

            :param text: standard display text
            :type text: :obj:`str`
            :param currentroi: current roi label
            :type currentroi: :obj:`str`
            :param text: roi sum value
            :type text: :obj:`str`
            """

            roilabel = "roi [%s]" % (currentroi + 1)
            slabel = []

            rlabel = str(self.__ui.labelROILineEdit.text())
            if rlabel:
                slabel = re.split(';|,| |\n', rlabel)
                slabel = [lb for lb in slabel if lb]
            if slabel:
                roilabel = "%s [%s]" % (
                    slabel[currentroi]
                    if currentroi < len(slabel) else slabel[-1],
                    (currentroi + 1)
                )
            if "/" in roiVal:
                self.roiInfoChanged.emit(
                    "%s, %s; values = %s" % (text, roilabel, roiVal))
            else:
                self.roiInfoChanged.emit("%s, %s = %s" % (text, roilabel, roiVal))

        @QtCore.pyqtSlot(int)
        def setROIsNumber(self, rid):
            """sets a number of rois

            :param rid: number of rois
            :type rid: :obj:`int`
            """
            self.__ui.roiSpinBox.setValue(rid)

    class LineCutToolWidget(ToolBaseWidget):
        """ line-cut tool widget
        """

        #: (:obj:`str`) tool name
        name = "LineCut"
        #: (:obj:`str`) tool name alias
        alias = "linecut"
        #: (:obj:`tuple` <:obj:`str`>) capitalized required packages
        requires = ()

        def __init__(self, parent=None):
            """ constructor

            :param parent: parent object
            :type parent: :class:`pyqtgraph.QtCore.QObject`
            """
            ToolBaseWidget.__init__(self, parent)

            #: (:class:`Ui_LineCutToolWidget') ui_toolwidget object from qtdesigner
            self.__ui = _cutformclass()
            self.__ui.setupUi(self)

            self.parameters.cuts = True
            self.parameters.bottomplot = True
            self.parameters.infolineedit = ""
            self.parameters.infotips = \
                "coordinate info display for the mouse pointer"

            #: (:obj:`int`) 1d x-coorindate index,
            #:          i.e. {0:Points, 1:"X-Pixels", 2:"Y-Pixels"}
            self.__xindex = 0
            #: (:obj:`bool`) plot cuts
            self.__allcuts = False
            #: (:obj:`list`<:class:`pyqtgraph.PlotDataItem`>) 1D plot freezed
            self.__freezed = []

            #: (:obj:`list`<:class:`pyqtgraph.PlotDataItem`>) 1D plot
            self.__curves = []
            #: (:obj:`int`) current plot number
            self.__nrplots = 0

            #: (:class:`lavuelib.settings.Settings`) configuration settings
            self.__settings = self._mainwidget.settings()

            #: (:obj:`list` < [:class:`pyqtgraph.QtCore.pyqtSignal`, :obj:`str`] >)
            #: list of [signal, slot] object to connect
            self.signal2slot = [
                [self.__ui.cutSpinBox.valueChanged, self._updateCuts],
                [self.__ui.cutSpinBox.valueChanged, self._mainwidget.emitTCC],
                [self._mainwidget.cutNumberChanged, self._setCutsNumber],
                [self._mainwidget.cutCoordsChanged, self._plotCuts],
                [self.__ui.xcoordsComboBox.currentIndexChanged,
                 self._setXCoords],
                [self.__ui.xcoordsComboBox.currentIndexChanged,
                 self._mainwidget.emitTCC],
                [self._mainwidget.mouseImagePositionChanged, self._message],
                [self.__ui.allcutsCheckBox.stateChanged, self._updateAllCuts],
                [self.__ui.allcutsCheckBox.stateChanged,
                 self._mainwidget.emitTCC],
                [self._mainwidget.freezeBottomPlotClicked, self._freezeplot],
                [self._mainwidget.clearBottomPlotClicked, self._clearplot],
            ]

        def configure(self, configuration):
            """ set configuration for the current tool

            :param configuration: configuration string
            :type configuration: :obj:`str`
            """
            if configuration:
                cnf = json.loads(configuration)
                if "cuts_number" in cnf.keys():
                    try:
                        self.__ui.cutSpinBox.setValue(int(cnf["cuts_number"]))
                    except Exception as e:
                        logger.warning(str(e))
                        # print(str(e))
                if "all_cuts" in cnf.keys():
                    self.__ui.allcutsCheckBox.setChecked(bool(cnf["all_cuts"]))
                if "x_coordinates" in cnf.keys():
                    idxs = ["points", "x-pixels", "y-pixels"]
                    xcrd = str(cnf["x_coordinates"]).lower()
                    try:
                        idx = idxs.index(xcrd)
                    except Exception:
                        idx = 0
                    self.__ui.xcoordsComboBox.setCurrentIndex(idx)

        def configuration(self):
            """ provides configuration for the current tool

            :returns configuration: configuration string
            :rtype configuration: :obj:`str`
            """
            cnf = {}
            cnf["x_coordinates"] = str(
                self.__ui.xcoordsComboBox.currentText()).lower()
            cnf["all_cuts"] = self.__ui.allcutsCheckBox.isChecked()
            cnf["cuts_number"] = self.__ui.cutSpinBox.value()
            return json.dumps(cnf, cls=numpyEncoder)

        @QtCore.pyqtSlot()
        def _freezeplot(self):
            """ freeze plot
            """
            self._clearplot()
            nrplots = self.__nrplots
            while nrplots > len(self.__freezed):
                cr = self._mainwidget.onedbottomplot()
                cr.setPen(_pg.mkColor(0.5))
                self.__freezed.append(cr)

            for i in range(nrplots):
                dt = self.__curves[i].xData, self.__curves[i].yData
                self.__freezed[i].setData(*dt)
                self.__freezed[i].show()
                self.__freezed[i].setVisible(True)
            for i in range(nrplots, len(self.__freezed)):
                self.__freezed[i].hide()
                self.__freezed[i].setVisible(True)

        @QtCore.pyqtSlot()
        def _clearplot(self):
            """ clear plot
            """
            for cr in self.__freezed:
                cr.setVisible(False)

        @QtCore.pyqtSlot(int)
        def _updateCuts(self, cid):
            """ update Cuts

            :param cid: cut id
            :type cid: :obj:`int`
            """
            if self.__allcuts:
                self.__allcuts = True
            else:
                self.__allcuts = False

            self._mainwidget.updateCuts(cid)

        def afterplot(self):
            """ command after plot
            """
            self._plotCuts()

        def activate(self):
            """ activates tool widget
            """
            if not self.__curves:
                self.__curves.append(self._mainwidget.onedbottomplot(True))
                self.__nrplots = 1
            for curve in self.__curves:
                curve.show()
                curve.setVisible(True)
            self._updateAllCuts(self.__allcuts)
            self._plotCuts()
            self._mainwidget.bottomplotShowMenu(True, True)

        def deactivate(self):
            """ activates tool widget
            """
            self._mainwidget.bottomplotShowMenu()
            for curve in self.__curves:
                curve.hide()
                curve.setVisible(False)
                self._mainwidget.removebottomplot(curve)
            self.__curves = []
            for freezed in self.__freezed:
                freezed.hide()
                freezed.setVisible(False)
                self._mainwidget.removebottomplot(freezed)
            self.__freezed = []

        @QtCore.pyqtSlot(int)
        def _updateAllCuts(self, value):
            """ updates X row status

            :param value: if True or not 0 x-cooridnates taken from the first row
            :param value: :obj:`int` or  :obj:`bool`
            """
            self.__allcuts = value
            self._updateCuts(self.__ui.cutSpinBox.value())

        @QtCore.pyqtSlot(int)
        def _setXCoords(self, xindex):
            """ sets x-coodinates for 1d plot

            :param xindex: 1d x-coorindate index,
            :type xindex: :obj:`int`
            """
            self.__xindex = xindex
            self._plotCuts()

        @QtCore.pyqtSlot()
        def _plotCuts(self):
            """ plots the current 1d Cut
            """
            if self.__allcuts:
                self._plotAllCuts()
            else:
                self._plotCut()

        def _plotAllCuts(self):
            """ plot all 1d Cuts
            """

            if self._mainwidget.currentTool() == self.name:
                if self.__settings.sendresults:
                    xl = []
                    yl = []
                nrplots = self.__ui.cutSpinBox.value()
                if self.__nrplots != nrplots:
                    while nrplots > len(self.__curves):
                        self.__curves.append(self._mainwidget.onedbottomplot())
                    for i in range(nrplots):
                        self.__curves[i].show()
                    for i in range(nrplots, len(self.__curves)):
                        self.__curves[i].hide()
                    self.__nrplots = nrplots
                    if nrplots:
                        for i, cr in enumerate(self.__curves):
                            if i < nrplots:
                                cr.setPen(_pg.hsvColor(i/float(nrplots)))
                coords = self._mainwidget.cutCoords()
                rws = self._mainwidget.rangeWindowScale()
                for i in range(nrplots):
                    dt = self._mainwidget.cutData(i)
                    if dt is not None:
                        if self.__settings.nanmask:
                            if dt.dtype.kind == 'f' and np.isnan(dt.min()):
                                dt = np.nan_to_num(dt)
                        if self.__xindex:
                            if i < len(coords):
                                crds = coords[i]
                            else:
                                crds = [0, 0, 1, 1, 0.00001]
                            if self.__xindex == 2:
                                dx = np.linspace(crds[1], crds[3], len(dt))
                            else:
                                dx = np.linspace(crds[0], crds[2], len(dt))
                            self.__curves[i].setData(x=dx, y=dt)
                            if self.__settings.sendresults:
                                xl.append([float(e) for e in dx])
                                yl.append([float(e) for e in dt])
                        else:
                            if rws > 1.0:
                                dx = np.linspace(0, len(dt - 1) * rws, len(dt))
                                self.__curves[i].setData(x=dx, y=dt)
                                if self.__settings.sendresults:
                                    xl.append([float(e) for e in dx])
                                    yl.append([float(e) for e in dt])
                            else:
                                self.__curves[i].setData(y=dt)
                                if self.__settings.sendresults:
                                    xl.append(list(range(len(dt))))
                                    yl.append([float(e) for e in dt])

                        self.__curves[i].setVisible(True)
                    else:
                        self.__curves[i].setVisible(False)
                if self.__settings.sendresults:
                    self.__sendresults(xl, yl)

        def _plotCut(self):
            """ plot the current 1d Cut
            """
            if self.__nrplots > 1:
                for i in range(1, len(self.__curves)):
                    self.__curves[i].setVisible(False)
                    self.__curves[i].hide()
                self.__nrplots = 1
            if self._mainwidget.currentTool() == self.name:
                if self.__settings.sendresults:
                    xl = []
                    yl = []
                dt = self._mainwidget.cutData()
                self.__curves[0].setPen(_pg.mkColor('r'))
                if dt is not None:
                    if self.__settings.nanmask:
                        if dt.dtype.kind == 'f' and np.isnan(dt.min()):
                            dt = np.nan_to_num(dt)
                    if self.__xindex:
                        crds = [0, 0, 1, 1, 0.00001]
                        if self._mainwidget.currentCut() > -1:
                            crds = self._mainwidget.cutCoords()[
                                self._mainwidget.currentCut()]
                        if self.__xindex == 2:
                            dx = np.linspace(crds[1], crds[3], len(dt))
                        else:
                            dx = np.linspace(crds[0], crds[2], len(dt))
                        self.__curves[0].setData(x=dx, y=dt)
                        if self.__settings.sendresults:
                            xl.append([float(e) for e in dx])
                            yl.append([float(e) for e in dt])
                    else:
                        rws = self._mainwidget.rangeWindowScale()
                        if rws > 1.0:
                            dx = np.linspace(0, len(dt - 1) * rws, len(dt))
                            self.__curves[0].setData(x=dx, y=dt)
                            if self.__settings.sendresults:
                                xl.append([float(e) for e in dx])
                                yl.append([float(e) for e in dt])
                        else:
                            self.__curves[0].setData(y=dt)
                            if self.__settings.sendresults:
                                xl.append(list(range(len(dt))))
                                yl.append([float(e) for e in dt])
                    self.__curves[0].setVisible(True)
                else:
                    self.__curves[0].setVisible(False)
                if self.__settings.sendresults:
                    self.__sendresults(xl, yl)

        def __sendresults(self, xl, yl):
            """ send results to LavueController

            :param xl:  list of x's for each diffractogram
            :type xl: :obj:`list` < :obj:`list` <float>>
            :param yl:  list of values for each diffractogram
            :type yl: :obj:`list` < :obj:`list` <float>>
            """
            results = {"tool": self.alias}
            npl = len(xl)
            results["imagename"] = self._mainwidget.imageName()
            results["timestamp"] = time.time()
            results["nrlinecuts"] = len(xl)
            for i in range(npl):
                results["linecut_%s" % (i + 1)] = [xl[i], yl[i]]
            results["unit"] = ["point", "x-pixel", "y-pixel"][self.__xindex]
            self._mainwidget.writeAttribute(
                "ToolResults", json.dumps(results, cls=numpyEncoder))

        @QtCore.pyqtSlot(int)
        def _setCutsNumber(self, cid):
            """sets a number of cuts

            :param cid: number of cuts
            :type cid: :obj:`int`
            """
            self.__ui.cutSpinBox.setValue(cid)

        @QtCore.pyqtSlot()
        def _message(self):
            """ provides cut message
            """
            _, _, intensity, x, y = self._mainwidget.currentIntensity()
            if isinstance(intensity, float) and np.isnan(intensity):
                intensity = 0
            ilabel = self._mainwidget.scalingLabel()
            if self._mainwidget.currentCut() > -1:
                crds = self._mainwidget.cutCoords()[
                    self._mainwidget.currentCut()]
                crds = "[[%.2f, %.2f], [%.2f, %.2f], width=%.2f]" % tuple(crds)
            else:
                crds = "[[0, 0], [0, 0], width=0]"
            if isinstance(intensity, np.ndarray) and \
               intensity.size <= 3:
                itn = [0 if (isinstance(it, float) and np.isnan(it))
                       else float(it) for it in intensity]
                if len(itn) >= 3:
                    message = "%s, x = %.2f, y = %.2f, " \
                        "%s = (%.2f, %.2f, %.2f)" % (
                            crds, x, y, ilabel,
                            itn[0], itn[1], itn[2])
            else:
                message = "%s, x = %.2f, y = %.2f, %s = %.2f" % (
                    crds, x, y, ilabel, intensity)
            self._mainwidget.setDisplayedText(message)

    class ProjectionToolWidget(ToolBaseWidget):
        """ Projections tool widget
        """

        #: (:obj:`str`) tool name
        name = "Projections"
        #: (:obj:`str`) tool name alias
        alias = "projections"
        #: (:obj:`tuple` <:obj:`str`>) capitalized required packages
        requires = ()

        def __init__(self, parent=None):
            """ constructor

            :param parent: parent object
            :type parent: :class:`pyqtgraph.QtCore.QObject`
            """
            ToolBaseWidget.__init__(self, parent)

            #: (:class:`Ui_ProjectionToolWidget')
            #:      ui_toolwidget object from qtdesigner
            self.__ui = _projectionformclass()
            self.__ui.setupUi(self)

            #: (:class:`pyqtgraph.PlotDataItem`) 1D bottom plot
            self.__bottomplot = None
            #: (:class:`pyqtgraph.PlotDataItem`) 1D bottom plot
            self.__rightplot = None
            #: (:obj:`int`) function index
            self.__funindex = 0

            #: (:obj:`slice`) selected rows
            self.__rows = None
            #: (:obj:`slice`) selected columns
            self.__columns = None
            #: (:obj:`slice`) selected rows
            self.__dsrows = None
            #: (:obj:`slice`) selected columns
            self.__dscolumns = None

            #: (:class:`lavuelib.settings.Settings`) configuration settings
            self.__settings = self._mainwidget.settings()

            self.parameters.bottomplot = True
            self.parameters.rightplot = True
            self.parameters.vhbounds = True
            self.parameters.infolineedit = ""
            self.parameters.infotips = \
                "coordinate info display for the mouse pointer"

            #: (:obj:`list` < [:class:`pyqtgraph.QtCore.pyqtSignal`, :obj:`str`] >)
            #: list of [signal, slot] object to connect
            self.signal2slot = [
                [self.__ui.funComboBox.currentIndexChanged,
                 self._setFunction],
                [self.__ui.funComboBox.currentIndexChanged,
                 self._mainwidget.emitTCC],
                [self.__ui.rowsliceLineEdit.textChanged, self._updateRows],
                [self.__ui.rowsliceLineEdit.textChanged, self._mainwidget.emitTCC],
                [self.__ui.columnsliceLineEdit.textChanged, self._updateColumns],
                [self.__ui.columnsliceLineEdit.textChanged,
                 self._mainwidget.emitTCC],
                [self._mainwidget.scalesChanged, self._updateRows],
                [self._mainwidget.scalesChanged, self._updateColumns],
                [self._mainwidget.mouseImagePositionChanged, self._message]
            ]

        # @debugmethod
        def configure(self, configuration):
            """ set configuration for the current tool

            :param configuration: configuration string
            :type configuration: :obj:`str`
            """
            if configuration:
                cnf = json.loads(configuration)
                if "mapping" in cnf.keys():
                    idxs = ["mean", "sum"]
                    xcrd = str(cnf["mapping"]).lower()
                    try:
                        idx = idxs.index(xcrd)
                    except Exception:
                        idx = 0
                    self.__ui.funComboBox.setCurrentIndex(idx)
                if "rows" in cnf.keys():
                    self.__ui.rowsliceLineEdit.setText(cnf["rows"])
                if "columns" in cnf.keys():
                    self.__ui.columnsliceLineEdit.setText(cnf["columns"])

        # @debugmethod
        def configuration(self):
            """ provides configuration for the current tool

            :returns configuration: configuration string
            :rtype configuration: :obj:`str`
            """
            cnf = {}
            cnf["mapping"] = str(
                self.__ui.funComboBox.currentText()).lower()
            cnf["rows"] = self.__ui.rowsliceLineEdit.text()
            cnf["columns"] = self.__ui.columnsliceLineEdit.text()
            return json.dumps(cnf, cls=numpyEncoder)

        def __updateslice(self, text, dx=None, ds=None):
            """ create slices from the text
            """
            rows = "ERROR"
            dsrows = "ERROR"
            if text:
                try:
                    if ":" in text:
                        slices = text.split(":")
                        s0 = int(slices[0]) if slices[0].strip() else 0
                        s1 = int(slices[1]) if slices[1].strip() else None
                        if len(slices) > 2:
                            s2 = int(slices[2]) if slices[2].strip() else None
                            rows = slice(s0, s1, s2)
                            if dx is not None:
                                dsrows = slice((s0-dx)/ds, (s1-dx)/ds, s2/ds)
                            else:
                                dsrows = rows
                        else:
                            rows = slice(s0, s1)
                            if dx is not None:
                                dsrows = slice((s0-dx)/ds, (s1-dx)/ds)
                            else:
                                dsrows = rows
                    else:
                        rows = int(text)
                        if dx is not None:
                            dsrows = int((rows - dx)/ds)
                        else:
                            dsrows = rows
                except Exception:
                    pass
            else:
                rows = None
                dsrows = None
            return rows, dsrows

        @QtCore.pyqtSlot(str)
        @QtCore.pyqtSlot()
        def _updateSlices(self):
            """ updates applied button"""
            rtext = str(self.__ui.rowsliceLineEdit.text()).strip()
            ctext = str(self.__ui.columnsliceLineEdit.text()).strip()
            rwe = self._mainwidget.rangeWindowEnabled()
            if rwe:
                dx, dy, ds1, ds2 = self._mainwidget.scale(
                    useraxes=False, noNone=True)
                self.__rows, self.__dsrows = self.__updateslice(
                    rtext, int(dy), int(ds2))
                self.__columns, self.__dscolumns = self.__updateslice(
                    ctext, int(dx), int(ds1))
            else:
                self.__rows, self.__dsrows = self.__updateslice(rtext)
                self.__columns, self.__dscolumns = self.__updateslice(ctext)
            if self.__rows is None:
                self._mainwidget.updateHBounds(None, None)
            elif isinstance(self.__rows, int):
                self._mainwidget.updateHBounds(self.__rows, self.__rows + 1)
            elif isinstance(self.__rows, slice):
                self._mainwidget.updateHBounds(self.__rows.start, self.__rows.stop)
            if self.__columns is None:
                self._mainwidget.updateVBounds(None, None)
            elif isinstance(self.__columns, int):
                self._mainwidget.updateVBounds(self.__columns, self.__columns + 1)
            elif isinstance(self.__columns, slice):
                self._mainwidget.updateVBounds(
                    self.__columns.start, self.__columns.stop)
            self._plotCurves()

        @QtCore.pyqtSlot(str)
        @QtCore.pyqtSlot()
        def _updateRows(self):
            """ updates applied button"""
            rtext = str(self.__ui.rowsliceLineEdit.text()).strip()
            rwe = self._mainwidget.rangeWindowEnabled()
            if rwe:
                dx, dy, ds1, ds2 = self._mainwidget.scale(
                    useraxes=False, noNone=True)
                self.__rows, self.__dsrows = self.__updateslice(
                    rtext, int(dy), int(ds2))
            else:
                self.__rows, self.__dsrows = self.__updateslice(rtext)
            if self.__rows is None:
                self._mainwidget.updateHBounds(None, None)
            elif isinstance(self.__rows, int):
                self._mainwidget.updateHBounds(self.__rows, self.__rows + 1)
            elif isinstance(self.__rows, slice):
                self._mainwidget.updateHBounds(self.__rows.start, self.__rows.stop)
            self._plotCurves()

        @QtCore.pyqtSlot(str)
        @QtCore.pyqtSlot()
        def _updateColumns(self):
            """ updates applied button"""
            text = str(self.__ui.columnsliceLineEdit.text()).strip()
            rwe = self._mainwidget.rangeWindowEnabled()
            if rwe:
                dx, dy, ds1, ds2 = self._mainwidget.scale(
                    useraxes=False, noNone=True)
            if rwe:
                self.__columns, self.__dscolumns = self.__updateslice(
                    text, int(dx), int(ds1))
            else:
                self.__columns, self.__dscolumns = self.__updateslice(text)
            if self.__columns is None:
                self._mainwidget.updateVBounds(None, None)
            elif isinstance(self.__columns, int):
                self._mainwidget.updateVBounds(self.__columns, self.__columns + 1)
            elif isinstance(self.__columns, slice):
                self._mainwidget.updateVBounds(
                    self.__columns.start, self.__columns.stop)
            self._plotCurves()

        @QtCore.pyqtSlot(int)
        def _setFunction(self, findex):
            """ set sum or mean function

            :param findex: function index, i.e. 0:mean, 1:sum
            :type findex: :obj:`int`
            """
            self.__funindex = findex
            self._plotCurves()

        def afterplot(self):
            """ command after plot
            """
            self._plotCurves()

        def activate(self):
            """ activates tool widget
            """
            if self.__bottomplot is None:
                self.__bottomplot = self._mainwidget.onedbarbottomplot()

            if self.__rightplot is None:
                self.__rightplot = self._mainwidget.onedbarrightplot()

            self.__bottomplot.show()
            self.__rightplot.show()
            self.__bottomplot.setVisible(True)
            self.__rightplot.setVisible(True)
            self._updateSlices()
            self._plotCurves()

        def deactivate(self):
            """ activates tool widget
            """
            if self.__bottomplot is not None:
                self.__bottomplot.hide()
                self.__bottomplot.setVisible(False)
                self._mainwidget.removebottomplot(self.__bottomplot)
                self.__bottomplot = None
            if self.__rightplot is not None:
                self.__rightplot.hide()
                self.__rightplot.setVisible(False)
                self._mainwidget.removerightplot(self.__rightplot)
                self.__rightplot = None

        @QtCore.pyqtSlot()
        def _plotCurves(self):
            """ plots the current image in 1d plots
            """
            if self._mainwidget.currentTool() == self.name:
                dts = self._mainwidget.rawData()
                if dts is not None:
                    while dts.ndim > 2:
                        dts = np.nanmean(dts, axis=2)
                if dts is not None:
                    if self.__funindex:
                        npfun = np.nansum
                    else:
                        npfun = np.nanmean

                    if self.__dsrows == "ERROR":
                        sx = []
                    elif self.__dsrows is not None:
                        try:
                            with np.warnings.catch_warnings():
                                np.warnings.filterwarnings(
                                    "ignore", r'Mean of empty slice')
                                if isinstance(self.__dsrows, slice):
                                    sx = npfun(dts[:, self.__dsrows], axis=1)
                                else:
                                    sx = dts[:, self.__dsrows]
                        except Exception:
                            sx = []

                    else:
                        try:
                            with np.warnings.catch_warnings():
                                np.warnings.filterwarnings(
                                    "ignore", r'Mean of empty slice')
                                sx = npfun(dts, axis=1)
                        except Exception:
                            sx = []

                    if self.__dscolumns == "ERROR":
                        sy = []
                    if self.__dscolumns is not None:
                        try:
                            with np.warnings.catch_warnings():
                                np.warnings.filterwarnings(
                                    "ignore", r'Mean of empty slice')
                                if isinstance(self.__dscolumns, slice):
                                    sy = npfun(dts[self.__dscolumns, :], axis=0)
                                else:
                                    sy = dts[self.__dscolumns, :]
                        except Exception:
                            sy = []
                    else:
                        with np.warnings.catch_warnings():
                            np.warnings.filterwarnings(
                                "ignore", r'Mean of empty slice')
                            sy = npfun(dts, axis=0)

                    rwe = self._mainwidget.rangeWindowEnabled()
                    if rwe:
                        x, y, s1, s2 = self._mainwidget.scale(
                            useraxes=False, noNone=True)
                        if self._mainwidget.transformations()[3]:
                            x, y = y, x
                            s1, s2 = s2, s1
                        xx = list(
                            range(int(x), len(sx) * int(s1) + int(x), int(s1)))
                        yy = list(
                            range(int(y), len(sy) * int(s2) + int(y), int(s2)))
                    else:
                        s1 = 1.0
                        s2 = 1.0
                        xx = list(range(len(sx)))
                        yy = list(range(len(sy)))
                    width = [s1] * len(sx)
                    height = [s2] * len(sy)
                    self.__bottomplot.setOpts(
                        y0=[0]*len(sx), y1=sx, x=xx,
                        width=width)
                    self.__bottomplot.drawPicture()
                    self.__rightplot.setOpts(
                        x0=[0]*len(sy), x1=sy, y=yy,
                        height=height)
                    self.__rightplot.drawPicture()
                    if self.__settings.sendresults:
                        xslice = self.__dsrows
                        yslice = self.__dscolumns
                        if hasattr(xslice, "start"):
                            xslice = [xslice.start, xslice.stop, xslice.step]
                        if hasattr(yslice, "start"):
                            yslice = [yslice.start, yslice.stop, yslice.step]
                        self.__sendresults(
                            xx,
                            [float(e) for e in sx],
                            s1, xslice,
                            yy,
                            [float(e) for e in sy],
                            s2, yslice,
                            "sum" if self.__funindex else "mean"
                        )

        def __sendresults(self, xx, sx, xscale, xslice,
                          yy, sy, yscale, yslice, fun):
            """ send results to LavueController

            :param xx:  x's coordinates
            :type xx:  :obj:`list` <float>
            :param sx:  projection to x coordinate
            :type sx:  :obj:`list` <float>
            :param xscale:  x scale
            :type xscale:  :obj:`float`
            :param xslice:  x slice
            :type xslice:  :obj:`list` <float>
            :param yy:  y's coordinates
            :type yy:  :obj:`list` <float>
            :param sy:  projection to y coordinate
            :type sy:  :obj:`list` <float>
            :param yscale:  y scale
            :type yscale:  :obj:`float`
            :param yslice:  y slice
            :type yslice:  :obj:`list` <float>
            :param fun:  projection function name
            :type fun:  :obj:`str`
            """
            results = {"tool": self.alias}
            results["imagename"] = self._mainwidget.imageName()
            results["timestamp"] = time.time()
            results["xx"] = xx
            results["sx"] = sx
            results["xscale"] = xscale
            results["xslice"] = xslice
            results["yy"] = yy
            results["sy"] = sy
            results["yscale"] = yscale
            results["yslice"] = yslice
            results["function"] = fun
            self._mainwidget.writeAttribute(
                "ToolResults", json.dumps(results, cls=numpyEncoder))

        @QtCore.pyqtSlot()
        def _message(self):
            """ provides intensity message
            """
            x, y, intensity = self._mainwidget.currentIntensity()[:3]
            if isinstance(intensity, float) and np.isnan(intensity):
                intensity = 0
            if isinstance(intensity, np.ndarray):
                intensity = np.nansum(
                    [0 if (isinstance(it, float) and np.isnan(it))
                     else it for it in intensity])
            ilabel = self._mainwidget.scalingLabel()
            message = "x = %i, y = %i, %s = %.2f" % (
                x, y, ilabel, intensity)
            self._mainwidget.setDisplayedText(message)

    class OneDToolWidget(ToolBaseWidget):
        """ 1d plot tool widget
        """

        #: (:obj:`str`) tool name
        name = "1d-Plot"
        #: (:obj:`str`) tool name alias
        alias = "1d-plot"
        #: (:obj:`tuple` <:obj:`str`>) capitalized required packages
        requires = ()

        def __init__(self, parent=None):
            """ constructor

            :param parent: parent object
            :type parent: :class:`pyqtgraph.QtCore.QObject`
            """
            ToolBaseWidget.__init__(self, parent)

            #: (:class:`Ui_OneDToolWidget') ui_toolwidget object from qtdesigner
            self.__ui = _onedformclass()
            self.__ui.setupUi(self)

            #: (:obj:`list`<:class:`pyqtgraph.PlotDataItem`>) 1D plot
            self.__curves = []
            #: (:obj:`int`) current plot number
            self.__nrplots = 0

            #: ((:obj:`list`<:obj:`int`>) selected rows
            self.__rows = [0]
            #: ((:obj:`list`<:obj:`int`>) selected rows
            self.__dsrows = [0]
            #: ((:obj:`list`<:obj:`str`>) legend labels
            self.__labels = []
            #: ((:obj:`bool`) x in first row
            self.__xinfirstrow = False
            #: ((:obj:`bool`) accumalate status
            self.__accumulate = False
            #: ((:obj:`int`) buffer size
            self.__buffersize = 1024
            #: ((:class:`ndarray`) buffer
            self.__buffer = None

            #: (:class:`lavuelib.settings.Settings`) configuration settings
            self.__settings = self._mainwidget.settings()

            self.__ui.rowsLineEdit.setText("0")
            self.parameters.bottomplot = True
            self.parameters.infolineedit = ""
            self.parameters.infotips = \
                "coordinate info display for the mouse pointer"

            #: (:obj:`list` < [:class:`pyqtgraph.QtCore.pyqtSignal`, :obj:`str`] >)
            #: list of [signal, slot] object to connect
            self.signal2slot = [
                [self.__ui.rowsLineEdit.textChanged, self._updateRows],
                [self.__ui.rowsLineEdit.textChanged, self._mainwidget.emitTCC],
                [self._mainwidget.scalesChanged, self._updateRows],
                [self.__ui.sizeLineEdit.textChanged, self._setBufferSize],
                [self.__ui.sizeLineEdit.textChanged, self._mainwidget.emitTCC],
                [self.__ui.xCheckBox.stateChanged, self._updateXRow],
                [self.__ui.xCheckBox.stateChanged, self._mainwidget.emitTCC],
                [self.__ui.accuPushButton.clicked, self._startStopAccu],
                [self.__ui.resetPushButton.clicked, self._resetAccu],
                [self.__ui.labelsPushButton.clicked, self._setLabels],
                [self._mainwidget.mouseImagePositionChanged, self._message]
            ]

        def configure(self, configuration):
            """ set configuration for the current tool

            :param configuration: configuration string
            :type configuration: :obj:`str`
            """
            if configuration:
                cnf = json.loads(configuration)
                if "rows_to_plot" in cnf.keys():
                    self.__ui.rowsLineEdit.setText(cnf["rows_to_plot"])
                if "buffer_size" in cnf.keys():
                    self.__ui.sizeLineEdit.setText(str(cnf["buffer_size"]))
                if "collect" in cnf.keys():
                    self._startStopAccu()
                if "xrow" in cnf.keys():
                    self.__ui.xCheckBox.setChecked(bool(cnf["xrow"]))
                if "reset" in cnf.keys():
                    if cnf["reset"]:
                        self._resetAccu()
                if "labels" in cnf.keys():
                    self.__labels = cnf["labels"]
                    self.deactivate()
                    self.activate()

                if "1d_stretch" in cnf.keys():
                    try:
                        val = int(cnf["1d_stretch"])
                        self._mainwidget.bottomplotStretch(val)
                    except Exception as e:
                        logger.warning(str(e))
                        # print(str(e))

        def configuration(self):
            """ provides configuration for the current tool

            :returns configuration: configuration string
            :rtype configuration: :obj:`str`
            """
            cnf = {}
            cnf["rows_to_plot"] = self.__ui.rowsLineEdit.text()
            try:
                cnf["buffer_size"] = int(self.__ui.sizeLineEdit.text())
            except Exception:
                cnf["buffer_size"] = self.__ui.sizeLineEdit.text()
            cnf["collect"] = self.__accumulate
            cnf["xrow"] = self.__ui.xCheckBox.isChecked()
            cnf["labels"] = self.__labels
            return json.dumps(cnf, cls=numpyEncoder)

        def afterplot(self):
            """ command after plot
            """
            self._plotCurves()

        def beforeplot(self, array, rawarray):
            """ command  before plot

            :param array: 2d image array
            :type array: :class:`numpy.ndarray`
            :param rawarray: 2d raw image array
            :type rawarray: :class:`numpy.ndarray`
            :return: 2d image array and raw image
            :rtype: (:class:`numpy.ndarray`, :class:`numpy.ndarray`)
            """

            if self.__accumulate:
                dts = rawarray
                while dts.ndim > 2:
                    dts = np.nanmean(dts, axis=2)
                newrow = np.sum(dts[:, self.__dsrows], axis=1)
                if self.__buffer is not None and \
                   self.__buffer.shape[1] == newrow.shape[0]:
                    if self.__buffer.shape[0] >= self.__buffersize:
                        self.__buffer = np.vstack(
                            [self.__buffer[
                                self.__buffer.shape[0] - self.__buffersize + 1:,
                                :],
                             newrow]
                        )
                    else:
                        self.__buffer = np.vstack([self.__buffer, newrow])
                else:
                    self.__buffer = np.array([newrow])
                return np.transpose(self.__buffer), rawarray

        def activate(self):
            """ activates tool widget
            """
            self.__ui.sizeLineEdit.setText(str(self.__buffersize))
            self._updateRows()
            self._mainwidget.onedshowlegend(True)

        def deactivate(self):
            """ activates tool widget
            """
            for cr in self.__curves:
                cr.hide()
                cr.setVisible(False)
                self._mainwidget.removebottomplot(cr)
            self.__curves = []
            self.__nrplots = 0
            self._mainwidget.onedshowlegend(False)

        @QtCore.pyqtSlot()
        def _setLabels(self):
            """ launches label widget

            :returns: apply status
            :rtype: :obj:`bool`
            """
            dform = edListDialog.EdListDialog(self)
            dform.record = list(self.__labels or [])
            dform.title = "Tango Detector Attributes"
            dform.createGUI()
            dform.exec_()
            if dform.dirty:
                self.__labels = dform.record
                self.deactivate()
                self.activate()
                self._mainwidget.emitTCC()

        @QtCore.pyqtSlot()
        def _resetAccu(self):
            """ reset accumulation buffer
            """
            self.__buffer = None
            self._mainwidget.emitTCC()

        @QtCore.pyqtSlot()
        def _startStopAccu(self):
            """ start/stop accumulation buffer
            """
            if not self.__accumulate:
                self.__accumulate = True
                self.__ui.accuPushButton.setText("Stop")
            else:
                self.__accumulate = False
                self.__ui.accuPushButton.setText("Collect")
            self._mainwidget.emitTCC()

        @QtCore.pyqtSlot(str)
        @QtCore.pyqtSlot()
        def _setBufferSize(self):
            """ start/stop accumulation buffer

            """
            try:
                self.__buffersize = int(self.__ui.sizeLineEdit.text())
            except Exception as e:
                # print(str(e))
                logger.warning(str(e))
                self.__buffersize = 1024

        @QtCore.pyqtSlot(int)
        def _updateXRow(self, value):
            """ updates X row status

            :param value: if True or not 0 x-cooridnates taken from the first row
            :param value: :obj:`int` or  :obj:`bool`
            """
            self.__xinfirstrow = True if value else False
            self._updateRows()

        @QtCore.pyqtSlot(str)
        @QtCore.pyqtSlot()
        def _updateRows(self):
            """ updates applied button"""
            text = str(self.__ui.rowsLineEdit.text()).strip()
            rows = []
            dsrows = []
            rwe = None
            if text:
                if text == "ALL":
                    rows = [None]
                else:
                    try:
                        rwe = self._mainwidget.rangeWindowEnabled()
                        if rwe:
                            dx, dy, ds1, ds2 = self._mainwidget.scale(
                                useraxes=False, noNone=True)
                            if self._mainwidget.transformations()[3]:
                                dx, dy = dy, dx
                                ds1, ds2 = ds2, ds1
                        stext = [rw for rw in re.split(",| ", text) if rw]
                        for rw in stext:
                            if ":" in rw:
                                slices = rw.split(":")
                                s0 = int(slices[0]) if slices[0].strip() else 0
                                s1 = int(slices[1]) if slices[1].strip() else 0
                                if len(slices) > 2:
                                    s2 = int(slices[2]) if slices[2].strip() else 1
                                    rows.extend(list(range(s0, s1, s2)))
                                    if rwe:
                                        dsrows.extend(
                                            list(range(int((s0-dy)/ds2),
                                                       int((s1-dy)/ds2),
                                                       int(s2/ds2))))
                                else:
                                    rows.extend(list(range(s0, s1)))
                                    if rwe:
                                        dsrows.extend(
                                            list(range(int((s0-dy)/ds2),
                                                       int((s1-dy)/ds2))))
                            else:
                                rows.append(int(rw))
                                if rwe:
                                    dsrows.append(int((int(rw)-dy)/ds2))
                    except Exception:
                        rows = []
            self.__rows = rows
            if not rwe:
                self.__dsrows = rows
            else:
                self.__dsrows = dsrows
            self._plotCurves()

        @QtCore.pyqtSlot()
        def _plotCurves(self):
            """ plots the current image in 1d plots
            """
            if self._mainwidget.currentTool() == self.name:
                reset = False
                if self.__settings.sendresults:
                    xl = []
                    yl = []
                dts = self._mainwidget.rawData()
                if dts is not None:
                    while dts.ndim > 2:
                        dts = np.nanmean(dts, axis=2)
                if dts is not None:
                    dtnrpts = dts.shape[1]
                    if self.__dsrows:
                        if self.__dsrows[0] is None:
                            if self.__xinfirstrow:
                                nrplots = dtnrpts - 1
                            else:
                                nrplots = dtnrpts

                        else:
                            nrplots = len(self.__dsrows)
                    else:
                        nrplots = 0
                    if self.__nrplots != nrplots:
                        while nrplots > len(self.__curves):
                            ii = len(self.__curves)
                            lb = str(ii + 1)
                            if self.__labels and len(self.__labels) > ii:
                                if self.__labels[ii] is not None:
                                    lb = self.__labels[ii]
                                else:
                                    lb = str(ii + 1)
                            self.__curves.append(
                                self._mainwidget.onedbottomplot(name=lb))
                        for i in range(nrplots):
                            self.__curves[i].show()
                        for i in range(nrplots, len(self.__curves)):
                            self.__curves[i].hide()
                        if nrplots < self.__nrplots:
                            reset = True
                        self.__nrplots = nrplots
                        if nrplots:
                            for i, cr in enumerate(self.__curves):
                                if i < nrplots:
                                    cr.setPen(_pg.hsvColor(i/float(nrplots)))
                    rwe = self._mainwidget.rangeWindowEnabled()
                    if rwe:
                        dx, dy, ds1, ds2 = self._mainwidget.scale(
                            useraxes=False, noNone=True)
                        if self._mainwidget.transformations()[3]:
                            dx, dy = dy, dx
                            ds1, ds2 = ds2, ds1
                    for i in range(nrplots):
                        if self.__dsrows:
                            if self.__dsrows[0] is None:
                                if self.__xinfirstrow and i:
                                    self.__curves[i].setData(
                                        x=dts[:, 0], y=dts[:, i])
                                    if self.__settings.sendresults:
                                        xl.append([float(e) for e in dts[:, 0]])
                                        yl.append([float(e) for e in dts[:, i]])
                                elif rwe:
                                    y = dts[:, i]
                                    x = np.linspace(
                                        dx, len(y - 1) * ds1 + dx, len(y))
                                    self.__curves[i].setData(x=x, y=y)
                                    if self.__settings.sendresults:
                                        xl.append([float(e) for e in x])
                                        yl.append([float(e) for e in y])
                                else:
                                    self.__curves[i].setData(dts[:, i])
                                    if self.__settings.sendresults:
                                        dt = dts[:, i]
                                        xl.append(list(range(len(dt))))
                                        yl.append([float(e) for e in dt])
                                self.__curves[i].setVisible(True)
                            elif (self.__dsrows[i] >= 0 and
                                  self.__dsrows[i] < dtnrpts):
                                if self.__xinfirstrow:
                                    self.__curves[i].setData(
                                        x=dts[:, 0], y=dts[:, self.__dsrows[i]])
                                    if self.__settings.sendresults:
                                        xl.append([float(e) for e in dts[:, 0]])
                                        yl.append([float(e) for e in
                                                   dts[:, self.__dsrows[i]]])
                                elif rwe:
                                    y = dts[:, self.__dsrows[i]]
                                    x = np.linspace(
                                        dx, len(y - 1) * ds1 + dx, len(y))
                                    self.__curves[i].setData(x=x, y=y)
                                    if self.__settings.sendresults:
                                        xl.append([float(e) for e in x])
                                        yl.append([float(e) for e in y])
                                else:
                                    self.__curves[i].setData(
                                        dts[:, self.__dsrows[i]])
                                    if self.__settings.sendresults:
                                        dt = dts[:, self.__dsrows[i]]
                                        xl.append(list(range(len(dt))))
                                        yl.append([float(e) for e in dt])
                                self.__curves[i].setVisible(True)
                            else:
                                self.__curves[i].setVisible(False)
                        else:
                            self.__curves[i].setVisible(False)
                    if self.__settings.sendresults:
                        self.__sendresults(xl, yl)
                else:
                    for cr in self.__curves:
                        cr.setVisible(False)
                if reset:
                    self.deactivate()
                    self.activate()

        def __sendresults(self, xl, yl):
            """ send results to LavueController

            :param xl:  list of x's for each diffractogram
            :type xl: :obj:`list` < :obj:`list` <float>>
            :param yl:  list of values for each diffractogram
            :type yl: :obj:`list` < :obj:`list` <float>>
            """
            results = {"tool": self.alias}
            npl = len(xl)
            results["imagename"] = self._mainwidget.imageName()
            results["timestamp"] = time.time()
            results["nrplots"] = len(xl)
            for i in range(npl):
                results["onedplot_%s" % (i + 1)] = [xl[i], yl[i]]
            self._mainwidget.writeAttribute(
                "ToolResults", json.dumps(results, cls=numpyEncoder))

        @QtCore.pyqtSlot()
        def _message(self):
            """ provides intensity message
            """
            x, y, intensity = self._mainwidget.currentIntensity()[:3]
            if isinstance(intensity, float) and np.isnan(intensity):
                intensity = 0
            if isinstance(intensity, np.ndarray):
                intensity = np.nansum(
                    [0 if (isinstance(it, float) and np.isnan(it))
                     else it for it in intensity])
            ilabel = self._mainwidget.scalingLabel()
            message = "x = %i, y = %i, %s = %.2f" % (
                x, y, ilabel, intensity)
            self._mainwidget.setDisplayedText(message)

    class AngleQToolWidget(ToolBaseWidget):
        """ angle/q tool widget
        """

        #: (:obj:`str`) tool name
        name = "Angle/Q"
        #: (:obj:`str`) tool name alias
        alias = "angle/q"
        #: (:obj:`tuple` <:obj:`str`>) capitalized required packages
        requires = ()

        def __init__(self, parent=None):
            """ constructor

            :param parent: parent object
            :type parent: :class:`pyqtgraph.QtCore.QObject`
            """
            ToolBaseWidget.__init__(self, parent)

            #: (:obj:`int`) geometry space index -> 0: angle, 1 q-space
            self.__gspaceindex = 0

            #: (:obj:`int`) plot index -> 0: Cartesian, 1 polar-th, 2 polar-q
            self.__plotindex = 0

            #: (:class:`Ui_ROIToolWidget') ui_toolwidget object from qtdesigner
            self.__ui = _angleqformclass()
            self.__ui.setupUi(self)

            #: (:obj:`bool`) old lock value
            self.__oldlocked = None

            #: (:class:`numpy.array`) radial array cache
            self.__lastradial = None
            #: (:class:`numpy.array`) angle array cache
            self.__lastangle = None
            #: (:obj:`float`) energy cache
            self.__lastenergy = None
            #: (:obj:`float`) radmax cache
            self.__lastradmax = None
            #: (:obj:`float`) plotindex cache
            self.__lastpindex = None
            #: (:obj:`float`) detdistance cache
            self.__lastdistance = None
            #: (:obj:`float`) center x cache
            self.__lastcenterx = None
            #: (:obj:`float`) center y cache
            self.__lastcentery = None
            #: (:obj:`float`) pixelsizeycache
            self.__lastpsizex = None
            #: (:obj:`float`) pixelsizey cache
            self.__lastpsizey = None
            #: (:class:`numpy.array`) x array cache
            self.__lastx = None
            #: (:class:`numpy.array`) y array cache
            self.__lasty = None
            #: (:obj:`float`) maxdim cache
            self.__lastmaxdim = None

            #: (:obj:`float`) start position of radial q coordinate
            self.__radqstart = None
            #: (:obj:`float`) end position of radial q coordinate
            self.__radqend = None
            #: (:obj:`int`) grid size of radial q coordinate
            self.__radqsize = None
            #: (:obj:`float`) start position of radial theta coordinate
            self.__radthstart = None
            #: (:obj:`float`) end position of radial theta coordinate
            self.__radthend = None
            #: (:obj:`int`) grid size of radial theta coordinate
            self.__radthsize = None
            #: (:obj:`float`) start position of polar angle
            self.__polstart = None
            #: (:obj:`float`) end position of polar angle
            self.__polend = None
            #: (:obj:`int`) grid size of polar angle
            self.__polsize = None

            #: (:obj:`float`) range changed flag
            self.__rangechanged = True

            # self.parameters.lines = True
            #: (:obj:`str`) infolineedit text
            self.parameters.infolineedit = ""
            self.parameters.infotips = ""
            self.parameters.centerlines = True
            self.parameters.toolscale = False
            # self.parameters.rightplot = True

            #: (`lavuelib.imageDisplayWidget.AxesParameters`) axes backup
            self.__axes = None

            #: (:class:`lavuelib.settings.Settings`) configuration settings
            self.__settings = self._mainwidget.settings()

            #: (:obj:`float`) radial coordinate factor
            self.__radmax = 1.

            #: (:obj:`float`) polar coordinate factor
            self.__polmax = 1.

            #: (:obj:`list` < [:class:`pyqtgraph.QtCore.pyqtSignal`, :obj:`str`] >)
            #: list of [signal, slot] object to connect
            self.signal2slot = [
                [self.__ui.angleqPushButton.clicked, self._setGeometry],
                [self.__ui.rangePushButton.clicked, self._setPolarRange],
                [self.__ui.angleqComboBox.currentIndexChanged,
                 self._setGSpaceIndex],
                [self.__ui.angleqComboBox.currentIndexChanged,
                 self._mainwidget.emitTCC],
                [self.__ui.plotComboBox.currentIndexChanged,
                 self._setPlotIndex],
                [self.__ui.plotComboBox.currentIndexChanged,
                 self._mainwidget.emitTCC],
                [self._mainwidget.mouseImageDoubleClicked,
                 self._updateCenter],
                [self._mainwidget.geometryChanged, self.updateGeometryTip],
                [self._mainwidget.geometryChanged, self._mainwidget.emitTCC],
                [self._mainwidget.mouseImagePositionChanged, self._message]
            ]

        # @debugmethod
        def configure(self, configuration):
            """ set configuration for the current tool

            :param configuration: configuration string
            :type configuration: :obj:`str`
            """
            if configuration:
                cnf = json.loads(configuration)
                if "geometry" in cnf.keys():
                    try:
                        self._updateGeometry(cnf["geometry"])
                    except Exception as e:
                        # print(str(e))
                        logger.warning(str(e))
                if "plot_type" in cnf.keys():
                    idxs = ["pixels", "polar-th", "polar-q"]
                    xcrd = str(cnf["plot_type"]).lower()
                    try:
                        idx = idxs.index(xcrd)
                    except Exception:
                        idx = 0
                    self.__ui.plotComboBox.setCurrentIndex(idx)
                if "units" in cnf.keys():
                    idxs = ["angles", "q-space"]
                    xcrd = str(cnf["units"]).lower()
                    try:
                        idx = idxs.index(xcrd)
                    except Exception:
                        idx = 0
                    self.__ui.angleqComboBox.setCurrentIndex(idx)
                if "plot_range" in cnf.keys():
                    try:
                        self._updatePolarRange(cnf["plot_range"])
                    except Exception as e:
                        # print(str(e))
                        logger.warning(str(e))

        # @debugmethod
        def configuration(self):
            """ provides configuration for the current tool

            :returns configuration: configuration string
            :rtype configuration: :obj:`str`
            """
            cnf = {}
            cnf["plot_type"] = str(
                self.__ui.plotComboBox.currentText()).lower()
            cnf["units"] = str(
                self.__ui.angleqComboBox.currentText()).lower()

            cnf["plot_range"] = [
                [self.__polstart, self.__polend, self.__polsize],
                [self.__radthstart, self.__radthend, self.__radthsize],
                [self.__radqstart, self.__radqend, self.__radqsize]
            ]
            cnf["geometry"] = {
                "centerx": self.__settings.centerx,
                "centery": self.__settings.centery,
                "energy": self.__settings.energy,
                "pixelsizex": self.__settings.pixelsizex,
                "pixelsizey": self.__settings.pixelsizey,
                "detdistance": self.__settings.detdistance,
            }
            return json.dumps(cnf, cls=numpyEncoder)

        # @debugmethod
        def activate(self):
            """ activates tool widget
            """
            self.__oldlocked = None
            self.updateGeometryTip()
            self.updateRangeTip()
            self._mainwidget.updateCenter(
                self.__settings.centerx, self.__settings.centery)

        # @debugmethod
        def deactivate(self):
            """ deactivates tool widget
            """
            self._setPlotIndex(0)

        # @debugmethod
        def beforeplot(self, array, rawarray):
            """ command  before plot

            :param array: 2d image array
            :type array: :class:`numpy.ndarray`
            :param rawarray: 2d raw image array
            :type rawarray: :class:`numpy.ndarray`
            :return: 2d image array and raw image
            :rtype: (:class:`numpy.ndarray`, :class:`numpy.ndarray`)
            """
            if self.__plotindex != 0:
                if self._mainwidget.transformations()[0]:
                    tdata = self.__plotPolarImage(np.transpose(array))
                else:
                    tdata = self.__plotPolarImage(array)
                return tdata, tdata

        def __havexychanged(self, radial, angle):
            """ if xy changed

            :param radial: radial coordinate
            :type radial: :obj:`float` or :class:`numpy.array`
            :param angle: polar angle coordinate
            :type angle: :obj:`float` or :class:`numpy.array`
            :returns: flag if (x, y) have changed
            :rtype: :obj:`bool`
            """
            recalc = False
            if self.__lastradial is None or self.__lastangle is None or \
               self.__lastenergy is None or self.__lastradmax is None or \
               self.__lastpindex is None or self.__lastdistance is None or \
               self.__lastcenterx is None or self.__lastcentery is None or \
               self.__lastpsizex is None or self.__lastpsizey is None or \
               self.__lastx is None or self.__lasty is None:
                recalc = True
            elif self.__lastenergy != self.__settings.energy:
                recalc = True
            elif self.__lastradmax != self.__radmax:
                recalc = True
            elif self.__lastpindex != self.__plotindex:
                recalc = True
            elif self.__lastdistance != self.__settings.detdistance:
                recalc = True
            elif self.__lastcenterx != self.__settings.centerx:
                recalc = True
            elif self.__lastcentery != self.__settings.centery:
                recalc = True
            elif self.__lastpsizex != self.__settings.pixelsizex:
                recalc = True
            elif self.__lastpsizey != self.__settings.pixelsizey:
                recalc = True
            elif (isinstance(radial, np.ndarray) and
                  not np.array_equal(self.__lastradial, radial)):
                recalc = True
            elif (not isinstance(radial, np.ndarray)
                  and self.__lastradial != radial):
                recalc = True
            elif (isinstance(angle, np.ndarray)
                  and not np.array_equal(self.__lastangle, angle)):
                recalc = True
            elif not isinstance(angle, np.ndarray) and self.__lastangle != angle:
                recalc = True
            return recalc

        def __intintensity(self, radial, angle):
            """ intensity interpolation function

            :param radial: radial coordinate
            :type radial: :obj:`float` or :class:`numpy.array`
            :param angle: polar angle coordinate
            :type angle: :obj:`float` or :class:`numpy.array`
            :return: interpolated intensity
            :rtype: :obj:`float` or :class:`numpy.array`
            """
            if self.__rangechanged or self.__havexychanged(radial, angle):
                if self.__plotindex == 1:
                    rstart = self.__radthstart \
                        if self.__radthstart is not None else 0
                    theta = radial * self.__radmax + rstart * math.pi / 180
                else:
                    wavelength = 12398.4193 / self.__settings.energy
                    rstart = self.__radqstart \
                        if self.__radqstart is not None else 0
                    theta = 2. * np.arcsin(
                        (radial * self.__radmax + rstart) * wavelength
                        / (4. * math.pi))
                if self.__polsize is not None or \
                   self.__polstart is not None or self.__polend is not None:
                    pstart = self.__polstart if self.__polstart is not None else 0
                    angle = angle * self.__polmax + pstart
                fac = 1000. * self.__settings.detdistance * np.tan(theta)
                self.__lastx = self.__settings.centerx + \
                    fac * np.sin(angle * math.pi / 180) \
                    / self.__settings.pixelsizex
                self.__lasty = self.__settings.centery + \
                    fac * np.cos(angle * math.pi / 180) \
                    / self.__settings.pixelsizey
                self.__lastenergy = self.__settings.energy
                self.__lastradmax = self.__radmax
                self.__lastpindex = self.__plotindex
                self.__lastdistance = self.__settings.detdistance
                self.__lastcenterx = self.__settings.centerx
                self.__lastcentery = self.__settings.centery
                self.__lastpsizex = self.__settings.pixelsizex
                self.__lastpsizey = self.__settings.pixelsizey
                self.__lastradial = radial
                self.__lastangle = angle
                self.__rangechanged = False

            if hasattr(self.__inter, "ev"):
                return self.__inter.ev(self.__lastx, self.__lasty)
            else:
                return self.__inter(np.transpose(
                    [self.__lastx, self.__lasty], axes=[1, 2, 0]))

        def __calculateRadMax(self, pindex, rdata=None):
            """ recalculates radmax

            :param rarray: 2d image array
            :type rarray: :class:`numpy.ndarray`
            """
            if rdata is None:
                rdata = self._mainwidget.currentData()
            if rdata is not None:

                if pindex == 1:
                    if self.__lastmaxdim is not None \
                       and self.__radthsize is not None:
                        maxdim = self.__lastmaxdim
                    else:
                        maxdim = max(rdata.shape[0], rdata.shape[1])
                    rstart = self.__radthstart \
                        if self.__radthstart is not None else 0
                    if self.__radthend is None:
                        _, _, th0 = self.__pixel2theta(0, 0, False)
                        _, _, th1 = self.__pixel2theta(0, rdata.shape[1], False)
                        _, _, th2 = self.__pixel2theta(rdata.shape[0], 0, False)
                        _, _, th3 = self.__pixel2theta(
                            rdata.shape[0], rdata.shape[1], False)
                        try:
                            rmax = max(th0, th1, th2, th3)
                        except TypeError:
                            rmax = None
                    else:
                        rmax = (self.__radthend - rstart) * math.pi / 180.
                    if self.__radthsize is not None:
                        maxdim = self.__radthsize
                    if rmax is None:
                        # self._setGeometry()
                        logger.warning(
                            "Please set the detector geometry to continue")
                        return False
                    self.__radmax = rmax/float(maxdim)

                elif pindex == 2:
                    if self.__lastmaxdim is not None \
                       and self.__radqsize is not None:
                        maxdim = self.__lastmaxdim
                    else:
                        maxdim = max(rdata.shape[0], rdata.shape[1])
                    rstart = self.__radqstart \
                        if self.__radqstart is not None else 0
                    if self.__radqend is None:
                        _, _, q0 = self.__pixel2q(0, 0, False)
                        _, _, q1 = self.__pixel2q(0, rdata.shape[1], False)
                        _, _, q2 = self.__pixel2q(rdata.shape[0], 0, False)
                        _, _, q3 = self.__pixel2q(
                            rdata.shape[0], rdata.shape[1], False)
                        try:
                            rmax = max(q0, q1, q2, q3)
                        except TypeError:
                            rmax = None
                    else:
                        rmax = (self.__radqend - rstart)
                    if self.__radqsize is not None:
                        maxdim = self.__radqsize
                    if rmax is None:
                        # self._setGeometry()
                        logger.warning(
                            "Please set the detector geometry to continue")
                        return False
                    self.__radmax = rmax/float(maxdim)
                if pindex:
                    psize = self.__polsize if self.__polsize is not None else 360
                    pstart = self.__polstart if self.__polstart is not None else 0
                    pend = self.__polend if self.__polend is not None else 360
                    self.__polmax = float(pend - pstart) / psize
            return True

        # @debugmethod
        def __plotPolarImage(self, rdata=None):
            """ intensity interpolation function

            :param rarray: 2d image array
            :type rarray: :class:`numpy.ndarray`
            :return: 2d image array
            :rtype: :class:`numpy.ndarray`
            """
            if self.__settings.energy > 0 and self.__settings.detdistance > 0 and \
               self.__settings.pixelsizex > 0 and self.__settings.pixelsizey > 0:
                if rdata is None:
                    rdata = self._mainwidget.currentData()
                rwe = self._mainwidget.rangeWindowEnabled()
                if rwe:
                    dx, dy, ds1, ds2 = self._mainwidget.scale(
                        useraxes=False, noNone=True)
                    xx = np.array(range(int(dx),
                                        int((rdata.shape[0])*ds1 + dx),
                                        int(ds1)))

                    yy = np.array(range(int(dy),
                                        int((rdata.shape[1])*ds2 + dy),
                                        int(ds2)))
                else:
                    xx = np.array(range(rdata.shape[0]))
                    yy = np.array(range(rdata.shape[1]))

                self.__inter = scipy.interpolate.RegularGridInterpolator(
                    (xx, yy), rdata,
                    fill_value=(0 if self._mainwidget.scaling() != 'log'
                                else -2),
                    bounds_error=False)

                maxpolar = self.__polsize if self.__polsize is not None else 360
                if self.__plotindex == 1:
                    if self.__radthsize is not None:
                        self.__lastmaxdim = self.__radthsize
                    else:
                        self.__lastmaxdim = max(rdata.shape[0], rdata.shape[1])
                else:
                    if self.__radqsize is not None:
                        self.__lastmaxdim = self.__radqsize
                    else:
                        self.__lastmaxdim = max(rdata.shape[0], rdata.shape[1])

                while not self.__calculateRadMax(self.__plotindex, rdata):
                    pass
                tdata = np.fromfunction(
                    lambda x, y: self.__intintensity(x, y),
                    (int(self.__lastmaxdim), int(maxpolar)),
                    dtype=float)
                # else:
                #     self.__inter = scipy.interpolate.RectBivariateSpline(
                #         xx, yy, rdata)
                #     tdata = np.fromfunction(
                #         lambda x, y: self.__intintensity(x, y),
                #         (int(self.__lastmaxdim), int(maxpolar)),
                #         dtype=float)
                return tdata

        # @debugmethod
        @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`
            """
            if self.__plotindex == 0:
                txdata = None
                if self._mainwidget.rangeWindowEnabled():
                    txdata, tydata = self._mainwidget.scaledxy(
                        xdata, ydata, useraxes=False)
                    if txdata is not None:
                        xdata = txdata
                        ydata = tydata
                self.__settings.centerx = float(xdata)
                self.__settings.centery = float(ydata)
                self._mainwidget.writeAttribute("BeamCenterX", float(xdata))
                self._mainwidget.writeAttribute("BeamCenterY", float(ydata))
                self._message()
                self.updateGeometryTip()
                self._mainwidget.emitTCC()

        # @debugmethod
        @QtCore.pyqtSlot()
        def _message(self):
            """ provides geometry message
            """
            message = ""
            _, _, intensity, x, y = self._mainwidget.currentIntensity()
            if isinstance(intensity, float) and np.isnan(intensity):
                intensity = 0
            if isinstance(intensity, np.ndarray):
                intensity = np.nansum(
                    [0 if (isinstance(it, float) and np.isnan(it))
                     else it for it in intensity])
            if self._mainwidget.rangeWindowEnabled():
                txdata, tydata = self._mainwidget.scaledxy(
                    x, y, useraxes=False)
                if txdata is not None:
                    x = txdata
                    y = tydata
            ilabel = self._mainwidget.scalingLabel()
            if self.__plotindex == 0:
                if self.__gspaceindex == 0:
                    thetax, thetay, thetatotal = self.__pixel2theta(x, y)
                    if thetax is not None:
                        message = "th_x = %f deg, th_y = %f deg," \
                                  " th_tot = %f deg, %s = %.2f" \
                                  % (thetax * 180 / math.pi,
                                     thetay * 180 / math.pi,
                                     thetatotal * 180 / math.pi,
                                     ilabel, intensity)
                else:
                    qx, qy, q = self.__pixel2q(x, y)
                    if qx is not None:
                        message = u"q_x = %f 1/\u212B, q_y = %f 1/\u212B, " \
                                  u"q = %f 1/\u212B, %s = %.2f" \
                                  % (qx, qy, q, ilabel, intensity)
            elif self.__plotindex == 1:
                rstart = self.__radthstart \
                    if self.__radthstart is not None else 0
                pstart = self.__polstart if self.__polstart is not None else 0
                iscaling = self._mainwidget.scaling()
                if iscaling != "linear" and not ilabel.startswith(iscaling):
                    if ilabel[0] == "(":
                        ilabel = "%s%s" % (iscaling, ilabel)
                    else:
                        ilabel = "%s(%s)" % (iscaling, ilabel)

                message = u"th_tot = %f deg, polar = %f deg, " \
                          u" %s = %.2f" % (
                              x * 180 / math.pi * self.__radmax + rstart,
                              y * self.__polmax + pstart,
                              ilabel, intensity)
            elif self.__plotindex == 2:
                iscaling = self._mainwidget.scaling()
                pstart = self.__polstart if self.__polstart is not None else 0
                rstart = self.__radqstart \
                    if self.__radqstart is not None else 0
                if iscaling != "linear" and not ilabel.startswith(iscaling):
                    if ilabel[0] == "(":
                        ilabel = "%s%s" % (iscaling, ilabel)
                    else:
                        ilabel = "%s(%s)" % (iscaling, ilabel)

                message = u"q = %f 1/\u212B, polar = %f deg, " \
                          u" %s = %.2f" % (
                              x * self.__radmax + rstart,
                              y * self.__polmax + pstart,
                              ilabel, intensity)

            self._mainwidget.setDisplayedText(message)

        def __pixel2theta(self, xdata, ydata, xy=True):
            """ converts coordinates from pixel positions to theta angles

            :param xdata: x pixel position
            :type xdata: :obj:`float`
            :param ydata: y-pixel position
            :type ydata: :obj:`float`
            :param xy: flag
            :type xy: :obj:`bool`
            :returns: x-theta, y-theta, total-theta
            :rtype: (:obj:`float`, :obj:`float`, :obj:`float`)
            """
            thetax = None
            thetay = None
            thetatotal = None
            if self.__settings.energy > 0 and self.__settings.detdistance > 0:
                xcentered = xdata - self.__settings.centerx
                ycentered = ydata - self.__settings.centery
                if xy:
                    thetax = math.atan(
                        xcentered * self.__settings.pixelsizex / 1000.
                        / self.__settings.detdistance)
                    thetay = math.atan(
                        ycentered * self.__settings.pixelsizey / 1000.
                        / self.__settings.detdistance)
                r = math.sqrt(
                    (xcentered * self.__settings.pixelsizex / 1000.) ** 2
                    + (ycentered * self.__settings.pixelsizey / 1000.) ** 2)
                thetatotal = math.atan(
                    r / self.__settings.detdistance)
            return thetax, thetay, thetatotal

        def __pixel2q(self, xdata, ydata, xy=True):
            """ converts coordinates from pixel positions to q-space coordinates

            :param xdata: x pixel position
            :type xdata: :obj:`float`
            :param ydata: y-pixel position
            :type ydata: :obj:`float`
            :param xy: flag
            :type xy: :obj:`bool`
            :returns: q_x, q_y, q_total
            :rtype: (:obj:`float`, :obj:`float`, :obj:`float`)
            """
            qx = None
            qy = None
            q = None
            if self.__settings.energy > 0 and self.__settings.detdistance > 0:
                thetax, thetay, thetatotal = self.__pixel2theta(
                    xdata, ydata, xy)
                wavelength = 12398.4193 / self.__settings.energy
                if xy:
                    qx = 4 * math.pi / wavelength * math.sin(thetax/2.)
                    qy = 4 * math.pi / wavelength * math.sin(thetay/2.)
                q = 4 * math.pi / wavelength * math.sin(thetatotal/2.)
            return qx, qy, q

        def __tipmessage(self):
            """ provides geometry messate

            :returns: geometry text
            :rtype: :obj:`unicode`
            """

            return u"geometry:\n" \
                u"  center = (%s, %s) pixels\n" \
                u"  pixel_size = (%s, %s) \u00B5m\n" \
                u"  detector_distance = %s mm\n" \
                u"  energy = %s eV" % (
                    self.__settings.centerx,
                    self.__settings.centery,
                    self.__settings.pixelsizex,
                    self.__settings.pixelsizey,
                    self.__settings.detdistance,
                    self.__settings.energy
                )

        # @debugmethod
        @QtCore.pyqtSlot()
        def _setPolarRange(self):
            """ launches range widget

            :returns: apply status
            :rtype: :obj:`bool`
            """
            cnfdlg = rangeDialog.RangeDialog()
            cnfdlg.polstart = self.__polstart
            cnfdlg.polend = self.__polend
            cnfdlg.polsize = self.__polsize
            cnfdlg.radqstart = self.__radqstart
            cnfdlg.radqend = self.__radqend
            cnfdlg.radqsize = self.__radqsize
            cnfdlg.radthstart = self.__radthstart
            cnfdlg.radthend = self.__radthend
            cnfdlg.radthsize = self.__radthsize
            cnfdlg.createGUI()
            if cnfdlg.exec_():
                self.__polstart = cnfdlg.polstart
                self.__polend = cnfdlg.polend
                self.__polsize = cnfdlg.polsize
                self.__radthstart = cnfdlg.radthstart
                self.__radthend = cnfdlg.radthend
                self.__radthsize = cnfdlg.radthsize
                self.__radqstart = cnfdlg.radqstart
                self.__radqend = cnfdlg.radqend
                self.__radqsize = cnfdlg.radqsize
                self.__rangechanged = True
                self.updateRangeTip()
                self._setPlotIndex(self.__plotindex)
                if self.__plotindex:
                    self._mainwidget.emitReplotImage()

        # @debugmethod
        def _updatePolarRange(self, plotrange):
            """ update polar range

            :returns: (start, end, size) for polar, theta and q coordinates
            :rtype: :obj:`list`< [:obj:`float` ,:obj:`float` ,:obj:`float`] >
            """
            try:
                self.__polstart = float(plotrange[0][0])
            except Exception:
                self.__polstart = None
            try:
                self.__polend = float(plotrange[0][1])
            except Exception:
                self.__polend = None
            try:
                self.__polsize = float(plotrange[0][2])
            except Exception:
                self.__polsize = None
            try:
                self.__radthstart = float(plotrange[1][0])
            except Exception:
                self.__radthstart = None
            try:
                self.__radthend = float(plotrange[1][1])
            except Exception:
                self.__radthend = None
            try:
                self.__radthsize = float(plotrange[1][2])
            except Exception:
                self.__radthsize = None
            try:
                self.__radqstart = float(plotrange[2][0])
            except Exception:
                self.__radqstart = None
            try:
                self.__radqend = float(plotrange[2][1])
            except Exception:
                self.__radqend = None
            try:
                self.__radqsize = float(plotrange[2][2])
            except Exception:
                self.__radqsize = None
            self.__rangechanged = True
            self.updateRangeTip()
            # self._setPlotIndex(self.__plotindex)
            if self.__plotindex:
                self._mainwidget.emitReplotImage()
            self._mainwidget.emitTCC()

        # @debugmethod
        @QtCore.pyqtSlot()
        def _updateGeometry(self, geometry):
            """ update geometry widget

            :param geometry: geometry dictionary
            :type geometry: :obj:`dict` < :obj:`str`, :obj:`list`>
            """
            try:
                if "centerx" in geometry.keys():
                    self.__settings.centerx = float(geometry["centerx"])
                    self._mainwidget.writeAttribute(
                        "BeamCenterX", float(self.__settings.centerx))
            except Exception:
                pass
            try:
                if "centery" in geometry.keys():
                    self.__settings.centery = float(geometry["centery"])
                    self._mainwidget.writeAttribute(
                        "BeamCenterY", float(self.__settings.centery))
            except Exception:
                pass
            try:
                if "energy" in geometry.keys():
                    self.__settings.energy = float(geometry["energy"])
                    self._mainwidget.writeAttribute(
                        "Energy", float(self.__settings.energy))
            except Exception:
                pass
            try:
                if "pixelsizex" in geometry.keys():
                    self.__settings.pixelsizex = float(geometry["pixelsizex"])
            except Exception:
                pass
            try:
                if "pixelsizey" in geometry.keys():
                    self.__settings.pixelsizey = float(geometry["pixelsizey"])
            except Exception:
                pass
            try:
                if "detdistance" in geometry.keys():
                    self.__settings.detdistance = float(geometry["detdistance"])
                self._mainwidget.writeAttribute(
                    "DetectorDistance",
                    float(self.__settings.detdistance))
            except Exception:
                pass
            if geometry:
                self.updateGeometryTip()
                self._mainwidget.updateCenter(
                    self.__settings.centerx, self.__settings.centery)
                if self.__plotindex:
                    self._mainwidget.emitReplotImage()
                self._mainwidget.emitTCC()

        # @debugmethod
        @QtCore.pyqtSlot()
        def _setGeometry(self):
            """ launches geometry widget

            :returns: apply status
            :rtype: :obj:`bool`
            """
            cnfdlg = geometryDialog.GeometryDialog()
            cnfdlg.centerx = self.__settings.centerx
            cnfdlg.centery = self.__settings.centery
            cnfdlg.energy = self.__settings.energy
            cnfdlg.pixelsizex = self.__settings.pixelsizex
            cnfdlg.pixelsizey = self.__settings.pixelsizey
            cnfdlg.detdistance = self.__settings.detdistance
            cnfdlg.createGUI()
            if cnfdlg.exec_():
                self.__settings.centerx = cnfdlg.centerx
                self.__settings.centery = cnfdlg.centery
                self.__settings.energy = cnfdlg.energy
                self.__settings.pixelsizex = cnfdlg.pixelsizex
                self.__settings.pixelsizey = cnfdlg.pixelsizey
                self.__settings.detdistance = cnfdlg.detdistance
                self._mainwidget.writeAttribute(
                    "BeamCenterX", float(self.__settings.centerx))
                self._mainwidget.writeAttribute(
                    "BeamCenterY", float(self.__settings.centery))
                self._mainwidget.writeAttribute(
                    "Energy", float(self.__settings.energy))
                self._mainwidget.writeAttribute(
                    "DetectorDistance",
                    float(self.__settings.detdistance))
                self.updateGeometryTip()
                self._mainwidget.updateCenter(
                    self.__settings.centerx, self.__settings.centery)
                if self.__plotindex:
                    self._mainwidget.emitReplotImage()
                self._mainwidget.emitTCC()

        # @debugmethod
        @QtCore.pyqtSlot(int)
        def _setGSpaceIndex(self, gindex):
            """ set gspace index

            :param gspace: g-space index, i.e. angle or q-space
            :type gspace: :obj:`int`
            """
            self.__gspaceindex = gindex

        # @debugmethod
        @QtCore.pyqtSlot(int)
        def _setPlotIndex(self, pindex=None):
            """ set gspace index

            :param gspace: g-space index,
            :         i.e. 0: Cartesian, 1: polar-th, 2: polar-q
            :type gspace: :obj:`int`
            """
            if pindex:
                if not self.__calculateRadMax(pindex):
                    self.__ui.plotComboBox.setCurrentIndex(0)
                    return
                self.parameters.centerlines = False
                self.parameters.toolscale = True
                if pindex == 1:
                    rscale = 180. / math.pi * self.__radmax
                    rstart = self.__radthstart \
                        if self.__radthstart is not None else 0
                else:
                    rscale = self.__radmax
                    rstart = self.__radqstart \
                        if self.__radqstart is not None else 0
                pstart = self.__polstart if self.__polstart is not None else 0
                pscale = self.__polmax
                self._mainwidget.setToolScale([rstart, pstart], [rscale, pscale])
                if not self.__plotindex:
                    self.__oldlocked = self._mainwidget.setAspectLocked(False)
            else:
                if self.__oldlocked is not None:
                    self._mainwidget.setAspectLocked(self.__oldlocked)
                self.parameters.centerlines = True
                self.parameters.toolscale = False
            if pindex is not None:
                self.__plotindex = pindex
                if self.__ui.plotComboBox.currentIndex != pindex:
                    self.__ui.plotComboBox.setCurrentIndex(pindex)
            self._mainwidget.updateinfowidgets(self.parameters)

            self._mainwidget.emitReplotImage()

        @QtCore.pyqtSlot()
        def updateGeometryTip(self):
            """ update geometry tips
            """
            message = self.__tipmessage()
            self._mainwidget.updateDisplayedTextTip(
                "coordinate info display for the mouse pointer\n%s"
                % message)
            self.__ui.angleqPushButton.setToolTip(
                "Input physical parameters\n%s" % message)
            self.__ui.angleqComboBox.setToolTip(
                "Select the display space\n%s" % message)
            self.__ui.toolLabel.setToolTip(
                "coordinate info display for the mouse pointer\n%s" % message)

        @QtCore.pyqtSlot()
        def updateRangeTip(self):
            """ update geometry tips
            """
            self.__ui.rangePushButton.setToolTip(
                u"Polar: [%s, %s] deg, size=%s\n"
                u"th_tot: [%s, %s] deg, size=%s\n"
                u"q: [%s, %s] 1/\u212B, size=%s" % (
                    self.__polstart if self.__polstart is not None else "0",
                    self.__polend if self.__polend is not None else "360",
                    self.__polsize if self.__polsize is not None else "max",
                    self.__radthstart if self.__radthstart is not None else "0",
                    self.__radthend if self.__radthend is not None else "thmax",
                    self.__radthsize if self.__radthsize is not None else "max",
                    self.__radqstart if self.__radqstart is not None else "0",
                    self.__radqend if self.__radqend is not None else "qmax",
                    self.__radqsize if self.__radqsize is not None else "max")
            )

    class DiffractogramToolWidget(ToolBaseWidget):
        """ diffractogram tool widget
        """

        #: (:obj:`str`) tool name
        name = "Diffractogram"
        #: (:obj:`str`) tool name alias
        alias = "diffractogram"
        #: (:obj:`tuple` <:obj:`str`>) capitalized required packages
        requires = ("PYFAI",)

        def __init__(self, parent=None):
            """ constructor

            :param parent: parent object
            :type parent: :class:`pyqtgraph.QtCore.QObject`
            """
            ToolBaseWidget.__init__(self, parent)

            #: (:obj:`int`) unit index
            #               ->  0: q_nm^-1, 1: q_A-1, 2: 2th_deg, 3: 2th_rad
            self.__unitindex = 0
            #: (:obj:`list` <:obj:`str`>) list of units
            self.__units = ["q_nm^-1", "q_A^-1", "2th_deg",
                            "2th_rad", "r_mm", "r_pixel"]

            #: (:obj:`int`) plot index
            #               ->  0: Image, <i>: Buffer <i>
            self.__plotindex = 0
            #: (:obj:`bool`) old lock value
            self.__oldlocked = None
            #: (:class:`Ui_ROIToolWidget') ui_toolwidget object from qtdesigner
            self.__ui = _diffractogramformclass()
            self.__ui.setupUi(self)

            #: (:obj:`bool`) old lock value
            self.__oldlocked = None

            #: (:obj:`bool`) reset scale flag
            self.__resetscale = True

            #: ((:obj:`bool`) show diffractogram status
            self.__showdiff = False

            #: (:obj:`list` < [:obj:`float`, :obj:`float`] >)
            #          range positions of radial in current units
            self.__radrange = [None]
            #: (:obj:`list` < :obj:`float`>) start position of radial in deg
            self.__radstart = [None]
            #: (:obj:`list` < :obj:`float >) end position of radial in deg
            self.__radend = [None]
            #: (:obj:`list` < :obj:`float`>) start position of azimuth angle in deg
            self.__azstart = [None]
            #: (:obj:`list` < :obj:`float`>) end position of azimuth angle in deg
            self.__azend = [None]
            #: (:obj:`list` < [:obj:`float`, :obj:`float`] > )
            #          range positions of azimuth in deg
            self.__azrange = [None]

            #: (:obj:`list`<:class:`pyqtgraph.PlotDataItem`>) 1D plot freezed
            self.__freezed = []

            #: (:obj:`list`<:class:`pyqtgraph.PlotDataItem`>) 1D plot
            self.__curves = []
            #: (:obj:`int`) current plot number
            self.__nrplots = 0

            #: (:obj:`bool`) progressbar is running
            self.__progressFlag = False
            #: (:class:`pyqtgraph.QtGui.QProgressDialog`) progress bar
            self.__progress = None
            #: (:obj:`list` <:class:`lavuelib.commandThread.CommandThread`>) \
            #:     command thread
            self.__commandthread = None

            #: ( :obj:`list` <  :obj:`list` <  :obj:`list` < (float, float) > > >)
            #    list of region lines
            self.__regions = []

            #: (:obj:`list` < (int, int, int) > ) list with region colors
            self.__colors = []
            #: ( (:obj:`int`, :obj:`int', :obj:`int`)) default pen color
            self.__defpen = (0, 255, 127)

            #: (:obj:`bool`) accumalate status
            self.__accumulate = False
            #: (:obj:`bool`) show buffer status
            self.__showbuffer = False
            #: (:obj:`int`) buffer size
            self.__buffersize = 1024
            #: ([:class:`ndarray`,:class:`ndarray` :class:`ndarray`,
            #     :class:`ndarray`]) y-buffers for diffractogram
            self.__buffers = [None, None, None, None]
            #: (:obj:`list` < [:obj:`int`, :obj:`int`] > )
            #      x-buffers of (position, scale) for diffractogram
            self.__xbuffers = [None, None, None, None]
            #: (:obj:`list` < `list` < :obj:`int` > > ) time stamps
            self.__timestamps = [[], [], [], []]

            # self.parameters.lines = True
            #: (:obj:`str`) infolineedit text
            self.parameters.infolineedit = ""
            self.parameters.infotips = ""
            self.parameters.bottomplot = True
            self.parameters.centerlines = True
            self.parameters.toolscale = False
            self.parameters.regions = True
            # self.parameters.rightplot = True

            #: (`lavuelib.imageDisplayWidget.AxesParameters`) axes backup
            self.__axes = None

            #: (:class:`lavuelib.settings.Settings`) configuration settings
            self.__settings = self._mainwidget.settings()
            self.setColors(self.__settings.roiscolors)
            #: (:obj:`list` < [:class:`pyqtgraph.QtCore.pyqtSignal`, :obj:`str`] >)
            #: list of [signal, slot] object to connect
            self.signal2slot = [
                [self.__ui.diffSpinBox.valueChanged, self._updateDiffNumber],
                [self.__ui.diffSpinBox.valueChanged, self._mainwidget.emitTCC],
                [self.__ui.rangePushButton.clicked, self._setPolarRange],
                [self.__ui.showPushButton.clicked, self._showhideDiff],
                [self.__ui.nextPushButton.clicked, self._nextPlotDiff],
                [self.__ui.calibrationPushButton.clicked, self._loadCalibration],
                [self.__ui.unitComboBox.currentIndexChanged,
                 self._setUnitIndex],
                [self.__ui.unitComboBox.currentIndexChanged,
                 self._mainwidget.emitTCC],
                [self.__ui.mainplotComboBox.currentIndexChanged,
                 self._setPlotIndex],
                [self.__ui.mainplotComboBox.currentIndexChanged,
                 self._mainwidget.emitTCC],
                [self._mainwidget.mouseImageDoubleClicked,
                 self._updateCenter],
                [self.__ui.sizeLineEdit.textChanged, self._setBufferSize],
                [self.__ui.sizeLineEdit.textChanged, self._mainwidget.emitTCC],
                [self.__ui.accuPushButton.clicked, self._startStopAccu],
                [self.__ui.bufferPushButton.clicked, self._showBuffer],
                [self.__ui.resetPushButton.clicked, self._resetAccu],
                [self._mainwidget.geometryChanged, self.updateGeometryTip],
                [self._mainwidget.geometryChanged, self._mainwidget.emitTCC],
                [self._mainwidget.geometryChanged, self._updateSetCenter],
                [self._mainwidget.freezeBottomPlotClicked, self._freezeplot],
                [self._mainwidget.clearBottomPlotClicked, self._clearplot],
                [self._mainwidget.mouseImagePositionChanged, self._message],
                [self._mainwidget.colorsChanged, self.setColors]
            ]
            # self.__ui.showPushButton.hide()
            # self.__ui.nextPushButton.hide()
            self._showBuffer(False)

        # @debugmethod
        def configure(self, configuration):
            """ set configuration for the current tool

            :param configuration: configuration string
            :type configuration: :obj:`str`
            """
            if configuration:
                cnf = json.loads(configuration)
                if "calibration" in cnf.keys():
                    calib = cnf["calibration"]
                    try:
                        self._loadCalibration(calib)
                    except Exception as e:
                        logger.warning(str(e))
                if "diff_number" in cnf.keys():
                    try:
                        self.__ui.diffSpinBox.setValue(int(cnf["diff_number"]))
                    except Exception as e:
                        logger.warning(str(e))
                        # print(str(e))
                if "diff_ranges" in cnf.keys():
                    try:
                        self._updatePolarRange(cnf["diff_ranges"])
                    except Exception as e:
                        # print(str(e))
                        logger.warning(str(e))
                if "diff_units" in cnf.keys():
                    idxs = ["q [1/nm]", "q [1/A]", "2th [deg]", "2th [rad]",
                            "r [mm]", "r [pixel]"]
                    xcrd = str(cnf["diff_units"]).lower()
                    try:
                        idx = idxs.index(xcrd)
                    except Exception:
                        idx = 0
                    self.__ui.unitComboBox.setCurrentIndex(idx)
                if "show_diff" in cnf.keys():
                    if cnf["show_diff"]:
                        if str(self.__ui.showPushButton.text()) == "Show":
                            self._showhideDiff()
                    else:
                        if str(self.__ui.showPushButton.text()) == "Stop":
                            self._showhideDiff()
                if "stop_diff" in cnf.keys():
                    if cnf["stop_diff"]:
                        if str(self.__ui.showPushButton.text()) == "Stop":
                            self._showhideDiff()
                if "next" in cnf.keys():
                    if cnf["next"]:
                        if str(self.__ui.nextPushButton.text()) == "Next":
                            self._nextPlotDiff()
                if "main_plot" in cnf.keys():
                    idxs = [
                        "image", "buffer 1", "buffer 2", "buffer 3", "buffer 4"]
                    xcrd = str(cnf["main_plot"]).lower()
                    try:
                        idx = idxs.index(xcrd)
                    except Exception as e:
                        logger.warning(str(e))
                        # print(str(e))
                        idx = 0
                    self.__ui.mainplotComboBox.setCurrentIndex(idx)
                if "buffering" in cnf.keys():
                    self._showBuffer()
                if "buffer_size" in cnf.keys():
                    self.__ui.sizeLineEdit.setText(str(cnf["buffer_size"]))
                if "reset" in cnf.keys():
                    if cnf["reset"]:
                        self._resetAccu()
                if "collect" in cnf.keys():
                    if cnf["collect"] and not self.__accumulate:
                        self._startStopAccu()
                    elif not cnf["collect"] and self.__accumulate:
                        self._startStopAccu()

        # @debugmethod
        def configuration(self):
            """ provides configuration for the current tool

            :returns configuration: configuration string
            :rtype configuration: :obj:`str`
            """
            cnf = {}
            cnf["calibration"] = self.__settings.calibrationfilename
            cnf["diff_number"] = self.__ui.diffSpinBox.value()
            ranges = []
            for nip in range(cnf["diff_number"]):
                azs = self.__azstart[nip] if len(self.__azstart) > nip else None
                aze = self.__azend[nip] if len(self.__azend) > nip else None
                rds = self.__radstart[nip] if len(self.__radstart) > nip else None
                rde = self.__radend[nip] if len(self.__radend) > nip else None
                ranges.append([azs, aze, rds, rde])
            cnf["diff_ranges"] = ranges
            units = ["q [1/nm]", "q [1/A]", "2th [deg]", "2th [rad]",
                     "r [mm]", "r [pixel]"]
            idx = self.__ui.unitComboBox.currentIndex()
            cnf["diff_units"] = units[idx]
            try:
                cnf["buffer_size"] = int(self.__ui.sizeLineEdit.text())
            except Exception:
                cnf["buffer_size"] = self.__ui.sizeLineEdit.text()
            cnf["buffering"] = self.__showbuffer
            cnf["collect"] = self.__accumulate
            cnf["show_diff"] = self.__showdiff
            cnf["main_plot"] = str(
                self.__ui.mainplotComboBox.currentText()).lower()
            return json.dumps(cnf, cls=numpyEncoder)

        # @debugmethod
        @QtCore.pyqtSlot()
        def _setBufferSize(self):
            """ start/stop accumulation buffer

            """
            try:
                self.__buffersize = int(self.__ui.sizeLineEdit.text())
                self._resetAccu()
            except Exception as e:
                # print(str(e))
                logger.warning(str(e))
                self.__buffersize = 1024

        # @debugmethod
        @QtCore.pyqtSlot()
        def _resetAccu(self):
            """ reset accumulation buffer
            """
            self.__buffers = [None, None, None, None]
            self.__xbuffers = [None, None, None, None]
            self.__timestamps = [[], [], [], []]
            # np.empty(shape=(int(self.__settings.diffnpt), 0))
            # diffdata = None
            self.__resetscale = True
            if self.__plotindex != 0:
                diffdata = np.zeros(shape=(1, 1))
                self._mainwidget.updateImage(diffdata, diffdata)
            self._mainwidget.emitTCC()

        # @debugmethod
        @QtCore.pyqtSlot()
        def _startStopAccu(self):
            """ start/stop accumulation buffer
            """
            if not self.__accumulate:
                self.__accumulate = True
                self.__ui.accuPushButton.setText("Stop")
            else:
                self.__accumulate = False
                self.__ui.accuPushButton.setText("Collect")
            self._mainwidget.emitTCC()

        # @debugmethod
        @QtCore.pyqtSlot()
        def _showBuffer(self, status=None):
            """ show/hide buffer widgets
            """
            if status is not None:
                self.__showbuffer = not status
            if not self.__showbuffer:
                self.__showbuffer = True
                if QtGui.QIcon.hasThemeIcon("go-up"):
                    icon = QtGui.QIcon.fromTheme("go-up")
                    self.__ui.bufferPushButton.setIcon(icon)
                else:
                    # self.__ui.bufferPushButton.setText(u" \u25B2 Buffering")
                    self.__ui.bufferPushButton.setText(u" \u25B4 Buffering ")
                self.__ui.bufferFrame.show()
            else:
                self.__showbuffer = False
                if QtGui.QIcon.hasThemeIcon("go-down"):
                    icon = QtGui.QIcon.fromTheme("go-down")
                    self.__ui.bufferPushButton.setIcon(icon)
                else:
                    # self.__ui.bufferPushButton.setText(u" \u25BC Buffering")
                    self.__ui.bufferPushButton.setText(u" \u25BE Buffering ")
                self.__ui.bufferFrame.hide()
                self.adjustSize()
            self._mainwidget.emitTCC()

        # @debugmethod
        @QtCore.pyqtSlot(str)
        def setColors(self, colors=None, force=False):
            """ sets colors

            :param colors: json list of roi colors
            :type colors: :obj:`str`
            :returns: change status
            :rtype: :obj:`bool`
            """
            if colors is not None:
                colors = json.loads(colors)
                if not isinstance(colors, list):
                    return False
                for cl in colors:
                    if not isinstance(cl, list):
                        return False
                    if len(cl) != 3:
                        return False
                    for clit in cl:
                        if not isinstance(clit, int):
                            return False
            else:
                colors = self.__colors
                force = True
            if self.__colors != colors or force:
                self.__colors = colors
                for i, cr in enumerate(self.__curves):
                    clr = tuple(colors[i % len(colors)]) if colors \
                        else self.__defpen
                    cr.setPen(_pg.mkPen(clr))

        # @debugmethod
        def runProgress(self, commands, onclose="_closeReset",
                        text="Updating diffractogram ranges ..."):
            """ starts progress thread with the given commands

            :param commands: list of commands
            :type commands: :obj:`list` <:obj:`str`>
            :param onclose: close command name
            :type onclose: :obj:`str`
            :param text: text to display
            :type text: :obj:`str`
            """
            if self.__progress:
                return
            if self.__commandthread:
                self.__commandthread.setParent(None)
                self.__commandthread = None
            self.__commandthread = commandThread.CommandThread(
                self, commands, self._mainwidget)
            oncloseaction = getattr(self, onclose)
            self.__commandthread.finished.connect(
                oncloseaction, QtCore.Qt.QueuedConnection)
            self.__progress = None
            self.__progress = QtGui.QProgressDialog(
                text, "Cancel", 0, 0, self)
            self.__progress.setWindowModality(QtCore.Qt.WindowModal)
            self.__progress.setCancelButton(None)
            self.__progress.rejected.connect(
                self.waitForThread, QtCore.Qt.QueuedConnection)
            self.__commandthread.start()
            self.__progress.show()

        # @debugmethod
        def waitForThread(self):
            """ waits for running thread
            """
            logger.debug("waiting for Thread")
            if self.__commandthread:
                try:
                    self.__commandthread.wait()
                except Exception as e:
                    logger.warning(str(e))
            logger.debug("waiting for Thread ENDED")

        # @debugmethod
        @QtCore.pyqtSlot()
        def _freezeplot(self):
            """ freeze plot
            """
            self._clearplot()
            nrplots = self.__nrplots
            while nrplots > len(self.__freezed):
                cr = self._mainwidget.onedbottomplot()
                cr.setPen(_pg.mkColor(0.5))
                self.__freezed.append(cr)

            for i in range(nrplots):
                dt = self.__curves[i].xData, self.__curves[i].yData
                self.__freezed[i].setData(*dt)
                self.__freezed[i].show()
                self.__freezed[i].setVisible(True)
            for i in range(nrplots, len(self.__freezed)):
                self.__freezed[i].hide()
                self.__freezed[i].setVisible(True)
                # print(type(cr))

        # @debugmethod
        @QtCore.pyqtSlot()
        def _clearplot(self):
            """ clear freezed plot
            """
            for cr in self.__freezed:
                cr.setVisible(False)

        # @debugmethod
        @QtCore.pyqtSlot(int)
        def _updateDiffNumber(self, did):
            """ update diffractorgram number

            :param did: diffractogram id
            :type did: :obj:`int`
            """
            QtCore.QCoreApplication.processEvents()
            self.updateRangeTip()
            self.__updateBufferCombobox(did)
            # self.__nrplots = self.__ui.diffSpinBox.value()
            #
            self._plotDiff()
            self.setColors(self.__settings.roiscolors, True)
            # self.__updateregion()
            QtCore.QCoreApplication.processEvents()
            while len(self.__regions) > self.__nrplots:
                self.regions.pop()
            QtCore.QCoreApplication.processEvents()
            self._updateRegionsAndPlot()

        # @debugmethod
        def __updateBufferCombobox(self, did):
            """ update buffer combobox

            :param did: diffractogram id
            :type did: :obj:`int`
            """
            combo = self.__ui.mainplotComboBox
            # idx = combo.currentIndex()
            cnt = combo.count()
            while did >= cnt:
                if cnt:
                    combo.addItem("Buffer %s" % (cnt))
                else:
                    combo.addItem("Image")
                cnt = combo.count()
            while did + 1 < cnt:
                combo.removeItem(cnt - 1)
                cnt = combo.count()
            # if idx >= cnt:
            #     changed = True

        # @debugmethod
        def afterplot(self):
            """ command after plot
            """

        # @debugmethod
        def activate(self):
            """ activates tool widget
            """
            self.__oldlocked = None
            self.__ui.sizeLineEdit.setText(str(self.__buffersize))
            self.updateGeometryTip()
            self.updateRangeTip()
            self._mainwidget.updateCenter(
                self.__settings.centerx, self.__settings.centery)
            self.__updateButtons()
            if not self.__curves:
                self.__curves.append(self._mainwidget.onedbottomplot(True))
                self.__nrplots = 1
            for curve in self.__curves:
                curve.show()
                curve.setVisible(True)
            self.setColors(self.__settings.roiscolors, True)
            # if self.__settings.calibrationfilename:
            #     self._loadCalibration(
            #         self.__settings.calibrationfilename)
            # self.__ui.diffSpinBox.setEnabled(False)
            with QtCore.QMutexLocker(self.__settings.aimutex):
                aistat = self.__settings.ai is not None
            if aistat:
                self._plotDiff()
            self._mainwidget.bottomplotShowMenu(True, True)
            self.__ui.mainplotComboBox.setCurrentIndex(0)
            self._setPlotIndex(0)

        # @debugmethod
        @QtCore.pyqtSlot()
        def _nextPlotDiff(self):
            """ plot all diffractograms and update
            """
            self._plotDiffWithBuffering()
            if self.__plotindex > 0 and \
               self.__plotindex <= len(self.__buffers):
                if self.__buffers[self.__plotindex - 1] is not None:
                    diffdata = np.transpose(self.__buffers[self.__plotindex - 1])
                else:
                    diffdata = None
                    diffdata = np.zeros(shape=(1, 1))
                    self.__resetscale = True
                    # np.empty(shape=(int(self.__settings.diffnpt), 0))
                self._mainwidget.updateImage(diffdata, diffdata)
            self._mainwidget.emitTCC()

        # @debugmethod
        def deactivate(self):
            """ deactivates tool widget
            """
            self.waitForThread()
            self._mainwidget.bottomplotShowMenu()
            for curve in self.__curves:
                curve.hide()
                curve.setVisible(False)
                self._mainwidget.removebottomplot(curve)
            self.__curves = []
            for freezed in self.__freezed:
                freezed.hide()
                freezed.setVisible(False)
                self._mainwidget.removebottomplot(freezed)
            self.__freezed = []

        # @debugmethod
        def _plotDiffWithBuffering(self):
            """ plot diffractogram with buffering
            """
            xl, yl, ts = self._plotDiff()
            # print(yl)
            if self.__accumulate:
                for i, yy in enumerate(yl):
                    newrow = np.array(yy)
                    if self.__buffers[i] is not None and \
                       self.__buffers[i].shape[1] == newrow.shape[0]:
                        if self.__buffers[i].shape[0] >= self.__buffersize:
                            self.__buffers[i] = np.vstack(
                                [self.__buffers[i][
                                    self.__buffers[i].shape[0]
                                    - self.__buffersize + 1:,
                                    :],
                                 newrow]
                            )
                            if self.__timestamps[i]:
                                self.__timestamps[i].pop(0)
                        else:
                            self.__buffers[i] = np.vstack(
                                [self.__buffers[i], newrow])
                        self.__timestamps[i].append(ts)
                    else:
                        self.__buffers[i] = np.array([yy])
                        self.__timestamps[i] = [ts]
                    if self.__xbuffers[i] is None or self.__resetscale:
                        xbuf = np.array(xl[i])
                        pos = 0.0
                        sc = 1.0
                        if len(xbuf) > 0:
                            pos = xbuf[0]
                        if len(xbuf) > 1:
                            sc = (xbuf[-1] - xbuf[0])/(len(xbuf) - 1)
                        self.__xbuffers[i] = [pos, sc]
                        if (self.__ui.mainplotComboBox.currentIndex() -
                           1 == i):
                            self._mainwidget.setToolScale(
                                [pos, 0], [sc, 1])
                            # self._mainwidget.setToolScale(
                            #    [0, 0], [1, 1])
                self.__resetscale = False

        # @debugmethod
        def beforeplot(self, array, rawarray):
            """ command  before plot

            :param array: 2d image array
            :type array: :class:`numpy.ndarray`
            :param rawarray: 2d raw image array
            :type rawarray: :class:`numpy.ndarray`
            :return: 2d image array and raw image
            :rtype: (:class:`numpy.ndarray`, :class:`numpy.ndarray`)
            """
            if self.__showdiff:
                self._plotDiffWithBuffering()
            if self.__plotindex > 0 and \
               self.__plotindex <= len(self.__buffers):
                if self.__buffers[self.__plotindex - 1] is not None:
                    diffdata = np.transpose(self.__buffers[self.__plotindex - 1])
                else:
                    diffdata = None
                    diffdata = np.zeros(shape=(1, 1))
                    self.__resetscale = True
                    # np.empty(shape=(int(self.__settings.diffnpt), 0))
                return (diffdata, diffdata)

        # @debugmethod
        @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`
            """
            if self.__plotindex == 0:
                txdata = None
                if self._mainwidget.rangeWindowEnabled():
                    txdata, tydata = self._mainwidget.scaledxy(
                        xdata, ydata, useraxes=False)
                    if txdata is not None:
                        xdata = txdata
                        ydata = tydata
                self.__settings.centerx = float(xdata)
                self.__settings.centery = float(ydata)
                with QtCore.QMutexLocker(self.__settings.aimutex):
                    if self.__settings.ai is not None:
                        aif = self.__settings.ai.getFit2D()
                        self.__settings.ai.setFit2D(aif["directDist"],
                                                    self.__settings.centerx,
                                                    self.__settings.centery,
                                                    aif["tilt"],
                                                    aif["tiltPlanRotation"])
                    self._mainwidget.writeAttribute("BeamCenterX", float(xdata))
                    self._mainwidget.writeAttribute("BeamCenterY", float(ydata))
                self._message()
                self.__updateregion()
                self._plotDiff()
                self.updateGeometryTip()
                self._resetAccu()
                self._mainwidget.emitTCC()
                # self.__resetscale = True

        # @debugmethod
        @QtCore.pyqtSlot()
        def _updateSetCenter(self):
            """ updates the image center

            """
            # return
            with QtCore.QMutexLocker(self.__settings.aimutex):
                if self.__settings.ai is not None:
                    aif = self.__settings.ai.getFit2D()
                    self.__settings.ai.setFit2D(aif["directDist"],
                                                self.__settings.centerx,
                                                self.__settings.centery,
                                                aif["tilt"],
                                                aif["tiltPlanRotation"])
            self._resetAccu()
            self.__updateregion()
            self._plotDiff()
            # self.__resetscale = True

        # @debugmethod
        @QtCore.pyqtSlot()
        def _loadCalibration(self, fileName=None):
            """ load calibration file
            """
            if fileName is None:
                fileDialog = QtGui.QFileDialog()
                fileout = fileDialog.getOpenFileName(
                    self._mainwidget, 'Open calibration file',
                    self.__settings.calibrationfilename or '/ramdisk/',
                    "PONI (*.poni);;All files (*)"
                )
                if isinstance(fileout, tuple):
                    fileName = str(fileout[0])
                else:
                    fileName = str(fileout)
            if fileName:
                try:
                    with QtCore.QMutexLocker(self.__settings.aimutex):
                        self.__settings.ai = pyFAI.load(fileName)
                    # self.__settings.ai.rot1 = 0
                    # self.__settings.ai.rot1 = math.pi/4 * 0.5
                    # self.__settings.ai.rot1 = math.pi/4 * 0.75
                    # self.__settings.ai.rot1 = math.pi/4.
                    # self.__settings.ai.rot2 = math.pi/4.
                    # self.__settings.ai.rot3 = math.pi/2.
                    # print(str(self.__settings.ai))
                    self.__settings.calibrationfilename = fileName
                    self.__writedetsettings()
                    self._mainwidget.updateCenter(
                        self.__settings.centerx, self.__settings.centery)
                except Exception as e:
                    # print(str(e))
                    logger.warning(str(e))
                    with QtCore.QMutexLocker(self.__settings.aimutex):
                        self.__settings.ai = None
                with QtCore.QMutexLocker(self.__settings.aimutex):
                    self.__updateButtons(self.__settings.ai is not None)
                self.__updateregion()
                self.updateGeometryTip()
                self._resetAccu()
            self._mainwidget.emitTCC()
            self.__resetscale = True

        # @debugmethod
        def __writedetsettings(self):
            """ write detector settings from ai object
            """
            self.__settings.updateDetectorParameters()
            self._mainwidget.writeDetectorAttributes()

        # @debugmethod
        def __updateButtons(self, status=None):
            """ update buttons

            :param status: show button flag
            :type status: :obj:`bool`
            """
            if status is None:
                status = self.__settings.ai is not None
            self.__ui.showPushButton.setEnabled(status)
            self.__ui.nextPushButton.setEnabled(status)
            self.__ui.diffSpinBox.setEnabled(status)

        # @debugmethod
        @QtCore.pyqtSlot()
        def _message(self):
            """ provides geometry message
            """
            message = ""
            if self.__plotindex == 0:
                _, _, intensity, x, y = self._mainwidget.currentIntensity()
                if isinstance(intensity, float) and np.isnan(intensity):
                    intensity = 0
                if isinstance(intensity, np.ndarray):
                    intensity = np.nansum(
                        [0 if (isinstance(it, float) and np.isnan(it))
                         else it for it in intensity])
                if self._mainwidget.rangeWindowEnabled():
                    txdata, tydata = self._mainwidget.scaledxy(
                        x, y, useraxes=False)
                    if txdata is not None:
                        x = txdata
                        y = tydata
                ilabel = self._mainwidget.scalingLabel()
                chi = None
                with QtCore.QMutexLocker(self.__settings.aimutex):
                    if self.__settings.ai is not None:
                        chi = self.__settings.ai.chi(
                            np.array([y - 0.5]), np.array([x - 0.5]))
                        if len(chi):
                            chi = chi[0]
                if self.__unitindex in [2, 3]:
                    tth = None
                    with QtCore.QMutexLocker(self.__settings.aimutex):
                        if self.__settings.ai is not None:
                            tth = self.__settings.ai.tth(
                                np.array([y - 0.5]), np.array([x - 0.5])),
                            if len(tth):
                                tth = tth[0]
                    if tth is not None and chi is not None:
                        unit = "rad"
                        if self.__unitindex == 2:
                            unit = "deg"
                            chi = chi * 180./math.pi
                            tth = tth * 180./math.pi
                        message = "x, y = [%s, %s], tth = %f %s, chi = %f %s," \
                                  " %s = %.2f" \
                                  % (x, y, tth, unit,
                                     chi, unit,
                                     ilabel, intensity)
                        if self.__unitindex == 3:
                            ap = self.__approxpoint(tth, chi)
                            message += " $%s$" % str(ap)
                    else:
                        message = "x, y = [%s, %s], %s = %.2f" % (
                            x, y, ilabel, intensity)
                elif self.__unitindex in [0, 1]:
                    qa = None
                    with QtCore.QMutexLocker(self.__settings.aimutex):
                        if self.__settings.ai is not None:
                            qa = self.__settings.ai.qFunction(
                                np.array([y - 0.5]), np.array([x - 0.5])),
                            if len(qa):
                                qa = qa[0]
                    if qa is not None and chi is not None:
                        unit = u"1/\u212B"
                        chi = chi * 180./math.pi
                        if self.__unitindex == 0:
                            unit = "1/nm"
                        if self.__unitindex == 1:
                            qa = qa / 10.
                        message = "x, y = [%s, %s], q = %f %s, chi = %f %s," \
                                  " %s = %.2f" \
                                  % (x, y, qa, unit,
                                     chi, "deg",
                                     ilabel, intensity)
                    else:
                        message = "x, y = [%s, %s], %s = %.2f" % (
                            x, y, ilabel, intensity)
                elif self.__unitindex in [4]:
                    ra = None
                    with QtCore.QMutexLocker(self.__settings.aimutex):
                        if self.__settings.ai is not None:
                            ra = self.__settings.ai.rFunction(
                                np.array([y - 0.5]), np.array([x - 0.5])),
                            if len(ra):
                                ra = ra[0] * 1000.
                    if ra is not None and chi is not None:
                        chi = chi * 180./math.pi
                        message = "x, y = [%s, %s], r = %f %s, chi = %f %s," \
                                  " %s = %.2f" \
                                  % (x, y, ra, "mm",
                                     chi, "deg",
                                     ilabel, intensity)
                    else:
                        message = "x, y = [%s, %s], %s = %.2f" % (
                            x, y, ilabel, intensity)
                elif self.__unitindex in [5]:
                    cx = self.__settings.centerx
                    cy = self.__settings.centery
                    ra = math.sqrt((cx - x)**2 + (cy - y)**2)
                    if ra is not None and chi is not None:
                        chi = chi * 180./math.pi
                        message = "x, y = [%s, %s], r = %f %s, chi = %f %s," \
                                  " %s = %.2f" \
                                  % (x, y, ra, "pixel",
                                     chi, "deg",
                                     ilabel, intensity)
                    else:
                        message = "x, y = [%s, %s], %s = %.2f" % (
                            x, y, ilabel, intensity)
            elif self.__plotindex > 0:
                ix, iy, intensity, x, y = self._mainwidget.currentIntensity()
                ilabel = self._mainwidget.scalingLabel()
                pindex = self.__ui.mainplotComboBox.currentIndex()
                pos = 0.0
                sc = 1.0
                tst = []
                ts = ""
                if len(self.__xbuffers) >= pindex and \
                   self.__xbuffers[pindex - 1]:
                    pos, sc = self.__xbuffers[pindex - 1]
                xc = pos + x * sc
                if len(self.__timestamps) >= pindex and \
                   self.__timestamps[pindex - 1]:
                    tst = self.__timestamps[pindex - 1]
                it = int(iy)
                # itx = int(ix)
                if it < len(tst) and it >= 0:
                    ts = tst[it]
                    # store first tst[0]
                    # ts = tst[it] - tst[0]
                units = self.__units[self.__unitindex]
                xlabel = self.__ui.unitComboBox.currentText()
                if "[" in xlabel:
                    xlabel, units = xlabel.split("[", 1)
                    units = units.replace("]", "")
                message = "no: %s, %s = %s %s, %s = %.2f (time = %s s)" % (
                            it, xlabel, xc, units,  ilabel, intensity, ts)
            self._mainwidget.setDisplayedText(message)

        # @debugmethod
        def __tipmessage(self):
            """ provides geometry messate

            :returns: geometry text
            :rtype: :obj:`unicode`
            """
            tips = ""
            with QtCore.QMutexLocker(self.__settings.aimutex):
                if self.__settings.ai:
                    tips = str(self.__settings.ai)
            return tips

        # @debugmethod
        @QtCore.pyqtSlot()
        def updateGeometryTip(self):
            """ update geometry tips
            """
            message = self.__tipmessage()
            self.__ui.calibrationPushButton.setToolTip(
                "Input physical parameters\n%s" % message)

        # @debugmethod
        @QtCore.pyqtSlot()
        def _showhideDiff(self):
            """ show or hide diffractogram
            """
            if not self.__showdiff:
                self.__showdiff = True
                self.__ui.showPushButton.setText("Stop")
                self._plotDiff()
            else:
                self.__showdiff = False
                self.__ui.showPushButton.setText("Show")
            self._mainwidget.emitTCC()

        # @debugmethod
        @QtCore.pyqtSlot()
        def _plotDiff(self):
            """ plot all diffractograms

            :returns: (list of x's for each diffractogram,
                       list of y's for each diffractogram,
                       integer timestamp)

            :rtype: (:obj:`list` < :obj:`list` <float>>,
                     :obj:`list` < :obj:`list` <float>>,
                     int)
            """
            with QtCore.QMutexLocker(self.__settings.aimutex):
                aistat = self.__settings.ai is not None
            xl = []
            yl = []
            timestamp = time.time()
            if aistat:
                if self._mainwidget.currentTool() == self.name:
                    if self.__settings.sendresults:
                        xl = []
                        yl = []
                        pxl = []
                        pyl = []
                        pel = []
                    nrplots = self.__ui.diffSpinBox.value()
                    if self.__nrplots != nrplots:
                        while nrplots > len(self.__curves):
                            self.__curves.append(self._mainwidget.onedbottomplot())
                        for i in range(nrplots):
                            self.__curves[i].show()
                        for i in range(nrplots, len(self.__curves)):
                            self.__curves[i].hide()
                        self.__nrplots = nrplots
                        if nrplots:
                            for i, cr in enumerate(self.__curves):
                                if i < nrplots:
                                    colors = self.__colors
                                    clr = tuple(colors[i % len(colors)]) \
                                        if colors else self.__defpen
                                    cr.setPen(_pg.mkPen(clr))
                    dts = self._mainwidget.rawData()
                    if dts is not None:
                        while dts.ndim > 2:
                            dts = np.nanmean(dts, axis=2)
                    if dts is not None:
                        trans = self._mainwidget.transformations()[0]
                        csa = self.__settings.correctsolidangle
                        unit = self.__units[self.__unitindex]
                        if self.__unitindex in [5]:
                            unit = "r_mm"
                        else:
                            unit = self.__units[self.__unitindex]
                        dts = dts if trans else dts.T
                        mask = None
                        if dts.dtype.kind == 'f' and np.isnan(dts.min()):
                            mask = np.isnan(dts)
                            dts = np.array(dts)
                            dts[mask] = 0.
                            if mask is not None:
                                mask = mask.astype("int")
                        if self.__settings.showhighvaluemask and \
                           self._mainwidget.maskValue() is not None and \
                           self._mainwidget.maskValueIndices() is not None:
                            if mask is None:
                                mask = np.zeros(dts.shape)
                            try:
                                mask[self._mainwidget.maskValueIndices().T] = 1
                            except Exception as e:
                                logger.warning(str(e))
                                return xl, yl, timestamp
                        if self.__settings.showmask and \
                           self._mainwidget.applyMask() and \
                           self._mainwidget.maskIndices() is not None:
                            if mask is None:
                                mask = np.zeros(dts.shape)
                            try:
                                mask[self._mainwidget.maskIndices().T] = 1
                            except Exception as e:
                                logger.warning(str(e))
                                return xl, yl, timestamp
                        for i in range(nrplots):
                            try:
                                with QtCore.QMutexLocker(self.__settings.aimutex):
                                    res = self.__settings.ai.integrate1d(
                                        dts,
                                        self.__settings.diffnpt,
                                        correctSolidAngle=csa,
                                        radial_range=(
                                            self.__radrange[i]
                                            if len(self.__radrange) > i else None),
                                        azimuth_range=(
                                            self.__azrange[i]
                                            if len(self.__azrange) > i else None),
                                        unit=unit, mask=mask)
                                # print(res)
                                x = res[0]
                                y = res[1]
                                if self.__unitindex in [5]:
                                    with QtCore.QMutexLocker(
                                            self.__settings.aimutex):
                                        aif = self.__settings.ai.getFit2D()
                                    if aif["pixelX"] and aif["pixelY"]:
                                        if self.__azrange[i] is None:
                                            azs, aze = 0, math.pi/2
                                        else:
                                            azs, aze = self.__azrange[i]
                                            azs *= math.pi / 180.
                                            aze *= math.pi / 180.
                                        facx = 1000./aif["pixelX"]
                                        facy = 1000./aif["pixelY"]
                                        with QtCore.QMutexLocker(
                                                self.__settings.aimutex):
                                            cs1 = math.cos(
                                                azs + self.__settings.ai.rot3)
                                            cs2 = math.cos(
                                                aze + self.__settings.ai.rot3)
                                            sn1 = math.sin(
                                                azs + self.__settings.ai.rot3)
                                            sn2 = math.sin(
                                                aze + self.__settings.ai.rot3)
                                            fc1 = facx * cs1 / math.cos(
                                                self.__settings.ai.rot1)
                                            fc2 = facx * cs2 / math.cos(
                                                self.__settings.ai.rot1)
                                            fs1 = facy * sn1 / math.cos(
                                                self.__settings.ai.rot2)
                                            fs2 = facy * sn2 / math.cos(
                                                self.__settings.ai.rot2)
                                        x = [
                                            (math.sqrt(
                                                (fc1 * r)**2 + (fs1 * r)**2)
                                             + math.sqrt(
                                                 (fc2 * r)**2 + (fs2 * r)**2))
                                            / 2
                                            for r in x]
                                self.__curves[i].setData(x=x, y=y)
                                if self.__settings.sendresults or \
                                   self.__accumulate:
                                    xl.append([float(e) for e in x])
                                    yl.append([float(e) for e in y])
                                if self.__settings.sendresults:
                                    px, py, pe = self.__findpeaks2(x, y)
                                    pxl.append([float(e) for e in px])
                                    pyl.append([float(e) for e in py])
                                    pel.append(float(pe))
                            except Exception as e:
                                # print(str(e))
                                logger.warning(str(e))
                                x = []
                                y = []
                                self.__curves[i].setData(x=x, y=y)
                            self.__curves[i].setVisible(True)
                    else:
                        for i in range(nrplots):
                            self.__curves[i].setVisible(False)
                    if self.__settings.sendresults:
                        self.__sendresults(xl, yl, pxl, pyl, pel, timestamp)
            return xl, yl, timestamp

        def __sendresults(self, xl, yl, pxl=None, pyl=None, pel=None,
                          timestamp=None):
            """ send results to LavueController

            :param xl:  list of x's for each diffractogram
            :type xl: :obj:`list` < :obj:`list` <float>>
            :param yl:  list of values for each diffractogram
            :type yl: :obj:`list` < :obj:`list` <float>>
            :param pxl:  list of peak x's for each diffractogram
            :type pxl: :obj:`list` < :obj:`list` <float>>
            :param pyl:  list of peak values for each diffractogram
            :type pyl: :obj:`list` < :obj:`list` <float>>
            :param pel:  peak x's errors for each diffractogram
            :type pel:  :obj:`list` <float>
            :param timestamp:  timestamp
            :type timestamp:  :obj:`int`
            """
            results = {"tool": self.alias}
            npl = len(xl)
            results["imagename"] = self._mainwidget.imageName()
            results["timestamp"] = timestamp
            results["nrdiffs"] = len(xl)
            results["calibration"] = self.__settings.calibrationfilename
            for i in range(npl):
                results["radial_range_%s" % (i + 1)] = self.__radrange[i] \
                    if len(self.__radrange) > i else None
                results["azimuth_range_%s" % (i + 1)] = self.__azrange[i] \
                    if len(self.__azrange) > i else None
                results["diff_%s" % (i + 1)] = [xl[i], yl[i]]
                if pxl is not None and pyl is not None:
                    if len(pxl) > i and len(pyl) > i:
                        results["peaks_%s" % (i + 1)] = [pxl[i], pyl[i]]
                        if pel is not None:
                            results["peaks_%s_error" % (i + 1)] = pel[i]
            results["unit"] = self.__units[self.__unitindex]
            self._mainwidget.writeAttribute(
                "ToolResults", json.dumps(results, cls=numpyEncoder))

        def __findpeaks(self, x, y, nr=20):
            """ find peaks from diffractogram

            :param x:  x of diffractogram
            :type x:  :obj:`list` <float>>
            :param y:  values of diffractogram
            :type y:  :obj:`list` <float>>
            :param nr:  list of peak x's for each diffractogram
            :type nr: :obj:`int`
            :returns: (peak_xs, peak_values, x_error)
            :rtype: (:obj:`list` <float>, :obj:`list` <float>, :obj:`float`)
            """
            t = np.array(y)
            xml = []
            yml = []
            iml = []
            eml = []

            it = 0
            while (t > 0).any() and it < nr:
                im = np.argmax(t)
                tm = t[im]
                iml.append(im)
                xml.append(x[im])
                yml.append(tm)
                er = max(x[im] - x[max(im - 1, 0)],
                         x[min(im + 1, len(x) - 1)] - x[im])
                eml.append(er)
                it += 1
                # print("%s. found %s (%s +- %s, %s)" % (it, im, x[im], er, tm))

                i1 = im
                while i1 and t[i1 - 1] < t[i1]:
                    i1 -= 1
                ib = i1

                i1 = im
                while i1 + 1 < len(t) and t[i1 + 1] < t[i1]:
                    i1 += 1
                ie = i1
                # print(t)
                # print("cut: [%s: %s]" %  (ib, ie))
                t[ib:(ie + 1)] = 0
                # print(t)
            return ([float(e) for e in xml],
                    [float(e) for e in yml],
                    max(eml))

        def __findpeaks2(self, x, y, nr=20):
            """ find peaks from diffractogram with scipy

            :param x:  x of diffractogram
            :type x:  :obj:`list` <float>>
            :param y:  values of diffractogram
            :type y:  :obj:`list` <float>>
            :param nr:  list of peak x's for each diffractogram
            :type nr: :obj:`int`
            :returns: (peak_xs, peak_values, x_error)
            :rtype: (:obj:`list` <float>, :obj:`list` <float>, :obj:`float`)
            """
            f = scipy.interpolate.InterpolatedUnivariateSpline(x, y, k=4)
            xml = f.derivative().roots()
            yml = f(xml)
            er = max([(x[i+1] - x[i]) for i in range(len(x) - 1)])
            iml = np.argpartition(yml, -nr)[-nr:]
            iml = iml[np.argsort(-yml[iml])]
            iml = iml[:nr]
            return ([float(e) for e in xml[iml]],
                    [float(e) for e in yml[iml]],
                    er)

        # @debugmethod
        @QtCore.pyqtSlot()
        def _setPolarRange(self):
            """ launches range widget

            :returns: apply status
            :rtype: :obj:`bool`
            """
            nrplots = self.__ui.diffSpinBox.value()
            if nrplots:
                cnfdlg = diffRangeDialog.DiffRangeTabDialog(nrplots)
                cnfdlg.azstart = self.__azstart
                cnfdlg.azend = self.__azend
                cnfdlg.radstart = self.__radstart
                cnfdlg.radend = self.__radend
                cnfdlg.radunitindex = 2
                cnfdlg.createGUI()
                if cnfdlg.exec_():
                    self.__azstart = cnfdlg.azstart
                    self.__azend = cnfdlg.azend
                    self.__radstart = cnfdlg.radstart
                    self.__radend = cnfdlg.radend
                    self.__updateregion()
                    self._resetAccu()
                    self._mainwidget.emitTCC()

        # @debugmethod
        def _updatePolarRange(self, ranges):
            """ update diffractogram ranges

            :param ranges: list of [azimuth_start, azimuth_end,
                                    radial_start, radial_end]
                           elements for each diffractograms
            :type ranges: :obj:`list` < [
                               :obj:`float`, :obj:`float`,
                               :obj:`float`, :obj:`float` ]>
            """
            nrplots = self.__ui.diffSpinBox.value()
            if nrplots:
                azstart = []
                azend = []
                radstart = []
                radend = []
                for rn in ranges:
                    azstart.append(rn[0])
                    azend.append(rn[1])
                    radstart.append(rn[2])
                    radend.append(rn[3])
                self.__azstart = azstart
                self.__azend = azend
                self.__radstart = radstart
                self.__radend = radend
                self.__updateregion()
                self._resetAccu()
                self._mainwidget.emitTCC()

        def __updateaz(self):
            """ update azimuth range in deg
            """
            nrplots = self.__ui.diffSpinBox.value()
            self.__azrange = []
            for _ in range(len(self.__azend), nrplots):
                self.__azend.append(None)
            for _ in range(len(self.__azstart), nrplots):
                self.__azstart.append(None)
            for i in range(nrplots):
                if self.__azend[i] is None and self.__azstart[i] is None:
                    self.__azrange.append(None)
                elif (self.__azend[i] is not None
                      or self.__azstart[i] is not None):
                    if self.__azstart[i] is None:
                        self.__azstart[i] = 0
                    if self.__azend[i] is None:
                        self.__azend[i] = 360
                    self.__azrange.append([self.__azstart[i], self.__azend[i]])

        def __updaterad(self):
            """update radial range in deg
            """
            nrplots = self.__ui.diffSpinBox.value()
            self.__radrange = []
            for _ in range(len(self.__radend), nrplots):
                self.__radend.append(None)
            for _ in range(len(self.__radstart), nrplots):
                self.__radstart.append(None)
            for i in range(nrplots):
                if self.__radend[i] is not None or \
                   self.__radstart[i] is not None:
                    if self.__radstart[i] is None:
                        self.__radstart[i] = 0
                    if self.__radend[i] is None:
                        self.__radend[i] = 90
                if self.__radend[i] is None or self.__radstart[i] is None:
                    self.__radrange.append(None)
                else:
                    if self.__unitindex == 2:
                        self.__radrange.append(
                            [self.__radstart[i], self.__radend[i]])
                    else:
                        rs = self.__radstart[i] * math.pi / 180.
                        re = self.__radend[i] * math.pi / 180.
                        if self.__unitindex < 2:
                            oneoverlength = self.__settings.energy / 12398.4193
                            fac = 4 * math.pi * oneoverlength
                            qs = fac * math.sin(rs/2.)
                            qe = fac * math.sin(re/2.)
                            if self.__unitindex == 0:
                                qs = qs * 10.
                                qe = qe * 10.
                            self.__radrange.append([qs, qe])
                        elif self.__unitindex == 3:
                            self.__radrange.append([rs, re])
                        elif self.__unitindex in [4, 5]:
                            try:
                                [rbb, rbe, reb, ree, _, _, _, _] = \
                                    self.__getcorners(
                                        self.__radstart[i],
                                        self.__radend[i],
                                        self.__azstart[i],
                                        self.__azend[i])
                                logger.debug(
                                    "RESULT %s %s %s" % (
                                        str(rbb.x), rbb.success, rbb.fun))
                                logger.debug(
                                    "RESULT %s %s %s" % (
                                        str(rbe.x), rbe.success, rbe.fun))
                                logger.debug(
                                    "RESULT %s %s %s" % (
                                        str(reb.x), reb.success, reb.fun))
                                logger.debug(
                                    "RESULT %s %s %s" % (
                                        str(ree.x), ree.success, ree.fun))
                                rsl = []
                                rel = []
                                if rbb.success:
                                    with QtCore.QMutexLocker(
                                            self.__settings.aimutex):
                                        ra = self.__settings.ai.rFunction(
                                            np.array([rbb.x[1] - 0.5]),
                                            np.array([rbb.x[0] - 0.5]))
                                        if len(ra):
                                            rsl.append(ra[0] * 1000.)
                                if rbe.success:
                                    with QtCore.QMutexLocker(
                                            self.__settings.aimutex):
                                        ra = self.__settings.ai.rFunction(
                                            np.array([rbe.x[1] - 0.5]),
                                            np.array([rbe.x[0] - 0.5]))
                                        if len(ra):
                                            rsl.append(ra[0] * 1000.)

                                if reb.success:
                                    with QtCore.QMutexLocker(
                                            self.__settings.aimutex):
                                        ra = self.__settings.ai.rFunction(
                                            np.array([reb.x[1] - 0.5]),
                                            np.array([reb.x[0] - 0.5]))
                                        if len(ra):
                                            rel.append(ra[0] * 1000.)
                                if ree.success:
                                    with QtCore.QMutexLocker(
                                            self.__settings.aimutex):
                                        ra = self.__settings.ai.rFunction(
                                            np.array([ree.x[1] - 0.5]),
                                            np.array([ree.x[0] - 0.5]))
                                        if len(ra):
                                            rel.append(ra[0] * 1000.)
                                if rsl:
                                    rs = np.mean(rsl)
                                else:
                                    rs = math.tan(rs) * self.__settings.detdistance
                                if rel:
                                    re = np.mean(rel)
                                else:
                                    re = math.tan(re) * self.__settings.detdistance
                            except Exception as e:
                                logger.debug(str(e))
                                rs = math.tan(rs) * self.__settings.detdistance
                                re = math.tan(re) * self.__settings.detdistance
                            self.__radrange.append([rs, re])

        def __updateregion(self):
            """ update diffractogram region
            """
            self.__updateaz()
            self.__updaterad()
            self.updateRangeTip()
            self.runProgress(["findregions"], "_updateRegionsAndPlot")

        # @debugmethod
        def findregions(self):
            """ find regions lists
            """
            nrplots = self.__ui.diffSpinBox.value()
            regions = []
            with QtCore.QMutexLocker(self.__settings.aimutex):
                aistat = self.__settings.ai is not None
            for i in range(nrplots):
                if ((self.__azrange and self.__azrange[i]) or
                   (self.__radrange and self.__radrange[i])) and aistat:
                    azstart = self.__azstart[i] \
                        if self.__azstart[i] is not None else 0
                    azend = self.__azend[i] \
                        if self.__azend[i] is not None else 360
                    radstart = self.__radstart[i] \
                        if self.__radstart[i] is not None else 0
                    radend = self.__radend[i] \
                        if self.__radend[i] is not None else 70
                    try:
                        regions.append(
                            self.__findregion(radstart, radend, azstart, azend))
                    except Exception as e:
                        try:
                            logger.warning(str(e))
                            # print(str(e))
                            regions.append(
                                self.__findregion2(
                                    radstart, radend, azstart, azend))
                        except Exception as e2:
                            logger.warning(str(e2))
                            # print(str(e2))
                            # regions.append([])
            self.__regions = regions

        # @debugmethod
        def _updateRegionsAndPlot(self, regions=None):
            """ update regions and plots

            :param regions: list of region lines
            :type regions: :obj:`list` <  :obj:`list` <  :obj:`list`
                      < (:obj:`float`, :obj:`float`) > > >
            """
            regions = regions if regions is not None else self.__regions
            self._mainwidget.updateRegions(regions)
            self._resetAccu()
            self._plotDiff()
            self._closeReset()

        # @debugmethod
        def _closeReset(self):
            """ close reset method for progressbar

            :returns: progress status
            :rtype: :obj:`bool`
            """
            status = True
            logger.debug("closing Progress")
            if self.__progress:
                self.__progress.reset()
            if self.__commandthread and self.__commandthread.error:
                logger.error(
                    "Problems in updating Channels %s" %
                    str(self.__commandthread.error))
                self.__commandthread.error = None
                status = False
            if self.__progress:
                self.__progress.setParent(None)
                self.__progress = None
            self.waitForThread()
            logger.debug("closing Progress ENDED")
            return status

        def __findregion2(self, radstart, radend, azstart, azend):
            """ find region defined by angle range using image masking method

            :param radstart: start of radial region
            :type radstart: :obj:`float`
            :param radend: end of radial region
            :type radend: :obj:`float`
            :param azstart: start of azimuth region
            :type azstart: :obj:`float`
            :param azend: end of azimuth region
            :type azend: :obj:`float`
            :returns: list of region lines
            :rtype:  :obj:`list` < :obj:`list` < (float, float) > >
            """
            if azend - azstart >= 360:
                azstart = 0
                azend = 360
            with QtCore.QMutexLocker(self.__settings.aimutex):
                aistat = self.__settings.ai is not None
            if aistat:
                dts = self._mainwidget.rawData()
                if dts is not None:
                    while dts.ndim > 2:
                        dts = np.nanmean(dts, axis=2)
                if dts is not None and dts.shape and len(dts.shape) == 2:
                    shape = dts.T.shape
                else:
                    shape = [1000., 1000.]
                with QtCore.QMutexLocker(self.__settings.aimutex):
                    tta = self.__settings.ai.twoThetaArray(shape)
                    cha = self.__settings.ai.chiArray(shape)
                rb = self.__degtrim(radstart, 0, 360) * math.pi / 180.
                re = self.__degtrim(radend, 0, 360) * math.pi / 180.

                ab = self.__degtrim(azstart, -180, 180) * math.pi / 180.
                ae = self.__degtrim(azend, -180, 180) * math.pi / 180.

                lines = []

                if azend - azstart < 360:
                    chmask = (cha < ab) | (cha > ae)
                    ttaa = (tta - rb) * (tta - re)
                    ttaa[chmask] = 6
                    rblines = functions.isocurve(
                        ttaa, 0, connected=True)
                    logger.debug("RUN1 %s " % len(rblines))

                    thmask = (tta < rb) | (tta > re)
                    chaa = (cha - ab) * (cha - ae)
                    chaa[thmask] = 6
                    ablines = functions.isocurve(
                        chaa, 0, connected=True)
                    logger.debug("RUN2 %s " % len(ablines))
                else:
                    ttaa = (tta - rb) * (tta - re)
                    rblines = functions.isocurve(
                        ttaa, 0, connected=True)
                    logger.debug("RUN4 %s " % len(rblines))

                for line in rblines:
                    lines.append([(p[1], p[0]) for p in line])
                if azend - azstart < 360:
                    for line in ablines:
                        lines.append([(p[1], p[0]) for p in line])
                # for line in relines:
                #     lines.append([(p[1], p[0]) for p in line])

                # print(lines)
                return lines
                # self._mainwidget.updateRegions(lines)

            else:
                return []
                # self._mainwidget.updateRegions([[(0, 0)]])

        def __findregion(self, radstart, radend, azstart, azend):
            """ find region defined by angle range using Newton method

            :param radstart: start of radial region
            :type radstart: :obj:`float`
            :param radend: end of radial region
            :type radend: :obj:`float`
            :param azstart: start of azimuth region
            :type azstart: :obj:`float`
            :param azend: end of azimuth region
            :type azend: :obj:`float`
            :returns: list of region lines
            :rtype:  :obj:`list` < :obj:`list` < (float, float) > >
            """
            if azend - azstart >= 360:
                azstart = 0
                azend = 360
            [rbb, rbe, reb, ree, rb, re, ab, ae] = self.__getcorners(
                radstart, radend, azstart, azend)
            azb = azstart * math.pi / 180.
            aze = azend * math.pi / 180.
            logger.debug("RESULT %s %s %s" % (str(rbb.x), rbb.success, rbb.fun))
            logger.debug("RESULT %s %s %s" % (str(rbe.x), rbe.success, rbe.fun))
            logger.debug("RESULT %s %s %s" % (str(reb.x), reb.success, reb.fun))
            logger.debug("RESULT %s %s %s" % (str(ree.x), ree.success, ree.fun))
            # print("RESULT %s %s %s" % (str(rbb.x), rbb.success, rbb.fun))
            # print("RESULT %s %s %s" % (str(rbe.x), rbe.success, rbe.fun))
            # print("RESULT %s %s %s" % (str(reb.x), reb.success, reb.fun))
            # print("RESULT %s %s %s" % (str(ree.x), ree.success, ree.fun))
            lines = []
            if azend - azstart < 360:
                pbbeb = self.__findfixchipath(rbb.x, reb.x, ab, rb, re)
                lines.append(pbbeb)
                pbeee = self.__findfixchipath(rbe.x, ree.x, ae, rb, re)
                lines.append(pbeee)
            if self.__radstart[0] > 0:
                pbbbe = self.__findfixradpath(
                    rbb.x, rbe.x, rb, azb, aze,
                    full=(azend - azstart >= 360))
                lines.append(pbbbe)
            if self.__radend[0] < 60:
                pebee = self.__findfixradpath(
                    reb.x, ree.x, re, azb, aze,
                    full=(azend - azstart >= 360))
                lines.append(pebee)
            return lines

        @classmethod
        def __degtrim(cls, vl, lowbound, upbound):
            """ trim angle value to bounds in deg

            :param vl: value to trim
            :type vl: :obj:`float`
            :param lowbound: lower bound of value
            :type lowbound: :obj:`float`
            :param upbound: upper blund of value
            :type upbound: :obj:`float`
            :returns: trimmed value
            :rtype: :obj:`float`
            """
            while vl >= upbound:
                vl -= 360
            while vl < lowbound:
                vl += 360
            return vl

        @classmethod
        def __radtrim(cls, vl, lowbound, upbound):
            """ trim angle value to bounds in rad

            :param vl: value to trim
            :type vl: :obj:`float`
            :param lowbound: lower bound of value
            :type lowbound: :obj:`float`
            :param upbound: upper blund of value
            :type upbound: :obj:`float`
            :returns: trimmed value
            :rtype: :obj:`float`
            """
            while vl >= upbound:
                vl -= 2 * math.pi
            while vl < lowbound:
                vl += 2 * math.pi
            return vl

        def __findfixchipath(self, xstart, xend, chi, radstart, radend,
                             step=4, fmax=1.e-10):
            """ find a path for fix azimuth angle

            :param xstart: start point
            :type xstart: :obj:`float`
            :param xend: end point
            :type xend: :obj:`float`
            :param chi: chi angle value
            :type chi: :obj:`float`
            :param radstart: radial angle start value
            :type radstart: :obj:`float`
            :param radend: radial angle end value
            :type radend: :obj:`float`
            :param fmax: maximal allowed value for the test function
            :type fmax: :obj:`float`
            :returns: list of points
            :rtype: :obj:`list` < (float, float) >
            """
            points = [tuple(xstart)]

            if self.__dist2(xstart, xend) < step * step:
                points.append(tuple(xend))
                return points

            alphas = []
            cut = None
            tchi = self.__radtrim(chi, -math.pi, math.pi)
            if tchi < -math.pi/2 or tchi > math.pi/2:
                cut = 0
            cchi = self.__chi(xstart, cut)
            x = xstart

            def rsfun(alpha, x, step, cut, chi):
                y = [x[0] + step * math.cos(alpha),
                     x[1] + step * math.sin(alpha)]
                return [self.__chi(y, cut) - chi]

            res = scipy.optimize.root(rsfun, cchi, (x, step, cut, cchi))
            y = [x[0] + step * math.cos(res.x[0]),
                 x[1] + step * math.sin(res.x[0])]
            if self.__tth(x) - self.__tth(y) > 0 or \
               (not res.success and abs(res.fun) > fmax):
                res = scipy.optimize.root(rsfun, - cchi, (x, step, cut, cchi))
                y = [x[0] + step * math.cos(res.x[0]),
                     x[1] + step * math.sin(res.x[0])]
                if self.__tth(x) - self.__tth(y) > 0 or \
                   (not res.success and abs(res.fun) > fmax):
                    raise Exception("__findfixchipath: Cannot find the next point")

            points.append(tuple(y))
            if self.__dist2(y, xend) < step * step:
                points.append(tuple(xend))
                return points
            alphas.append(res.x[0])

            waking = True
            maxit = 10000
            it = 0
            istep = step
            while waking and maxit > it:
                it += 1
                alp = self.__fitnext(alphas[-5:])
                x = y
                res = scipy.optimize.root(rsfun, alp, (x, istep, cut, cchi))
                y = [x[0] + istep * math.cos(res.x[0]),
                     x[1] + istep * math.sin(res.x[0])]
                if self.__tth(x) - self.__tth(y) > 0 or \
                   (not res.success and abs(res.fun) > fmax):
                    istep = step / 2.
                    continue

                points.append(tuple(y))
                if self.__dist2(y, xend) < istep * istep:
                    waking = False
                alphas.append(res.x[0])
                istep = step
            points.append(tuple(xend))
            return points

        def __findfixradpath(self, xstart, xend, rad, azstart, azend,
                             step=4, fmax=1.e-10, full=False):
            """ find a path for fixed radial angle

            :param xstart: start point
            :type xstart: :obj:`float`
            :param xend: end point
            :type xend: :obj:`float`
            :param rad: radial angle value
            :type rad: :obj:`float`
            :param azstart: azimuth angle start value
            :type azstart: :obj:`float`
            :param azend: azimuth angle end value
            :type azend: :obj:`float`
            :param fmax: maximal allowed value for the test function
            :type fmax: :obj:`float`
            :param full: full angle flag
            :type full: :obj:`bool`
            :returns: list of points
            :rtype: :obj:`list` < (float, float) >
            """
            aze = azend
            points = [tuple(xstart)]
            while azstart > aze:
                aze += 2 * math.pi

            alphas = []
            cut = None
            tth1 = self.__tth(xstart)
            tth2 = self.__tth([xstart[0] + 1, xstart[1]])
            tth3 = self.__tth([xstart[0], xstart[1] + 1])
            dth = max(abs(tth1 - tth2), abs(tth1 - tth3))
            if (rad/dth) < 10. * step:
                step = (rad / dth) / 10.
            elif (rad/dth) > 10000. * step:
                step = (rad / dth) / 10000.
            if ((self.__dist2(xstart, xend) < step * step
                 and abs(azstart - aze) < math.pi) or step < 1.e-6):
                points.append(tuple(xend))
                return points

            # tth = self.__tth(xstart)
            tchi = self.__chi(xstart)

            if tchi < -math.pi/2 or tchi > math.pi/2:
                cut = 0
            cchi = self.__chi(xstart, cut)
            x = xstart
            logger.debug("CUT %s %s " % (cut, cchi))

            def rsfun(alpha, x, step, cut, tth):
                y = [x[0] + step * math.cos(alpha),
                     x[1] + step * math.sin(alpha)]
                return [self.__tth(y) - tth]

            itm = 10
            it = 0
            istep = step
            while it < itm:
                it += 1
                res = scipy.optimize.root(rsfun, cchi + math.pi/2,
                                          (x, istep, cut, rad))
                y = [x[0] + istep * math.cos(res.x[0]),
                     x[1] + istep * math.sin(res.x[0])]
                if self.__chi(x, cut) - self.__chi(y, cut) > 0 or \
                   (not res.success and abs(res.fun) > fmax):
                    res = scipy.optimize.root(rsfun, -cchi - math.pi/2,
                                              (x, istep, cut, rad))
                    y = [x[0] + istep * math.cos(res.x[0]),
                         x[1] + istep * math.sin(res.x[0])]
                    if self.__chi(x, cut) - self.__chi(y, cut) > 0 or \
                       (not res.success and abs(res.fun) > fmax):
                        istep = istep / 2.
                    else:
                        break
                else:
                    break
            if it == itm:
                raise Exception("__findfixradpath: Cannot find the next point")

            points.append(tuple(y))
            if self.__dist2(y, xend) < step * step and not full:
                points.append(tuple(xend))
                return points
            alphas.append(res.x[0])

            waking = True
            maxit = 10000
            it = 0
            istep = step
            while waking and maxit > it:
                it += 1
                alp = self.__fitnext(alphas[-5:])
                x = y
                tchi = self.__chi(x, cut)
                if tchi < -math.pi/2 or tchi > math.pi/2:
                    cut = 0
                else:
                    cut = None

                res = scipy.optimize.root(rsfun, alp, (x, istep, cut, rad))
                y = [x[0] + istep * math.cos(res.x[0]),
                     x[1] + istep * math.sin(res.x[0])]
                if self.__chi(x, cut) - self.__chi(y, cut) > 0 or \
                   (not res.success and abs(res.fun) > fmax):
                    istep = step / 2.
                    continue

                points.append(tuple(y))
                if self.__dist2(y, xend) < istep * istep:
                    waking = False
                alphas.append(res.x[0])
                istep = step
            points.append(tuple(xend))
            return points

        def __fitnext(self, y, x=None, x0=None):
            """ fits next y value

            :param y: list of y values
            :type y: :obj:`list` <:obj:`float`>
            :param x: list of x values
            :type x: :obj:`list` <:obj:`float`>
            :param x0: next x value
            :type x0: :obj:`float`
            :returns: next y value
            :rtype: :obj:`float`
            """
            n = len(y)
            if x is None:
                x = range(n)
                x0 = n
            return np.poly1d(np.polyfit(x, y, n - 1))(x0)

        def __dist2(self, x, y):
            """ distance square of x and y

            :param x: 2d point x
            :type x: :obj:`list` <:obj:`float`>
            :param y: 2d point y
            :type y: :obj:`list` <:obj:`float`>
            :returns: distance square
            :rtype: :obj:`float`
            """
            d0 = x[0] - y[0]
            d1 = x[1] - y[1]
            return d0 * d0 + d1 * d1

        def __chi(self, x, cut=None):
            """ chi of left bottom pixel corner in rad

            :param x: 2d point x
            :type x: :obj:`list` <:obj:`float`>
            :param cut: cut position in rad
            :type cut: :obj:`float`
            :returns: chi value
            :rtype: :obj:`float`
            """
            with QtCore.QMutexLocker(self.__settings.aimutex):
                chi = float(self.__settings.ai.chi(
                    np.array([x[1] - 0.5]), np.array([x[0] - 0.5]))[0])
            if cut is not None and chi > cut and cut > -math.pi:
                chi += 2 * math.pi
            return chi

        def __tth(self, x, path=None):
            """ tth of left bottom pixel corner in rad

            :param x: 2d point x
            :type x: :obj:`list` <:obj:`float`>
            :param path: path method
            :type path: :obj:`str`
            :returns: chi value
            :rtype: :obj:`float`
            """
            with QtCore.QMutexLocker(self.__settings.aimutex):
                tth = self.__settings.ai.tth(
                    np.array([x[1] - 0.5]), np.array([x[0] - 0.5]), path=path)[0]
            return float(tth)

        def __findpoint(self, rd, az, shape, start=None, itmax=20, fmax=1e-9):
            """ find a point in pixels

            :param rd: radial coordinate
            :type rd: :obj:`float`
            :param az: azimuth coordinate
            :type az: :obj:`float`
            :param shape: shape of the image
            :type shape: [:obj:`float`, :obj:`float`]
            :param start: start coordinate
            :type start: [:obj:`float`, :obj:`float`]
            :param itmaxstart: maximal number of tries
            :type start: :obj:`int`
            :param fmax: maximal allowed value for the test function
            :type fmax: :obj:`float`
            """
            def rafun(x, f1, f2):
                return [self.__tth(x) - f1, self.__chi(x) - f2]
            found = False
            it = 0
            if start is None:
                start = self.__approxpoint(rd, az)
            while not found and it < itmax:
                res = scipy.optimize.root(rafun, start, (rd, az))
                f = res.fun
                f2 = f[0] * f[0] + f[1] * f[1]
                found = res.success and f2 < fmax
                it += 1
                start = [random.randint(0, shape[0]),
                         random.randint(0, shape[1])]
            logger.debug("Tries: %s" % it)
            logger.debug(res)
            return res

        def __getcorners(self, radstart, radend, azstart, azend):
            """ find region corners

            :param radstart: start of radial region in deg
            :type radstart: :obj:`float`
            :param radend: end of radial region in deg
            :type radend: :obj:`float`
            :param azstart: start of azimuth region in deg
            :type azstart: :obj:`float`
            :param azend: end of azimuth region in deg
            :type azend: :obj:`float`
            :returns: [rbb, rbe, reb, ree, rb, re, ab, ae]
                      where rbb, rbe, reb, ree
                       are result objects of found region corners
                      while rb, re, ab, ae
                       are input angles in radians
            :rtype:  :obj:`list` <:obj:`float`>
            """

            with QtCore.QMutexLocker(self.__settings.aimutex):
                aistat = self.__settings.ai is not None
            if aistat:
                dts = self._mainwidget.rawData()
                if dts is not None:
                    while dts.ndim > 2:
                        dts = np.nanmean(dts, axis=2)
                if dts is not None and dts.shape and len(dts.shape) == 2:
                    shape = dts.shape
                else:
                    shape = [1000., 1000.]

                rb = self.__degtrim(radstart, 0, 360) * math.pi / 180.
                re = self.__degtrim(radend, 0, 360) * math.pi / 180.
                ab = self.__degtrim(azstart, -180, 180) * math.pi / 180.
                ae = self.__degtrim(azend, -180, 180) * math.pi / 180.

                rbb = self.__findpoint(rb, ab, shape)
                rbe = self.__findpoint(rb, ae, shape)
                ree = self.__findpoint(re, ae, shape)
                reb = self.__findpoint(re, ab, shape)

                return [rbb, rbe, reb, ree, rb, re, ab, ae]

        def __approxpoint(self, rad, az):
            """ find approximate x,y coorinates from angles

            :param rad: radial angle in rad
            :type rad: :obj:`float`
            :param az: azimuth angle in rad
            :type az: :obj:`float`
            :returns: [x, y] coordinates
            :rtype:  [:obj:`float`, :obj:`float`]
            """
            with QtCore.QMutexLocker(self.__settings.aimutex):
                aistat = self.__settings.ai is not None
            if aistat:
                tnr = math.tan(rad)
                csa = math.cos(az)
                sna = math.sin(az)
                dst = self.__settings.detdistance
                xc = self.__settings.centerx
                yc = self.__settings.centery
                px = self.__settings.pixelsizex / 1000.
                py = self.__settings.pixelsizey / 1000.
                y = (dst * sna * tnr + yc * py)/py
                x = (dst * csa * tnr + xc * px)/px
                return [x, y]

        # @debugmethod
        @QtCore.pyqtSlot(int)
        def _setUnitIndex(self, uindex):
            """ set unit index

            :param uindex: unit index, i.e. q_nm^-1, q_A^-1, 2th_deg, 2th_rad
            :type uindex: :obj:`int`
            """
            if self.__unitindex != uindex:
                self._resetAccu()
                self.__unitindex = uindex
            self.__updaterad()
            self._plotDiff()

        # @debugmethod
        @QtCore.pyqtSlot(int)
        def _setPlotIndex(self, pindex):
            """ set plot index

            :param pindex: plot index -> 0: Image, <i>: Buffer <i>
            :type pindex: :obj:`int`
            """
            if pindex and pindex > 0:
                self.parameters.centerlines = False
                self.parameters.toolscale = True
                self.parameters.regions = False
                if len(self.__xbuffers) >= pindex and \
                   self.__xbuffers[pindex - 1]:
                    pos, sc = self.__xbuffers[pindex - 1]
                    self._mainwidget.setToolScale([pos, 0], [sc, 1])
                else:
                    self._mainwidget.setToolScale([0, 0], [1, 1])
                # self._mainwidget.setToolScale([0, 0], [1, 1])
                if not self.__plotindex:
                    self.__oldlocked = self._mainwidget.setAspectLocked(False)
            else:
                if self.__oldlocked is not None:
                    self._mainwidget.setAspectLocked(self.__oldlocked)
                self.parameters.centerlines = True
                self.parameters.toolscale = False
                self.parameters.regions = True
            if pindex is not None:
                self.__plotindex = pindex
            self._mainwidget.updateinfowidgets(self.parameters)
            self._mainwidget.emitReplotImage()

        # @debugmethod
        @QtCore.pyqtSlot()
        def updateRangeTip(self):
            """ update geometry tips
            """
            text = u""
            nrplots = self.__ui.diffSpinBox.value()
            for i in range(nrplots):
                if nrplots > 1:
                    text += "%s. " % (i + 1)
                text += \
                    u"Radial range: [%s, %s] deg; Azimuth range: [%s, %s] deg" % (
                        self.__radstart[i]
                        if (len(self.__radstart) > i and
                            self.__radstart[i] is not None)
                        else "0",
                        self.__radend[i]
                        if (len(self.__radend) > i and
                            self.__radend[i] is not None)
                        else "90",
                        self.__azstart[i]
                        if (len(self.__azstart) > i and
                            self.__azstart[i] is not None)
                        else "0",
                        self.__azend[i]
                        if (len(self.__azend) > i and
                            self.__azend[i] is not None)
                        else "360")
                if i < nrplots - 1:
                    text += "\n"
            self.__ui.rangePushButton.setToolTip(text)

    class MaximaToolWidget(ToolBaseWidget):
        """ maxima tool widget
        """

        #: (:obj:`str`) tool name
        name = "Maxima"
        #: (:obj:`str`) tool name alias
        alias = "maxima"
        #: (:obj:`tuple` <:obj:`str`>) capitalized required packages
        requires = ()

        def __init__(self, parent=None):
            """ constructor

            :param parent: parent object
            :type parent: :class:`pyqtgraph.QtCore.QObject`
            """
            ToolBaseWidget.__init__(self, parent)

            #: (:obj:`int`) geometry space index -> 0: angle, 1 q-space
            self.__gspaceindex = 0

            #: (:obj:`int`) plot index -> 0: Cartesian, 1 polar-th, 2 polar-q
            self.__plotindex = 0

            #: (:class:`Ui_ROIToolWidget') ui_toolwidget object from qtdesigner
            self.__ui = _maximaformclass()
            self.__ui.setupUi(self)

            # self.parameters.lines = True
            #: (:obj:`str`) infolineedit text
            self.parameters.infolineedit = ""
            self.parameters.infotips = ""
            self.parameters.centerlines = True
            self.parameters.toolscale = False
            self.parameters.maxima = True
            # self.parameters.rightplot = True

            #: (`lavuelib..imageDisplayWidget.AxesParameters`) axes backup
            self.__axes = None

            #: (:class:`lavuelib.settings.Settings`) configuration settings
            self.__settings = self._mainwidget.settings()

            #: (:obj:`float`) radail coordinate factor
            self.__radmax = 1.

            #: (:obj:`float`) polar coordinate factor
            self.__polmax = 1.

            #: (:obj:`bool`) reploting flag
            self.__reploting = False

            #: (:obj:`bool`) reploting flag
            self.__updating = False

            #: (:obj:`list`) last combo items
            self.__lastcomboitems = []

            #: (:obj:`list` < [:class:`pyqtgraph.QtCore.pyqtSignal`, :obj:`str`] >)
            #: list of [signal, slot] object to connect
            self.signal2slot = [
                [self.__ui.angleqPushButton.clicked, self._setGeometry],
                [self.__ui.angleqComboBox.currentIndexChanged,
                 self._setGSpaceIndex],
                [self.__ui.angleqComboBox.currentIndexChanged,
                 self._mainwidget.emitTCC],
                [self._mainwidget.mouseImageDoubleClicked,
                 self._updateCenter],
                [self.__ui.maximaComboBox.currentIndexChanged, self._replot],
                [self.__ui.maximaComboBox.currentIndexChanged,
                 self._mainwidget.emitTCC],
                [self.__ui.numberSpinBox.valueChanged, self._replot],
                [self.__ui.numberSpinBox.valueChanged, self._mainwidget.emitTCC],
                [self._mainwidget.geometryChanged, self.updateGeometryTip],
                [self._mainwidget.geometryChanged, self._mainwidget.emitTCC],
                [self._mainwidget.mouseImagePositionChanged, self._message]
            ]

        # @debugmethod
        def configure(self, configuration):
            """ set configuration for the current tool

            :param configuration: configuration string
            :type configuration: :obj:`str`
            """
            if configuration:
                cnf = json.loads(configuration)
                if "geometry" in cnf.keys():
                    try:
                        self._updateGeometry(cnf["geometry"])
                    except Exception as e:
                        # print(str(e))
                        logger.warning(str(e))
                if "maxima_number" in cnf.keys():
                    try:
                        self.__ui.numberSpinBox.setValue(int(cnf["maxima_number"]))
                    except Exception as e:
                        logger.warning(str(e))
                        # print(str(e))
                if "units" in cnf.keys():
                    idxs = ["angles", "q-space", "xy-space"]
                    xcrd = str(cnf["units"]).lower()
                    try:
                        idx = idxs.index(xcrd)
                    except Exception:
                        idx = 0
                    self.__ui.angleqComboBox.setCurrentIndex(idx)
                if "current_maximum" in cnf.keys():
                    try:
                        cmx = int(cnf["current_maximum"]) - 1
                        self.__ui.maximaComboBox.setCurrentIndex(cmx)
                    except Exception as e:
                        # print(str(e))
                        logger.warning(str(e))
                        cmx = 0

        # @debugmethod
        def configuration(self):
            """ provides configuration for the current tool

            :returns configuration: configuration string
            :rtype configuration: :obj:`str`
            """
            cnf = {}
            cnf["units"] = str(
                self.__ui.angleqComboBox.currentText()).lower()
            cnf["maxima_number"] = self.__ui.numberSpinBox.value()
            cnf["current_maximum"] = self.__ui.maximaComboBox.currentIndex() + 1
            cnf["geometry"] = {
                "centerx": self.__settings.centerx,
                "centery": self.__settings.centery,
                "energy": self.__settings.energy,
                "pixelsizex": self.__settings.pixelsizex,
                "pixelsizey": self.__settings.pixelsizey,
                "detdistance": self.__settings.detdistance,
            }
            return json.dumps(cnf, cls=numpyEncoder)

        def activate(self):
            """ activates tool widget
            """
            self.updateGeometryTip()
            self._mainwidget.updateCenter(
                self.__settings.centerx, self.__settings.centery)

        def deactivate(self):
            """ deactivates tool widget
            """

        def beforeplot(self, array, rawarray):
            """ command  before plot

            :param array: 2d image array
            :type array: :class:`numpy.ndarray`
            :param rawarray: 2d raw image array
            :type rawarray: :class:`numpy.ndarray`
            :return: 2d image array and raw image
            :rtype: (:class:`numpy.ndarray`, :class:`numpy.ndarray`)
            """
            if rawarray is not None and rawarray.any():
                while rawarray.ndim > 2:
                    rawarray = np.nanmean(rawarray, axis=2)
                nr = self.__ui.numberSpinBox.value()
                nr = min(nr, rawarray.size)
                if nr > 0:
                    offset = [0.5, 0.5]
                    if self.__settings.nanmask:
                        if rawarray.dtype.kind == 'f' and \
                           np.isnan(rawarray.min()):
                            rawarray = np.nan_to_num(rawarray)
                    fidxs = np.argsort(rawarray, axis=None)[-nr:]
                    aidxs = [np.unravel_index(idx, rawarray.shape)
                             for idx in fidxs]
                    naidxs = aidxs
                    rwe = self._mainwidget.rangeWindowEnabled()
                    if rwe:
                        x, y, s1, s2 = self._mainwidget.scale(
                            useraxes=False, noNone=True)
                        naidxs = [(int(i * s1 + x), int(j * s2 + y))
                                  for i, j in aidxs]
                        offset = [offset[0] * s1, offset[1] * s2]
                    maxidxs = [[naidxs[n][0], naidxs[n][1], rawarray[i, j]]
                               for n, (i, j) in enumerate(aidxs)]
                    current = self.__updatemaxima(maxidxs)
                    if current >= 0:
                        aidxs.append(aidxs.pop(len(naidxs) - current - 1))
                    self._mainwidget.setMaximaPos(naidxs, offset)
                else:
                    self.__updatemaxima([])
                    self._mainwidget.setMaximaPos([])
            self.__reploting = False

        # @debugmethod
        def __updatemaxima(self, maxidxs):
            """ updates maxima in the combobox

            :param maxidxs: list with [[xn,yn, maxn], ... [x1,y1, max1]]
            :type maxidxs:
            """
            self.__updating = True
            combo = self.__ui.maximaComboBox
            idx = combo.currentIndex()
            if len(maxidxs) < idx:
                idx = len(maxidxs)
            if len(maxidxs) and idx < 0:
                idx = 0

            QtCore.QCoreApplication.processEvents()
            trans = self._mainwidget.transformations()[0]
            if trans:
                comboitems = ["%s: %s at (%s, %s)" % (i + 1, vl[2], vl[1], vl[0])
                              for i, vl in enumerate(reversed(maxidxs))]
            else:
                comboitems = ["%s: %s at (%s, %s)" % (i + 1, vl[2], vl[0], vl[1])
                              for i, vl in enumerate(reversed(maxidxs))]
            if self.__lastcomboitems != comboitems:
                self.__lastcomboitems != comboitems
                combo.clear()
                # self.__reploting = True
                combo.addItems(comboitems)
                combo.setCurrentIndex(idx)
            self.__updating = False
            if self.__settings.sendresults:
                self.__sendresults(maxidxs)
            return idx

        def __sendresults(self, maxidxs):
            """ send results to LavueController

            :param maxidxs: list with [[xn,yn, maxn], ... [x1,y1, max1]]
            :type maxidxs:
            """
            results = {"tool": self.alias}
            results["imagename"] = self._mainwidget.imageName()
            results["timestamp"] = time.time()
            results["maxima"] = maxidxs
            self._mainwidget.writeAttribute(
                "ToolResults", json.dumps(results, cls=numpyEncoder))

        @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`
            """
            txdata = None
            if self._mainwidget.rangeWindowEnabled():
                txdata, tydata = self._mainwidget.scaledxy(
                    xdata, ydata, useraxes=False)
                if txdata is not None:
                    xdata = txdata
                    ydata = tydata
            self.__settings.centerx = float(xdata)
            self.__settings.centery = float(ydata)
            self._mainwidget.writeAttribute("BeamCenterX", float(xdata))
            self._mainwidget.writeAttribute("BeamCenterY", float(ydata))
            self._message()
            self.updateGeometryTip()

        @QtCore.pyqtSlot()
        def _replot(self):
            if not self.__reploting and not self.__updating:
                self.__reploting = True
                self._mainwidget.emitReplotImage(False)

        @QtCore.pyqtSlot()
        def _message(self):
            """ provides geometry message
            """
            message = ""
            _, _, intensity, x, y = self._mainwidget.currentIntensity()
            if isinstance(intensity, float) and np.isnan(intensity):
                intensity = 0
            if isinstance(intensity, np.ndarray):
                intensity = np.nansum(
                    [0 if (isinstance(it, float) and np.isnan(it))
                     else it for it in intensity])
            txdata = None
            if self._mainwidget.rangeWindowEnabled():
                txdata, tydata = self._mainwidget.scaledxy(
                    x, y, useraxes=False)
            if txdata is not None:
                x, y = txdata, tydata
            ilabel = self._mainwidget.scalingLabel()
            if self.__gspaceindex == 0:
                thetax, thetay, thetatotal = self.__pixel2theta(x, y)
                if thetax is not None:
                    message = "th_x = %f deg, th_y = %f deg," \
                              " th_tot = %f deg, %s = %.2f" \
                              % (thetax * 180 / math.pi,
                                 thetay * 180 / math.pi,
                                 thetatotal * 180 / math.pi,
                                 ilabel, intensity)
            elif self.__gspaceindex == 1:
                qx, qy, q = self.__pixel2q(x, y)
                if qx is not None:
                    message = u"q_x = %f 1/\u212B, q_y = %f 1/\u212B, " \
                              u"q = %f 1/\u212B, %s = %.2f" \
                              % (qx, qy, q, ilabel, intensity)
            else:
                message = "x = %.2f, y = %.2f, %s = %.2f" % (
                    x, y, ilabel, intensity)
            self._mainwidget.setDisplayedText(message)

        def __pixel2theta(self, xdata, ydata, xy=True):
            """ converts coordinates from pixel positions to theta angles

            :param xdata: x pixel position
            :type xdata: :obj:`float`
            :param ydata: y-pixel position
            :type ydata: :obj:`float`
            :param xy: flag
            :type xy: :obj:`bool`
            :returns: x-theta, y-theta, total-theta
            :rtype: (:obj:`float`, :obj:`float`, :obj:`float`)
            """
            thetax = None
            thetay = None
            thetatotal = None
            if self.__settings.energy > 0 and self.__settings.detdistance > 0:
                xcentered = xdata - self.__settings.centerx
                ycentered = ydata - self.__settings.centery
                if xy:
                    thetax = math.atan(
                        xcentered * self.__settings.pixelsizex / 1000.
                        / self.__settings.detdistance)
                    thetay = math.atan(
                        ycentered * self.__settings.pixelsizey / 1000.
                        / self.__settings.detdistance)
                r = math.sqrt(
                    (xcentered * self.__settings.pixelsizex / 1000.) ** 2
                    + (ycentered * self.__settings.pixelsizey / 1000.) ** 2)
                thetatotal = math.atan(
                    r / self.__settings.detdistance)
            return thetax, thetay, thetatotal

        def __pixel2q(self, xdata, ydata, xy=True):
            """ converts coordinates from pixel positions to q-space coordinates

            :param xdata: x pixel position
            :type xdata: :obj:`float`
            :param ydata: y-pixel position
            :type ydata: :obj:`float`
            :param xy: flag
            :type xy: :obj:`bool`
            :returns: q_x, q_y, q_total
            :rtype: (:obj:`float`, :obj:`float`, :obj:`float`)
            """
            qx = None
            qy = None
            q = None
            if self.__settings.energy > 0 and self.__settings.detdistance > 0:
                thetax, thetay, thetatotal = self.__pixel2theta(
                    xdata, ydata, xy)
                wavelength = 12398.4193 / self.__settings.energy
                if xy:
                    qx = 4 * math.pi / wavelength * math.sin(thetax/2.)
                    qy = 4 * math.pi / wavelength * math.sin(thetay/2.)
                q = 4 * math.pi / wavelength * math.sin(thetatotal/2.)
            return qx, qy, q

        def __tipmessage(self):
            """ provides geometry messate

            :returns: geometry text
            :rtype: :obj:`unicode`
            """

            return u"geometry:\n" \
                u"  center = (%s, %s) pixels\n" \
                u"  pixel_size = (%s, %s) \u00B5m\n" \
                u"  detector_distance = %s mm\n" \
                u"  energy = %s eV" % (
                    self.__settings.centerx,
                    self.__settings.centery,
                    self.__settings.pixelsizex,
                    self.__settings.pixelsizey,
                    self.__settings.detdistance,
                    self.__settings.energy
                )

        # @debugmethod
        @QtCore.pyqtSlot()
        def _setGeometry(self):
            """ launches geometry widget

            :returns: apply status
            :rtype: :obj:`bool`
            """
            cnfdlg = geometryDialog.GeometryDialog()
            cnfdlg.centerx = self.__settings.centerx
            cnfdlg.centery = self.__settings.centery
            cnfdlg.energy = self.__settings.energy
            cnfdlg.pixelsizex = self.__settings.pixelsizex
            cnfdlg.pixelsizey = self.__settings.pixelsizey
            cnfdlg.detdistance = self.__settings.detdistance
            cnfdlg.createGUI()
            if cnfdlg.exec_():
                self.__settings.centerx = cnfdlg.centerx
                self.__settings.centery = cnfdlg.centery
                self.__settings.energy = cnfdlg.energy
                self.__settings.pixelsizex = cnfdlg.pixelsizex
                self.__settings.pixelsizey = cnfdlg.pixelsizey
                self.__settings.detdistance = cnfdlg.detdistance
                self._mainwidget.writeAttribute(
                    "BeamCenterX", float(self.__settings.centerx))
                self._mainwidget.writeAttribute(
                    "BeamCenterY", float(self.__settings.centery))
                self._mainwidget.writeAttribute(
                    "Energy", float(self.__settings.energy))
                self._mainwidget.writeAttribute(
                    "DetectorDistance",
                    float(self.__settings.detdistance))
                self.updateGeometryTip()
                self._mainwidget.updateCenter(
                    self.__settings.centerx, self.__settings.centery)
                if self.__plotindex:
                    self._mainwidget.emitReplotImage()
                self._mainwidget.emitTCC()

        # @debugmethod
        @QtCore.pyqtSlot()
        def _updateGeometry(self, geometry):
            """ update geometry widget

            :param geometry: geometry dictionary
            :type geometry: :obj:`dict` < :obj:`str`, :obj:`list`>
            """
            try:
                if "centerx" in geometry.keys():
                    self.__settings.centerx = float(geometry["centerx"])
                    self._mainwidget.writeAttribute(
                        "BeamCenterX", float(self.__settings.centerx))
            except Exception:
                pass
            try:
                if "centery" in geometry.keys():
                    self.__settings.centery = float(geometry["centery"])
                    self._mainwidget.writeAttribute(
                        "BeamCenterY", float(self.__settings.centery))
            except Exception:
                pass
            try:
                if "energy" in geometry.keys():
                    self.__settings.energy = float(geometry["energy"])
                    self._mainwidget.writeAttribute(
                        "Energy", float(self.__settings.energy))
            except Exception:
                pass
            try:
                if "pixelsizex" in geometry.keys():
                    self.__settings.pixelsizex = float(geometry["pixelsizex"])
            except Exception:
                pass
            try:
                if "pixelsizey" in geometry.keys():
                    self.__settings.pixelsizey = float(geometry["pixelsizey"])
            except Exception:
                pass
            try:
                if "detdistance" in geometry.keys():
                    self.__settings.detdistance = float(geometry["detdistance"])
                self._mainwidget.writeAttribute(
                    "DetectorDistance",
                    float(self.__settings.detdistance))
            except Exception:
                pass
            if geometry:
                self.updateGeometryTip()
                self._mainwidget.updateCenter(
                    self.__settings.centerx, self.__settings.centery)
                if self.__plotindex:
                    self._mainwidget.emitReplotImage()
                self._mainwidget.emitTCC()

        @QtCore.pyqtSlot(int)
        def _setGSpaceIndex(self, gindex):
            """ set gspace index

            :param gspace: g-space index, i.e. angle or q-space
            :type gspace: :obj:`int`
            """
            self.__gspaceindex = gindex

        @QtCore.pyqtSlot()
        def updateGeometryTip(self):
            """ update geometry tips
            """
            message = self.__tipmessage()
            self._mainwidget.updateDisplayedTextTip(
                "coordinate info display for the mouse pointer\n%s"
                % message)
            self.__ui.angleqPushButton.setToolTip(
                "Input physical parameters\n%s" % message)
            self.__ui.angleqComboBox.setToolTip(
                "Select the display space\n%s" % message)

    class QROIProjToolWidget(ToolBaseWidget):
        """ angle/q +roi + projections tool widget
        """

        #: (:class:`pyqtgraph.QtCore.pyqtSignal`) apply ROI pressed signal
        applyROIPressed = QtCore.pyqtSignal(str, int)
        #: (:class:`pyqtgraph.QtCore.pyqtSignal`) fetch ROI pressed signal
        fetchROIPressed = QtCore.pyqtSignal(str)
        #: (:class:`pyqtgraph.QtCore.pyqtSignal`) roi info Changed signal
        roiInfoChanged = QtCore.pyqtSignal(str)

        #: (:obj:`str`) tool name
        name = "Q+ROI+Proj"
        #: (:obj:`str`) tool name alias
        alias = "q+roi+proj"
        #: (:obj:`tuple` <:obj:`str`>) capitalized required packages
        requires = ()

        def __init__(self, parent=None):
            """ constructor

            :param parent: parent object
            :type parent: :class:`pyqtgraph.QtCore.QObject`
            """
            ToolBaseWidget.__init__(self, parent)

            #: (:obj:`int`) geometry space index -> 0: angle, 1 q-space
            self.__gspaceindex = 0

            #: (:class:`Ui_ROIToolWidget') ui_toolwidget object from qtdesigner
            self.__ui = _qroiprojformclass()
            self.__ui.setupUi(self)

            #: (:class:`pyqtgraph.PlotDataItem`) 1D bottom plot
            self.__bottomplot = None
            #: (:class:`pyqtgraph.PlotDataItem`) 1D bottom plot
            self.__rightplot = None
            #: (:obj:`int`) function index
            self.__funindex = 0

            #: (:obj:`slice`) selected rows
            self.__rows = None
            #: (:obj:`slice`) selected columns
            self.__columns = None
            #: (:obj:`slice`) selected rows
            self.__dsrows = None
            #: (:obj:`slice`) selected columns
            self.__dscolumns = None

            #: (:obj:`list`< :obj:`str`>) sardana aliases
            self.__aliases = []
            #: (:obj:`int`) ROI label length
            self.__textlength = 0

            self.parameters.bottomplot = True
            self.parameters.rightplot = True
            self.parameters.rois = True
            self.parameters.infolineedit = ""
            self.parameters.infotips = ""
            self.parameters.centerlines = True

            #: (:class:`lavuelib.settings.Settings`) configuration settings
            self.__settings = self._mainwidget.settings()

            self._updateApplyButton()
            #: (:obj:`list` < [:class:`pyqtgraph.QtCore.pyqtSignal`, :obj:`str`] >)
            #: list of [signal, slot] object to connect
            self.signal2slot = [
                [self.__ui.applyROIPushButton.clicked, self._emitApplyROIPressed],
                [self.__ui.fetchROIPushButton.clicked, self._emitFetchROIPressed],
                [self.__ui.angleqPushButton.clicked, self._setGeometry],
                [self.__ui.angleqComboBox.currentIndexChanged,
                 self._setGSpaceIndex],
                [self.__ui.angleqComboBox.currentIndexChanged,
                 self._mainwidget.emitTCC],
                [self._mainwidget.mouseImageDoubleClicked,
                 self._updateCenter],
                [self._mainwidget.mouseImagePositionChanged, self._message],
                [self._mainwidget.geometryChanged, self.updateGeometryTip],
                [self._mainwidget.geometryChanged, self._mainwidget.emitTCC],

                [self.applyROIPressed, self._mainwidget.applyROIs],
                [self.fetchROIPressed, self._mainwidget.fetchROIs],
                [self.roiInfoChanged, self._mainwidget.updateDisplayedText],
                [self.__ui.labelROILineEdit.textChanged,
                 self._updateApplyButton],
                [self.__ui.roiSpinBox.valueChanged, self._mainwidget.updateROIs],
                [self.__ui.roiSpinBox.valueChanged, self._mainwidget.emitTCC],
                [self.__ui.roiSpinBox.valueChanged,
                 self._mainwidget.writeDetectorROIsAttribute],
                [self.__ui.labelROILineEdit.textEdited,
                 self._writeDetectorROIs],
                [self.__ui.labelROILineEdit.textEdited, self._mainwidget.emitTCC],
                [self._mainwidget.roiLineEditChanged, self._updateApplyButton],
                [self._mainwidget.roiAliasesChanged, self.updateROILineEdit],
                [self._mainwidget.roiValueChanged, self.updateROIDisplayText],
                [self._mainwidget.roiNumberChanged, self.setROIsNumber],
                # [self._mainwidget.sardanaEnabled, self.updateROIButton],
                [self._mainwidget.mouseImagePositionChanged, self._roimessage],

                [self.__ui.funComboBox.currentIndexChanged,
                 self._setFunction],
                [self.__ui.rowsliceLineEdit.textChanged, self._updateRows],
                [self.__ui.rowsliceLineEdit.textChanged, self._mainwidget.emitTCC],
                [self.__ui.columnsliceLineEdit.textChanged, self._updateColumns],
                [self.__ui.columnsliceLineEdit.textChanged,
                 self._mainwidget.emitTCC],
                [self._mainwidget.scalesChanged, self._updateRows],
                [self._mainwidget.scalesChanged, self._updateColumns],
            ]

        def configure(self, configuration):
            """ set configuration for the current tool

            :param configuration: configuration string
            :type configuration: :obj:`str`
            """
            if configuration:
                cnf = json.loads(configuration)
                if "geometry" in cnf.keys():
                    try:
                        self._updateGeometry(cnf["geometry"])
                    except Exception as e:
                        # print(str(e))
                        logger.warning(str(e))
                if "rois_number" in cnf.keys():
                    try:
                        self.__ui.roiSpinBox.setValue(int(cnf["rois_number"]))
                    except Exception as e:
                        logger.warning(str(e))
                        # print(str(e))
                if "rois_coords" in cnf.keys():
                    self._mainwidget.updateROIs(
                        len(cnf["rois_coords"]), cnf["rois_coords"])
                if "aliases" in cnf.keys():
                    aliases = cnf["aliases"]
                    if isinstance(aliases, list):
                        aliases = " ".join(aliases)
                    self.__ui.labelROILineEdit.setText(aliases)
                if "units" in cnf.keys():
                    idxs = ["angles", "q-space"]
                    xcrd = str(cnf["units"]).lower()
                    try:
                        idx = idxs.index(xcrd)
                    except Exception:
                        idx = 0
                    self.__ui.angleqComboBox.setCurrentIndex(idx)
                if "apply" in cnf.keys():
                    if cnf["apply"]:
                        self._emitApplyROIPressed()
                if "fetch" in cnf.keys():
                    if cnf["fetch"]:
                        self._emitFetchROIPressed()
                if "mapping" in cnf.keys():
                    idxs = ["mean", "sum"]
                    xcrd = str(cnf["mapping"]).lower()
                    try:
                        idx = idxs.index(xcrd)
                    except Exception:
                        idx = 0
                    self.__ui.funComboBox.setCurrentIndex(idx)
                if "rows" in cnf.keys():
                    self.__ui.rowsliceLineEdit.setText(cnf["rows"])
                if "columns" in cnf.keys():
                    self.__ui.columnsliceLineEdit.setText(cnf["columns"])

        def configuration(self):
            """ provides configuration for the current tool

            :returns configuration: configuration string
            :rtype configuration: :obj:`str`
            """
            cnf = {}
            cnf["aliases"] = str(self.__ui.labelROILineEdit.text()).split(" ")
            cnf["rois_number"] = self.__ui.roiSpinBox.value()
            cnf["rois_coords"] = self._mainwidget.roiCoords()
            cnf["mapping"] = str(
                self.__ui.funComboBox.currentText()).lower()
            cnf["rows"] = self.__ui.rowsliceLineEdit.text()
            cnf["columns"] = self.__ui.columnsliceLineEdit.text()
            cnf["units"] = str(
                self.__ui.angleqComboBox.currentText()).lower()
            cnf["geometry"] = {
                "centerx": self.__settings.centerx,
                "centery": self.__settings.centery,
                "energy": self.__settings.energy,
                "pixelsizex": self.__settings.pixelsizex,
                "pixelsizey": self.__settings.pixelsizey,
                "detdistance": self.__settings.detdistance,
            }
            return json.dumps(cnf, cls=numpyEncoder)

        def activate(self):
            """ activates tool widget
            """
            self.updateGeometryTip()
            self._mainwidget.updateCenter(
                self.__settings.centerx, self.__settings.centery)
            self._mainwidget.changeROIRegion()
            self.setROIsNumber(len(self._mainwidget.roiCoords()))
            self.__aliases = self._mainwidget.getElementNames("ExpChannelList")
            self.updateROILineEdit(self._mainwidget.roilabels)
            self.__updateCompleter()
            # self.updateROIButton(
            #     self.__settings.sardana or bool(self.__settings.analysisdevice))

            if self.__bottomplot is None:
                self.__bottomplot = self._mainwidget.onedbarbottomplot()

            if self.__rightplot is None:
                self.__rightplot = self._mainwidget.onedbarrightplot()

            self.__bottomplot.show()
            self.__rightplot.show()
            self.__bottomplot.setVisible(True)
            self.__rightplot.setVisible(True)
            self._updateSlices()
            self._plotCurves()

        def deactivate(self):
            """ deactivates tool widget
            """
            self._mainwidget.roiCoordsChanged.emit()

            if self.__bottomplot is not None:
                self.__bottomplot.hide()
                self.__bottomplot.setVisible(False)
                self._mainwidget.removebottomplot(self.__bottomplot)
                self.__bottomplot = None
            if self.__rightplot is not None:
                self.__rightplot.hide()
                self.__rightplot.setVisible(False)
                self._mainwidget.removerightplot(self.__rightplot)
                self.__rightplot = None

        @QtCore.pyqtSlot()
        def _writeDetectorROIs(self):
            """ writes Detector rois and updates roi labels
            """
            self._mainwidget.roilabels = str(self.__ui.labelROILineEdit.text())
            self._mainwidget.writeDetectorROIsAttribute()

        def __updateslice(self, text, dx=None, ds=None):
            """ create slices from the text
            """
            rows = "ERROR"
            dsrows = "ERROR"
            if text:
                try:
                    if ":" in text:
                        slices = text.split(":")
                        s0 = int(slices[0]) if slices[0].strip() else 0
                        s1 = int(slices[1]) if slices[1].strip() else None
                        if len(slices) > 2:
                            s2 = int(slices[2]) if slices[2].strip() else None
                            rows = slice(s0, s1, s2)
                            if dx is not None:
                                dsrows = slice((s0-dx)/ds, (s1-dx)/ds, s2/ds)
                            else:
                                dsrows = rows
                        else:
                            rows = slice(s0, s1)
                            if dx is not None:
                                dsrows = slice((s0-dx)/ds, (s1-dx)/ds)
                            else:
                                dsrows = rows
                    else:
                        rows = int(text)
                        if dx is not None:
                            dsrows = int((rows - dx)/ds)
                        else:
                            dsrows = rows
                except Exception:
                    pass
            else:
                rows = None
                dsrows = None
            return rows, dsrows

        @QtCore.pyqtSlot(str)
        @QtCore.pyqtSlot()
        def _updateSlices(self):
            """ updates applied button"""
            rtext = str(self.__ui.rowsliceLineEdit.text()).strip()
            ctext = str(self.__ui.columnsliceLineEdit.text()).strip()
            rwe = self._mainwidget.rangeWindowEnabled()
            if rwe:
                dx, dy, ds1, ds2 = self._mainwidget.scale(
                    useraxes=False, noNone=True)
                self.__rows, self.__dsrows = self.__updateslice(
                    rtext, int(dy), int(ds2))
                self.__columns, self.__dscolumns = self.__updateslice(
                    ctext, int(dx), int(ds1))
            else:
                self.__rows, self.__dsrows = self.__updateslice(rtext)
                self.__columns, self.__dscolumns = self.__updateslice(ctext)
            if self.__rows is None:
                self._mainwidget.updateHBounds(None, None)
            elif isinstance(self.__rows, int):
                self._mainwidget.updateHBounds(self.__rows, self.__rows + 1)
            elif isinstance(self.__rows, slice):
                self._mainwidget.updateHBounds(self.__rows.start, self.__rows.stop)
            if self.__columns is None:
                self._mainwidget.updateVBounds(None, None)
            elif isinstance(self.__columns, int):
                self._mainwidget.updateVBounds(self.__columns, self.__columns + 1)
            elif isinstance(self.__columns, slice):
                self._mainwidget.updateVBounds(
                    self.__columns.start, self.__columns.stop)
            self._plotCurves()

        @QtCore.pyqtSlot(str)
        @QtCore.pyqtSlot()
        def _updateRows(self):
            """ updates applied button"""
            rtext = str(self.__ui.rowsliceLineEdit.text()).strip()
            rwe = self._mainwidget.rangeWindowEnabled()
            if rwe:
                dx, dy, ds1, ds2 = self._mainwidget.scale(
                    useraxes=False, noNone=True)
                self.__rows, self.__dsrows = self.__updateslice(
                    rtext, int(dy), int(ds2))
            else:
                self.__rows, self.__dsrows = self.__updateslice(rtext)
            if self.__rows is None:
                self._mainwidget.updateHBounds(None, None)
            elif isinstance(self.__rows, int):
                self._mainwidget.updateHBounds(self.__rows, self.__rows + 1)
            elif isinstance(self.__rows, slice):
                self._mainwidget.updateHBounds(self.__rows.start, self.__rows.stop)
            self._plotCurves()

        @QtCore.pyqtSlot(str)
        @QtCore.pyqtSlot()
        def _updateColumns(self):
            """ updates applied button"""
            text = str(self.__ui.columnsliceLineEdit.text()).strip()
            rwe = self._mainwidget.rangeWindowEnabled()
            if rwe:
                dx, dy, ds1, ds2 = self._mainwidget.scale(
                    useraxes=False, noNone=True)
            if rwe:
                self.__columns, self.__dscolumns = self.__updateslice(
                    text, int(dx), int(ds1))
            else:
                self.__columns, self.__dscolumns = self.__updateslice(text)
            if self.__columns is None:
                self._mainwidget.updateVBounds(None, None)
            elif isinstance(self.__columns, int):
                self._mainwidget.updateVBounds(self.__columns, self.__columns + 1)
            elif isinstance(self.__columns, slice):
                self._mainwidget.updateVBounds(
                    self.__columns.start, self.__columns.stop)
            self._plotCurves()

        @QtCore.pyqtSlot(int)
        def _setFunction(self, findex):
            """ set sum or mean function

            :param findex: function index, i.e. 0:mean, 1:sum
            :type findex: :obj:`int`
            """
            self.__funindex = findex
            self._plotCurves()

        def afterplot(self):
            """ command after plot
            """
            self._plotCurves()

        @QtCore.pyqtSlot()
        def _plotCurves(self):
            """ plots the current image in 1d plots
            """
            if self._mainwidget.currentTool() == self.name:
                dts = self._mainwidget.rawData()
                if dts is not None:
                    while dts.ndim > 2:
                        dts = np.nanmean(dts, axis=2)
                if dts is not None:
                    if self.__funindex:
                        npfun = np.nansum
                    else:
                        npfun = np.nanmean

                    if self.__dsrows == "ERROR":
                        sx = []
                    elif self.__dsrows is not None:
                        try:
                            with np.warnings.catch_warnings():
                                np.warnings.filterwarnings(
                                    "ignore", r'Mean of empty slice')
                                if isinstance(self.__dsrows, slice):
                                    sx = npfun(dts[:, self.__dsrows], axis=1)
                                else:
                                    sx = dts[:, self.__dsrows]
                        except Exception:
                            sx = []

                    else:
                        with np.warnings.catch_warnings():
                            np.warnings.filterwarnings(
                                "ignore", r'Mean of empty slice')
                            sx = npfun(dts, axis=1)

                    if self.__dscolumns == "ERROR":
                        sy = []
                    if self.__dscolumns is not None:
                        try:
                            with np.warnings.catch_warnings():
                                np.warnings.filterwarnings(
                                    "ignore", r'Mean of empty slice')
                                if isinstance(self.__dscolumns, slice):
                                    sy = npfun(dts[self.__dscolumns, :], axis=0)
                                else:
                                    sy = dts[self.__dscolumns, :]
                        except Exception:
                            sy = []
                    else:
                        try:
                            with np.warnings.catch_warnings():
                                np.warnings.filterwarnings(
                                    "ignore", r'Mean of empty slice')
                                sy = npfun(dts, axis=0)
                        except Exception:
                            sy = []

                    rwe = self._mainwidget.rangeWindowEnabled()
                    if rwe:
                        x, y, s1, s2 = self._mainwidget.scale(
                            useraxes=False, noNone=True)
                        if self._mainwidget.transformations()[3]:
                            x, y = y, x
                            s1, s2 = s2, s1
                        xx = list(
                            range(int(x), len(sx) * int(s1) + int(x), int(s1)))
                        yy = list(
                            range(int(y), len(sy) * int(s2) + int(y), int(s2)))
                    else:
                        s1 = 1.0
                        s2 = 1.0
                        xx = list(range(len(sx)))
                        yy = list(range(len(sy)))
                    width = [s1] * len(sx)
                    height = [s2] * len(sy)
                    self.__bottomplot.setOpts(
                        y0=[0]*len(sx), y1=sx, x=xx,
                        width=width)
                    self.__bottomplot.drawPicture()
                    self.__rightplot.setOpts(
                        x0=[0]*len(sy), x1=sy, y=yy,
                        height=height)
                    self.__rightplot.drawPicture()
                    if self.__settings.sendresults:
                        xslice = self.__dsrows
                        yslice = self.__dscolumns
                        if hasattr(xslice, "start"):
                            xslice = [xslice.start, xslice.stop, xslice.step]
                        if hasattr(yslice, "start"):
                            yslice = [yslice.start, yslice.stop, yslice.step]
                        self.__sendresults(
                            xx,
                            [float(e) for e in sx],
                            s1, xslice,
                            yy,
                            [float(e) for e in sy],
                            s2, yslice,
                            "sum" if self.__funindex else "mean"
                        )

        def __sendresults(self, xx, sx, xscale, xslice,
                          yy, sy, yscale, yslice, fun):
            """ send results to LavueController

            :param xx:  x's coordinates
            :type xx:  :obj:`list` <float>
            :param sx:  projection to x coordinate
            :type sx:  :obj:`list` <float>
            :param xscale:  x scale
            :type xscale:  :obj:`float`
            :param xslice:  x slice
            :type xslice:  :obj:`list` <float>
            :param yy:  y's coordinates
            :type yy:  :obj:`list` <float>
            :param sy:  projection to y coordinate
            :type sy:  :obj:`list` <float>
            :param yscale:  y scale
            :type yscale:  :obj:`float`
            :param yslice:  y slice
            :type yslice:  :obj:`list` <float>
            :param fun:  projection function name
            :type fun:  :obj:`str`
            """
            results = {"tool": self.alias}
            results["imagename"] = self._mainwidget.imageName()
            results["timestamp"] = time.time()
            results["xx"] = xx
            results["sx"] = sx
            results["xscale"] = xscale
            results["xslice"] = xslice
            results["yy"] = yy
            results["sy"] = sy
            results["yscale"] = yscale
            results["yslice"] = yslice
            results["function"] = fun
            self._mainwidget.writeAttribute(
                "ToolResults", json.dumps(results, cls=numpyEncoder))

        def __updateCompleter(self):
            """ updates the labelROI help
            """
            text = str(self.__ui.labelROILineEdit.text())
            sttext = text.strip()
            sptext = sttext.split()
            stext = ""
            if text.endswith(" "):
                stext = sttext
            elif len(sptext) > 1:
                stext = " ".join(sptext[:-1])

            if stext:
                if self.__aliases:
                    hints = ["%s %s" % (stext, al) for al in self.__aliases]
                else:
                    hints = [stext]
            else:
                hints = self.__aliases or []
            completer = QtGui.QCompleter(hints, self)
            self.__ui.labelROILineEdit.setCompleter(completer)

        @QtCore.pyqtSlot()
        def _roimessage(self):
            """ provides roi message
            """
            message = ""
            current = self._mainwidget.currentROI()
            coords = self._mainwidget.roiCoords()
            if current > -1 and current < len(coords):
                message = "%s" % coords[current]
            self.__setDisplayedText(message)

        def __setDisplayedText(self, text=None):
            """ sets displayed info text and recalculates the current roi sum

            :param text: text to display
            :type text: :obj:`str`
            """
            sroiVal = ""
            if text is not None:
                self.__lasttext = text
            else:
                text = self.__lasttext
            if self.__settings.showallrois:
                currentroi = self._mainwidget.currentROI()
                roiVals = self._mainwidget.calcROIsums()
                if roiVals is not None:
                    sroiVal = " / ".join(
                        [(("%g" % roiv) if roiv is not None else "?")
                         for roiv in roiVals])
            else:
                roiVal, currentroi = self._mainwidget.calcROIsum()
                if roiVal is not None:
                    sroiVal = "%.4f" % roiVal
            if currentroi is not None:
                self.updateROIDisplayText(text, currentroi, sroiVal)
            else:
                self.__ui.roiinfoLineEdit.setText(text)

        @QtCore.pyqtSlot()
        def _emitApplyROIPressed(self):
            """ emits applyROIPressed signal"""

            text = str(self.__ui.labelROILineEdit.text())
            roispin = int(self.__ui.roiSpinBox.value())
            self.applyROIPressed.emit(text, roispin)

        @QtCore.pyqtSlot()
        def _emitFetchROIPressed(self):
            """ emits fetchROIPressed signal"""
            text = str(self.__ui.labelROILineEdit.text())
            self.fetchROIPressed.emit(text)

        @QtCore.pyqtSlot(str)
        @QtCore.pyqtSlot()
        def _updateApplyButton(self):
            """ updates applied button"""
            stext = str(self.__ui.labelROILineEdit.text())
            self._mainwidget.roilabels = stext
            currentlength = len(stext)
            if not stext.strip():
                self.__ui.applyROIPushButton.setEnabled(False)
                self.__updateCompleter()
            else:
                self.__ui.applyROIPushButton.setEnabled(True)
            if stext.endswith(" ") or currentlength < self.__textlength:
                self.__updateCompleter()
            self.__textlength = currentlength

        @QtCore.pyqtSlot(str)
        def updateROILineEdit(self, text):
            """ updates ROI line edit text

            :param text: text to update
            :type text: :obj:`str`
            """
            if not self.__ui.labelROILineEdit.hasFocus():
                self.__ui.labelROILineEdit.setText(text)
                self._updateApplyButton()

        @QtCore.pyqtSlot(bool)
        def updateROIButton(self, enabled):
            """ enables/disables ROI buttons

            :param enable: buttons enabled
            :type enable: :obj:`bool`
            """
            # self.__ui.applyROIPushButton.setEnabled(enabled)
            # self.__ui.fetchROIPushButton.setEnabled(enabled)

        @QtCore.pyqtSlot(int)
        def updateApplyTips(self, rid):
            """ updates apply tips

            :param rid: current roi id
            :type rid: :obj:`int`
            """
            if rid < 0:
                self.__ui.applyROIPushButton.setToolTip(
                    "remove ROI aliases from the Door environment"
                    " as well as from Active MntGrp")
            else:
                self.__ui.applyROIPushButton.setToolTip(
                    "add ROI aliases to the Door environment "
                    "as well as to Active MntGrp")

        @QtCore.pyqtSlot(str, int, str)
        def updateROIDisplayText(self, text, currentroi, roiVal):
            """ updates ROI display text

            :param text: standard display text
            :type text: :obj:`str`
            :param currentroi: current roi label
            :type currentroi: :obj:`str`
            :param text: roi sum value
            :type text: :obj:`str`
            """

            roilabel = "roi [%s]" % (currentroi + 1)
            slabel = []

            rlabel = str(self.__ui.labelROILineEdit.text())
            if rlabel:
                slabel = re.split(';|,| |\n', rlabel)
                slabel = [lb for lb in slabel if lb]
            if slabel:
                roilabel = "%s [%s]" % (
                    slabel[currentroi]
                    if currentroi < len(slabel) else slabel[-1],
                    (currentroi + 1)
                )
            if "/" in roiVal:
                self.__ui.roiinfoLineEdit.setText(
                    "%s, %s; values = %s" % (text, roilabel, roiVal))
            else:
                self.__ui.roiinfoLineEdit.setText(
                    "%s, %s = %s" % (text, roilabel, roiVal))

        @QtCore.pyqtSlot(int)
        def setROIsNumber(self, rid):
            """sets a number of rois

            :param rid: number of rois
            :type rid: :obj:`int`
            """
            self.__ui.roiSpinBox.setValue(rid)
            # self._mainwidget.writeDetectorROIsAttribute()

        @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`
            """
            txdata = None
            if self._mainwidget.rangeWindowEnabled():
                txdata, tydata = self._mainwidget.scaledxy(
                    xdata, ydata, useraxes=False)
                if txdata is not None:
                    xdata = txdata
                    ydata = tydata
            self.__settings.centerx = float(xdata)
            self.__settings.centery = float(ydata)
            self._mainwidget.writeAttribute("BeamCenterX", float(xdata))
            self._mainwidget.writeAttribute("BeamCenterY", float(ydata))
            self._message()
            self.updateGeometryTip()
            self._mainwidget.emitTCC()

        @QtCore.pyqtSlot()
        def _message(self):
            """ provides geometry message
            """
            message = ""
            _, _, intensity, x, y = self._mainwidget.currentIntensity()
            if isinstance(intensity, float) and np.isnan(intensity):
                intensity = 0
            if isinstance(intensity, np.ndarray):
                intensity = np.nansum(
                    [0 if (isinstance(it, float) and np.isnan(it))
                     else it for it in intensity])
            if self._mainwidget.rangeWindowEnabled():
                txdata, tydata = self._mainwidget.scaledxy(
                    x, y, useraxes=False)
                if txdata is not None:
                    x = txdata
                    y = tydata
            ilabel = self._mainwidget.scalingLabel()
            if self.__gspaceindex == 0:
                thetax, thetay, thetatotal = self.__pixel2theta(x, y)
                if thetax is not None:
                    message = "th_x = %f deg, th_y = %f deg," \
                              " th_tot = %f deg, %s = %.2f" \
                              % (thetax * 180 / math.pi,
                                 thetay * 180 / math.pi,
                                 thetatotal * 180 / math.pi,
                                 ilabel, intensity)
            else:
                qx, qy, q = self.__pixel2q(x, y)
                if qx is not None:
                    message = u"q_x = %f 1/\u212B, q_y = %f 1/\u212B, " \
                              u"q = %f 1/\u212B, %s = %.2f" \
                              % (qx, qy, q, ilabel, intensity)

            self._mainwidget.updateDisplayedText(message)

        def __pixel2theta(self, xdata, ydata):
            """ converts coordinates from pixel positions to theta angles

            :param xdata: x pixel position
            :type xdata: :obj:`float`
            :param ydata: y-pixel position
            :type ydata: :obj:`float`
            :returns: x-theta, y-theta, total-theta
            :rtype: (:obj:`float`, :obj:`float`, :obj:`float`)
            """
            thetax = None
            thetay = None
            thetatotal = None
            if self.__settings.energy > 0 and self.__settings.detdistance > 0:
                xcentered = xdata - self.__settings.centerx
                ycentered = ydata - self.__settings.centery
                thetax = math.atan(
                    xcentered * self.__settings.pixelsizex / 1000.
                    / self.__settings.detdistance)
                thetay = math.atan(
                    ycentered * self.__settings.pixelsizey / 1000.
                    / self.__settings.detdistance)
                r = math.sqrt(
                    (xcentered * self.__settings.pixelsizex / 1000.) ** 2
                    + (ycentered * self.__settings.pixelsizey / 1000.) ** 2)
                thetatotal = math.atan(
                    r / self.__settings.detdistance)
            return thetax, thetay, thetatotal

        def __pixel2q(self, xdata, ydata):
            """ converts coordinates from pixel positions to q-space coordinates

            :param xdata: x pixel position
            :type xdata: :obj:`float`
            :param ydata: y-pixel position
            :type ydata: :obj:`float`
            :returns: q_x, q_y, q_total
            :rtype: (:obj:`float`, :obj:`float`, :obj:`float`)
            """
            qx = None
            qy = None
            q = None
            if self.__settings.energy > 0 and self.__settings.detdistance > 0:
                thetax, thetay, thetatotal = self.__pixel2theta(
                    xdata, ydata)
                wavelength = 12398.4193 / self.__settings.energy
                qx = 4 * math.pi / wavelength * math.sin(thetax/2.)
                qy = 4 * math.pi / wavelength * math.sin(thetay/2.)
                q = 4 * math.pi / wavelength * math.sin(thetatotal/2.)
            return qx, qy, q

        def __tipmessage(self):
            """ provides geometry messate

            :returns: geometry text
            :rtype: :obj:`unicode`
            """

            return u"geometry:\n" \
                u"  center = (%s, %s) pixels\n" \
                u"  pixel_size = (%s, %s) \u00B5m\n" \
                u"  detector_distance = %s mm\n" \
                u"  energy = %s eV" % (
                    self.__settings.centerx,
                    self.__settings.centery,
                    self.__settings.pixelsizex,
                    self.__settings.pixelsizey,
                    self.__settings.detdistance,
                    self.__settings.energy
                )

        @QtCore.pyqtSlot()
        def _setGeometry(self):
            """ launches geometry widget

            :returns: apply status
            :rtype: :obj:`bool`
            """
            cnfdlg = geometryDialog.GeometryDialog()
            cnfdlg.centerx = self.__settings.centerx
            cnfdlg.centery = self.__settings.centery
            cnfdlg.energy = self.__settings.energy
            cnfdlg.pixelsizex = self.__settings.pixelsizex
            cnfdlg.pixelsizey = self.__settings.pixelsizey
            cnfdlg.detdistance = self.__settings.detdistance
            cnfdlg.createGUI()
            if cnfdlg.exec_():
                self.__settings.centerx = cnfdlg.centerx
                self.__settings.centery = cnfdlg.centery
                self.__settings.energy = cnfdlg.energy
                self.__settings.pixelsizex = cnfdlg.pixelsizex
                self.__settings.pixelsizey = cnfdlg.pixelsizey
                self.__settings.detdistance = cnfdlg.detdistance
                self.__settings.updateAISettings()
                self._mainwidget.writeDetectorAttributes()
                self.updateGeometryTip()
                self._mainwidget.updateCenter(
                    self.__settings.centerx, self.__settings.centery)

        # @debugmethod
        @QtCore.pyqtSlot()
        def _updateGeometry(self, geometry):
            """ update geometry widget

            :param geometry: geometry dictionary
            :type geometry: :obj:`dict` < :obj:`str`, :obj:`list`>
            """
            try:
                if "centerx" in geometry.keys():
                    self.__settings.centerx = float(geometry["centerx"])
                    self._mainwidget.writeAttribute(
                        "BeamCenterX", float(self.__settings.centerx))
            except Exception:
                pass
            try:
                if "centery" in geometry.keys():
                    self.__settings.centery = float(geometry["centery"])
                    self._mainwidget.writeAttribute(
                        "BeamCenterY", float(self.__settings.centery))
            except Exception:
                pass
            try:
                if "energy" in geometry.keys():
                    self.__settings.energy = float(geometry["energy"])
                    self._mainwidget.writeAttribute(
                        "Energy", float(self.__settings.energy))
            except Exception:
                pass
            try:
                if "pixelsizex" in geometry.keys():
                    self.__settings.pixelsizex = float(geometry["pixelsizex"])
            except Exception:
                pass
            try:
                if "pixelsizey" in geometry.keys():
                    self.__settings.pixelsizey = float(geometry["pixelsizey"])
            except Exception:
                pass
            try:
                if "detdistance" in geometry.keys():
                    self.__settings.detdistance = float(geometry["detdistance"])
                self._mainwidget.writeAttribute(
                    "DetectorDistance",
                    float(self.__settings.detdistance))
            except Exception:
                pass
            if geometry:
                self.updateGeometryTip()
                self._mainwidget.updateCenter(
                    self.__settings.centerx, self.__settings.centery)
                self._mainwidget.emitTCC()

        @QtCore.pyqtSlot(int)
        def _setGSpaceIndex(self, gindex):
            """ set gspace index

            :param gspace: g-space index, i.e. angle or q-space
            :type gspace: :obj:`int`
            """
            self.__gspaceindex = gindex

        @QtCore.pyqtSlot()
        def updateGeometryTip(self):
            """ update geometry tips
            """
            message = self.__tipmessage()
            self._mainwidget.updateDisplayedTextTip(
                "coordinate info display for the mouse pointer\n%s"
                % message)
            self.__ui.angleqPushButton.setToolTip(
                "Input physical parameters\n%s" % message)
            self.__ui.angleqComboBox.setToolTip(
                "Select the display space\n%s" % message)
            self.__ui.toolLabel.setToolTip(
                "coordinate info display for the mouse pointer\n%s" % message)

    #: ( :obj:`dict` < :obj:`str`, any > ) tool widget properties
    twproperties = []
    for nm in __all__:
        if nm.endswith("ToolWidget"):
            cl = globals()[nm]
            twproperties.append(
                {
                    'alias': cl.alias,
                    'name': cl.name,
                    'widget': nm,
                    'requires': cl.requires,
                })