import logging
from typing import Iterable, Optional
from silx.gui.dialog.DataFileDialog import DataFileDialog
from silx.io.url import DataUrl
from silx.gui import qt
from est.io import InputType
from est.io.utils import ascii
from est.core.types import dimensions
from est.core.utils.symbol import MU_CHAR
from est.core.io import read_from_url
from est.core.io import read_from_file
from est.gui.unit.energy import EnergyUnitSelector
_logger = logging.getLogger(__name__)
class _InputType(qt.QWidget):
def __init__(self, parent=None):
qt.QWidget.__init__(self, parent)
self.setLayout(qt.QHBoxLayout())
self.layout().addWidget(qt.QLabel("input type:"))
self._inputTypeCB = qt.QComboBox(parent=self)
for input_type in InputType.values():
self._inputTypeCB.addItem(input_type)
self.layout().addWidget(self._inputTypeCB)
# expose API
self.currentChanged = self._inputTypeCB.currentIndexChanged
def getInputType(self):
"""Return the current input type"""
return InputType.from_value(self._inputTypeCB.currentText())
def setInputType(self, input_type):
_input_type = InputType.from_value(input_type)
idx = self._inputTypeCB.findText(_input_type.value)
assert idx >= 0
self._inputTypeCB.setCurrentIndex(idx)
[docs]
class XASObjectWindow(qt.QMainWindow):
def __init__(self, parent):
qt.QMainWindow.__init__(self, parent)
self.setWindowFlags(qt.Qt.Widget)
self._mainWindow = XASObjectDialog(self)
self.setCentralWidget(self._mainWindow)
self._plotDW = qt.QDockWidget(self)
self.addDockWidget(qt.Qt.BottomDockWidgetArea, self._plotDW)
self._plotDW.setFeatures(qt.QDockWidget.DockWidgetMovable)
self._plot = _ToggleableSpectrumPlot(self)
self._plotDW.setWidget(self._plot)
# connect signal / slot
self._mainWindow.editingFinished.connect(self.loadXasObject)
[docs]
def getMainWindow(self):
return self._mainWindow
[docs]
def setAsciiFile(self, file_path):
self._mainWindow.setAsciiFile(file_path=file_path)
[docs]
def setEnergyColName(self, name):
self._mainWindow.setEnergyColName(name)
[docs]
def setAbsColName(self, name):
self._mainWindow.setAbsColName(name)
[docs]
def setMonitorColName(self, name):
self._mainWindow.setMonitorColName(name)
[docs]
def setScanTitle(self, name):
self._mainWindow.setScanTitle(name)
[docs]
def setCurrentType(self, input_type):
self._mainWindow.setCurrentType(input_type=input_type)
[docs]
def getCurrentType(self):
return self._mainWindow.getCurrentType()
[docs]
def setSpectraUrl(self, url):
self._mainWindow.setSpectraUrl(url=url)
[docs]
def setEnergyUrl(self, url):
self._mainWindow.setEnergyUrl(url=url)
[docs]
def setEnergyUnit(self, unit):
self._mainWindow.setEnergyUnit(unit=unit)
[docs]
def setDimensions(self, dims):
self._mainWindow.setDimensions(dims=dims)
[docs]
def setConfigurationUrl(self, url):
self._mainWindow.setConfigurationUrl(url)
[docs]
def getConfigurationUrl(self):
return self._mainWindow.getConfigurationUrl()
[docs]
def loadXasObject(self):
"""Load XasObject from information contained in the GUI
and update plot"""
self._plot.clear()
try:
xas_obj = self._mainWindow.buildXASObject()
except Exception as e:
_logger.error(str(e))
else:
if xas_obj is not None:
self._plot.setXasObject(xas_obj=xas_obj)
[docs]
def buildXASObject(self):
return self._mainWindow.buildXASObject()
class _ToggleableSpectrumPlot(qt.QWidget):
_BUTTON_ICON = qt.QStyle.SP_ToolBarVerticalExtensionButton
def __init__(self, parent=None):
qt.QWidget.__init__(self, parent=parent)
self.setLayout(qt.QGridLayout())
self._toggleButton = qt.QPushButton(self)
self.layout().addWidget(self._toggleButton, 0, 1, 1, 1)
self._plot = _SpectrumPlot(parent=self)
self.layout().addWidget(self._plot, 1, 0, 1, 2)
# set up
self._setButtonIcon(show=True)
# Signal / slot connection
self._toggleButton.clicked.connect(self.toggleSpectrumPlot)
def toggleSpectrumPlot(self):
visible = not self._plot.isVisible()
self._setButtonIcon(show=visible)
self._plot.setVisible(visible)
def _setButtonIcon(self, show):
style = qt.QApplication.instance().style()
# return a QIcon
icon = style.standardIcon(self._BUTTON_ICON)
if show is False:
pixmap = icon.pixmap(32, 32).transformed(qt.QTransform().scale(1, -1))
icon = qt.QIcon(pixmap)
self._toggleButton.setIcon(icon)
def setXasObject(self, xas_obj):
self._plot.setXasObject(xas_obj=xas_obj)
def clear(self):
self._plot.clear()
class _SpectrumPlot(qt.QWidget):
def __init__(self, parent=None):
qt.QWidget.__init__(self, parent)
self.setLayout(qt.QHBoxLayout())
from est.gui.XasObjectViewer import SpectrumViewer
self._plot = SpectrumViewer(self)
from est.gui.XasObjectViewer import _plot_spectrum
self._plot.addCurveOperation(_plot_spectrum)
self._plot.setWindowFlags(qt.Qt.Widget)
# self._plot.setVisible(True)
self.layout().addWidget(self._plot)
# expose API
self.addXMarker = self._plot._plotWidget.addXMarker
def setXasObject(self, xas_obj):
self._plot.setXasObject(xas_obj=xas_obj)
def clear(self):
self._plot.setXasObject(None)
[docs]
class XASObjectDialog(qt.QWidget):
"""
Interface used to select inputs for defining a XASObject
"""
editingFinished = qt.Signal()
def __init__(self, parent=None):
qt.QWidget.__init__(self, parent)
self.setLayout(qt.QGridLayout())
spacer = qt.QWidget(parent=self)
spacer.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Minimum)
self.layout().addWidget(spacer, 0, 0)
self._inputType = _InputType(parent=self)
self.layout().addWidget(self._inputType, 0, 1)
# single ASCII file (.csv, .xmu, .dat, .spec)
self._asciiDialog = _XASObjectFromAscii(parent=self)
self.layout().addWidget(self._asciiDialog, 1, 0, 1, 2)
# .h5 file
self._h5Dialog = _XASObjectFromH5(parent=self)
self.layout().addWidget(self._h5Dialog, 2, 0, 1, 2)
spacer = qt.QWidget(parent=self)
spacer.setSizePolicy(qt.QSizePolicy.Minimum, qt.QSizePolicy.Expanding)
self.layout().addWidget(spacer, 99, 0)
# connect signal / slot
self._inputType.currentChanged.connect(self._updateWidgetVisibility)
self._inputType.currentChanged.connect(self._editingIsFinished)
self._asciiDialog.editingFinished.connect(self._editingIsFinished)
self._h5Dialog.editingFinished.connect(self._editingIsFinished)
# set default read mode to hdf5
self.setCurrentType(InputType.hdf5_spectra)
# default setting
self._updateWidgetVisibility()
def _updateWidgetVisibility(self):
self._asciiDialog.setVisible(
self._inputType.getInputType() == InputType.ascii_spectrum
)
self._h5Dialog.setVisible(
self._inputType.getInputType() == InputType.hdf5_spectra
)
[docs]
def buildXASObject(self):
if self.getCurrentType() == InputType.ascii_spectrum:
return read_from_file(
file_path=self._asciiDialog.getFileSelected(),
energy_unit=self._asciiDialog.getEnergyUnit(),
columns_names=self._asciiDialog.getColumnSelected(),
scan_title=self._asciiDialog.getScanTitle(),
)
elif self.getCurrentType() == InputType.hdf5_spectra:
spectra_url = self._h5Dialog.getSpectraUrl()
energy_url = self._h5Dialog.getEnergyUrl()
energy_unit = self._h5Dialog.getEnergyUnit()
def check_url(url_path, name):
if isinstance(url_path, DataUrl):
url = url_path
else:
url = DataUrl(path=url_path)
if not url.is_valid():
raise ValueError(
" ".join(
(
name,
"url is invalid. Does the file / path still exists ?",
)
)
)
# if we are not able to build a XASObj
if spectra_url in (None, "") or energy_url in (None, ""):
return None
check_url(spectra_url, "spectra")
check_url(energy_url, "energy/channel")
xas_obj = read_from_url(
spectra_url=self._h5Dialog.getSpectraUrl(),
channel_url=self._h5Dialog.getEnergyUrl(),
energy_unit=energy_unit,
config_url=self._h5Dialog.getConfigurationUrl(),
dimensions=self._h5Dialog.getDimensions(),
)
attr_names = "I0", "I1", "I2", "mu_ref"
attr_values = (
self._h5Dialog.advanceInfo.getI0Url(),
self._h5Dialog.advanceInfo.getI1Url(),
self._h5Dialog.advanceInfo.getI2Url(),
self._h5Dialog.advanceInfo.getMuRefUrl(),
)
for attr_name, attr_value in zip(attr_names, attr_values):
if attr_value is not None:
xas_obj.attach_3d_array(attr_name, attr_value)
return xas_obj
else:
raise ValueError("unmanaged input type")
def _editingIsFinished(self, *args, **kwargs):
self.editingFinished.emit()
# gettter / setter exposed
[docs]
def getCurrentType(self):
return self._inputType.getInputType()
[docs]
def setCurrentType(self, input_type):
self._inputType.setInputType(input_type=input_type)
[docs]
def getEnergyColName(self):
return self._asciiDialog.getEnergyColName()
[docs]
def setEnergyColName(self, name):
self._asciiDialog.setEnergyColName(name=name)
[docs]
def getAbsColName(self):
return self._asciiDialog.getAbsColName()
[docs]
def setAbsColName(self, name):
self._asciiDialog.setAbsColName(name=name)
[docs]
def getMonitorColName(self):
return self._asciiDialog.getMonitorColName()
[docs]
def setMonitorColName(self, name):
self._asciiDialog.setMonitorColName(name=name)
[docs]
def getScanTitle(self):
return self._asciiDialog.getScanTitle()
[docs]
def setScanTitle(self, scan_title):
self._asciiDialog.setScanTitle(scan_title=scan_title)
[docs]
def getAsciiFile(self):
return self._asciiDialog.getFileSelected()
[docs]
def setAsciiFile(self, file_path):
self._asciiDialog.setFileSelected(file_path=file_path)
[docs]
def getSpectraUrl(self) -> Optional[DataUrl]:
return self._h5Dialog.getSpectraUrl()
[docs]
def setSpectraUrl(self, url):
self._h5Dialog.setSpectraUrl(url=url)
[docs]
def getEnergyUrl(self) -> Optional[DataUrl]:
return self._h5Dialog.getEnergyUrl()
[docs]
def setEnergyUrl(self, url):
self._h5Dialog.setEnergyUrl(url=url)
[docs]
def getEnergyUnit(self):
# TODO: FIXME: design should be improve to avoid those kind of conditions
if self.getCurrentType() == InputType.ascii_spectrum:
return self._asciiDialog.getEnergyUnit()
elif self.getCurrentType() == InputType.hdf5_spectra:
return self._h5Dialog.getEnergyUnit()
else:
return self._asciiDialog.getEnergyUnit()
[docs]
def setEnergyUnit(self, unit):
# TODO: FIXME: design should be improved to avoid setting unit to all sub-widgets
self._h5Dialog.setEnergyUnit(unit=unit)
self._asciiDialog.setEnergyUnit(unit=unit)
self._asciiDialog.setEnergyUnit(unit=unit)
[docs]
def getConfigurationUrl(self):
return self._h5Dialog.getConfigurationUrl()
[docs]
def setConfigurationUrl(self, url):
self._h5Dialog.setConfigurationUrl(url=url)
[docs]
def getDimensions(self):
return self._h5Dialog.getDimensions()
[docs]
def setDimensions(self, dims):
self._h5Dialog.setDimensions(dims=dims)
class _XASObjectFromAscii(qt.QWidget):
"""
Interface used to define a XAS object from a single ASCII file
"""
editingFinished = qt.Signal()
"""signal emitted when edition is finished"""
class FileQLineEdit(qt.QLineEdit):
"""QLineEdit to handle a file path"""
def dropEvent(self, event):
if event.mimeData().hasFormat("text/uri-list"):
for url in event.mimeData().urls():
self.setText(str(url.path()))
def supportedDropActions(self):
"""Inherited method to redefine supported drop actions."""
return qt.Qt.CopyAction | qt.Qt.MoveAction
def dragEnterEvent(self, event):
if event.mimeData().hasFormat("text/uri-list"):
event.accept()
event.setDropAction(qt.Qt.CopyAction)
else:
qt.QWidget.dragEnterEvent(self, event)
def dragMoveEvent(self, event):
if event.mimeData().hasFormat("text/uri-list"):
event.setDropAction(qt.Qt.CopyAction)
event.accept()
else:
qt.QWidget.dragMoveEvent(self, event)
def __init__(self, parent=None):
qt.QWidget.__init__(self, parent)
self.setLayout(qt.QGridLayout())
# select file
self.layout().addWidget(qt.QLabel("file", self), 0, 0, 1, 1)
self._inputLe = self.FileQLineEdit("", self)
self.layout().addWidget(self._inputLe, 0, 1, 1, 1)
self._selectPB = qt.QPushButton("select", self)
self.layout().addWidget(self._selectPB, 0, 2, 1, 1)
# select energy
self.layout().addWidget(qt.QLabel("energy unit", self), 1, 0, 1, 1)
self._energyUnitSelector = EnergyUnitSelector(parent=self)
self.layout().addWidget(self._energyUnitSelector, 1, 1, 1, 1)
# select scan from multi-spectrum file
self.layout().addWidget(qt.QLabel("scan title"), 2, 0, 1, 1)
self._scanTitleName = qt.QComboBox(self)
self.layout().addWidget(self._scanTitleName, 2, 1, 1, 1)
# select columns
self._dimensionWidget = _ColNameSelection(parent=self)
self.layout().addWidget(self._dimensionWidget, 3, 0, 1, 2)
# spacer
spacer = qt.QWidget(parent=self)
spacer.setSizePolicy(qt.QSizePolicy.Minimum, qt.QSizePolicy.Expanding)
self.layout().addWidget(spacer, 999, 0, 1, 1)
# signal / slot connection
self._selectPB.pressed.connect(self._selectFile)
self._inputLe.editingFinished.connect(self._newFileSelected)
self._scanTitleName.currentIndexChanged.connect(self._newScanSelected)
self._dimensionWidget.sigInputChanged.connect(self._editingIsFinished)
self._scanTitleName.currentIndexChanged.connect(self._editingIsFinished)
self._energyUnitSelector.currentIndexChanged.connect(self._editingIsFinished)
def _editingIsFinished(self, *args, **kwargs):
self.editingFinished.emit()
def getFileSelected(self):
return self._inputLe.text()
def _getNameFilters(self):
return [
"Ascii (*.dat *.spec *.csv *.xmu)",
"All Files (*)",
]
def _selectFile(self, *args, **kwargs):
old = self.blockSignals(True)
try:
dialog = qt.QFileDialog(self)
dialog.setFileMode(qt.QFileDialog.ExistingFile)
dialog.setNameFilters(self._getNameFilters())
if not dialog.exec_():
dialog.close()
return
fileSelected = dialog.selectedFiles()
if len(fileSelected) == 0:
return
assert len(fileSelected) == 1
self.setFileSelected(fileSelected[0])
finally:
self.blockSignals(old)
def setFileSelected(self, file_path):
self._inputLe.setText(file_path)
self._inputLe.editingFinished.emit()
def _newFileSelected(self):
old = self.blockSignals(True)
try:
scan_title = self.getScanTitle()
self._scanTitleName.clear()
titles = ascii.get_all_scan_titles(self.getFileSelected())
for title in sorted(titles):
self._scanTitleName.addItem(title)
if titles and scan_title not in titles:
scan_title = titles[0]
self.setScanTitle(scan_title)
self._scanTitleName.currentIndexChanged.emit(
self._scanTitleName.currentIndex()
)
except Exception as e:
_logger.warning(e)
finally:
self.blockSignals(old)
def _newScanSelected(self, index):
old = self.blockSignals(True)
try:
self._dimensionWidget.setScan(self.getFileSelected(), self.getScanTitle())
except Exception as e:
_logger.warning(e)
finally:
self.blockSignals(old)
def getEnergyUnit(self):
return self._energyUnitSelector.getUnit()
def setEnergyUnit(self, unit):
return self._energyUnitSelector.setUnit(unit=unit)
def setEnergyColName(self, name):
self._dimensionWidget.setEnergyColName(name)
def getEnergyColName(self):
return self._dimensionWidget.getEnergyColName()
def setAbsColName(self, name):
self._dimensionWidget.setAbsColName(name)
def getAbsColName(self):
return self._dimensionWidget.getAbsColName()
def setMonitorColName(self, name):
self._dimensionWidget.setMonitorColName(name)
def getMonitorColName(self):
return self._dimensionWidget.getMonitorColName()
def getColumnSelected(self):
return self._dimensionWidget.getColumnSelected()
def setScanTitle(self, scan_title):
index = self._scanTitleName.findText(scan_title)
if index >= 0:
self._scanTitleName.setCurrentIndex(index)
def getScanTitle(self):
return self._scanTitleName.currentText()
class _URLSelector(qt.QWidget):
def __init__(self, parent, name, layout=None, position=None):
qt.QWidget.__init__(self, parent)
self.name = name
if layout is None:
layout = self.setLayout(qt.QGridLayout())
position = (0, 0)
layout.addWidget(qt.QLabel(name + ":", parent=self), position[0], position[1])
self._qLineEdit = qt.QLineEdit("", parent=self)
layout.addWidget(self._qLineEdit, position[0], position[1] + 1)
self._qPushButton = qt.QPushButton("select", parent=self)
layout.addWidget(self._qPushButton, position[0], position[1] + 2)
# connect signal / slot
self._qPushButton.clicked.connect(self._selectFile)
def _selectFile(self, *args, **kwargs):
dialog = DataFileDialog(self)
url = self._qLineEdit.text()
if url:
dialog.selectUrl(url)
if not dialog.exec_():
dialog.close()
return None
if dialog.selectedUrl() is not None:
self.setUrlPath(dialog.selectedUrl())
def getUrlPath(self):
url = self._qLineEdit.text()
if url == "":
return None
else:
return DataUrl(path=url)
def setUrlPath(self, url):
if isinstance(url, DataUrl):
url = url.path()
self._qLineEdit.setText(url)
self._qLineEdit.editingFinished.emit()
class _XASObjectFromH5(qt.QTabWidget):
"""
Interface used to define a XAS object from h5 files and data path
"""
editingFinished = qt.Signal()
"""signal emitted when edition is finished"""
def __init__(self, parent=None):
qt.QTabWidget.__init__(self, parent)
# Mandatory information
self._basicInformation = _MandatoryXASObjectFromH5(self)
self.addTab(self._basicInformation, "basic information")
# Optional information
self._advanceInformation = _OptionalRefsXASObjectFromH5(self)
self.addTab(self._advanceInformation, "advanced information")
# connect signal / slot
self._basicInformation.editingFinished.connect(self._editingIsFinished)
self._advanceInformation.editingFinished.connect(self._editingIsFinished)
@property
def advanceInfo(self):
return self._advanceInformation
def _editingIsFinished(self, *args, **kwargs):
self.editingFinished.emit()
def getEnergyUnit(self):
return self._basicInformation.getEnergyUnit()
def setEnergyUnit(self, unit):
self._basicInformation.setEnergyUnit(unit=unit)
def getDimensions(self):
return self._basicInformation.getDimensions()
def setDimensions(self, dims):
self._basicInformation.setDimensions(dims=dims)
def getConfigurationUrl(self):
return self._basicInformation.getConfigurationUrl()
def setConfigurationUrl(self, url):
self._basicInformation.setConfigurationUrl(url=url)
def getEnergyUrl(self):
return self._basicInformation.getEnergyUrl()
def setEnergyUrl(self, url):
self._basicInformation.setEnergyUrl(url=url)
def getSpectraUrl(self):
return self._basicInformation.getSpectraUrl()
def setSpectraUrl(self, url):
return self._basicInformation.setSpectraUrl(url=url)
class _OptionalRefsXASObjectFromH5(qt.QWidget):
"""Widget containing optional information that we can associate to
spectrum.
"""
editingFinished = qt.Signal()
"""signal emitted when edition is finished"""
def __init__(self, parent=None):
qt.QWidget.__init__(self, parent)
self.setLayout(qt.QGridLayout())
# I0
self._I0Selector = _URLSelector(
parent=self, name="I0 url", layout=self.layout(), position=(0, 0)
)
# I1
self._I1Selector = _URLSelector(
parent=self, name="I1 url", layout=self.layout(), position=(1, 0)
)
# I2
self._I2Selector = _URLSelector(
parent=self, name="I2 url", layout=self.layout(), position=(2, 0)
)
# mu ref
self._muRefSelector = _URLSelector(
parent=self,
name="{} ref".format(MU_CHAR),
layout=self.layout(),
position=(3, 0),
)
# connect signal / slot
self._I0Selector._qLineEdit.textChanged.connect(self._editingIsFinished)
self._I1Selector._qLineEdit.textChanged.connect(self._editingIsFinished)
self._I2Selector._qLineEdit.textChanged.connect(self._editingIsFinished)
self._muRefSelector._qLineEdit.textChanged.connect(self._editingIsFinished)
def _editingIsFinished(self, *args, **kwargs):
self.editingFinished.emit()
def getI0Url(self):
return self._I0Selector.getUrlPath()
def setI0Url(self, url):
self._I0Selector.setUrlPath(url)
def getI1Url(self):
return self._I1Selector.getUrlPath()
def setI1Url(self, url):
self._I1Selector.setUrlPath(url)
def getI2Url(self):
return self._I2Selector.getUrlPath()
def setI2Url(self, url):
self._I2Selector.setUrlPath(url)
def getMuRefUrl(self):
return self._muRefSelector.getUrlPath()
def setMuRefUrl(self, url):
self._muRefSelector.setUrlPath(url)
class _MandatoryXASObjectFromH5(qt.QWidget):
"""Widget containing mandatory information"""
editingFinished = qt.Signal()
"""signal emitted when edition is finished"""
def __init__(self, parent=None):
qt.QWidget.__init__(self, parent)
self.setLayout(qt.QGridLayout())
# spectra url
self._spectraSelector = _URLSelector(
parent=self, name="spectra url", layout=self.layout(), position=(0, 0)
)
self._bufWidget = qt.QWidget(parent=self)
self._bufWidget.setLayout(qt.QHBoxLayout())
spacer = qt.QWidget(parent=self)
spacer.setSizePolicy(qt.QSizePolicy.Expanding, qt.QSizePolicy.Minimum)
self._bufWidget.layout().addWidget(spacer)
self._dimensionSelection = _SpectraDimensions(parent=self._bufWidget)
self._bufWidget.layout().addWidget(self._dimensionSelection)
self.layout().addWidget(self._bufWidget, 1, 1)
# channel/energy url
self._energySelector = _URLSelector(
parent=self,
name="channel/energy url",
layout=self.layout(),
position=(2, 0),
)
self._energyUnit = EnergyUnitSelector(parent=self)
self.layout().addWidget(self._energyUnit, 2, 3, 1, 1)
# configuration url
self._configSelector = _URLSelector(
parent=self, name="configuration url", layout=self.layout(), position=(3, 0)
)
# connect signal / slot
self._spectraSelector._qLineEdit.editingFinished.connect(
self._editingIsFinished
)
self._energySelector._qLineEdit.editingFinished.connect(self._editingIsFinished)
self._configSelector._qLineEdit.editingFinished.connect(self._editingIsFinished)
self._dimensionSelection.sigDimensionChanged.connect(self._editingIsFinished)
self._energyUnit.currentIndexChanged.connect(self._editingIsFinished)
# expose APi
self.setDimensions = self._dimensionSelection.setDimensions
self.getDimensions = self._dimensionSelection.getDimensions
def getSpectraUrl(self):
"""
:return: the DataUrl of the spectra
:rtype: DataUrl
"""
return self._spectraSelector.getUrlPath()
def getEnergyUrl(self):
"""
:return: the DataUrl of energy / channel
:rtype: DataUrl
"""
return self._energySelector.getUrlPath()
def getConfigurationUrl(self):
"""
:return: the DataUrl of the configuration
:rtype: DataUrl
"""
return self._configSelector.getUrlPath()
def setSpectraUrl(self, url):
self._spectraSelector.setUrlPath(url)
def setEnergyUrl(self, url):
self._energySelector.setUrlPath(url)
def setConfigurationUrl(self, url):
self._configSelector.setUrlPath(url)
def _editingIsFinished(self, *args, **kwargs):
self.editingFinished.emit()
def getDimensionsInfo(self) -> dimensions.DimensionsType:
return self._dimensionSelection.getDimensions()
def getEnergyUnit(self):
return self._energyUnit.getUnit()
def setEnergyUnit(self, unit):
self._energyUnit.setUnit(unit=unit)
class _QColumnComboBox(qt.QComboBox):
def __init__(self, parent):
qt.QComboBox.__init__(self, parent)
def setColumnsNames(self, colums_names: Iterable):
for column_name in colums_names:
self.addItem(column_name)
def setColumnName(self, name):
index = self.findText(name)
if index >= 0:
self.setCurrentIndex(index)
else:
_logger.info("Unable to find name: {}".format(name))
def getColumnName(self):
return self.currentText()
class _QDimComboBox(qt.QComboBox):
def __init__(self, parent):
qt.QComboBox.__init__(self, parent)
self.addItem("Dim 0", 0)
self.addItem("Dim 1", 1)
self.addItem("Dim 2", 2)
self.setCurrentIndex(0)
def setDim(self, dim: int):
index = self.findData(dim)
assert index >= 0
self.setCurrentIndex(index)
def getDim(self):
return self.currentData()
class _SpectraDimensions(qt.QWidget):
sigDimensionChanged = qt.Signal()
"""Signal emitted when dimension change"""
def __init__(self, parent):
qt.QWidget.__init__(self, parent=parent)
self.setLayout(qt.QFormLayout())
self._dim_x = _QDimComboBox(parent=self)
self.layout().addRow("X", self._dim_x)
self._dim_y = _QDimComboBox(parent=self)
self.layout().addRow("Y", self._dim_y)
self._dim_channel = _QDimComboBox(parent=self)
self.layout().addRow("Channel/Energy", self._dim_channel)
# set up
self._dim_x.setDim(dimensions.STANDARD_DIMENSIONS[0])
self._dim_y.setDim(dimensions.STANDARD_DIMENSIONS[1])
self._dim_channel.setDim(dimensions.STANDARD_DIMENSIONS[2])
# connect Signal / Slot
self._dim_x.currentTextChanged.connect(self._insureDimUnicity)
self._dim_y.currentTextChanged.connect(self._insureDimUnicity)
self._dim_channel.currentTextChanged.connect(self._insureDimUnicity)
def _insureDimUnicity(self):
last_modified = self.sender()
if last_modified is self._dim_x:
get_second, get_third = self._dim_y, self._dim_channel
elif last_modified is self._dim_y:
get_second, get_third = self._dim_x, self._dim_channel
elif last_modified is self._dim_channel:
get_second, get_third = self._dim_x, self._dim_y
else:
raise RuntimeError("Sender should be in dim0, dim1, dim2")
assert last_modified != get_second
assert last_modified != get_third
assert type(last_modified) is type(get_second) is type(get_third)
value_set = {0, 1, 2}
last_value_set = last_modified.getDim()
value_set.remove(last_value_set)
old_1 = last_modified.blockSignals(True)
old_2 = get_second.blockSignals(True)
old_3 = get_third.blockSignals(True)
if get_second.getDim() in value_set:
value_set.remove(get_second.getDim())
get_third.setDim(value_set.pop())
elif get_third.getDim() in value_set:
value_set.remove(get_third.getDim())
get_second.setDim(value_set.pop())
else:
get_second.setDim(value_set.pop())
get_third.setDim(value_set.pop())
last_modified.blockSignals(old_1)
get_second.blockSignals(old_2)
get_third.blockSignals(old_3)
self.sigDimensionChanged.emit()
def getDimensions(self) -> dimensions.DimensionsType:
return (
self._dim_x.getDim(),
self._dim_y.getDim(),
self._dim_channel.getDim(),
)
def setDimensions(self, dims: dimensions.DimensionsType) -> None:
dims = dimensions.parse_dimensions(dims)
self._dim_x.setDim(dims[0])
self._dim_y.setDim(dims[1])
self._dim_channel.setDim(dims[2])
class _ColNameSelection(qt.QWidget):
sigInputChanged = qt.Signal()
"""Signal emitted when dimension change"""
def __init__(self, parent):
qt.QWidget.__init__(self, parent=parent)
self.setLayout(qt.QFormLayout())
self._energyColNamCB = _QColumnComboBox(parent=self)
self.layout().addRow("energy col name", self._energyColNamCB)
self._absColNamCB = _QColumnComboBox(parent=self)
self.layout().addRow("absorption col name", self._absColNamCB)
self._monitorColNamCB = _QColumnComboBox(parent=self)
self._monitorColNamCB.setEnabled(False)
self._useMonitorCB = qt.QCheckBox("monitor col name")
self.layout().addRow(self._useMonitorCB, self._monitorColNamCB)
# connect Signal / Slot
self._energyColNamCB.currentIndexChanged.connect(self._propagateSigChanged)
self._absColNamCB.currentIndexChanged.connect(self._propagateSigChanged)
self._monitorColNamCB.currentIndexChanged.connect(self._propagateSigChanged)
self._useMonitorCB.toggled.connect(self._propagateSigChanged)
self._useMonitorCB.toggled.connect(self._monitorColNamCB.setEnabled)
def _propagateSigChanged(self):
self.sigInputChanged.emit()
def getColumnSelected(self) -> dict:
"""
:return: return the information regarding each energy and mu
:rtype: dict
"""
return {
"energy": self.getEnergyColName(),
"mu": self.getAbsColName(),
"monitor": self.getMonitorColName(),
}
def getMonitorColName(self):
if self.useMonitor():
return self._monitorColNamCB.currentText()
else:
return None
def setMonitorColName(self, name):
self._monitorColNamCB.setColumnName(name)
def useMonitor(self):
return self._useMonitorCB.isChecked()
def setEnergyColName(self, name):
self._energyColNamCB.setColumnName(name)
def getEnergyColName(self):
return self._energyColNamCB.getColumnName()
def setAbsColName(self, name):
self._absColNamCB.setColumnName(name)
def getAbsColName(self):
return self._absColNamCB.getColumnName()
def setScan(self, file_path, scan_title):
old = self.blockSignals(True)
try:
prev_energy = self._energyColNamCB.getColumnName()
prev_absorption = self._absColNamCB.getColumnName()
prev_monitor = self._monitorColNamCB.currentText()
self._energyColNamCB.clear()
self._absColNamCB.clear()
self._monitorColNamCB.clear()
col_names = ascii.get_scan_column_names(file_path, scan_title)
self._energyColNamCB.setColumnsNames(col_names)
self._absColNamCB.setColumnsNames(col_names)
self._monitorColNamCB.setColumnsNames(col_names)
if col_names:
ncols = len(col_names)
if prev_energy not in col_names and ncols >= 1:
prev_energy = col_names[0]
if prev_absorption not in col_names and ncols >= 2:
prev_absorption = col_names[1]
if prev_monitor not in col_names and ncols >= 3:
prev_monitor = col_names[2]
self._energyColNamCB.setColumnName(prev_energy)
self._absColNamCB.setColumnName(prev_absorption)
self._monitorColNamCB.setColumnName(prev_monitor)
finally:
self.blockSignals(old)