normalization.py 8.76 KB
Newer Older
payno's avatar
payno committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# coding: utf-8
# /*##########################################################################
#
# Copyright (c) 2016-2017 European Synchrotron Radiation Facility
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#
# ###########################################################################*/

__authors__ = ["H. Payno"]
__license__ = "MIT"
28
__date__ = "05/10/2021"
payno's avatar
payno committed
29
30


31
import logging
payno's avatar
payno committed
32
from Orange.widgets import gui
payno's avatar
payno committed
33
34
35
from PyMca5.PyMcaGui.physics.xas.XASNormalizationParameters import (
    XASNormalizationParameters,
)
36
37
from silx.gui import qt
from silx.gui.plot import LegendSelector
payno's avatar
payno committed
38
39
40
41
42
43
from est.gui.XasObjectViewer import (
    _plot_edge,
    _plot_norm,
    _plot_post_edge,
    _plot_pre_edge,
)
payno's avatar
payno committed
44
from orangecontrib.est.process import _ProcessForOrangeMixIn
45
from orangecontrib.est.widgets.container import _ParameterWindowContainer
Henri Payno's avatar
Henri Payno committed
46

47
48
49
from est.gui.e0calculator import E0CalculatorDialog
from est.gui.XasObjectViewer import XasObjectViewer, ViewType
import est.core.process.pymca.normalization
50
from ewokscore.hashing import MissingData
payno's avatar
payno committed
51

payno's avatar
payno committed
52
53
_logger = logging.getLogger(__file__)

54

55
56
57
58
59
60
61
62
63
64
65
66
67
class _XASNormalizationParametersPatched(XASNormalizationParameters):
    """This class will try to patch the XASNormalizationParameters with the
    E0Calculation widget"""

    sigE0CalculationRequested = qt.Signal()
    """Signal emitted when E0 computation is required"""

    def __init__(self, *args, **kwargs):
        XASNormalizationParameters.__init__(self, *args, **kwargs)
        # add E0CalculationWidget if can
        try:
            self.__addE0CalculationDialog()
        except Exception as e:
payno's avatar
payno committed
68
            _logger.warning("Fail to add the E0CalculationDialog. Reason is", str(e))
69
70
71
72
73
74
        else:
            self._e0CalcPB.pressed.connect(self._launchE0Calculator)

    def setE0(self, e0):
        e0_min = self.e0SpinBox.minimum()
        e0_max = self.e0SpinBox.maximum()
payno's avatar
payno committed
75
76
77
78
79
        if not (e0_min <= e0 <= e0_max):
            _logger.warning(
                "given e0 value (%s) is invalid, value should be "
                "between %s and %s" % (e0, e0_min, e0_max)
            )
80
81
82

        params = self.getParameters()
        params["E0Value"] = e0
payno's avatar
payno committed
83
        params["E0Method"] = "Manual"
84
85
86
87
        self.setParameters(ddict=params, signal=False)

    def __addE0CalculationDialog(self):
        # check we know where to set the button
payno's avatar
payno committed
88
        for attr in ("e0SpinBox", "jumpLine", "e0CheckBox"):
89
            if not hasattr(self, attr):
payno's avatar
payno committed
90
91
92
                raise NameError(
                    "%s not defined - pymca version not " "recognized" % attr
                )
93
94
        for widget, widget_index in zip((self.e0SpinBox, self.jumpLine), (3, 5)):
            if self.layout().indexOf(widget) != widget_index:
payno's avatar
payno committed
95
                raise ValueError("XASNormalizationParameters layout is not recognized.")
96
97
98

        style = qt.QApplication.instance().style()
        icon = style.standardIcon(qt.QStyle.SP_FileDialogContentsView)
payno's avatar
payno committed
99
        self._e0CalcPB = qt.QPushButton(icon, "", self)
100
101
102
103
104
105
        self.layout().addWidget(self._e0CalcPB, 1, 2)

    def _launchE0Calculator(self, *args, **kwargs):
        self.sigE0CalculationRequested.emit()


payno's avatar
payno committed
106
class NormalizationWindow(qt.QMainWindow):
107
108
109
110
111
112
    """Widget embedding the pymca parameter window and the display of the
    data currently process"""

    sigE0CalculationRequested = qt.Signal()
    """Signal emitted when E0 computation is required"""

payno's avatar
payno committed
113
114
    def __init__(self, parent=None):
        qt.QMainWindow.__init__(self, parent)
115
116

        # xas object viewer
payno's avatar
payno committed
117
        mapKeys = ["mu", "NormalizedMu", "NormalizedSignal", "NormalizedBackground"]
118
        self.xasObjViewer = XasObjectViewer(mapKeys=mapKeys)
payno's avatar
payno committed
119
120
121
        self.xasObjViewer._spectrumViews[0]._plotWidget.getXAxis().setLabel(
            "Energy (eV)"
        )
122
        self.xasObjViewer._spectrumViews[0]._plotWidget.getYAxis().setLabel(
payno's avatar
payno committed
123
124
            "Absorption (a.u.)"
        )
125
        self.setCentralWidget(self.xasObjViewer)
payno's avatar
payno committed
126
127
128
        self._pymcaWindow = _ParameterWindowContainer(
            parent=self, parametersWindow=_XASNormalizationParametersPatched
        )
