mback.py 9.99 KB
Newer Older
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
28
29
30
31
32
33
34
# 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"
__date__ = "06/07/2019"


import functools
import logging
from Orange.widgets import gui
from Orange.widgets.settings import Setting
35
from Orange.widgets.widget import OWWidget
36
37
from Orange.widgets.widget import Input, Output
import Orange.data
38
39
from silx.gui import qt
from silx.gui.plot import LegendSelector
payno's avatar
payno committed
40
41
42
43
44
45
46
47
48
import est.core.process.larch.mback
from orangecontrib.est.process import _ProcessForOrangeMixIn
from est.core.types import XASObject
from est.gui.XasObjectViewer import XasObjectViewer, ViewType
from est.gui.larch.mback import _MBackParameters
from orangecontrib.est.progress import QProgress
from est.gui.XasObjectViewer import _plot_norm, _plot_raw, _plot_fpp, _plot_f2
from orangecontrib.est.utils import Converter
from orangecontrib.est.widgets.container import _ParameterWindowContainer
49
50
51

_logger = logging.getLogger(__file__)

52
_USE_THREAD = False
53

54
55
56
57
58
59

class MbackWindow(qt.QMainWindow):
    def __init__(self, parent=None):
        qt.QMainWindow.__init__(self, parent)

        # xas object viewer
payno's avatar
payno committed
60
        mapKeys = ["mu", "fpp", "f2"]
61
        self.xasObjViewer = XasObjectViewer(mapKeys=mapKeys)
payno's avatar
payno committed
62
63
64
        self.xasObjViewer._spectrumViews[0]._plotWidget.getXAxis().setLabel(
            "Energy (eV)"
        )
65
        self.setCentralWidget(self.xasObjViewer)
payno's avatar
payno committed
66
67
68
        self._parametersWindow = _ParameterWindowContainer(
            parent=self, parametersWindow=_MBackParameters
        )
69
70
71
72
73
74
75
76
77
        dockWidget = qt.QDockWidget(parent=self)

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

        # legend selector
payno's avatar
payno committed
78
        self.legendDockWidget = LegendSelector.LegendsDockWidget(
79
            parent=self, plot=self.xasObjViewer._spectrumViews[0]._plotWidget
payno's avatar
payno committed
80
81
82
83
        )
        self.legendDockWidget.setAllowedAreas(
            qt.Qt.RightDockWidgetArea | qt.Qt.LeftDockWidgetArea
        )
84
85
86
87
        self.legendDockWidget.setFeatures(qt.QDockWidget.NoDockWidgetFeatures)
        self.addDockWidget(qt.Qt.RightDockWidgetArea, self.legendDockWidget)

        # volume key selection
payno's avatar
payno committed
88
89
90
        self.addDockWidget(
            qt.Qt.RightDockWidgetArea, self.xasObjViewer._mapView.keySelectionDocker
        )
91
92

        # plot settings
93
        for ope in (_plot_fpp, _plot_f2, _plot_raw, _plot_norm):
94
95
96
97
98
99
100
101
102
103
104
105
106
            self.xasObjViewer._spectrumViews[0].addCurveOperation(ope)

        self.setWindowFlags(qt.Qt.Widget)

        # connect signal / slot
        self.xasObjViewer.viewTypeChanged.connect(self._updateLegendView)

        # set up
        self._updateLegendView()

    def _updateLegendView(self):
        index, viewType = self.xasObjViewer.getViewType()
        self.legendDockWidget.setVisible(viewType is ViewType.spectrum)
payno's avatar
payno committed
107
108
109
        self.xasObjViewer._mapView.keySelectionDocker.setVisible(
            viewType is ViewType.map
        )
110

111
    def getNCurves(self):
112
        return len(self.xasObjViewer._spectrumViews[0]._plotWidget.getAllCurves())
113

114

115
class MbackOW(_ProcessForOrangeMixIn, OWWidget):
116
117
118
    """
    Widget used for signal extraction
    """
payno's avatar
payno committed
119

120
    name = "mback"
payno's avatar
payno committed
121
    id = "orange.widgets.est.larch.mback.MbackOW"
payno's avatar
payno committed
122
123
124
125
    description = (
        "Match mu(E) data for tabulated f''(E) using the MBACK "
        "algorithm and, optionally, the Lee & Xiang extension"
    )
126
    icon = "icons/mback.svg"
127
128
    priority = 5
    category = "esrfWidgets"
129
    keywords = ["spectroscopy", "mback"]
130
131
132

    want_main_area = True
    resizing_enabled = True
133
    allows_cycle = False
134
135

    _larchSettings = Setting(dict())
136
137
    # kept for compatibility
    static_input = Setting({"mback": None})
138
139
    """Store the configuration of the larch configuration"""

140
141
    ewokstaskclass = est.core.process.larch.mback.Larch_mback

142
    class Inputs:
payno's avatar
payno committed
143
        xas_obj = Input("xas_obj", XASObject, default=True)
144
145
        # simple compatibility for some Orange widget and especialy the
        # 'spectroscopy add-on'
payno's avatar
payno committed
146
        data_table = Input("Data", Orange.data.Table)
