Commit d71d9c8b authored by payno's avatar payno Committed by Henri Payno
Browse files

[orangecontrib] Add larch orange widgets.

parent 9209196d
......@@ -35,7 +35,7 @@ import logging
_logger = logging.getLogger(__file__)
class _PyMcaProcessForOrangeMixIn(object):
class _ProcessForOrangeMixIn(object):
"""
Group processing and progress display in a common class for pymca 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__ = "06/07/2019"
import functools
import logging
from Orange.widgets import gui
from Orange.widgets.settings import Setting
from Orange.widgets.widget import OWWidget
from silx.gui import qt
from silx.gui.plot import LegendSelector
import xas.core.process.larch.autobk
from orangecontrib.xas.process import _ProcessForOrangeMixIn
from orangecontrib.xas.process import ProcessRunnable
from xas.core.types import XASObject, Spectrum
from xas.gui.XasObjectViewer import XasObjectViewer, _CurveOperation, ViewType
from xas.gui.larch.autobk import _AutobkParameters
from orangecontrib.xas.progress import QProgress
_logger = logging.getLogger(__file__)
def _plot_bkg(spectrum):
# bkg = dgroup.bkg
# ylabel = plotlabels.mu
# if label is None:
# label = 'mu'
# #endif
# emin, emax = _get_erange(dgroup, emin, emax)
# if norm:
# mu = dgroup.norm
# bkg = (dgroup.bkg - dgroup.pre_edge) / dgroup.edge_step
# ylabel = "%s (norm)" % ylabel
# label = "%s (norm)" % label
# #endif
# title = _get_title(dgroup, title=title)
#
# opts = dict(win=win, show_legend=True, linewidth=3,
# delay_draw=True, _larch=_larch)
# _plot(dgroup.energy, mu+offset, xlabel=plotlabels.energy, ylabel=ylabel,
# title=title, label=label, zorder=20, new=new, xmin=emin, xmax=emax,
# **opts)
# _plot(dgroup.energy, bkg+offset, zorder=18, label='bkg', **opts)
# if show_e0:
# _plot_axvline(dgroup.e0, zorder=2, size=3, label='E0',
# color=plotlabels.e0color, win=win, _larch=_larch)
# disp = _getDisplay(win=win, _larch=_larch)
# if disp is not None:
# disp.panel.conf.draw_legend()
assert isinstance(spectrum, Spectrum)
missing_keys = spectrum.get_missing_keys(('Edge',))
if missing_keys:
_logger.error('missing keys:', missing_keys, 'unable to compute spectrum plot')
return
e0 = spectrum['Edge']
x = spectrum.energy - e0
y = spectrum.mu
return _CurveOperation(x=x, y=y, legend='Spectrum', yaxis=None)
def _plot_chik(spectrum):
# if kweight is None:
# kweight = 0
# xft = getattr(dgroup, 'xftf_details', None)
# if xft is not None:
# kweight = xft.call_args.get('kweight', 0)
# #endif
# #endif
#
# chi = dgroup.chi * dgroup.k ** kweight
# opts = dict(win=win, show_legend=True, delay_draw=True, linewidth=3,
# _larch=_larch)
# if label is None:
# label = 'chi'
# #endif
# title = _get_title(dgroup, title=title)
# _plot(dgroup.k, chi+offset, xlabel=plotlabels.k,
# ylabel=plotlabels.chikw.format(kweight), title=title,
# label=label, zorder=20, new=new, xmax=kmax, **opts)
#
# if show_window and hasattr(dgroup, 'kwin'):
# kwin = dgroup.kwin
# if scale_window:
# kwin = kwin*max(abs(chi))
# _plot(dgroup.k, kwin+offset, zorder=12, label='window', **opts)
assert isinstance(spectrum, Spectrum)
missing_keys = spectrum.get_missing_keys(('Edge', 'NormalizedEnergy',
'NormalizedPlotMin', 'NormalizedPlotMax',
'NormalizedMu'))
if missing_keys:
_logger.error('missing keys:', missing_keys, 'unable to compute normalized spectrum plot')
return None
idx = (spectrum["NormalizedEnergy"] >= spectrum["NormalizedPlotMin"]) & \
(spectrum["NormalizedEnergy"] <= spectrum["NormalizedPlotMax"])
e0 = spectrum['Edge']
x = spectrum["NormalizedEnergy"][idx] - e0
y = spectrum["NormalizedMu"][idx]
return _CurveOperation(x=x, y=y, legend='Normalized', yaxis='right')
class AutobkWindow(qt.QMainWindow):
def __init__(self, parent=None):
qt.QMainWindow.__init__(self, parent)
# xas object viewer
mapKeys = ['Mu', 'bkg', 'chie', 'k', 'chi', 'e0']
self.xasObjViewer = XasObjectViewer(mapKeys=mapKeys)
self.xasObjViewer._spectrumViews[0]._plot.getXAxis().setLabel("Energy (eV)")
self.xasObjViewer._spectrumViews[0]._plot.getYAxis().setLabel("Absorption (a.u.)")
self.setCentralWidget(self.xasObjViewer)
self._parametersWindow = _AutobkParameters(parent=self)
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
self.legendDockWidget = LegendSelector.LegendsDockWidget(parent=self,
plot=self.xasObjViewer._spectrumViews[0]._plot)
self.legendDockWidget.setAllowedAreas(qt.Qt.RightDockWidgetArea | qt.Qt.LeftDockWidgetArea)
self.legendDockWidget.setFeatures(qt.QDockWidget.NoDockWidgetFeatures)
self.addDockWidget(qt.Qt.RightDockWidgetArea, self.legendDockWidget)
# volume key selection
self.addDockWidget(qt.Qt.RightDockWidgetArea,
self.xasObjViewer._mapView.keySelectionDocker)
# plot settings
for ope in (_plot_bkg, _plot_chik):
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 getNCurves(self):
return len(self.xasObjViewer._spectrumViews._plot.getAllCurves())
def _updateLegendView(self):
index, viewType = self.xasObjViewer.getViewType()
self.legendDockWidget.setVisible(viewType is ViewType.spectrum)
self.xasObjViewer._mapView.keySelectionDocker.setVisible(viewType is ViewType.map)
class AutobkOW(_ProcessForOrangeMixIn, OWWidget):
"""
Widget used for signal extraction
"""
name = "autobk"
id = "orange.widgets.xas.larch.autobk"
description = "background removal"
icon = "icons/autobk.png"
priority = 1
category = "esrfWidgets"
keywords = ["spectroscopy", "autobk", 'background']
want_main_area = True
resizing_enabled = True
inputs = [("spectra", XASObject, "process")]
outputs = [("spectra", XASObject), ("curves", tuple)]
process_function = xas.core.process.larch.autobk.Larch_autobk
_larchSettings = Setting(dict())
"""Store the configuration of the PyMca XASClass"""
def __init__(self):
super().__init__()
self._latest_xas_obj = None
self._window = AutobkWindow(parent=self)
layout = gui.vBox(self.mainArea, 'fourier transform').layout()
layout.addWidget(self._window)
self._window.xasObjViewer.setWindowTitle('spectra')
# manage settings
if self._larchSettings != dict():
self._window._parametersWindow.setParameters(self._larchSettings)
# connect signals / slots
# TODO
def _updateProcess(self):
self._update_settings()
if self._latest_xas_obj:
self.process(self._latest_xas_obj)
def _update_settings(self):
self._pymcaSettings = self._window._parametersWindow.getParameters()
def process(self, xas_obj):
if xas_obj is None:
return
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.copy()
self._startProcess()
# setup the normalization process
process_obj = QLarch_autobk()
process_obj._advancement.sigProgress.connect(self._setProgressValue)
process_obj.setProperties({
'_pymcaSettings': self._window._parametersWindow.getParameters()})
# update the processing thread
thread = self.getProcessingThread()
thread.init(process_obj=process_obj, xas_obj=xas_obj)
self._callback_finish = functools.partial(self._endProcess, xas_obj)
thread.finished.connect(self._callback_finish)
# start processing
thread.start(priority=qt.QThread.LowPriority)
class QLarch_autobk(xas.core.process.larch.autobk.Larch_autobk):
"""
Normalization able to give advancement using qt.Signal and QThreadPool
"""
def __init__(self):
xas.core.process.larch.autobk.Larch_autobk.__init__(self)
self._advancement = QProgress('autobk')
def _pool_process(self, xas_obj):
self.pool = qt.QThreadPool()
self.pool.setMaxThreadCount(5)
for spectrum in xas_obj.spectra:
runnable = ProcessRunnable(fct=xas.core.process.larch.autobk.process_spectr_autobk,
spectrum=spectrum,
configuration=xas_obj.configuration,
callback=self._advancement.increaseAdvancement)
self.pool.start(runnable)
self.pool.waitForDone()
<?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="64px"
height="64px"
id="svg2985"
version="1.1"
inkscape:version="0.48.5 r10040"
sodipodi:docname="New document 2">
<defs
id="defs2987" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.5"
inkscape:cx="32"
inkscape:cy="32"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1098"
inkscape:window-x="0"
inkscape:window-y="31"
inkscape:window-maximized="1" />
<metadata
id="metadata2990">
<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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<text
xml:space="preserve"
style="font-size:25.14691544px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Amiri;-inkscape-font-specification:Amiri"
x="2.5559556"
y="35.342133"
id="text2993"
sodipodi:linespacing="125%"
transform="scale(0.82993331,1.2049161)"><tspan
sodipodi:role="line"
id="tspan2995"
x="2.5559556"
y="35.342133">Autobk</tspan></text>
</g>
</svg>
<?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="64px"
height="64px"
id="svg2985"
version="1.1"
inkscape:version="0.48.5 r10040"
sodipodi:docname="New document 2">
<defs
id="defs2987" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="5.5"
inkscape:cx="32"
inkscape:cy="17.454545"
inkscape:current-layer="layer1"
showgrid="true"
inkscape:document-units="px"
inkscape:grid-bbox="true"
inkscape:window-width="1920"
inkscape:window-height="1098"
inkscape:window-x="0"
inkscape:window-y="31"
inkscape:window-maximized="1" />
<metadata
id="metadata2990">
<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></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<g
id="layer1"
inkscape:label="Layer 1"
inkscape:groupmode="layer">
<text
xml:space="preserve"
style="font-size:23.98252678px;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:125%;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;font-family:Amiri;-inkscape-font-specification:Amiri"
x="1.0966778"
y="40.086723"
id="text2993"
sodipodi:linespacing="125%"
transform="scale(0.99101565,1.0090658)"><tspan
sodipodi:role="line"
id="tspan2995"
x="1.0966778"
y="40.086723">mback</tspan></text>
</g>
</svg>
# 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
from Orange.widgets.widget import OWWidget
from silx.gui import qt
from silx.gui.plot import LegendSelector
import xas.core.process.larch.mback
from orangecontrib.xas.process import _ProcessForOrangeMixIn
from orangecontrib.xas.process import ProcessRunnable
from xas.core.types import XASObject, Spectrum
from xas.gui.XasObjectViewer import XasObjectViewer, _CurveOperation, ViewType
from xas.gui.larch.mback import _MBackParameters
from orangecontrib.xas.progress import QProgress
_logger = logging.getLogger(__file__)
def _plot_fpp(spectrum):
assert isinstance(spectrum, Spectrum)
if not hasattr(spectrum, 'fpp'):
_logger.error('fpp has not been computed yet')
return
return _CurveOperation(x=spectrum.energy, y=spectrum.fpp, legend='fpp')
def _plot_f2(spectrum):
assert isinstance(spectrum, Spectrum)
if not hasattr(spectrum, 'f2'):
_logger.error('f2 has not been computed yet')
return
return _CurveOperation(x=spectrum.energy, y=spectrum.f2, legend='f2')
class MbackWindow(qt.QMainWindow):
def __init__(self, parent=None):
qt.QMainWindow.__init__(self, parent)
# xas object viewer
mapKeys = ['Mu', 'fpp', 'f2']
self.xasObjViewer = XasObjectViewer(mapKeys=mapKeys)
self.xasObjViewer._spectrumViews[0]._plot.getXAxis().setLabel("Energy (eV)")
self.setCentralWidget(self.xasObjViewer)
self._parametersWindow = _MBackParameters(parent=self)
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
self.legendDockWidget = LegendSelector.LegendsDockWidget(parent=self,
plot=self.xasObjViewer._spectrumViews[0]._plot)
self.legendDockWidget.setAllowedAreas(qt.Qt.RightDockWidgetArea | qt.Qt.LeftDockWidgetArea)
self.legendDockWidget.setFeatures(qt.QDockWidget.NoDockWidgetFeatures)
self.addDockWidget(qt.Qt.RightDockWidgetArea, self.legendDockWidget)
# volume key selection
self.addDockWidget(qt.Qt.RightDockWidgetArea,
self.xasObjViewer._mapView.keySelectionDocker)
# plot settings
for ope in (_plot_fpp, _plot_f2):
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)
self.xasObjViewer._mapView.keySelectionDocker.setVisible(viewType is ViewType.map)
class MbackOW(_ProcessForOrangeMixIn, OWWidget):
"""
Widget used for signal extraction
"""
name = "mback"
id = "orange.widgets.xas.larch.mback"
description = "Match mu(E) data for tabulated f''(E) using the MBACK " \
"algorithm and, optionally, the Lee & Xiang extension"
icon = "icons/mback.png"
priority = 5
category = "esrfWidgets"
keywords = ["spectroscopy", "backm"]
want_main_area = True
resizing_enabled = True
inputs = [("spectra", XASObject, "process")]
outputs = [("spectra", XASObject), ("curves", tuple)]
process_function = xas.core.process.larch.mback.Larch_mback
_larchSettings = Setting(dict())
"""Store the configuration of the larch configuration"""
def __init__(self):
super().__init__()
self._latest_xas_obj = None
self._window = MbackWindow(parent=self)
layout = gui.vBox(self.mainArea, 'backm').layout()
layout.addWidget(self._window)
self._window.xasObjViewer.setWindowTitle('spectra')
# manage settings
if self._larchSettings != dict():
self._window._parametersWindow.setParameters(self._larchSettings)
# connect signals / slots
self._window._parametersWindow.sigChanged.connect(self._updateProcess)
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()
def process(self, xas_obj):
if xas_obj is None:
return
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.copy()
self._startProcess()
# setup the normalization process
process_obj = QLarch_mback()
process_obj._advancement