payno's avatar
payno committed
129
130
131
132
133
134
135
136
        dockWidget = qt.QDockWidget(parent=self)

        # pymca window
        dockWidget.setWidget(self._pymcaWindow)
        self.addDockWidget(qt.Qt.RightDockWidgetArea, dockWidget)
        dockWidget.setAllowedAreas(qt.Qt.RightDockWidgetArea | qt.Qt.LeftDockWidgetArea)
        dockWidget.setFeatures(qt.QDockWidget.NoDockWidgetFeatures)

137
        # legend selector
payno's avatar
payno committed
138
        self.legendDockWidget = LegendSelector.LegendsDockWidget(
139
            parent=self, plot=self.xasObjViewer._spectrumViews[0]._plotWidget
payno's avatar
payno committed
140
141
142
143
        )
        self.legendDockWidget.setAllowedAreas(
            qt.Qt.RightDockWidgetArea | qt.Qt.LeftDockWidgetArea
        )
payno's avatar
payno committed
144
145
146
        self.legendDockWidget.setFeatures(qt.QDockWidget.NoDockWidgetFeatures)
        self.addDockWidget(qt.Qt.RightDockWidgetArea, self.legendDockWidget)

147
        # volume key selection
payno's avatar
payno committed
148
149
150
        self.addDockWidget(
            qt.Qt.RightDockWidgetArea, self.xasObjViewer._mapView.keySelectionDocker
        )
payno's avatar
payno committed
151

152
        # plot settings
153
        for ope in (_plot_edge, _plot_norm, _plot_post_edge, _plot_pre_edge):
154
            self.xasObjViewer._spectrumViews[0].addCurveOperation(ope)
155

156
157
        self.setWindowFlags(qt.Qt.Widget)

158
159
160
        # expose API
        self.setE0 = self._pymcaWindow._mainwidget.setE0

161
162
163
164
165
166
        # connect signal / slot
        self.xasObjViewer.viewTypeChanged.connect(self._updateLegendView)

        # set up
        self._updateLegendView()

167
    def getNCurves(self):
168
        return len(self.xasObjViewer._spectrumViews._plot.getAllCurves())
169
170

    def _updateLegendView(self):
171
172
        index, viewType = self.xasObjViewer.getViewType()
        self.legendDockWidget.setVisible(viewType is ViewType.spectrum)
payno's avatar
payno committed
173
174
175
        self.xasObjViewer._mapView.keySelectionDocker.setVisible(
            viewType is ViewType.map
        )
176

payno's avatar
payno committed
177

178
179
class NormalizationOW(
    _ProcessForOrangeMixIn,
180
    ewokstaskclass=est.core.process.pymca.normalization.PyMca_normalization,
181
):
payno's avatar
payno committed
182
183
184
    """
    Widget used for signal extraction
    """
payno's avatar
payno committed
185

payno's avatar
payno committed
186
    name = "normalization"
payno's avatar
payno committed
187
    id = "orange.widgets.est.pymca.normalization"
188
    description = "Progress spectra normalization"
payno's avatar
payno committed
189
    icon = "icons/normalization.png"
190
    priority = 1
payno's avatar
payno committed
191
192
193
194
195
196
197
    category = "esrfWidgets"
    keywords = ["spectroscopy", "normalization"]

    want_main_area = True
    resizing_enabled = True

    def __init__(self):
198
        super().__init__()
payno's avatar
payno committed
199
        self._window = NormalizationWindow(parent=self)
payno's avatar
payno committed
200
        layout = gui.vBox(self.mainArea, "normalization").layout()
payno's avatar
payno committed
201
        layout.addWidget(self._window)
payno's avatar
payno committed
202
        self._window.xasObjViewer.setWindowTitle("spectra")
payno's avatar
payno committed
203

204
205
206
207
        norm_params = self.task_input_values.get("normalization", MissingData)
        if norm_params is not MissingData:
            self._window._pymcaWindow.setParameters(norm_params)

208
209
210
        # expose API
        self.setE0 = self._window.setE0

211
        # connect signals / slots
212
        pymcaWindowContainer = self._window._pymcaWindow
payno's avatar
PEP8    
payno committed
213
        pymcaWindowContainer.sigChanged.connect(self._updateProcess)
payno's avatar
payno committed
214
215
216
217
        if hasattr(pymcaWindowContainer._mainwidget, "sigE0CalculationRequested"):
            pymcaWindowContainer._mainwidget.sigE0CalculationRequested.connect(
                self._getE0FrmDialog
            )
218

219
    def _updateProcess(self):
220
221
222
        self.receiveDynamicInputs(
            "normalization", self._window._pymcaWindow.getParameters()
        )
223
        self.handleNewSignals()
224

225
    def task_input_changed(self):
226
227
228
229
230
        if "xas_obj" in self.task_inputs:
            xas_obj = self.task_inputs["xas_obj"]
            if "e0" in xas_obj.configuration:
                self.setE0(xas_obj.configuration["e0"])

231
232
233
234
235
236
237
    def _getE0FrmDialog(self):
        """Pop up an instance of E0CalculationDialog and get E0 from it"""
        if self._latest_xas_obj is None:
            return
        dialog = E0CalculatorDialog(xas_obj=self._latest_xas_obj, parent=None)
        if dialog.exec_():
            self.setE0(dialog.getE0())