147
148

    class Outputs:
149
        xas_obj = Output("xas_obj", XASObject)
150
151
152
        # by default we want to avoid sending 'Orange.data.Table' to avoid
        # loosing the XASObject flow process and results.

153
154
155
156
    def __init__(self):
        super().__init__()
        self._latest_xas_obj = None
        self._window = MbackWindow(parent=self)
payno's avatar
payno committed
157
        layout = gui.vBox(self.mainArea, "mback").layout()
158
        layout.addWidget(self._window)
payno's avatar
payno committed
159
        self._window.xasObjViewer.setWindowTitle("spectra")
160
161

        # manage settings
162
163
164
165
166
        larch_settings = self.static_input.get("mback", None)
        if larch_settings is None:
            larch_settings = self._larchSettings
        if larch_settings != dict():
            self._window._parametersWindow.setParameters(larch_settings)
167
168
169
170

        # connect signals / slots
        self._window._parametersWindow.sigChanged.connect(self._updateProcess)

171
172
        # required to display advancement
        if _USE_THREAD is False:
payno's avatar
payno committed
173
            self._advancement = QProgress("mback")
174
175
            self._advancement.sigProgress.connect(self._setProgressValue)

176
        # set up (insure settings will be store
177
        self._update_settings()
178
        # self.handleNewSignals()
179

180
181
182
183
184
185
186
    def _updateProcess(self):
        self._update_settings()
        if self._latest_xas_obj:
            self.process(self._latest_xas_obj)

    def _update_settings(self):
        self._larchSettings = self._window._parametersWindow.getParameters()
187
188
189
190
        self.static_input = {
            "mback": self._window._parametersWindow.getParameters(),
            "xas_obj": None,
        }
191

192
193
194
195
196
197
198
    @Inputs.data_table
    def processFrmDataTable(self, data_table):
        if data_table is None:
            return
        self.process(Converter.toXASObject(data_table=data_table))

    @Inputs.xas_obj
199
200
201
202
203
    def process(self, xas_obj):
        if xas_obj is None:
            return

        if not self._canProcess():
payno's avatar
payno committed
204
205
206
207
            _logger.warning(
                "There is some processing on going already, will"
                "not process the new dataset"
            )
208

209
        self._latest_xas_obj = xas_obj
210
211
212
        self._startProcess()

        # setup the normalization process
213
        inputs = {"xas_obj": xas_obj}
214
215
216
        if _USE_THREAD is True:
            # note: for now with larch with cannot do thread computation (see
            # PreEdgeOW )
217
            process_obj = QLarch_mback(inputs=inputs)
218
            process_obj._advancement.sigProgress.connect(self._setProgressValue)
219
            process_obj.set_properties(
payno's avatar
payno committed
220
221
                {"_larchSettings": self._window._parametersWindow.getParameters()}
            )
222
223
224
            # update the processing thread
            thread = self.getProcessingThread()
            thread.init(process_obj=process_obj, xas_obj=self._latest_xas_obj)
payno's avatar
payno committed
225
226
227
            self._callback_finish = functools.partial(
                self._endProcess, self._latest_xas_obj
            )
228
229
230
            thread.finished.connect(self._callback_finish)
            # start processing
            thread.start(priority=qt.QThread.LowPriority)
231
232
233
234
        else:
            # manage advancement
            self._advancement.setAdvancement(0)
            self._advancement.setMaxSpectrum(self._latest_xas_obj.n_spectrum)
235
            process_obj = est.core.process.larch.mback.Larch_mback(inputs=inputs)
236
            process_obj.advancement = self._advancement
237
            process_obj.set_properties(
payno's avatar
payno committed
238
239
                {"_larchSettings": self._window._parametersWindow.getParameters()}
            )
240
241
242
243
            # hack (reason explained in the thread part)
            # to avoid gui freeze, processevents after each spectrum process
            process_obj.addCallback(qt.QApplication.instance().processEvents)
            process_obj.addCallback(self._advancement.increaseAdvancement)
244
            process_obj.run()
245
246
            self._callback_finish = None
            self._endProcess(self._latest_xas_obj)
247
248


payno's avatar
payno committed
249
class QLarch_mback(est.core.process.larch.mback.Larch_mback):
250
251
252
253
    """
    Normalization able to give advancement using qt.Signal and QThreadPool
    """

254
255
    def __init__(self, *args, **kwargs):
        est.core.process.larch.mback.Larch_mback.__init__(self, *args, **kwargs)
payno's avatar
payno committed
256
        self._advancement = QProgress("mback")
257
258
259
260
261

    def _pool_process(self, xas_obj):
        self.pool = qt.QThreadPool()
        self.pool.setMaxThreadCount(5)
        for spectrum in xas_obj.spectra:
payno's avatar
payno committed
262
263
264
265
266
267
            runnable = ProcessRunnable(
                fct=est.core.process.larch.mback.process_spectr_mback,
                spectrum=spectrum,
                configuration=xas_obj.configuration,
                callback=self._advancement.increaseAdvancement,
            )
268
269
            self.pool.start(runnable)
        self.pool.waitForDone()