Commit bbc69af9 authored by payno's avatar payno

[orangecontrib] add widget to compute E0.

- add orangecontrib widget e0calculation
- pymca and larch widgets are now sensible to any 'e0' defnition from XASObject configuration.
- E0 calculator now reset the roi by default when a new xas object is given.
parent 287f444b
Pipeline #18579 failed with stage
in 73 minutes and 20 seconds
......@@ -68,6 +68,9 @@ def process_spectr_norm(spectrum, configuration, overwrite=True, callbacks=None,
pymca_xas.setSpectrum(energy=spectrum.energy,
mu=spectrum.mu)
if configuration is not None:
if 'e0' in configuration:
configuration["E0Value"] = configuration['e0']
configuration["E0Method"] = 'Manual'
pymca_xas.setConfiguration(configuration)
configuration = pymca_xas.getConfiguration()
try:
......
# 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/11/2019"
from est.core.types import XASObject, Spectrum
from .process import Process
import logging
_logger = logging.getLogger(__name__)
def _xas_set_config(xas_obj):
"""
Copy process configuration to object
:param xas_obj: object containing the configuration and spectra to process
:type: Union[:class:`.XASObject`, dict]
:return: spectra dict
:rtype: :class:`.XASObject`
"""
xas_set_config = _SetConfigProcess()
return xas_set_config.process(xas_obj=xas_obj)
class _SetConfigProcess(Process):
def __init__(self):
Process.__init__(self, name='set_config')
def process(self, xas_obj):
"""
:param xas_obj: object containing the configuration and spectra to process
:type: Union[:class:`.XASObject`, dict]
:return: spectra dict
:rtype: :class:`.XASObject`
"""
assert xas_obj is not None
_xas_obj = self.getXasObject(xas_obj=xas_obj)
conf_obj = _xas_obj.configuration
conf_process = self.getConfiguration()
for key, value in conf_process.items():
conf_obj[key] = value
_xas_obj.configuration = conf_obj
return _xas_obj
......@@ -38,19 +38,22 @@ import tempfile
import os
import shutil
from silx.utils.enum import Enum
_logger = logging.getLogger(__name__)
try:
import larch
except ImportError:
_Spectrum_Base = object
class _Spectrum_Base(object):
pass
_has_larch = False
_logger.info('larch not found')
else:
from larch.symboltable import Group
_Spectrum_Base = Group
from larch.symboltable import Group as LarchGroup
class _Spectrum_Base(LarchGroup):
pass
from est.core.utils import larchutils
_has_larch = True
_logger = logging.getLogger(__name__)
_logger.info('larch found')
class Dim(Enum):
......@@ -681,7 +684,7 @@ class Spectrum(_Spectrum_Base):
_Y_POS_KEY = 'YPos'
def __init__(self, energy=None, mu=None, x=None, y=None):
_Spectrum_Base.__init__(self)
super().__init__()
if energy is not None:
assert isinstance(energy, numpy.ndarray)
......
......@@ -105,14 +105,13 @@ class E0Calculator(qt.QMainWindow):
"""Signal emitted when E0 is changing"""
def __init__(self, parent=None, xas_obj=None):
assert isinstance(xas_obj, XASObject)
self._xas_obj = xas_obj
assert isinstance(xas_obj, (XASObject, type(None)))
self._xas_obj = None
qt.QMainWindow.__init__(self, parent=parent)
# map view
self._muView = MapViewer(parent=self, keys=('Mu',))
self._muView.menuBar().hide()
self._muView.setXasObject(xas_obj=self._xas_obj)
self._muView.keySelectionDocker.hide()
# for now we don't wan't to manage the roi depending on the dimension.
self._muView.setPerspectiveVisible(False)
......@@ -124,7 +123,7 @@ class E0Calculator(qt.QMainWindow):
self._roiManager.setColor('red') # Set the color of ROI
self._roi = RectangleROI()
self._roi.setGeometry(origin=(0, 0), size=(xas_obj.dim2, xas_obj.dim1))
self._updateRoi(xas_obj=xas_obj)
self._roi.setName('ROI')
self._roi.setEditable(True)
self._roiManager.addRoi(self._roi)
......@@ -147,9 +146,34 @@ class E0Calculator(qt.QMainWindow):
self._muView.sigFrameChanged.connect(functools.partial(self._updateE0, 'frame'))
self._e0CalculationWidget.sigComputationScaleChanged.connect(functools.partial(self._updateE0, 'scale'))
# set up
if xas_obj is not None:
self.setXasObject(xas_obj=xas_obj)
def setXasObject(self, xas_obj, update_roi=True):
"""
set XasObject to the E0 calculator
:param xas_obj: dataset
:param bool update_roi: if True, fir the ROI to the XAS map
"""
self._xas_obj = xas_obj
self._muView.setXasObject(xas_obj=self._xas_obj)
if update_roi:
self._updateRoi(self._xas_obj)
# compute E0 for the current roi
self._updateE0(origin='init')
def getXasObject(self):
return self._xas_obj
def _updateRoi(self, xas_obj):
if xas_obj is not None:
dim_1, dim_2 = xas_obj.dim1, xas_obj.dim2
else:
dim_1, dim_2 = 100, 100
self._roi.setGeometry(origin=(0, 0), size=(dim_2, dim_1))
def _updateE0(self, origin):
"""
......
......@@ -189,6 +189,11 @@ class PreEdgeOW(_ProcessForOrangeMixIn, OWWidget):
'not process the new dataset')
self._latest_xas_obj = xas_obj
# e0 can be defined from the flow previously
if 'e0' in xas_obj.configuration:
conf = {'e0': xas_obj.configuration['e0']}
self._window._parametersWindow.setParameters(conf)
self._startProcess()
# setup the normalization process
......
......@@ -76,7 +76,7 @@ class _XASNormalizationParametersPatched(XASNormalizationParameters):
e0_min = self.e0SpinBox.minimum()
e0_max = self.e0SpinBox.maximum()
if not ( e0_min <= e0 <= e0_max):
_logger.warning('given e0 value (e0) is invalid, value should be '
_logger.warning('given e0 value (%s) is invalid, value should be '
'between %s and %s' % (e0, e0_min, e0_max))
params = self.getParameters()
......@@ -234,12 +234,14 @@ class NormalizationOW(_ProcessForOrangeMixIn, OWWidget):
def process(self, xas_obj):
if xas_obj is None:
return
print('xas obj received x is %s' % xas_obj.spectra[0].x)
if not self._canProcess():
_logger.warning('There is some processing on going already, will'
'not process the new dataset')
self._latest_xas_obj = xas_obj
# update E0 if necessary
if 'e0' in xas_obj.configuration:
self.setE0(xas_obj.configuration['e0'])
self._startProcess()
# setup the normalization process
......
# 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__ = "02/10/2018"
import logging
from Orange.widgets import gui
from Orange.widgets.widget import OWWidget
from Orange.widgets.widget import Input, Output
import Orange.data
from orangecontrib.est.utils import Converter
from silx.gui import qt
import functools
import est.core.process.setconfig
from est.core.types import XASObject
from est.gui.e0calculator import E0Calculator, E0ComputationMethod
_logger = logging.getLogger(__file__)
class RoiSelectionOW(OWWidget):
"""
Widget used to make compute E0 from the dataset.
"""
name = "e0 calculator"
id = "orange.widgets.xas.utils.e0calculator"
description = "Compute E0 from a region of the dataset"
icon = "icons/e0.svg"
priority = 25
category = "esrfWidgets"
keywords = ['E0', 'Energy', "dataset", "data", "selection", "ROI",
"Region of Interest"]
process_function = est.core.process.setconfig._xas_set_config
want_main_area = True
resizing_enabled = True
compress_signal = False
class Inputs:
xas_obj = Input('xas_obj', XASObject, default=True)
# simple compatibility for some Orange widget and especialy the
# 'spectroscopy add-on'
data_table = Input('Data', Orange.data.Table)
class Outputs:
res_xas_obj = Output('xas_obj', XASObject)
# by default we want to avoid sending 'Orange.data.Table' to avoid
# loosing the XASObject flow process and results.
def __init__(self):
super().__init__()
self._widget = E0Calculator(parent=self)
layout = gui.vBox(self.mainArea, 'data selection').layout()
layout.addWidget(self._widget)
# add the buttons
style = qt.QApplication.instance().style()
icon = style.standardIcon(qt.QStyle.SP_DialogApplyButton)
self._buttons = qt.QDialogButtonBox(parent=self)
self._useMedian = qt.QPushButton(icon, 'use median', self)
self._buttons.addButton(self._useMedian, qt.QDialogButtonBox.ActionRole)
self._useMean = qt.QPushButton(icon, 'use mean', self)
self._buttons.addButton(self._useMean, qt.QDialogButtonBox.ActionRole)
layout.addWidget(self._buttons)
# connect signal / slot
self._useMean.released.connect(functools.partial(self.validateMethodToUse, E0ComputationMethod.MEAN))
self._useMedian.released.connect(functools.partial(self.validateMethodToUse, E0ComputationMethod.MEDIAN))
# set up
self._buttons.hide()
def validateMethodToUse(self, method):
"""Define the method to use and close the dialog"""
method = E0ComputationMethod.from_value(method)
assert method in (
None, E0ComputationMethod.MEDIAN, E0ComputationMethod.MEAN)
self._methodToUse = method
self.validate()
def getE0(self):
if self._methodToUse is None:
return None
else:
return self._widget.getE0(method=self._methodToUse)
@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
def process(self, xas_obj):
if xas_obj is None:
return
self._widget.setXasObject(xas_obj=xas_obj)
self._buttons.show()
self.show()
def validate(self):
"""
callback when the ROI has been validated
"""
if self._widget.getXasObject() is None:
return
try:
xas_obj = self._widget.getXasObject()
prop = xas_obj.configuration
prop['e0'] = self.getE0()
xas_obj.configuration = prop
_logger.info('e0 define: ', str(self.getE0()))
self.Outputs.res_xas_obj.send(xas_obj)
except Exception as e:
_logger.error(e)
else:
OWWidget.accept(self)
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="48"
height="48"
viewBox="0 0 12.7 12.7"
version="1.1"
id="svg8"
inkscape:version="0.92.4 (5da689c313, 2019-01-14)"
sodipodi:docname="e0.svg">
<defs
id="defs2" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="2.8"
inkscape:cx="16.73825"
inkscape:cy="70.494563"
inkscape:document-units="mm"
inkscape:current-layer="text817"
showgrid="false"
units="px"
inkscape:window-width="1920"
inkscape:window-height="1136"
inkscape:window-x="0"
inkscape:window-y="27"
inkscape:window-maximized="1" />
<metadata
id="metadata5">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-284.3)">
<g
aria-label="E0"
transform="scale(1.0473381,0.95480149)"
style="font-style:normal;font-weight:normal;font-size:8.83826447px;line-height:1.25;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.22095659"
id="text817">
<path
d="m 3.1545417,302.25775 v 5.30296 c 0,0.36237 -0.4419133,0.0796 -0.4861046,0.33586 0.00884,0.053 0.00884,0.0707 0.044191,0.10605 h 3.7651007 l 0.4949428,-1.00756 c -0.044191,-0.0884 -0.1237357,-0.15909 -0.2209566,-0.15909 -0.3888836,0 -0.132574,0.9457 -2.5542584,0.9457 -0.3358541,0 -0.3712071,-0.0265 -0.3800454,-0.22096 v -2.48355 h 1.2992249 c 0.1944418,0 0.2121183,0.0442 0.2209566,0.28282 v 0.31818 c 0.026515,0.0265 0.079544,0.0442 0.1148974,0.0442 0.035353,0 0.070706,-0.0177 0.1060592,-0.0442 v -1.5467 c -0.026515,-0.0265 -0.070706,-0.0442 -0.1060592,-0.0442 -0.044191,0 -0.079544,0.009 -0.1148974,0.0442 v 0.28283 c 0,0.24747 -0.044191,0.3005 -0.2209566,0.31818 H 3.8174115 v -2.47472 c 0,-0.19444 0.097221,-0.22095 0.2386331,-0.22095 h 0.1414123 c 2.2714339,0 1.9709329,0.94569 2.3774931,0.94569 0.097221,0 0.1767653,-0.0707 0.2209566,-0.15909 L 6.3009638,301.81584 H 2.7126284 c -0.035353,0.0353 -0.035353,0.0707 -0.044191,0.10606 0.044191,0.25631 0.4861046,-0.0442 0.4861046,0.33585 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-family:'Accanthis ADF Std No2';-inkscape-font-specification:'Accanthis ADF Std No2';stroke-width:0.22095659"
id="path823" />
<path
d="m 8.3689797,305.44837 c 0.7468333,0 0.8617308,0.80428 0.8617308,1.40528 0,0.38446 -0.1590888,1.10478 -0.6584507,1.10478 -0.7026421,0 -0.86615,-0.83079 -0.86615,-1.40086 0,-0.37563 0.1811845,-1.1092 0.6628699,-1.1092 z m 0.092802,-0.0972 c -0.7556716,0 -1.1268787,0.68496 -1.1268787,1.34783 0,0.66287 0.3844645,1.34784 1.1268787,1.34784 0.7556717,0 1.1312979,-0.68055 1.1312979,-1.34784 0,-0.66729 -0.3888836,-1.34783 -1.1312979,-1.34783 z"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.41913223px;font-family:'Accanthis ADF Std No2';-inkscape-font-specification:'Accanthis ADF Std No2';stroke-width:0.22095659"
id="path825" />
</g>
</g>
</svg>
......@@ -93,6 +93,8 @@ class RoiSelectionOW(OWWidget):
layout.addWidget(self._buttons)
self._buttons.hide()
# connect signal / slot
self._buttons.accepted.connect(self.validate)
# expose API
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment