New Upstream Release - pymca
Ready changes
Summary
Merged new upstream version: 5.9.2+dfsg (was: 5.8.7+dfsg).
Diff
diff --git a/LICENSE b/LICENSE
index cf7cc538..3fe5313a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
- The PyMca X-Ray Fluorescence Toolkit is Copyright (C) 2004-2022 of the European Synchrotron Radiation Facility (ESRF).
+ The PyMca X-Ray Fluorescence Toolkit is Copyright (C) 2004-2023 of the European Synchrotron Radiation Facility (ESRF).
Unless otherways stated in the relevant accompanying source code, the default license of these modules is MIT.
diff --git a/PKG-INFO b/PKG-INFO
index 4c137173..b73be076 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,12 +1,12 @@
Metadata-Version: 2.1
Name: PyMca5
-Version: 5.8.7
+Version: 5.9.2
Summary: Mapping and X-Ray Fluorescence Analysis
Home-page: http://pymca.sourceforge.net
+Download-URL: https://github.com/vasole/pymca/archive/v5.9.2.tar.gz
Author: V. Armando Sole
Author-email: sole@esrf.fr
License: MIT
-Download-URL: https://github.com/vasole/pymca/archive/v5.8.7.tar.gz
Platform: any
Classifier: Development Status :: 5 - Production/Stable
Classifier: Programming Language :: Python :: 3
@@ -26,7 +26,9 @@ License-File: LICENSE
License-File: LICENSE.GPL
License-File: LICENSE.LGPL
License-File: LICENSE.MIT
+Requires-Dist: numpy
+Requires-Dist: matplotlib>1.0
+Requires-Dist: fisx>=1.1.6
+Requires-Dist: h5py
Stand-alone application and Python tools for interactive and/or batch processing analysis of X-Ray Fluorescence Spectra. Graphical user interface (GUI) and batch processing capabilities provided
-
-
diff --git a/PyMca5.egg-info/PKG-INFO b/PyMca5.egg-info/PKG-INFO
index 4c137173..b73be076 100644
--- a/PyMca5.egg-info/PKG-INFO
+++ b/PyMca5.egg-info/PKG-INFO
@@ -1,12 +1,12 @@
Metadata-Version: 2.1
Name: PyMca5
-Version: 5.8.7
+Version: 5.9.2
Summary: Mapping and X-Ray Fluorescence Analysis
Home-page: http://pymca.sourceforge.net
+Download-URL: https://github.com/vasole/pymca/archive/v5.9.2.tar.gz
Author: V. Armando Sole
Author-email: sole@esrf.fr
License: MIT
-Download-URL: https://github.com/vasole/pymca/archive/v5.8.7.tar.gz
Platform: any
Classifier: Development Status :: 5 - Production/Stable
Classifier: Programming Language :: Python :: 3
@@ -26,7 +26,9 @@ License-File: LICENSE
License-File: LICENSE.GPL
License-File: LICENSE.LGPL
License-File: LICENSE.MIT
+Requires-Dist: numpy
+Requires-Dist: matplotlib>1.0
+Requires-Dist: fisx>=1.1.6
+Requires-Dist: h5py
Stand-alone application and Python tools for interactive and/or batch processing analysis of X-Ray Fluorescence Spectra. Graphical user interface (GUI) and batch processing capabilities provided
-
-
diff --git a/PyMca5.egg-info/SOURCES.txt b/PyMca5.egg-info/SOURCES.txt
index 8fdd95c3..ccb1e6ec 100644
--- a/PyMca5.egg-info/SOURCES.txt
+++ b/PyMca5.egg-info/SOURCES.txt
@@ -428,6 +428,7 @@ PyMca5/PyMcaGui/plotting/RGBCorrelatorGraph.py
PyMca5/PyMcaGui/plotting/RenameCurveDialog.py
PyMca5/PyMcaGui/plotting/ScatterPlotCorrelatorWidget.py
PyMca5/PyMcaGui/plotting/SilxMaskImageWidget.py
+PyMca5/PyMcaGui/plotting/SilxPlotActions.py
PyMca5/PyMcaGui/plotting/SilxRGBCorrelatorGraph.py
PyMca5/PyMcaGui/plotting/Silx_Icons.py
PyMca5/PyMcaGui/plotting/Toolbars.py
@@ -746,6 +747,7 @@ PyMca5/tests/ConfigDictTest.py
PyMca5/tests/DataTest.py
PyMca5/tests/EdfFileTest.py
PyMca5/tests/ElementsTest.py
+PyMca5/tests/FastXRFLinearFitTest.py
PyMca5/tests/GefitTest.py
PyMca5/tests/HDF5UtilsTest.py
PyMca5/tests/McaAdvancedFitWidgetTest.py
diff --git a/PyMca5/PyMcaCore/RedisTools.py b/PyMca5/PyMcaCore/RedisTools.py
index 27922dc7..e509d726 100644
--- a/PyMca5/PyMcaCore/RedisTools.py
+++ b/PyMca5/PyMcaCore/RedisTools.py
@@ -33,8 +33,13 @@ import logging
_logger = logging.getLogger(__name__)
from bliss.config import get_sessions_list
-from bliss.config.settings import scan as rdsscan
-from bliss.data.node import get_node, get_nodes
+try:
+ from blissdata.settings import scan as rdsscan
+ from blissdata.data.node import get_node, get_nodes
+except ImportError:
+ _logger.info("Trying deprecated access to Redis")
+ from bliss.config.settings import scan as rdsscan
+ from bliss.data.node import get_node, get_nodes
_NODE_TYPES = [ "channel",
"lima",
diff --git a/PyMca5/PyMcaCore/StackBase.py b/PyMca5/PyMcaCore/StackBase.py
index 5e86a675..89818bec 100644
--- a/PyMca5/PyMcaCore/StackBase.py
+++ b/PyMca5/PyMcaCore/StackBase.py
@@ -284,7 +284,7 @@ class StackBase(object):
elif self.mcaIndex == 0:
mcaMax = numpy.nanmax(numpy.nanmax(self._stack.data, axis=-1), axis=-1)
else:
- _logger.info("Unsupported index for max spectrum calculation")
+ logger.info("Unsupported index for max spectrum calculation")
else:
t0 = time.time()
shape = self._stack.data.shape
diff --git a/PyMca5/PyMcaGraph/Plot.py b/PyMca5/PyMcaGraph/Plot.py
index 24a8eb14..b66affe8 100644
--- a/PyMca5/PyMcaGraph/Plot.py
+++ b/PyMca5/PyMcaGraph/Plot.py
@@ -200,7 +200,9 @@ class Plot(PlotBase.PlotBase):
# zoom handling (should we take care of it?)
self.enableZoom = self.setZoomModeEnabled
- self.setZoomModeEnabled(True)
+ # next line was giving troubles with silx backend(s) issue #1026
+ if backend in ["matplotlib", "mpl"]:
+ self.setZoomModeEnabled(True)
self._defaultDataMargins = (0., 0., 0., 0.)
diff --git a/PyMca5/PyMcaGraph/backends/MatplotlibBackend.py b/PyMca5/PyMcaGraph/backends/MatplotlibBackend.py
index c36fb0bb..f4d4ae37 100644
--- a/PyMca5/PyMcaGraph/backends/MatplotlibBackend.py
+++ b/PyMca5/PyMcaGraph/backends/MatplotlibBackend.py
@@ -99,15 +99,18 @@ elif 'PyQt6.QtCore' in sys.modules:
QtGui.QApplication = QtWidgets.QApplication
else:
try:
- from PyQt4 import QtCore, QtGui
- matplotlib.rcParams['backend'] = 'Qt4Agg'
+ from PyQt5 import QtCore, QtGui, QtWidgets
+ QtGui.QApplication = QtWidgets.QApplication
+ matplotlib.rcParams['backend'] = 'Qt5Agg'
except ImportError:
try:
- from PyQt5 import QtCore, QtGui, QtWidgets
+ from PyQt6 import QtCore, QtGui, QtWidgets
QtGui.QApplication = QtWidgets.QApplication
matplotlib.rcParams['backend'] = 'Qt5Agg'
except ImportError:
- from PySide import QtCore, QtGui
+ from PySide6 import QtCore, QtGui, QtWidgets
+ QtGui.QApplication = QtWidgets.QApplication
+ matplotlib.rcParams['backend'] = 'Qt5Agg'
if ("PyQt4.QtCore" in sys.modules) or ("PySide.QtCore" in sys.modules):
from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas
TK = False
@@ -985,6 +988,11 @@ class MatplotlibGraph(FigureCanvas):
ddict['y'] = artist.get_ydata()
ddict['xdata'] = artist.get_xdata()
ddict['ydata'] = artist.get_ydata()
+ # matplotlib 3.7.x was giving different output than previous versions
+ for key in ["x", "y", "xdata", "ydata"]:
+ if hasattr(ddict[key], "__len__"):
+ if len(ddict[key]) == 1:
+ ddict[key] = ddict[key][0]
self._callback(ddict)
self._pickingInfo = {}
return
@@ -1598,7 +1606,7 @@ class MatplotlibBackend(PlotBackend.PlotBackend):
scatterPlot = True
if scatterPlot:
# scatter plot
- if color.dtype not in [numpy.float32, numpy.float]:
+ if color.dtype not in [numpy.float32, numpy.float64]:
actualColor = color / 255.
else:
actualColor = color
@@ -1665,6 +1673,8 @@ class MatplotlibBackend(PlotBackend.PlotBackend):
axes.fill_between(x, 1.0e-8, y)
#curveList[-1].set_fillstyle('bottom')
if hasattr(curveList[-1], "set_marker"):
+ if symbol is None:
+ symbol = "None"
curveList[-1].set_marker(symbol)
curveList[-1]._plot_info = {'color':color,
'linewidth':linewidth,
diff --git a/PyMca5/PyMcaGui/PyMcaQt.py b/PyMca5/PyMcaGui/PyMcaQt.py
index e71aeb93..4778a8a9 100644
--- a/PyMca5/PyMcaGui/PyMcaQt.py
+++ b/PyMca5/PyMcaGui/PyMcaQt.py
@@ -351,6 +351,53 @@ elif BINDING == 'PyQt6':
QAbstractItemView.AnyKeyPressed = QAbstractItemView.EditTrigger.AnyKeyPressed
QAbstractItemView.AllEditTriggers = QAbstractItemView.EditTrigger.AllEditTriggers
+ if not hasattr(QPalette, "Normal"):
+ if hasattr(QPalette, "Active"):
+ QPalette.Normal = QPalette.Active
+ else:
+ QPalette.Disabled = QPalette.ColorGroup.Disabled
+ QPalette.Active = QPalette.ColorGroup.Active
+ QPalette.Inactive = QPalette.ColorGroup.Inactive
+ QPalette.Normal = QPalette.ColorGroup.Normal
+
+ QPalette.Window = QPalette.ColorRole.Window
+ QPalette.WindowText = QPalette.ColorRole.WindowText
+ QPalette.Base = QPalette.ColorRole.Base
+ QPalette.AlternateBase = QPalette.ColorRole.AlternateBase
+ QPalette.ToolTipBase = QPalette.ColorRole.ToolTipBase
+ QPalette.ToolTipText = QPalette.ColorRole.ToolTipText
+ QPalette.PlaceholderText = QPalette.ColorRole.PlaceholderText
+ QPalette.Text = QPalette.ColorRole.Text
+ QPalette.Button = QPalette.ColorRole.Button
+ QPalette.ButtonText = QPalette.ColorRole.ButtonText
+ QPalette.BrightText = QPalette.ColorRole.BrightText
+
+ try:
+ from silx.gui import qt as SilxQt
+ if not hasattr(SilxQt.QPalette, "Normal"):
+ if hasattr(SilxQt.QPalette, "Active"):
+ SilxQt.QPalette.Normal = SilxQt.QPalette.Active
+ else:
+ SilxQt.QPalette.Disabled = SilxQt.QPalette.ColorGroup.Disabled
+ SilxQt.QPalette.Active = SilxQt.QPalette.ColorGroup.Active
+ SilxQt.QPalette.Inactive = SilxQt.QPalette.ColorGroup.Inactive
+ SilxQt.QPalette.Normal = SilxQt.QPalette.ColorGroup.Normal
+
+ SilxQt.QPalette.Window = SilxQt.QPalette.ColorRole.Window
+ SilxQt.QPalette.WindowText = SilxQt.QPalette.ColorRole.WindowText
+ SilxQt.QPalette.Base = SilxQt.QPalette.ColorRole.Base
+ SilxQt.QPalette.AlternateBase = SilxQt.QPalette.ColorRole.AlternateBase
+ SilxQt.QPalette.ToolTipBase = SilxQt.QPalette.ColorRole.ToolTipBase
+ SilxQt.QPalette.ToolTipText = SilxQt.QPalette.ColorRole.ToolTipText
+ SilxQt.QPalette.PlaceholderText = SilxQt.QPalette.ColorRole.PlaceholderText
+ SilxQt.QPalette.Text = SilxQt.QPalette.ColorRole.Text
+ SilxQt.QPalette.Button = SilxQt.QPalette.ColorRole.Button
+ SilxQt.QPalette.ButtonText = SilxQt.QPalette.ColorRole.ButtonText
+ SilxQt.QPalette.BrightText = SilxQt.QPalette.ColorRole.BrightText
+ except Exception:
+ _logger.info("Exception patching silx")
+ pass
+
# use a (bad) replacement for QDesktopWidget
class QDesktopWidget:
def height(self):
diff --git a/PyMca5/PyMcaGui/io/QSpecFileWidget.py b/PyMca5/PyMcaGui/io/QSpecFileWidget.py
index 256f8ad6..0b6900c7 100644
--- a/PyMca5/PyMcaGui/io/QSpecFileWidget.py
+++ b/PyMca5/PyMcaGui/io/QSpecFileWidget.py
@@ -819,7 +819,10 @@ class QSpecFileWidget(QSelectorWidget.QSelectorWidget):
else:
ddict['auto'] = "OFF"
ddict["2d"]= self.meshBox.isChecked()
- ddict["3d"]= self.object3DBox.isChecked()
+ if hasattr(self, "object3DBox"):
+ ddict["3d"] = self.object3DBox.isChecked()
+ else:
+ ddict["3d"] = False
ddict["mca"]= self.forceMcaBox.isChecked()
return ddict
diff --git a/PyMca5/PyMcaGui/io/SpecFileDataInfo.py b/PyMca5/PyMcaGui/io/SpecFileDataInfo.py
index 83082a5c..83809368 100644
--- a/PyMca5/PyMcaGui/io/SpecFileDataInfo.py
+++ b/PyMca5/PyMcaGui/io/SpecFileDataInfo.py
@@ -30,25 +30,19 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
import sys
from PyMca5.PyMcaGui import PyMcaQt as qt
-try:
- from silx.gui.widgets.TableWidget import TableWidget
-except ImportError:
- from PyMca5.PyMcaGui.misc.TableWidget import TableWidget
+from PyMca5.PyMcaGui.misc.TableWidget import TableWidget
QTVERSION = qt.qVersion()
class QTable(TableWidget):
def setText(self, row, col, text):
- if qt.qVersion() < "4.0.0":
- QTable.setText(self, row, col, text)
+ item = self.item(row, col)
+ if item is None:
+ item = qt.QTableWidgetItem(text,
+ qt.QTableWidgetItem.Type)
+ self.setItem(row, col, item)
else:
- item = self.item(row, col)
- if item is None:
- item = qt.QTableWidgetItem(text,
- qt.QTableWidgetItem.Type)
- self.setItem(row, col, item)
- else:
- item.setText(text)
+ item.setText(text)
class SpecFileDataInfoCustomEvent(qt.QEvent):
def __init__(self, ddict):
@@ -84,13 +78,10 @@ class SpecFileDataInfo(qt.QTabWidget):
]
def __init__(self, info, parent=None, name="DataSpecFileInfo", fl=0):
- if QTVERSION < '4.0.0':
- qt.QTabWidget.__init__(self, parent, name, fl)
- self.setContentsMargins(5, 5, 5, 5)
- else:
- qt.QTabWidget.__init__(self, parent)
- if name is not None:self.setWindowTitle(name)
- self._notifyCloseEventToWidget = []
+ qt.QTabWidget.__init__(self, parent)
+ if name is not None:
+ self.setWindowTitle(name)
+ self._notifyCloseEventToWidget = []
self.info= info
self.__createInfoTable()
self.__createMotorTable()
@@ -99,14 +90,13 @@ class SpecFileDataInfo(qt.QTabWidget):
self.__createEDFHeaderText()
self.__createFileHeaderText()
- if QTVERSION > '4.0.0':
- def sizeHint(self):
- return qt.QSize(2 * qt.QTabWidget.sizeHint(self).width(),
- 3 * qt.QTabWidget.sizeHint(self).height())
+ def sizeHint(self):
+ return qt.QSize(2 * qt.QTabWidget.sizeHint(self).width(),
+ 3 * qt.QTabWidget.sizeHint(self).height())
- def notifyCloseEventToWidget(self, widget):
- if widget not in self._notifyCloseEventToWidget:
- self._notifyCloseEventToWidget.append(widget)
+ def notifyCloseEventToWidget(self, widget):
+ if widget not in self._notifyCloseEventToWidget:
+ self._notifyCloseEventToWidget.append(widget)
def __createInfoTable(self):
pars= [ par for par in self.InfoTableItems if par[0] in self.info.keys() ]
@@ -119,41 +109,32 @@ class SpecFileDataInfo(qt.QTabWidget):
self.__adjustTable(table)
self.addTab(table, "Info")
- def __createTable(self, rows, head_par, head_val):
- if qt.qVersion() < '4.0.0':
- table= QTable(self)
+ def __createTable(self, rows, head_par, head_val, index=False):
+ table= QTable()
+ if index:
+ labels = ["Index"]
else:
- table= QTable()
- table.setColumnCount(2)
+ labels = []
+ labels = labels + [head_par, head_val]
+ table.setColumnCount(len(labels))
table.setRowCount(rows)
- if qt.qVersion() < '4.0.0':
- table.setReadOnly(1)
- table.setSelectionMode(QTable.SingleRow)
- else:
- table.setSelectionMode(qt.QTableWidget.NoSelection)
+ #table.setSelectionMode(qt.QTableWidget.NoSelection)
table.verticalHeader().hide()
- if qt.qVersion() < '4.0.0':
- table.setLeftMargin(0)
- table.horizontalHeader().setLabel(0, head_par)
- table.horizontalHeader().setLabel(1, head_val)
- else:
- labels = [head_par, head_val]
- for i in range(len(labels)):
- item = table.horizontalHeaderItem(i)
- if item is None:
- item = qt.QTableWidgetItem(labels[i],
- qt.QTableWidgetItem.Type)
- item.setText(labels[i])
- table.setHorizontalHeaderItem(i,item)
+ for i in range(len(labels)):
+ item = table.horizontalHeaderItem(i)
+ if item is None:
+ item = qt.QTableWidgetItem(labels[i],
+ qt.QTableWidgetItem.Type)
+ item.setText(labels[i])
+ table.setHorizontalHeaderItem(i,item)
return table
def __adjustTable(self, table):
for col in range(table.columnCount()):
table.resizeColumnToContents(col)
- if qt.qVersion() > '4.0.0':
- rheight = table.horizontalHeader().sizeHint().height()
- for row in range(table.rowCount()):
- table.setRowHeight(row, rheight)
+ rheight = table.horizontalHeader().sizeHint().height()
+ for row in range(table.rowCount()):
+ table.setRowHeight(row, rheight)
def __createMotorTable(self):
nameKeys = ["MotorNames", "motor_mne"]
@@ -188,17 +169,27 @@ class SpecFileDataInfo(qt.QTabWidget):
print("Incorrent number of labels or values")
return
if num:
- table= self.__createTable(num, "Motor", "Position")
- if sys.version_info > (3, 3):
- sorted_list = sorted(names, key=str.casefold)
- else:
- sorted_list = sorted(names)
- for i in range(num):
- idx = names.index(sorted_list[i])
- table.setText(i, 0, str(names[idx]))
- table.setText(i, 1, str(pos[idx]))
+ table= self.__createTable(num, "Motor", "Position", index=True)
+ numbers = list(range(num))
+ def sort_column(column, table=table, numbers=numbers, names=names, positions=pos):
+ if column == 1:
+ sorted_list = sorted(names, key=str.casefold)
+ sorted_list = [names.index(x) for x in sorted_list]
+ else:
+ sorted_list = [int(x) for x in numbers]
+ for i in range(num):
+ idx = sorted_list[i]
+ table.setText(i, 0, "%d" % numbers[idx])
+ table.setText(i, 1, str(names[idx]))
+ table.setText(i, 2, str(pos[idx]))
+ sort_column(0)
+ tip = "Copy selection to clipboard with CTRL-C."
+ tip += "\nDoubleclick on Index or Motor header to sort accordingly."
+ table.setToolTip(tip)
self.__adjustTable(table)
self.addTab(table, "Motors")
+ headerView = table.horizontalHeader()
+ headerView.sectionDoubleClicked[int].connect(sort_column)
def __createCounterTable(self):
nameKeys = ["LabelNames", "counter_mne"]
@@ -241,12 +232,8 @@ class SpecFileDataInfo(qt.QTabWidget):
return
text= self.info.get("Header", None)
if text is not None:
- if qt.qVersion() < '4.0.0':
- wid = qt.QTextEdit(self)
- wid.setText("\n".join(text))
- else:
- wid = qt.QTextEdit()
- wid.insertHtml("<BR>".join(text))
+ wid = qt.QTextEdit()
+ wid.insertHtml("<BR>".join(text))
wid.setReadOnly(1)
self.addTab(wid, "Scan Header")
@@ -274,12 +261,8 @@ class SpecFileDataInfo(qt.QTabWidget):
def __createFileHeaderText(self):
text= self.info.get("FileHeader", None)
if text not in [None, []]:
- if qt.qVersion() < '4.0.0':
- wid = qt.QTextEdit(self)
- wid.setText("\n".join(text))
- else:
- wid = qt.QTextEdit()
- wid.insertHtml("<BR>".join(text))
+ wid = qt.QTextEdit()
+ wid.insertHtml("<BR>".join(text))
wid.setReadOnly(1)
self.addTab(wid, "File Header")
diff --git a/PyMca5/PyMcaGui/io/hdf5/Hdf5NodeView.py b/PyMca5/PyMcaGui/io/hdf5/Hdf5NodeView.py
index 5f40497a..d3913346 100644
--- a/PyMca5/PyMcaGui/io/hdf5/Hdf5NodeView.py
+++ b/PyMca5/PyMcaGui/io/hdf5/Hdf5NodeView.py
@@ -110,6 +110,10 @@ class Plot2DWithPlugins(Plot2D):
method="getPlugin2DInstance",
directoryList=PLUGINS_DIR)
self._toolbar.addWidget(pluginsToolButton)
+ if hasattr(self, "getIntensityHistogramAction"):
+ self.getIntensityHistogramAction().setVisible(True)
+ else:
+ print("Plot2D getIntensityHistogramAction missing")
class Plot2DViewWithPlugins(DataViews._Plot2dView):
@@ -117,7 +121,6 @@ class Plot2DViewWithPlugins(DataViews._Plot2dView):
widget = Plot2DWithPlugins(parent=parent)
widget.setDefaultColormap(self.defaultColormap())
widget.getColormapAction().setColorDialog(self.defaultColorDialog())
- widget.getIntensityHistogramAction().setVisible(True)
widget.setKeepDataAspectRatio(False)
widget.getXAxis().setLabel('X')
widget.getYAxis().setLabel('Y')
diff --git a/PyMca5/PyMcaGui/physics/xrf/McaCalWidget.py b/PyMca5/PyMcaGui/physics/xrf/McaCalWidget.py
index f0244a8e..2f4c9b85 100644
--- a/PyMca5/PyMcaGui/physics/xrf/McaCalWidget.py
+++ b/PyMca5/PyMcaGui/physics/xrf/McaCalWidget.py
@@ -429,7 +429,7 @@ class McaCalWidget(qt.QDialog):
self.current = current
order = dict['caldict'][current]['order']
self.caldict[current]['order'] = order
- if order == "ID18":
+ if order in ["ID18", "ID14"]:
result = self.timeCalibratorCalibration()
if result is None:
return
@@ -808,6 +808,8 @@ class McaCalWidget(qt.QDialog):
"""
if order == "TOF":
return self.calculateTOF(usedpeaks)
+ if order in ["ID18", "ID14"]:
+ order = 2
if len(usedpeaks) == 1:
if (usedpeaks[0][0] - 0.0) > 1.0E-20:
return [0.0, usedpeaks[0][1]/usedpeaks[0][0], 0.0]
@@ -1024,7 +1026,7 @@ class CalibrationParameters(qt.QWidget):
options=['1st','2nd'])
else:
self.orderbox = SimpleComboBox(parw,
- options=['1st','2nd','TOF', 'ID18'])
+ options=['1st','2nd','TOF', 'ID14'])
layout.addWidget(lab)
layout.addWidget(self.orderbox)
lab= qt.QLabel("A:", parw)
@@ -1126,8 +1128,8 @@ class CalibrationParameters(qt.QWidget):
self.CLabel.setText("Vr:")
self.CText.setReadOnly(0)
self.CFixed.show()
- elif qstring == "ID18":
- self.caldict[self.currentcal]['order'] = 'ID18'
+ elif qstring in ["ID14", "ID18"]:
+ self.caldict[self.currentcal]['order'] = 'ID14'
self.CLabel.setText("C:")
self.CText.setReadOnly(1)
if QTVERSION > '4.0.0':
diff --git a/PyMca5/PyMcaGui/physics/xrf/QtMcaAdvancedFitReport.py b/PyMca5/PyMcaGui/physics/xrf/QtMcaAdvancedFitReport.py
index afe0c54b..3898d1c3 100644
--- a/PyMca5/PyMcaGui/physics/xrf/QtMcaAdvancedFitReport.py
+++ b/PyMca5/PyMcaGui/physics/xrf/QtMcaAdvancedFitReport.py
@@ -739,6 +739,9 @@ class QtMcaAdvancedFitReport:
for peak0 in iterator:
name = peak0+"esc"
peak = peak0+"esc"
+ if name not in result[group]:
+ # i.e. peak0 = "Al K" and Si detector
+ continue
if result[group][name]['ratio'] > 0.0:
text += '<tr><td></td>'
energy = ("%.3f" % (result[group][peak]['energy']))
diff --git a/PyMca5/PyMcaGui/plotting/MaskScatterWidget.py b/PyMca5/PyMcaGui/plotting/MaskScatterWidget.py
index 3f64fcb4..312a221f 100644
--- a/PyMca5/PyMcaGui/plotting/MaskScatterWidget.py
+++ b/PyMca5/PyMcaGui/plotting/MaskScatterWidget.py
@@ -191,7 +191,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow):
image = numpy.histogram2d(y[idx], x[idx],
bins=bins,
#range=(binsY, binsX),
- normed=False)
+ density=False)
self._binsX = image[2]
self._binsY = image[1]
self._bins = bins
@@ -240,7 +240,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow):
self.yScale = (y0, deltaY)
binsX = numpy.arange(bins[0]) * deltaX
binsY = numpy.arange(bins[1]) * deltaY
- image = numpy.histogram2d(y[idx], x[idx], bins=(binsY, binsX), normed=False)
+ image = numpy.histogram2d(y[idx], x[idx], bins=(binsY, binsX), density=False)
self._binsX = image[2]
self._binsY = image[1]
self._bins = bins
@@ -258,13 +258,13 @@ class MaskScatterWidget(PlotWindow.PlotWindow):
mask = numpy.round(numpy.histogram2d(y[idx], x[idx],
bins=(binsY, binsX),
weights=weights,
- normed=True)[0] * weightsSum * volume).astype(numpy.uint8)
+ density=True)[0] * weightsSum * volume).astype(numpy.uint8)
else:
#print("GOOD PATH")
mask = numpy.histogram2d(y[idx], x[idx],
bins=(binsY, binsX),
weights=weights,
- normed=False)[0]
+ density=False)[0]
mask[mask > 0] = 1
#print(mask.min(), mask.max())
self._densityPlotWidget.setSelectionMask(mask, plot=False)
@@ -454,7 +454,9 @@ class MaskScatterWidget(PlotWindow.PlotWindow):
color = self._selectionColors[i].copy()
if useAlpha:
if len(color) == 4:
- if type(color[3]) in [numpy.uint8, numpy.int]:
+ if type(color[3]) in [numpy.uint8,
+ numpy.int32,
+ numpy.int64]:
color[3] = self._alphaLevel
# a copy of the input info is needed in order not
# to set the main curve to that color
diff --git a/PyMca5/PyMcaGui/plotting/Q4PyMcaPrintPreview.py b/PyMca5/PyMcaGui/plotting/Q4PyMcaPrintPreview.py
index 571a9e36..20bd248b 100644
--- a/PyMca5/PyMcaGui/plotting/Q4PyMcaPrintPreview.py
+++ b/PyMca5/PyMcaGui/plotting/Q4PyMcaPrintPreview.py
@@ -606,7 +606,7 @@ class GraphicsResizeRectItem(qt.QGraphicsRectItem):
pen.setStyle(qt.Qt.NoPen)
self.setPen(pen)
self.setBrush(color)
- self.setFlag(self.ItemIsMovable, True)
+ self.setFlag(qt.QGraphicsItem.ItemIsMovable, True)
self.show()
def hoverEnterEvent(self, event):
diff --git a/PyMca5/PyMcaGui/plotting/SilxMaskImageWidget.py b/PyMca5/PyMcaGui/plotting/SilxMaskImageWidget.py
index 7f1f66ce..4d6173cf 100644
--- a/PyMca5/PyMcaGui/plotting/SilxMaskImageWidget.py
+++ b/PyMca5/PyMcaGui/plotting/SilxMaskImageWidget.py
@@ -65,7 +65,8 @@ logging.disable(logging.ERROR)
import silx
from silx.gui.plot import PlotWidget
-from silx.gui.plot import PlotActions
+from PyMca5.PyMcaGui.plotting import SilxPlotActions as PlotActions
+
from silx.gui.plot import PlotToolButtons
from silx.gui.plot.MaskToolsWidget import MaskToolsWidget, MaskToolsDockWidget
from silx.gui.plot.AlphaSlider import NamedImageAlphaSlider
@@ -579,6 +580,11 @@ class SilxMaskImageWidget(qt.QMainWindow):
PlotActions.ColormapAction(plot=self.plot, parent=self))
self.addAction(self.colormapAction)
+ self.pixelIntensitiesHistoAction = self.group.addAction(
+ PlotActions.PixelIntensitiesHistoAction(plot=self.plot, parent=self))
+ self.addAction(self.pixelIntensitiesHistoAction)
+ self.pixelIntensitiesHistoAction.setVisible(True)
+
self.copyAction = self.group.addAction(
PlotActions.CopyAction(plot=self.plot, parent=self))
self.addAction(self.copyAction)
@@ -675,8 +681,8 @@ class SilxMaskImageWidget(qt.QMainWindow):
index = objects.index(self.colormapAction)
objects.insert(index + 1, self.keepDataAspectRatioButton)
objects.insert(index + 2, self.yAxisInvertedButton)
- objects.insert(index + 3, self.saveToolbutton)
- objects.insert(index + 4, self.backgroundButton)
+ objects.insert(index + 4, self.saveToolbutton)
+ objects.insert(index + 5, self.backgroundButton)
for obj in objects:
if isinstance(obj, qt.QAction):
toolbar.addAction(obj)
diff --git a/PyMca5/PyMcaGui/plotting/SilxPlotActions.py b/PyMca5/PyMcaGui/plotting/SilxPlotActions.py
new file mode 100644
index 00000000..b3c76508
--- /dev/null
+++ b/PyMca5/PyMcaGui/plotting/SilxPlotActions.py
@@ -0,0 +1,35 @@
+#/*##########################################################################
+# Copyright (C) 2023 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.
+#
+#############################################################################*/
+__author__ = "V.A. Sole - ESRF Data Analysis"
+__contact__ = "sole@esrf.fr"
+__license__ = "MIT"
+__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
+
+__doc__ = """Silx 1.x and 2.x compatibility layer"""
+
+try:
+ from silx.gui.plot.PlotActions import *
+except ImportError:
+ from silx.gui.plot.actions.control import *
+ from silx.gui.plot.actions.io import *
+ from silx.gui.plot.actions.histogram import PixelIntensitiesHistoAction
diff --git a/PyMca5/PyMcaGui/pymca/McaWindow.py b/PyMca5/PyMcaGui/pymca/McaWindow.py
index d670dcb1..ce5f2d2a 100644
--- a/PyMca5/PyMcaGui/pymca/McaWindow.py
+++ b/PyMca5/PyMcaGui/pymca/McaWindow.py
@@ -1031,7 +1031,7 @@ class McaWindow(ScanWindow):
legend=legend,
info=curveinfo,
own=True)
- if calibrationOrder == 'ID18':
+ if calibrationOrder in ["ID14", "ID18"]:
self.setGraphXLabel('Time')
else:
self.setGraphXLabel('Energy')
@@ -1076,7 +1076,7 @@ class McaWindow(ScanWindow):
legend=legend,
info=curveinfo,
own=True)
- if calibrationOrder == 'ID18':
+ if calibrationOrder in ["ID14", "ID18"]:
self.setGraphXLabel('Time')
else:
self.setGraphXLabel('Energy')
diff --git a/PyMca5/PyMcaGui/pymca/PyMcaBatch.py b/PyMca5/PyMcaGui/pymca/PyMcaBatch.py
index 96a032c2..eea0aea1 100644
--- a/PyMca5/PyMcaGui/pymca/PyMcaBatch.py
+++ b/PyMca5/PyMcaGui/pymca/PyMcaBatch.py
@@ -35,6 +35,7 @@ import subprocess
import signal
import atexit
import logging
+import traceback
from glob import glob
from contextlib import contextmanager
try:
@@ -1822,6 +1823,10 @@ class McaBatchWindow(qt.QWidget):
except Exception:
_logger.warning("ERROR on REPORT %s", sys.exc_info())
_logger.warning("%s", sys.exc_info()[1])
+ try:
+ _logger.warning("%s", ''.join(traceback.format_tb(sys.exc_info()[2])))
+ except Exception:
+ pass
_logger.warning("filename = %s key =%s " , filename, key)
_logger.warning("If your batch is stopped, please report this")
_logger.warning("error sending the above mentioned file and the")
diff --git a/PyMca5/PyMcaGui/pymca/PyMcaImageWindow.py b/PyMca5/PyMcaGui/pymca/PyMcaImageWindow.py
index e0f43c89..ee3c156d 100644
--- a/PyMca5/PyMcaGui/pymca/PyMcaImageWindow.py
+++ b/PyMca5/PyMcaGui/pymca/PyMcaImageWindow.py
@@ -88,7 +88,7 @@ class PyMcaImageWindow(RGBImageCalculator.RGBImageCalculator):
self._connectCorrelator()
if self._imageData is None:
return
- if self._imageData == []:
+ if len(self._imageData) == 0:
return
if not RGBImageCalculator.RGBImageCalculator._addImageClicked(self):
diff --git a/PyMca5/PyMcaGui/pymca/PyMcaMain.py b/PyMca5/PyMcaGui/pymca/PyMcaMain.py
index 12e385be..71549d40 100644
--- a/PyMca5/PyMcaGui/pymca/PyMcaMain.py
+++ b/PyMca5/PyMcaGui/pymca/PyMcaMain.py
@@ -127,11 +127,7 @@ if __name__ == '__main__':
from PyMca5.PyMcaGui import PyMcaQt as qt
from PyMca5.PyMcaGui.io import PyMcaFileDialogs
QTVERSION = qt.qVersion()
-if sys.platform == 'darwin':
- if backend is not None:
- if backend.lower() in ["gl", "opengl"]:
- if hasattr(qt, 'QOpenGLWidget'):
- print("Warning: OpenGL backend not fully supported")
+
try:
import silx
# try to import silx prior to importing matplotlib to prevent
diff --git a/PyMca5/PyMcaGui/pymca/QHDF5StackWizard.py b/PyMca5/PyMcaGui/pymca/QHDF5StackWizard.py
index 34c62c75..20810807 100644
--- a/PyMca5/PyMcaGui/pymca/QHDF5StackWizard.py
+++ b/PyMca5/PyMcaGui/pymca/QHDF5StackWizard.py
@@ -35,6 +35,9 @@ safe_str = qt.safe_str
from PyMca5.PyMcaGui.io.hdf5 import QNexusWidget
from PyMca5.PyMcaCore import NexusDataSource
from PyMca5 import PyMcaDirs
+import logging
+
+_logger = logging.getLogger(__name__)
class IntroductionPage(qt.QWizardPage):
@@ -150,6 +153,12 @@ class StackIndexWidget(qt.QWidget):
self.buttonGroup.buttonClicked[int].connect(self._slot)
def _slot(self, button):
+ if hasattr(button, "text"):
+ # received a button
+ pass
+ else:
+ # received an integer
+ button = self.buttonGroup.button(button)
if "first" in safe_str(button.text()).lower():
self._stackIndex = 0
else:
diff --git a/PyMca5/PyMcaGui/pymca/RGBImageCalculator.py b/PyMca5/PyMcaGui/pymca/RGBImageCalculator.py
index 6f315ad8..76cd538b 100644
--- a/PyMca5/PyMcaGui/pymca/RGBImageCalculator.py
+++ b/PyMca5/PyMcaGui/pymca/RGBImageCalculator.py
@@ -331,7 +331,7 @@ class RGBImageCalculator(qt.QWidget):
_logger.debug("Add image clicked")
if self._imageData is None:
return
- if self._imageData == []:
+ if len(self._imageData) == 0:
return
text = "%s" % self.name.text()
if not len(text):
diff --git a/PyMca5/PyMcaGui/pymca/ScanWindowInfoWidget.py b/PyMca5/PyMcaGui/pymca/ScanWindowInfoWidget.py
index fa6f7247..7138bbfe 100644
--- a/PyMca5/PyMcaGui/pymca/ScanWindowInfoWidget.py
+++ b/PyMca5/PyMcaGui/pymca/ScanWindowInfoWidget.py
@@ -31,6 +31,10 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
import numpy
from PyMca5.PyMcaGui import PyMcaQt as qt
+import logging
+
+_logger = logging.getLogger(__name__)
+
QTVERSION = qt.qVersion()
"""
diff --git a/PyMca5/PyMcaGui/pymca/SilxMcaWindow.py b/PyMca5/PyMcaGui/pymca/SilxMcaWindow.py
index 09c487ea..30522adb 100644
--- a/PyMca5/PyMcaGui/pymca/SilxMcaWindow.py
+++ b/PyMca5/PyMcaGui/pymca/SilxMcaWindow.py
@@ -1022,7 +1022,7 @@ class McaWindow(ScanWindow.ScanWindow):
legend=legend,
info=curveinfo,
own=True)
- if calibrationOrder == 'ID18':
+ if calibrationOrder in ["ID14", "ID18"]:
self.setGraphXLabel('Time')
else:
self.setGraphXLabel('Energy')
@@ -1066,7 +1066,7 @@ class McaWindow(ScanWindow.ScanWindow):
legend=legend,
info=curveinfo,
own=True)
- if calibrationOrder == 'ID18':
+ if calibrationOrder in ["ID14", "ID18"]:
self.setGraphXLabel('Time')
else:
self.setGraphXLabel('Energy')
diff --git a/PyMca5/PyMcaGui/pymca/StackPluginResultsWindow.py b/PyMca5/PyMcaGui/pymca/StackPluginResultsWindow.py
index 40386e7f..1a167876 100644
--- a/PyMca5/PyMcaGui/pymca/StackPluginResultsWindow.py
+++ b/PyMca5/PyMcaGui/pymca/StackPluginResultsWindow.py
@@ -1,5 +1,5 @@
-#/*##########################################################################
-# Copyright (C) 2004-2022 European Synchrotron Radiation Facility
+# /*##########################################################################
+# Copyright (C) 2004-2023 European Synchrotron Radiation Facility
#
# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
# the ESRF.
@@ -31,6 +31,7 @@ import os
import sys
import numpy
from PyMca5.PyMcaGui import PyMcaQt as qt
+
if hasattr(qt, "QString"):
QString = qt.QString
else:
@@ -43,30 +44,32 @@ from PyMca5.PyMcaGui.pymca import ImageListStatsWidget
from PyMca5.PyMcaGui.io import PyMcaFileDialogs
from PyMca5.PyMcaIO import ArraySave
+
class StackPluginResultsWindow(MaskImageWidget.MaskImageWidget):
def __init__(self, *var, **kw):
ddict = {}
- ddict['usetab'] = kw.get("usetab",True)
- ddict['aspect'] = kw.get("aspect",True)
- ddict['profileselection'] = kw.get("profileselection",True)
+ ddict["usetab"] = kw.get("usetab", True)
+ ddict["aspect"] = kw.get("aspect", True)
+ ddict["profileselection"] = kw.get("profileselection", True)
ddict.update(kw)
- ddict['standalonesave'] = False
+ ddict["standalonesave"] = False
MaskImageWidget.MaskImageWidget.__init__(self, *var, **ddict)
self.slider = qt.QSlider(self)
self.slider.setOrientation(qt.Qt.Horizontal)
self.slider.setMinimum(0)
self.slider.setMaximum(0)
- if ddict['usetab']:
+ if ddict["usetab"]:
# The 1D graph
self.spectrumGraph = ScanWindow.ScanWindow(self)
self.spectrumGraph.enableOwnSave(False)
- self.spectrumGraph.sigIconSignal.connect( \
- self._spectrumGraphIconSlot)
+ self.spectrumGraph.sigIconSignal.connect(self._spectrumGraphIconSlot)
self.spectrumGraph.saveMenu = qt.QMenu()
- self.spectrumGraph.saveMenu.addAction(QString("Save From Current"),
- self.saveCurrentSpectrum)
- self.spectrumGraph.saveMenu.addAction(QString("Save From All"),
- self.saveAllSpectra)
+ self.spectrumGraph.saveMenu.addAction(
+ QString("Save From Current"), self.saveCurrentSpectrum
+ )
+ self.spectrumGraph.saveMenu.addAction(
+ QString("Save From All"), self.saveAllSpectra
+ )
self.mainTab.addTab(self.spectrumGraph, "VECTORS")
self.mainLayout.addWidget(self.slider)
@@ -79,43 +82,42 @@ class StackPluginResultsWindow(MaskImageWidget.MaskImageWidget):
self.spectrumGraphTitles = None
standalonesave = kw.get("standalonesave", True)
if standalonesave:
- self.graphWidget.saveToolButton.clicked.connect(\
- self._saveToolButtonSignal)
+ self.graphWidget.saveToolButton.clicked.connect(self._saveToolButtonSignal)
self._saveMenu = qt.QMenu()
- self._saveMenu.addAction(QString("Image Data"),
- self.saveImageList)
- self._saveMenu.addAction(QString("Standard Graphics"),
- self.graphWidget._saveIconSignal)
- self._saveMenu.addAction(QString("Matplotlib") ,
- self._saveMatplotlibImage)
+ self._saveMenu.addAction(QString("Image Data"), self.saveImageList)
+ self._saveMenu.addAction(
+ QString("Standard Graphics"), self.graphWidget._saveIconSignal
+ )
+ self._saveMenu.addAction(QString("Matplotlib"), self._saveMatplotlibImage)
self.multiplyIcon = qt.QIcon(qt.QPixmap(IconDict["swapsign"]))
infotext = "Multiply image by -1"
- self.multiplyButton = self.graphWidget._addToolButton(\
- self.multiplyIcon,
- self._multiplyIconChecked,
- infotext,
- toggle = False,
- position = 12)
+ self.multiplyButton = self.graphWidget._addToolButton(
+ self.multiplyIcon,
+ self._multiplyIconChecked,
+ infotext,
+ toggle=False,
+ position=12,
+ )
# The density plot widget
self.__scatterPlotWidgetDataToUpdate = True
- self.scatterPlotWidget = ScatterPlotCorrelatorWidget.ScatterPlotCorrelatorWidget(None,
- labels=["Legend",
- "X",
- "Y"],
- types=["Text",
- "RadioButton",
- "RadioButton"],
- maxNRois=1)
+ self.scatterPlotWidget = (
+ ScatterPlotCorrelatorWidget.ScatterPlotCorrelatorWidget(
+ None,
+ labels=["Legend", "X", "Y"],
+ types=["Text", "RadioButton", "RadioButton"],
+ maxNRois=1,
+ )
+ )
self.__scatterPlotWidgetDataToUpdate = True
self.__maskToScatterConnected = True
self.sigMaskImageWidgetSignal.connect(self._internalSlot)
- self.scatterPlotWidget.sigMaskScatterWidgetSignal.connect( \
- self._internalSlot)
+ self.scatterPlotWidget.sigMaskScatterWidgetSignal.connect(self._internalSlot)
# add the command to show it to the menu
- self.additionalSelectionMenu().addAction(QString("Show scatter plot"),
- self.showScatterPlot)
+ self.additionalSelectionMenu().addAction(
+ QString("Show scatter plot"), self.showScatterPlot
+ )
# The stats widget
self.statsWidget = None
@@ -149,9 +151,9 @@ class StackPluginResultsWindow(MaskImageWidget.MaskImageWidget):
self.spectrumGraph.replot()
def buildAndConnectImageButtonBox(self, replace=True, multiple=False):
- super(StackPluginResultsWindow, self).\
- buildAndConnectImageButtonBox(replace=replace,
- multiple=multiple)
+ super(StackPluginResultsWindow, self).buildAndConnectImageButtonBox(
+ replace=replace, multiple=multiple
+ )
def showImage(self, index=0, moveslider=True):
if self.imageList is None:
@@ -164,9 +166,15 @@ class StackPluginResultsWindow(MaskImageWidget.MaskImageWidget):
if moveslider:
self.slider.setValue(index)
- def setStackPluginResults(self, images, spectra=None,
- image_names = None, spectra_names = None,
- xvalues=None, spectra_titles=None):
+ def setStackPluginResults(
+ self,
+ images,
+ spectra=None,
+ image_names=None,
+ spectra_names=None,
+ xvalues=None,
+ spectra_titles=None,
+ ):
self.spectrumList = spectra
if type(images) == type([]):
self.imageList = images
@@ -180,13 +188,13 @@ class StackPluginResultsWindow(MaskImageWidget.MaskImageWidget):
nimages = images.shape[0]
self.imageList = [0] * nimages
for i in range(nimages):
- self.imageList[i] = images[i,:]
+ self.imageList[i] = images[i, :]
if 0:
- #leave the data as they originally come
+ # leave the data as they originally come
if self.imageList[i].max() < 0:
self.imageList[i] *= -1
if self.spectrumList is not None:
- self.spectrumList [i] *= -1
+ self.spectrumList[i] *= -1
if image_names is None:
self.imageNames = []
for i in range(nimages):
@@ -195,7 +203,7 @@ class StackPluginResultsWindow(MaskImageWidget.MaskImageWidget):
self.imageNames = image_names
if self.imageList is not None:
- self.slider.setMaximum(len(self.imageList)-1)
+ self.slider.setMaximum(len(self.imageList) - 1)
self.showImage(0)
else:
self.slider.setMaximum(0)
@@ -246,9 +254,11 @@ class StackPluginResultsWindow(MaskImageWidget.MaskImageWidget):
# only the the scatter plot to be updated unless hidden
if self.scatterPlotWidget.isHidden():
return
- if ddict["event"] in ["selectionMaskChanged",
- "resetSelection",
- "invertSelection"]:
+ if ddict["event"] in [
+ "selectionMaskChanged",
+ "resetSelection",
+ "invertSelection",
+ ]:
mask = self.getSelectionMask()
if mask is None:
mask = numpy.zeros(self.imageList[0].shape, numpy.uint8)
@@ -258,12 +268,13 @@ class StackPluginResultsWindow(MaskImageWidget.MaskImageWidget):
elif ddict["id"] == id(self.scatterPlotWidget):
# signal generated by the scatter plot
- if ddict["event"] in ["selectionMaskChanged",
- "resetSelection",
- "invertSelection"]:
+ if ddict["event"] in [
+ "selectionMaskChanged",
+ "resetSelection",
+ "invertSelection",
+ ]:
mask = self.scatterPlotWidget.getSelectionMask()
- super(StackPluginResultsWindow, self).setSelectionMask(mask,
- plot=True)
+ super(StackPluginResultsWindow, self).setSelectionMask(mask, plot=True)
ddict["id"] = id(self)
try:
self.__maskToScatterConnected = False
@@ -275,6 +286,8 @@ class StackPluginResultsWindow(MaskImageWidget.MaskImageWidget):
super(StackPluginResultsWindow, self).setSelectionMask(*var, **kw)
if not self.scatterPlotWidget.isHidden():
self._updateScatterPlotWidget()
+ if self.statsWidget is not None:
+ self.statsWidget.setSelectionMask(self.getSelectionMask())
def showScatterPlot(self):
if self.scatterPlotWidget.isHidden():
@@ -287,10 +300,10 @@ class StackPluginResultsWindow(MaskImageWidget.MaskImageWidget):
return
labels = []
for i in range(len(self.imageList)):
- labels.append(self.imageNames[i].replace(" ","_"))
- return MaskImageWidget.MaskImageWidget.saveImageList(self,
- imagelist=self.imageList,
- labels=labels)
+ labels.append(self.imageNames[i].replace(" ", "_"))
+ return MaskImageWidget.MaskImageWidget.saveImageList(
+ self, imagelist=self.imageList, labels=labels
+ )
def _spectrumGraphIconSlot(self, ddict):
if ddict["event"] == "iconClicked" and ddict["key"] == "save":
@@ -300,21 +313,25 @@ class StackPluginResultsWindow(MaskImageWidget.MaskImageWidget):
return self.spectrumGraph._QSimpleOperation("save")
def saveAllSpectra(self):
- fltrs = ['Raw ASCII *.txt',
- '","-separated CSV *.csv',
- '";"-separated CSV *.csv',
- '"tab"-separated CSV *.csv',
- 'OMNIC CSV *.csv']
+ fltrs = [
+ "Raw ASCII *.txt",
+ '","-separated CSV *.csv',
+ '";"-separated CSV *.csv',
+ '"tab"-separated CSV *.csv',
+ "OMNIC CSV *.csv",
+ ]
message = "Enter file name to be used as root"
- fileList, fileFilter = PyMcaFileDialogs.getFileList(parent=self,
- filetypelist=fltrs,
- message=message,
- currentdir=None,
- mode="SAVE",
- getfilter=True,
- single=True,
- currentfilter=None,
- native=None)
+ fileList, fileFilter = PyMcaFileDialogs.getFileList(
+ parent=self,
+ filetypelist=fltrs,
+ message=message,
+ currentdir=None,
+ mode="SAVE",
+ getfilter=True,
+ single=True,
+ currentfilter=None,
+ native=None,
+ )
if not len(fileList):
return
@@ -335,7 +352,7 @@ class StackPluginResultsWindow(MaskImageWidget.MaskImageWidget):
# extension is csv but saved as ASCII
csv = False
ext = "csv"
- csvseparator = ","
+ csvseparator = ","
else:
csv = True
ext = "csv"
@@ -356,22 +373,23 @@ class StackPluginResultsWindow(MaskImageWidget.MaskImageWidget):
x = self.xValues[index]
y = self.spectrumList[index]
filename = os.path.join(dirname, root + fmt % (index, ext))
- ArraySave.saveXY(x, y, filename, ylabel=legend,
- csv=csv, csvseparator=csvseparator)
+ ArraySave.saveXY(
+ x, y, filename, ylabel=legend, csv=csv, csvseparator=csvseparator
+ )
def setImageList(self, imagelist):
self.imageList = imagelist
self.spectrumList = None
if imagelist is not None:
- self.slider.setMaximum(len(self.imageList)-1)
+ self.slider.setMaximum(len(self.imageList) - 1)
self.showImage(0)
def _addAllImageClicked(self):
ddict = {}
- ddict['event'] = "addAllClicked"
- ddict['images'] = self.imageList
- ddict['titles'] = self.imageNames
- ddict['id'] = id(self)
+ ddict["event"] = "addAllClicked"
+ ddict["images"] = self.imageList
+ ddict["titles"] = self.imageNames
+ ddict["id"] = id(self)
self.emitMaskImageSignal(ddict)
def showStatsWidget(self):
@@ -383,6 +401,7 @@ class StackPluginResultsWindow(MaskImageWidget.MaskImageWidget):
self.statsWidget.setImageList(self.imageList, image_names=self.imageNames)
self.statsWidget.show()
+
def test():
app = qt.QApplication([])
app.lastWindowClosed.connect(app.quit)
@@ -390,18 +409,24 @@ def test():
container = StackPluginResultsWindow()
data = numpy.arange(20000)
data.shape = 2, 100, 100
- data[1, 0:100,0:50] = 100
- container.setStackPluginResults(data, spectra=[numpy.arange(100.), numpy.arange(100.)+10],
- image_names=["I1", "I2"], spectra_names=["V1", "V2"])
+ data[1, 0:100, 0:50] = 100
+ container.setStackPluginResults(
+ data,
+ spectra=[numpy.arange(100.0), numpy.arange(100.0) + 10],
+ image_names=["I1", "I2"],
+ spectra_names=["V1", "V2"],
+ )
container.show()
container.showStatsWidget()
+
def theSlot(ddict):
- print(ddict['event'])
+ print(ddict["event"])
container.sigMaskImageWidgetSignal.connect(theSlot)
app.exec()
+
if __name__ == "__main__":
import numpy
- test()
+ test()
diff --git a/PyMca5/PyMcaGui/pymca/XMCDWindow.py b/PyMca5/PyMcaGui/pymca/XMCDWindow.py
index 07cdb405..88aafe62 100644
--- a/PyMca5/PyMcaGui/pymca/XMCDWindow.py
+++ b/PyMca5/PyMcaGui/pymca/XMCDWindow.py
@@ -1,5 +1,5 @@
-#/*##########################################################################
-# Copyright (C) 2004-2022 European Synchrotron Radiation Facility
+# /*##########################################################################
+# Copyright (C) 2004-2023 European Synchrotron Radiation Facility
#
# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
# the ESRF.
@@ -52,9 +52,10 @@ _logger = logging.getLogger(__name__)
if _logger.getEffectiveLevel() == logging.DEBUG:
numpy.set_printoptions(threshold=50)
-NEWLINE = '\n'
-class TreeWidgetItem(qt.QTreeWidgetItem):
+NEWLINE = "\n"
+
+class TreeWidgetItem(qt.QTreeWidgetItem):
__legendColumn = 1
def __init__(self, parent, itemList):
@@ -62,96 +63,101 @@ class TreeWidgetItem(qt.QTreeWidgetItem):
def __lt__(self, other):
col = self.treeWidget().sortColumn()
- val = self.text(col)
+ val = self.text(col)
valOther = other.text(col)
if val == valOther:
ret = False
- elif val in ['---']:
+ elif val in ["---"]:
ret = True
elif col > self.__legendColumn:
try:
- ret = (float(val) < float(valOther))
+ ret = float(val) < float(valOther)
except ValueError:
- ret = val < valOther
+ ret = val < valOther
else:
- ret = val < valOther
+ ret = val < valOther
return ret
-class XMCDOptions(qt.QDialog):
+class XMCDOptions(qt.QDialog):
def __init__(self, parent, mList, full=True):
qt.QDialog.__init__(self, parent)
- self.setWindowTitle('XLD/XMCD Options')
+ self.setWindowTitle("XLD/XMCD Options")
self.setModal(True)
self.motorList = mList
self.saved = False
# Buttons
- buttonOK = qt.QPushButton('OK')
- buttonOK.setToolTip('Accept the configuration')
- buttonCancel = qt.QPushButton('Cancel')
- buttonCancel.setToolTip('Return to XMCD Analysis\nwithout changes')
+ buttonOK = qt.QPushButton("OK")
+ buttonOK.setToolTip("Accept the configuration")
+ buttonCancel = qt.QPushButton("Cancel")
+ buttonCancel.setToolTip("Return to XMCD Analysis\nwithout changes")
if full:
- buttonSave = qt.QPushButton('Save')
- buttonSave.setToolTip('Save configuration to *.cfg-File')
- buttonLoad = qt.QPushButton('Load')
- buttonLoad.setToolTip('Load existing configuration from *.cfg-File')
+ buttonSave = qt.QPushButton("Save")
+ buttonSave.setToolTip("Save configuration to *.cfg-File")
+ buttonLoad = qt.QPushButton("Load")
+ buttonLoad.setToolTip("Load existing configuration from *.cfg-File")
# OptionLists and ButtonGroups
# GroupBox can be generated from self.getGroupBox
- normOpts = ['No &normalization',
- 'Normalize &after average',
- 'Normalize &before average']
- xrangeOpts = ['&First curve in sequence',
- 'Active &curve',
- '&Use equidistant x-range']
+ normOpts = [
+ "No &normalization",
+ "Normalize &after average",
+ "Normalize &before average",
+ ]
+ xrangeOpts = [
+ "&First curve in sequence",
+ "Active &curve",
+ "&Use equidistant x-range",
+ ]
# ButtonGroups
- normBG = qt.QButtonGroup(self)
+ normBG = qt.QButtonGroup(self)
xrangeBG = qt.QButtonGroup(self)
# ComboBoxes
normMeth = qt.QComboBox()
- normMeth.addItems(['(y-min(y))/trapz(max(y)-min(y),x)',
- 'y/max(y)',
- '(y-min(y))/(max(y)-min(y))',
- '(y-min(y))/sum(max(y)-min(y))'])
+ normMeth.addItems(
+ [
+ "(y-min(y))/trapz(max(y)-min(y),x)",
+ "y/max(y)",
+ "(y-min(y))/(max(y)-min(y))",
+ "(y-min(y))/sum(max(y)-min(y))",
+ ]
+ )
normMeth.setEnabled(False)
self.optsDict = {
- 'normalization' : normBG,
- 'normalizationMethod' : normMeth,
- 'xrange' : xrangeBG
+ "normalization": normBG,
+ "normalizationMethod": normMeth,
+ "xrange": xrangeBG,
}
for idx in range(5):
# key: motor0, motor1, ...
- key = 'motor%d'%idx
+ key = "motor%d" % idx
tmp = qt.QComboBox()
tmp.addItems(mList)
self.optsDict[key] = tmp
# Subdivide into GroupBoxes
- normGroupBox = self.getGroupBox('Normalization',
- normOpts,
- normBG)
- xrangeGroupBox = self.getGroupBox('Interpolation x-range',
- xrangeOpts,
- xrangeBG)
- motorGroupBox = qt.QGroupBox('Motors')
+ normGroupBox = self.getGroupBox("Normalization", normOpts, normBG)
+ xrangeGroupBox = self.getGroupBox("Interpolation x-range", xrangeOpts, xrangeBG)
+ motorGroupBox = qt.QGroupBox("Motors")
# Layouts
mainLayout = qt.QVBoxLayout()
buttonLayout = qt.QHBoxLayout()
normLayout = qt.QHBoxLayout()
motorLayout = qt.QGridLayout()
- if full: buttonLayout.addWidget(buttonSave)
+ if full:
+ buttonLayout.addWidget(buttonSave)
buttonLayout.addWidget(buttonLoad)
buttonLayout.addWidget(qt.HorizontalSpacer())
buttonLayout.addWidget(buttonOK)
buttonLayout.addWidget(buttonCancel)
- normLayout.addWidget(qt.QLabel('Method:'))
+ normLayout.addWidget(qt.QLabel("Method:"))
normLayout.addWidget(normMeth)
for idx in range(5):
- label = qt.QLabel('Motor %d:'%(idx+1))
- cbox = self.optsDict['motor%d'%idx]
- motorLayout.addWidget(label,idx,0)
- motorLayout.addWidget(cbox,idx,1)
+ label = qt.QLabel("Motor %d:" % (idx + 1))
+ cbox = self.optsDict["motor%d" % idx]
+ motorLayout.addWidget(label, idx, 0)
+ motorLayout.addWidget(cbox, idx, 1)
motorGroupBox.setLayout(motorLayout)
normGroupBox.layout().addLayout(normLayout)
mainLayout.addWidget(normGroupBox)
@@ -180,8 +186,8 @@ class XMCDOptions(qt.QDialog):
qt.QDialog.showEvent(self, event)
def updateMotorList(self, mList):
- for (key, obj) in self.optsDict.items():
- if key.startswith('motor') and isinstance(obj, qt.QComboBox):
+ for key, obj in self.optsDict.items():
+ if key.startswith("motor") and isinstance(obj, qt.QComboBox):
curr = obj.currentText()
obj.clear()
obj.addItems(mList)
@@ -209,7 +215,7 @@ class XMCDOptions(qt.QDialog):
groupBox = qt.QGroupBox(title, None)
gbLayout = qt.QVBoxLayout(None)
gbLayout.addStretch(1)
- for (idx, radioText) in enumerate(optionList):
+ for idx, radioText in enumerate(optionList):
radio = qt.QRadioButton(radioText)
gbLayout.addWidget(radio)
if buttongroup:
@@ -223,12 +229,12 @@ class XMCDOptions(qt.QDialog):
def normalizationMethod(self, ident):
ret = None
normDict = {
- 'toMaximum' : r'y/max(y)',
- 'offsetAndMaximum': r'(y-min(y))/(max(y)-min(y))',
- 'offsetAndCounts' : r'(y-min(y))/sum(max(y)-min(y))',
- 'offsetAndArea' : r'(y-min(y))/trapz(max(y)-min(y),x)'
+ "toMaximum": r"y/max(y)",
+ "offsetAndMaximum": r"(y-min(y))/(max(y)-min(y))",
+ "offsetAndCounts": r"(y-min(y))/sum(max(y)-min(y))",
+ "offsetAndArea": r"(y-min(y))/trapz(max(y)-min(y),x)",
}
- for (name, eq) in normDict.items():
+ for name, eq in normDict.items():
if ident == name:
return eq
if ident == eq:
@@ -249,15 +255,16 @@ class XMCDOptions(qt.QDialog):
def saveOptions(self, filename=None):
saveDir = PyMcaDirs.outputDir
- filter = ['PyMca (*.cfg)']
+ filter = ["PyMca (*.cfg)"]
if filename is None:
try:
- filename = PyMcaFileDialogs.\
- getFileList(parent=self,
- filetypelist=filter,
- message='Save XLD/XMCD Analysis Configuration',
- mode='SAVE',
- single=True)[0]
+ filename = PyMcaFileDialogs.getFileList(
+ parent=self,
+ filetypelist=filter,
+ message="Save XLD/XMCD Analysis Configuration",
+ mode="SAVE",
+ single=True,
+ )[0]
except IndexError:
# Returned list is empty
return
@@ -265,20 +272,20 @@ class XMCDOptions(qt.QDialog):
if len(filename) == 0:
self.saved = False
return False
- if not str(filename).endswith('.cfg'):
- filename += '.cfg'
+ if not str(filename).endswith(".cfg"):
+ filename += ".cfg"
confDict = ConfigDict.ConfigDict()
tmp = self.getOptions()
- for (key, value) in tmp.items():
- if key.startswith('Motor') and len(value) == 0:
- tmp[key] = 'None'
- confDict['XMCDOptions'] = tmp
+ for key, value in tmp.items():
+ if key.startswith("Motor") and len(value) == 0:
+ tmp[key] = "None"
+ confDict["XMCDOptions"] = tmp
try:
confDict.write(filename)
except IOError:
msg = qt.QMessageBox()
- msg.setWindowTitle('XLD/XMCD Options Error')
- msg.setText('Unable to write configuration to \'%s\''%filename)
+ msg.setWindowTitle("XLD/XMCD Options Error")
+ msg.setText("Unable to write configuration to '%s'" % filename)
msg.exec()
self.saved = True
return True
@@ -288,74 +295,76 @@ class XMCDOptions(qt.QDialog):
def loadOptions(self):
openDir = PyMcaDirs.outputDir
- ffilter = 'PyMca (*.cfg)'
- filename = qt.QFileDialog.\
- getOpenFileName(self,
- 'Load XLD/XMCD Analysis Configuration',
- openDir,
- ffilter)
+ ffilter = "PyMca (*.cfg)"
+ filename = qt.QFileDialog.getOpenFileName(
+ self, "Load XLD/XMCD Analysis Configuration", openDir, ffilter
+ )
confDict = ConfigDict.ConfigDict()
try:
confDict.read(filename)
except IOError:
msg = qt.QMessageBox()
- msg.setTitle('XMCD Options Error')
- msg.setText('Unable to read configuration file \'%s\''%filename)
+ msg.setTitle("XMCD Options Error")
+ msg.setText("Unable to read configuration file '%s'" % filename)
return
- if 'XMCDOptions'not in confDict:
+ if "XMCDOptions" not in confDict:
return
try:
- self.setOptions(confDict['XMCDOptions'])
+ self.setOptions(confDict["XMCDOptions"])
except ValueError as e:
- _logger.debug('loadOptions -- int conversion failed:\n'
- 'Invalid value for option \'%s\'', e)
+ _logger.debug(
+ "loadOptions -- int conversion failed:\n"
+ "Invalid value for option '%s'",
+ e,
+ )
msg = qt.QMessageBox()
- msg.setWindowTitle('XMCD Options Error')
- msg.setText('Configuration file \'%s\' corruted' % filename)
+ msg.setWindowTitle("XMCD Options Error")
+ msg.setText("Configuration file '%s' corruted" % filename)
msg.exec()
return
except KeyError as e:
- _logger.debug('loadOptions -- invalid identifier:\n'
- 'option \'%s\' not found', e)
+ _logger.debug(
+ "loadOptions -- invalid identifier:\n" "option '%s' not found", e
+ )
msg = qt.QMessageBox()
- msg.setWindowTitle('XMCD Options Error')
- msg.setText('Configuration file \'%s\' corruted' % filename)
+ msg.setWindowTitle("XMCD Options Error")
+ msg.setText("Configuration file '%s' corruted" % filename)
msg.exec()
return
self.saved = True
def getOptions(self):
ddict = {}
- for (option, obj) in self.optsDict.items():
+ for option, obj in self.optsDict.items():
if isinstance(obj, qt.QButtonGroup):
ddict[option] = obj.checkedId()
elif isinstance(obj, qt.QComboBox):
tmp = str(obj.currentText())
- if option == 'normalizationMethod':
+ if option == "normalizationMethod":
tmp = self.normalizationMethod(tmp)
- if option.startswith('motor') and (not len(tmp)):
- tmp = 'None'
+ if option.startswith("motor") and (not len(tmp)):
+ tmp = "None"
ddict[option] = tmp
else:
- ddict[option] = 'None'
+ ddict[option] = "None"
return ddict
def getMotors(self):
- motors = sorted([key for key in self.optsDict.keys()\
- if key.startswith('motor')])
- return [str(self.optsDict[motor].currentText()) \
- for motor in motors]
+ motors = sorted(
+ [key for key in self.optsDict.keys() if key.startswith("motor")]
+ )
+ return [str(self.optsDict[motor].currentText()) for motor in motors]
def setOptions(self, ddict):
for option in ddict.keys():
obj = self.optsDict[option]
if isinstance(obj, qt.QComboBox):
name = ddict[option]
- if option == 'normalizationMethod':
+ if option == "normalizationMethod":
name = self.normalizationMethod(name)
- if option.startswith('Motor') and name == 'None':
- name = ''
+ if option.startswith("Motor") and name == "None":
+ name = ""
idx = obj.findText(QString(name))
obj.setCurrentIndex(idx)
elif isinstance(obj, qt.QButtonGroup):
@@ -365,60 +374,60 @@ class XMCDOptions(qt.QDialog):
raise ValueError(option)
button = self.optsDict[option].button(idx)
if type(button) == type(qt.QRadioButton()):
- button.setChecked(True)
+ button.setChecked(True)
-class XMCDScanWindow(ScanWindow.ScanWindow):
+class XMCDScanWindow(ScanWindow.ScanWindow):
xmcdToolbarOptions = {
- 'logx': False,
- 'logy': False,
- 'flip': False,
- 'fit': False,
- 'roi': False,
+ "logx": False,
+ "logy": False,
+ "flip": False,
+ "fit": False,
+ "roi": False,
}
plotModifiedSignal = qt.pyqtSignal()
- saveOptionsSignal = qt.pyqtSignal('QString')
+ saveOptionsSignal = qt.pyqtSignal("QString")
- def __init__(self,
- origin,
- parent=None):
+ def __init__(self, origin, parent=None):
"""
:param origin: Plot window containing the data on which the analysis is performed
:type origin: ScanWindow
:param parent: Parent Widget, None per default
:type parent: QWidget
"""
- ScanWindow.ScanWindow.__init__(self,
- parent,
- name='XLD/XMCD Analysis',
- specfit=None,
- plugins=False,
- newplot=False,
- **self.xmcdToolbarOptions)
- if hasattr(self, 'pluginsIconFlag'):
+ ScanWindow.ScanWindow.__init__(
+ self,
+ parent,
+ name="XLD/XMCD Analysis",
+ specfit=None,
+ plugins=False,
+ newplot=False,
+ **self.xmcdToolbarOptions
+ )
+ if hasattr(self, "pluginsIconFlag"):
self.pluginsIconFlag = False
self.plotWindow = origin
- if hasattr(self, 'scanWindowInfoWidget'):
+ if hasattr(self, "scanWindowInfoWidget"):
if self.scanWindowInfoWidget:
self.scanWindowInfoWidget.hide()
# Buttons to push spectra to main Window
buttonWidget = qt.QWidget()
- buttonAdd = qt.QPushButton('Add', self)
- buttonAdd.setToolTip('Add active curve to main window')
- buttonReplace = qt.QPushButton('Replace', self)
+ buttonAdd = qt.QPushButton("Add", self)
+ buttonAdd.setToolTip("Add active curve to main window")
+ buttonReplace = qt.QPushButton("Replace", self)
buttonReplace.setToolTip(
- 'Replace all curves in main window '
- +'with active curve in analysis window')
- buttonAddAll = qt.QPushButton('Add all', self)
- buttonAddAll.setToolTip(
- 'Add all curves in analysis window '
- +'to main window')
- buttonReplaceAll = qt.QPushButton('Replace all', self)
+ "Replace all curves in main window "
+ + "with active curve in analysis window"
+ )
+ buttonAddAll = qt.QPushButton("Add all", self)
+ buttonAddAll.setToolTip("Add all curves in analysis window " + "to main window")
+ buttonReplaceAll = qt.QPushButton("Replace all", self)
buttonReplaceAll.setToolTip(
- 'Replace all curves in main window '
- +'with all curves from analysis window')
+ "Replace all curves in main window "
+ + "with all curves from analysis window"
+ )
self.graphBottomLayout.addWidget(qt.HorizontalSpacer())
self.graphBottomLayout.addWidget(buttonAdd)
self.graphBottomLayout.addWidget(buttonAddAll)
@@ -431,14 +440,14 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
buttonReplaceAll.clicked.connect(self.replaceAll)
# Copy spectra from origin
- self.selectionDict = {'A':[], 'B':[]}
+ self.selectionDict = {"A": [], "B": []}
self.curvesDict = {}
self.optsDict = {
- 'normAfterAvg' : False,
- 'normBeforeAvg' : False,
- 'useActive' : False,
- 'equidistant' : False,
- 'normalizationMethod' : self.NormOffsetAndArea
+ "normAfterAvg": False,
+ "normBeforeAvg": False,
+ "useActive": False,
+ "equidistant": False,
+ "normalizationMethod": self.NormOffsetAndArea,
}
self.xRange = None
@@ -446,84 +455,82 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
self.avgA = None
self.avgB = None
self.xmcd = None
- self.xas = None
+ self.xas = None
- if hasattr(self, '_buildLegendWidget'):
+ if hasattr(self, "_buildLegendWidget"):
self._buildLegendWidget()
def sizeHint(self):
if self.parent():
- height = .5 * self.parent().height()
+ height = 0.5 * self.parent().height()
else:
height = self.height()
return qt.QSize(self.width(), int(height))
def processOptions(self, options):
- tmp = { 'equidistant': False,
- 'useActive': False,
- 'normAfterAvg': False,
- 'normBeforeAvg': False,
- 'normalizationMethod': None
+ tmp = {
+ "equidistant": False,
+ "useActive": False,
+ "normAfterAvg": False,
+ "normBeforeAvg": False,
+ "normalizationMethod": None,
}
- xRange = options['xrange']
- normalization = options['normalization']
- normMethod = options['normalizationMethod']
+ xRange = options["xrange"]
+ normalization = options["normalization"]
+ normMethod = options["normalizationMethod"]
# xRange Options. Default: Use first scan
if xRange == 1:
- tmp['useActive'] = True
+ tmp["useActive"] = True
elif xRange == 2:
- tmp['equidistant'] = True
+ tmp["equidistant"] = True
# Normalization Options. Default: No Normalization
if normalization == 1:
- tmp['normAfterAvg'] = True
+ tmp["normAfterAvg"] = True
elif normalization == 2:
- tmp['normBeforeAvg'] = True
+ tmp["normBeforeAvg"] = True
# Normalization Method. Default: offsetAndArea
- tmp['normalizationMethod'] = self.setNormalizationMethod(normMethod)
+ tmp["normalizationMethod"] = self.setNormalizationMethod(normMethod)
# Trigger reclaculation
self.optsDict = tmp
- groupA = self.selectionDict['A']
- groupB = self.selectionDict['B']
+ groupA = self.selectionDict["A"]
+ groupB = self.selectionDict["B"]
self.processSelection(groupA, groupB)
def setNormalizationMethod(self, fname):
- if fname == 'toMaximum':
+ if fname == "toMaximum":
func = self.NormToMaximum
- elif fname == 'offsetAndMaximum':
+ elif fname == "offsetAndMaximum":
func = self.NormToOffsetAndMaximum
- elif fname == 'offsetAndCounts':
+ elif fname == "offsetAndCounts":
func = self.NormOffsetAndCounts
else:
func = self.NormOffsetAndArea
return func
- def NormToMaximum(self,x,y):
- ymax = numpy.max(y)
- ynorm = y/ymax
+ def NormToMaximum(self, x, y):
+ ymax = numpy.max(y)
+ ynorm = y / ymax
return ynorm
- def NormToOffsetAndMaximum(self,x,y):
+ def NormToOffsetAndMaximum(self, x, y):
ynorm = y - numpy.min(y)
- ymax = numpy.max(ynorm)
+ ymax = numpy.max(ynorm)
ynorm /= ymax
return ynorm
def NormOffsetAndCounts(self, x, y):
ynorm = y - numpy.min(y)
- ymax = numpy.sum(ynorm)
+ ymax = numpy.sum(ynorm)
ynorm /= ymax
return ynorm
- def NormOffsetAndArea(self, x, y):
+ def NormOffsetAndArea(self, x, y):
ynorm = y - numpy.min(y)
- ymax = numpy.trapz(ynorm, x)
+ ymax = numpy.trapz(ynorm, x)
ynorm /= ymax
return ynorm
- def interpXRange(self,
- xRange=None,
- equidistant=False,
- xRangeList=None):
+ def interpXRange(self, xRange=None, equidistant=False, xRangeList=None):
"""
Input
-----
@@ -551,7 +558,7 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
keys = sorted(self.curvesDict.keys())
xRangeList = [self.curvesDict[k].x[0] for k in keys]
if not len(xRangeList):
- _logger.debug('interpXRange -- Nothing to do')
+ _logger.debug("interpXRange -- Nothing to do")
return None
num = 0
@@ -562,14 +569,13 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
if x.max() < xmax:
xmax = x.max()
if xmin >= xmax:
- raise ValueError('No overlap between curves')
+ raise ValueError("No overlap between curves")
pass
if equidistant:
for x in xRangeList:
- curr = numpy.nonzero((x >= xmin) &
- (x <= xmax))[0].size
- num = curr if curr>num else num
+ curr = numpy.nonzero((x >= xmin) & (x <= xmax))[0].size
+ num = curr if curr > num else num
num = int(num)
# Exclude first and last point
out = numpy.linspace(xmin, xmax, num, endpoint=False)[1:]
@@ -579,17 +585,16 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
else:
x = xRangeList[0]
# Ensure monotonically increasing x-range
- if not numpy.all(numpy.diff(x)>0.):
- mask = numpy.nonzero(numpy.diff(x)>0.)[0]
+ if not numpy.all(numpy.diff(x) > 0.0):
+ mask = numpy.nonzero(numpy.diff(x) > 0.0)[0]
x = numpy.take(x, mask)
# Exclude the endpoints
- mask = numpy.nonzero((x > xmin) &
- (x < xmax))[0]
+ mask = numpy.nonzero((x > xmin) & (x < xmax))[0]
out = numpy.sort(numpy.take(x, mask))
- _logger.debug('interpXRange -- Resulting xrange:')
- _logger.debug('\tmin = %f', out.min())
- _logger.debug('\tmax = %f', out.max())
- _logger.debug('\tnum = %f', len(out))
+ _logger.debug("interpXRange -- Resulting xrange:")
+ _logger.debug("\tmin = %f", out.min())
+ _logger.debug("\tmax = %f", out.max())
+ _logger.debug("\tnum = %f", len(out))
return out
def processSelection(self, groupA, groupB):
@@ -604,35 +609,35 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
all = self.getAllCurves(just_legend=True)
self.removeCurves(all)
self.avgB, self.avgA = None, None
- self.xas, self.xmcd = None, None
+ self.xas, self.xmcd = None, None
- self.selectionDict['A'] = groupA[:]
- self.selectionDict['B'] = groupB[:]
+ self.selectionDict["A"] = groupA[:]
+ self.selectionDict["B"] = groupB[:]
self.curvesDict = self.copyCurves(groupA + groupB)
- if (len(self.curvesDict) == 0) or\
- ((len(self.selectionDict['A']) == 0) and
- (len(self.selectionDict['B']) == 0)):
+ if (len(self.curvesDict) == 0) or (
+ (len(self.selectionDict["A"]) == 0) and (len(self.selectionDict["B"]) == 0)
+ ):
# Nothing to do
return
# Make sure to use active curve when specified
- if self.optsDict['useActive']:
+ if self.optsDict["useActive"]:
# Get active curve
active = self.plotWindow.getActiveCurve()
if active:
- _logger.debug('processSelection -- xrange: use active')
+ _logger.debug("processSelection -- xrange: use active")
x, y, leg, info = active[0:4]
xRange = self.interpXRange(xRange=x)
else:
return
- elif self.optsDict['equidistant']:
- _logger.debug('processSelection -- xrange: use equidistant')
+ elif self.optsDict["equidistant"]:
+ _logger.debug("processSelection -- xrange: use equidistant")
xRange = self.interpXRange(equidistant=True)
else:
- _logger.debug('processSelection -- xrange: use first')
+ _logger.debug("processSelection -- xrange: use first")
xRange = self.interpXRange()
- if hasattr(self.plotWindow, 'graph'):
+ if hasattr(self.plotWindow, "graph"):
activeLegend = self.plotWindow.graph.getActiveCurve(justlegend=True)
else:
activeLegend = self.plotWindow.getActiveCurve(just_legend=True)
@@ -643,10 +648,10 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
xlabel, ylabel = self.extractLabels(active.info)
# Calculate averages and add them to the plot
- normalization = self.optsDict['normalizationMethod']
- normBefore = self.optsDict['normBeforeAvg']
- normAfter = self.optsDict['normAfterAvg']
- for idx in ['A','B']:
+ normalization = self.optsDict["normalizationMethod"]
+ normBefore = self.optsDict["normBeforeAvg"]
+ normAfter = self.optsDict["normAfterAvg"]
+ for idx in ["A", "B"]:
sel = self.selectionDict[idx]
if not len(sel):
continue
@@ -662,32 +667,33 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
yVals = tmp.y[0]
xvalList.append(xVals)
yvalList.append(yVals)
- avg_x, avg_y = self.specAverage(xvalList,
- yvalList,
- xRange)
+ avg_x, avg_y = self.specAverage(xvalList, yvalList, xRange)
if normAfter:
avg_y = normalization(avg_x, avg_y)
- avgName = 'avg_' + idx
- #info = {'xlabel': xlabel, 'ylabel': ylabel}
+ avgName = "avg_" + idx
+ # info = {'xlabel': xlabel, 'ylabel': ylabel}
info = {}
- if idx == 'A':
- #info.update({'plot_color':'red'})
- color="red"
+ if idx == "A":
+ # info.update({'plot_color':'red'})
+ color = "red"
else:
- #info.update({'plot_color':'blue'})
- color="blue"
- self.addCurve(avg_x, avg_y,
- legend=avgName,
- info=info,
- xlabel=xlabel,
- ylabel=ylabel,
- color=color)
- if idx == 'A':
+ # info.update({'plot_color':'blue'})
+ color = "blue"
+ self.addCurve(
+ avg_x,
+ avg_y,
+ legend=avgName,
+ info=info,
+ xlabel=xlabel,
+ ylabel=ylabel,
+ color=color,
+ )
+ if idx == "A":
self.avgA = self.dataObjectsList[-1]
- if idx == 'B':
+ if idx == "B":
self.avgB = self.dataObjectsList[-1]
- if (self.avgA and self.avgB):
+ if self.avgA and self.avgB:
self.performXMCD()
self.performXAS()
@@ -717,11 +723,11 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
if tmp:
tmp = copy.deepcopy(tmp)
xarr, yarr = tmp.x, tmp.y
- #if len(tmp.x) == len(tmp.y):
+ # if len(tmp.x) == len(tmp.y):
xprocArr, yprocArr = [], []
- for (x,y) in zip(xarr,yarr):
+ for x, y in zip(xarr, yarr):
# Sort
- idx = numpy.argsort(x, kind='mergesort')
+ idx = numpy.argsort(x, kind="mergesort")
xproc = numpy.take(x, idx)
yproc = numpy.take(y, idx)
# Ravel, Increase
@@ -762,10 +768,9 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
Average spectrum. In case of invalid input,
(None, None) tuple is returned.
"""
- if (len(xarr) != len(yarr)) or\
- (len(xarr) == 0) or (len(yarr) == 0):
- _logger.debug('specAverage -- invalid input!')
- _logger.debug('Array lengths do not match or are 0')
+ if (len(xarr) != len(yarr)) or (len(xarr) == 0) or (len(yarr) == 0):
+ _logger.debug("specAverage -- invalid input!")
+ _logger.debug("Array lengths do not match or are 0")
return None, None
same = True
@@ -786,8 +791,8 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
xsort = []
ysort = []
- for (x,y) in zip(xarr, yarr):
- if numpy.all(numpy.diff(x) > 0.):
+ for x, y in zip(xarr, yarr):
+ if numpy.all(numpy.diff(x) > 0.0):
# All values sorted
xsort.append(x)
ysort.append(y)
@@ -813,41 +818,39 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
if xmax < xmax0:
xmax0 = xmax
if xmax <= xmin:
- _logger.debug('specAverage --\n'
- 'No overlap between spectra!')
+ _logger.debug("specAverage --\n" "No overlap between spectra!")
return numpy.array([]), numpy.array([])
# Clip xRange to maximal overlap in spectra
if xRange is None:
xRange = xsort[0]
- mask = numpy.nonzero((xRange>=xmin0) &
- (xRange<=xmax0))[0]
+ mask = numpy.nonzero((xRange >= xmin0) & (xRange <= xmax0))[0]
xnew = numpy.take(xRange, mask)
ynew = numpy.zeros(len(xnew))
# Perform average
- for (x, y) in zip(xsort, ysort):
+ for x, y in zip(xsort, ysort):
if same:
ynew += y
else:
yinter = numpy.interp(xnew, x, y)
- ynew += numpy.asarray(yinter)
+ ynew += numpy.asarray(yinter)
num = len(yarr)
ynew /= num
return xnew, ynew
def extractLabels(self, info):
- xlabel = 'X'
- ylabel = 'Y'
- sel = info.get('selection', None)
- labelNames = info.get('LabelNames',[])
+ xlabel = "X"
+ ylabel = "Y"
+ sel = info.get("selection", None)
+ labelNames = info.get("LabelNames", [])
if not len(labelNames):
pass
elif len(labelNames) == 2:
- [xlabel, ylabel] = labelNames
+ [xlabel, ylabel] = labelNames
elif sel:
- xsel = sel.get('x',[])
- ysel = sel.get('y',[])
+ xsel = sel.get("x", [])
+ ysel = sel.get("y", [])
if len(xsel) > 0:
x = xsel[0]
xlabel = labelNames[x]
@@ -862,28 +865,29 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
a = self.dataObjectsDict[self.avgA]
b = self.dataObjectsDict[self.avgB]
else:
- _logger.debug('performXAS -- Data not found: ')
- _logger.debug('\tavg_m = %f', self.avgA)
- _logger.debug('\tavg_p = %f', self.avgB)
+ _logger.debug("performXAS -- Data not found: ")
+ _logger.debug("\tavg_m = %f", self.avgA)
+ _logger.debug("\tavg_p = %f", self.avgB)
return
- if numpy.all( a.x[0] == b.x[0] ):
- avg = .5*(b.y[0] + a.y[0])
+ if numpy.all(a.x[0] == b.x[0]):
+ avg = 0.5 * (b.y[0] + a.y[0])
else:
- _logger.debug('performXAS -- x ranges are not the same! ')
- _logger.debug('Force interpolation')
- avg = self.performAverage([a.x[0], b.x[0]],
- [a.y[0], b.y[0]],
- b.x[0])
- xmcdLegend = 'XAS'
+ _logger.debug("performXAS -- x ranges are not the same! ")
+ _logger.debug("Force interpolation")
+ avg = self.performAverage([a.x[0], b.x[0]], [a.y[0], b.y[0]], b.x[0])
+ xmcdLegend = "XAS"
xlabel, ylabel = self.extractLabels(a.info)
- #info = {'xlabel': xlabel, 'ylabel': ylabel, 'plot_color': 'pink'}
+ # info = {'xlabel': xlabel, 'ylabel': ylabel, 'plot_color': 'pink'}
info = {}
- self.addCurve(a.x[0], avg,
- legend=xmcdLegend,
- info=info,
- xlabel=xlabel,
- ylabel=ylabel,
- color="pink")
+ self.addCurve(
+ a.x[0],
+ avg,
+ legend=xmcdLegend,
+ info=info,
+ xlabel=xlabel,
+ ylabel=ylabel,
+ color="pink",
+ )
self.xas = self.dataObjectsList[-1]
def performXMCD(self):
@@ -892,29 +896,32 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
a = self.dataObjectsDict[self.avgA]
b = self.dataObjectsDict[self.avgB]
else:
- _logger.debug('performXMCD -- Data not found:')
+ _logger.debug("performXMCD -- Data not found:")
return
- if numpy.all( a.x[0] == b.x[0] ):
+ if numpy.all(a.x[0] == b.x[0]):
diff = b.y[0] - a.y[0]
else:
- _logger.debug('performXMCD -- x ranges are not the same! ')
- _logger.debug('Force interpolation using p Average xrange')
+ _logger.debug("performXMCD -- x ranges are not the same! ")
+ _logger.debug("Force interpolation using p Average xrange")
# Use performAverage d = 2 * avg(y1, -y2)
# and force interpolation on p-xrange
- diff = 2. * self.performAverage([a.x[0], b.x[0]],
- [-a.y[0], b.y[0]],
- b.x[0])
- xmcdLegend = 'XMCD'
+ diff = 2.0 * self.performAverage(
+ [a.x[0], b.x[0]], [-a.y[0], b.y[0]], b.x[0]
+ )
+ xmcdLegend = "XMCD"
xlabel, ylabel = self.extractLabels(a.info)
- #info = {'xlabel': xlabel, 'ylabel': ylabel, 'plot_yaxis': 'right', 'plot_color': 'green'}
- info={}
- self.addCurve(b.x[0], diff,
- legend=xmcdLegend,
- info=info,
- color="green",
- xlabel=xlabel,
- ylabel=ylabel,
- yaxis="right")
+ # info = {'xlabel': xlabel, 'ylabel': ylabel, 'plot_yaxis': 'right', 'plot_color': 'green'}
+ info = {}
+ self.addCurve(
+ b.x[0],
+ diff,
+ legend=xmcdLegend,
+ info=info,
+ color="green",
+ xlabel=xlabel,
+ ylabel=ylabel,
+ yaxis="right",
+ )
# DELETE ME self.graph.mapToY2(' '.join([xmcdLegend, ylabel]))
self._zoomReset()
self.xmcd = self.dataObjectsList[-1]
@@ -926,22 +933,24 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
stored selectionDict.
"""
sel = self.selectionDict[idx]
- ret = '%s: '%idx
+ ret = "%s: " % idx
for legend in sel:
curr = self.curvesDict[legend]
value = curr.info.get(key, None)
if value:
- ret = ' '.join([ret, value])
+ ret = " ".join([ret, value])
return ret
- def _saveIconSignal(self):
+ def _saveIconSignalReplacement(self, motors=None):
saveDir = PyMcaDirs.outputDir
- filter = 'spec File (*.spec);;Any File (*.*)'
+ filter = "spec File (*.spec);;Any File (*.*)"
try:
- (filelist, append, comment) = getSaveFileName(parent=self,
- caption='Save XMCD Analysis',
- filter=filter,
- directory=saveDir)
+ (filelist, append, comment) = getSaveFileName(
+ parent=self,
+ caption="Save XMCD Analysis",
+ filter=filter,
+ directory=saveDir,
+ )
filename = filelist[0]
except IndexError:
# Returned list is empty
@@ -951,60 +960,60 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
return
if append:
- specf = specfile.Specfile(filename)
+ specf = specfile.Specfile(filename)
scanno = specf.scanno() + 1
else:
scanno = 1
ext = splitext(filename)[1]
if not len(ext):
- ext = '.spec'
+ ext = ".spec"
filename += ext
try:
if append:
sepFile = splitext(basename(filename))
- sepFileName = sepFile[0] + '_%.2d'%scanno + sepFile[1]
- sepFileName = pathjoin(dirname(filename),sepFileName)
+ sepFileName = sepFile[0] + "_%.2d" % scanno + sepFile[1]
+ sepFileName = pathjoin(dirname(filename), sepFileName)
if scanno == 2:
# Case: Scan appended to file containing
# a single scan. Make sure, that the first
- # scan is also written to seperate file and
+ # scan is also written to separate file and
# the corresponding cfg-file is copied
# 1. Create filename of first scan
- sepFirstFileName = sepFile[0] + '_01' + sepFile[1]
- sepFirstFileName = pathjoin(dirname(filename),sepFirstFileName)
+ sepFirstFileName = sepFile[0] + "_01" + sepFile[1]
+ sepFirstFileName = pathjoin(dirname(filename), sepFirstFileName)
# 2. Guess filename of first config
- confname = sepFile[0] + '.cfg'
- confname = pathjoin(dirname(filename),confname)
+ confname = sepFile[0] + ".cfg"
+ confname = pathjoin(dirname(filename), confname)
# 3. Create new filename of first config
- sepFirstConfName = sepFile[0] + '_01' + '.cfg'
- sepFirstConfName = pathjoin(dirname(filename),sepFirstConfName)
+ sepFirstConfName = sepFile[0] + "_01" + ".cfg"
+ sepFirstConfName = pathjoin(dirname(filename), sepFirstConfName)
# Copy contents
- firstSeperateFile = open(sepFirstFileName, 'wb')
- firstSeperateConf = open(sepFirstConfName, 'wb')
- filehandle = open(filename, 'rb')
- confhandle = open(confname, 'rb')
+ firstSeparateFile = open(sepFirstFileName, "wb")
+ firstSeparateConf = open(sepFirstConfName, "wb")
+ filehandle = open(filename, "rb")
+ confhandle = open(confname, "rb")
firstFile = filehandle.read()
firstConf = confhandle.read()
- firstSeperateFile.write(firstFile)
- firstSeperateConf.write(firstConf)
- firstSeperateFile.close()
- firstSeperateConf.close()
- filehandle = open(filename, 'ab')
- seperateFile = open(sepFileName, 'wb')
+ firstSeparateFile.write(firstFile)
+ firstSeparateConf.write(firstConf)
+ firstSeparateFile.close()
+ firstSeparateConf.close()
+ filehandle = open(filename, "ab")
+ separateFile = open(sepFileName, "wb")
else:
- filehandle = open(filename, 'wb')
- seperateFile = None
+ filehandle = open(filename, "wb")
+ separateFile = None
except IOError:
- msg = qt.QMessageBox(text="Unable to open '%s'"%filename)
+ msg = qt.QMessageBox(text="Unable to open '%s'" % filename)
msg.exec()
return
- title = ''
+ title = ""
legends = self.dataObjectsList
tmpLegs = sorted(self.curvesDict.keys())
if len(tmpLegs) > 0:
- title += self.curvesDict[tmpLegs[0]].info.get('selectionlegend','')
+ title += self.curvesDict[tmpLegs[0]].info.get("selectionlegend", "")
# Keep plots in the order they were added!
curves = [self.dataObjectsDict[leg] for leg in legends]
yVals = [curve.y[0] for curve in curves]
@@ -1020,52 +1029,83 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
ncols = outArray.shape[1]
else:
ncols = 1
- delim = ' '
- title = 'XMCD Analysis ' + title
- header = '#S %d %s'%(scanno,title) + NEWLINE
- header += ('#U00 Selected in Group ' +\
- self.selectionInfo('A', 'Key') + NEWLINE)
- header += ('#U01 Selected in Group ' +\
- self.selectionInfo('B', 'Key') + NEWLINE)
+ delim = " "
+ title = "XMCD Analysis " + title
+ header = "#S %d %s" % (scanno, title) + NEWLINE
+ header += "#U00 Selected in Group " + self.selectionInfo("A", "Key") + NEWLINE
+ header += "#U01 Selected in Group " + self.selectionInfo("B", "Key") + NEWLINE
# Write Comments
if len(comment) > 0:
- header += ('#U02 User commentary:' + NEWLINE)
+ header += "#U02 User commentary:" + NEWLINE
lines = comment.splitlines()[:97]
- for (idx, line) in enumerate(lines):
- header += ('#U%.2d %s'%(idx+3, line) + NEWLINE)
- header += '#N %d'%ncols + NEWLINE
- if ext == '.spec':
- if hasattr(self, 'getGraphXLabel'):
- header += ('#L ' + self.getGraphXLabel() + ' ' + ' '.join(legends) + NEWLINE)
+ for idx, line in enumerate(lines):
+ header += "#U%.2d %s" % (idx + 3, line) + NEWLINE
+
+ # Write motor mnemonics and positions of first selected scan
+ if isinstance(motors, dict):
+ if len(motors):
+ motorNames = [x for x in motors]
+ motorValues = [motors[x] for x in motors]
+ motorNamesText = ""
+ motorValuesText = ""
+ motorsPerLine = 5
+ n = 0
+ while n < len(motors):
+ idx = n // motorsPerLine
+ motorNamesText += "#O%d" % idx
+ motorValuesText += "#P%d" % idx
+ for i in range(
+ idx * motorsPerLine, min((idx + 1) * motorsPerLine, len(motors))
+ ):
+ motorNamesText += " " + motorNames[i]
+ motorValuesText += " " + motorValues[i]
+ n += 1
+ motorNamesText += NEWLINE
+ motorValuesText += NEWLINE
+ header += motorNamesText
+ header += motorValuesText
+
+ header += "#N %d" % ncols + NEWLINE
+ if ext == ".spec":
+ if hasattr(self, "getGraphXLabel"):
+ header += (
+ "#L " + self.getGraphXLabel() + " " + " ".join(legends) + NEWLINE
+ )
else:
- header += ('#L ' + self.getGraphXTitle() + ' ' + ' '.join(legends) + NEWLINE)
+ header += (
+ "#L " + self.getGraphXTitle() + " " + " ".join(legends) + NEWLINE
+ )
else:
- if hasattr(self, 'getGraphXLabel'):
- header += ('#L ' + self.getGraphXLabel() + ' ' + ' '.join(legends) + NEWLINE)
+ if hasattr(self, "getGraphXLabel"):
+ header += (
+ "#L " + self.getGraphXLabel() + " " + " ".join(legends) + NEWLINE
+ )
else:
- header += ('#L ' + self.getGraphXTitle() + ' ' + delim.join(legends) + NEWLINE)
+ header += (
+ "#L " + self.getGraphXTitle() + " " + delim.join(legends) + NEWLINE
+ )
- for fh in [filehandle, seperateFile]:
+ for fh in [filehandle, separateFile]:
if fh is not None:
if sys.version < "3.0":
fh.write(bytes(NEWLINE))
fh.write(bytes(header))
for line in outArray:
- tmp = delim.join(['%f'%num for num in line])
+ tmp = delim.join(["%f" % num for num in line])
fh.write(bytes(tmp + NEWLINE))
fh.write(bytes(NEWLINE))
else:
- fh.write(bytes(NEWLINE, 'ascii'))
- fh.write(bytes(header, 'ascii'))
+ fh.write(bytes(NEWLINE, "ascii"))
+ fh.write(bytes(header, "ascii"))
for line in outArray:
- tmp = delim.join(['%f'%num for num in line])
- fh.write(bytes(tmp + NEWLINE, 'ascii'))
- fh.write(bytes(NEWLINE, 'ascii'))
+ tmp = delim.join(["%f" % num for num in line])
+ fh.write(bytes(tmp + NEWLINE, "ascii"))
+ fh.write(bytes(NEWLINE, "ascii"))
fh.close()
# Emit saveOptionsSignal to save config file
self.saveOptionsSignal.emit(splitext(filename)[0])
- if seperateFile is not None:
+ if separateFile is not None:
self.saveOptionsSignal.emit(splitext(sepFileName)[0])
def add(self):
@@ -1074,34 +1114,28 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
activeCurve = self.getActiveCurve()
if activeCurve is None:
return
- (xVal, yVal, legend, info) = activeCurve[0:4]
- #if 'selectionlegend' in info:
+ (xVal, yVal, legend, info) = activeCurve[0:4]
+ # if 'selectionlegend' in info:
# newLegend = info['selectionlegend']
- #elif 'operation' in info:
+ # elif 'operation' in info:
# newLegend = (str(operation) + ' ' + self.title)
- #else:
+ # else:
# newLegend = (legend + ' ' + self.title)
newLegend = legend
- self.plotWindow.addCurve(xVal,
- yVal,
- legend=newLegend,
- info=info)
+ self.plotWindow.addCurve(xVal, yVal, legend=newLegend, info=info)
self.plotModifiedSignal.emit()
def addAll(self):
for curve in self.getAllCurves():
(xVal, yVal, legend, info) = curve[0:4]
- #if 'selectionlegend' in info:
+ # if 'selectionlegend' in info:
# newLegend = info['selectionlegend']
- #elif 'operation' in info:
+ # elif 'operation' in info:
# newLegend = (str(operation) + ' ' + self.title)
- #else:
+ # else:
# newLegend = (legend + ' ' + self.title)
newLegend = legend
- self.plotWindow.addCurve(xVal,
- yVal,
- legend=newLegend,
- info=info)
+ self.plotWindow.addCurve(xVal, yVal, legend=newLegend, info=info)
self.plotModifiedSignal.emit()
def replace(self):
@@ -1110,46 +1144,38 @@ class XMCDScanWindow(ScanWindow.ScanWindow):
activeCurve = self.getActiveCurve()
if activeCurve is None:
return
- (xVal, yVal, legend, info) = activeCurve[0:4]
- if 'selectionlegend' in info:
- newLegend = info['selectionlegend']
- elif 'operation' in info:
- newLegend = (str(info['operation']) + ' ' + self.title)
+ (xVal, yVal, legend, info) = activeCurve[0:4]
+ if "selectionlegend" in info:
+ newLegend = info["selectionlegend"]
+ elif "operation" in info:
+ newLegend = str(info["operation"]) + " " + self.title
else:
- newLegend = (legend + self.title)
- self.plotWindow.addCurve(xVal,
- yVal,
- legend=newLegend,
- info=info,
- replace=True)
+ newLegend = legend + self.title
+ self.plotWindow.addCurve(xVal, yVal, legend=newLegend, info=info, replace=True)
self.plotModifiedSignal.emit()
def replaceAll(self):
allCurves = self.getAllCurves()
- for (idx, curve) in enumerate(allCurves):
+ for idx, curve in enumerate(allCurves):
(xVal, yVal, legend, info) = curve[0:4]
- if 'selectionlegend' in info:
- newLegend = info['selectionlegend']
- elif 'operation' in info:
- newLegend = (str(info['operation']) + ' ' + self.title)
+ if "selectionlegend" in info:
+ newLegend = info["selectionlegend"]
+ elif "operation" in info:
+ newLegend = str(info["operation"]) + " " + self.title
else:
- newLegend = (legend + ' ' + self.title)
+ newLegend = legend + " " + self.title
if idx == 0:
- self.plotWindow.addCurve(xVal,
- yVal,
- legend=newLegend,
- info=info,
- replace=True)
+ self.plotWindow.addCurve(
+ xVal, yVal, legend=newLegend, info=info, replace=True
+ )
else:
- self.plotWindow.addCurve(xVal,
- yVal,
- legend=newLegend,
- info=info)
+ self.plotWindow.addCurve(xVal, yVal, legend=newLegend, info=info)
self.plotModifiedSignal.emit()
+
class XMCDMenu(qt.QMenu):
- def __init__(self, parent, title=None):
- qt.QMenu.__init__(self, parent)
+ def __init__(self, parent, title=None):
+ qt.QMenu.__init__(self, parent)
if title:
self.setTitle(title)
@@ -1161,52 +1187,52 @@ class XMCDMenu(qt.QMenu):
"""
if not update:
self.clear()
- for (name, function) in actionList:
- if name == '$SEPARATOR':
+ for name, function in actionList:
+ if name == "$SEPARATOR":
self.addSeparator()
continue
- if name != '':
+ if name != "":
fName = name
else:
fName = function.func_name
- act = qt.QAction(fName, self)
+ act = qt.QAction(fName, self)
# Force triggered() instead of triggered(bool)
# to ensure proper interaction with default parameters
act.triggered.connect(function)
self.addAction(act)
-class XMCDTreeWidget(qt.QTreeWidget):
- __colGroup = 0
- __colLegend = 1
- __colScanNo = 2
+class XMCDTreeWidget(qt.QTreeWidget):
+ __colGroup = 0
+ __colLegend = 1
+ __colScanNo = 2
__colCounter = 3
selectionModifiedSignal = qt.pyqtSignal()
- def __init__(self, parent, groups = ['B','A','D'], color=True):
- qt.QTreeWidget.__init__(self, parent)
+ def __init__(self, parent, groups=["B", "A", "D"], color=True):
+ qt.QTreeWidget.__init__(self, parent)
# Last identifier in groups is the ignore instruction
self.groupList = groups
- self.actionList = []
- self.contextMenu = qt.QMenu('Perform', self)
+ self.actionList = []
+ self.contextMenu = qt.QMenu("Perform", self)
self.color = color
self.colorDict = {
- groups[0] : qt.QBrush(qt.QColor(220, 220, 255)),
- groups[1] : qt.QBrush(qt.QColor(255, 210, 210)),
- '': qt.QBrush(qt.QColor(255, 255, 255))
+ groups[0]: qt.QBrush(qt.QColor(220, 220, 255)),
+ groups[1]: qt.QBrush(qt.QColor(255, 210, 210)),
+ "": qt.QBrush(qt.QColor(255, 255, 255)),
}
def sizeHint(self):
vscrollbar = self.verticalScrollBar()
width = vscrollbar.width()
for i in range(self.columnCount()):
- width += (2 + self.columnWidth(i))
- return qt.QSize( int(width), 20*22)
+ width += 2 + self.columnWidth(i)
+ return qt.QSize(int(width), 20 * 22)
def setContextMenu(self, menu):
self.contextMenu = menu
- def contextMenuEvent(self, event):
+ def contextMenuEvent(self, event):
if event.reason() == event.Mouse:
pos = event.globalPos()
item = self.itemAt(event.pos())
@@ -1247,9 +1273,9 @@ class XMCDTreeWidget(qt.QTreeWidget):
the items of selected rows are returned.
"""
out = []
- convert = (convertType != str)
- if ncol > (self.columnCount()-1):
- _logger.debug('getColum -- Selected column out of bounds')
+ convert = convertType != str
+ if ncol > (self.columnCount() - 1):
+ _logger.debug("getColum -- Selected column out of bounds")
raise IndexError("Selected column '%d' out of bounds" % ncol)
if selectedOnly:
sel = self.selectedItems()
@@ -1263,14 +1289,14 @@ class XMCDTreeWidget(qt.QTreeWidget):
tmp = convertType(tmp)
except (TypeError, ValueError):
if convertType == float:
- tmp = float('NaN')
+ tmp = float("NaN")
else:
- _logger.debug('getColum -- Conversion failed!')
+ _logger.debug("getColum -- Conversion failed!")
raise TypeError
out += [tmp]
return out
- def build(self, items, headerLabels):
+ def build(self, items, headerLabels):
"""
(Re-) Builds the tree display
@@ -1282,7 +1308,7 @@ class XMCDTreeWidget(qt.QTreeWidget):
self.clear()
self.setHeaderLabels(headerLabels)
for item in items:
- treeItem = TreeWidgetItem(self, item)
+ treeItem = TreeWidgetItem(self, item)
if self.color:
idx = str(treeItem.text(self.__colGroup))
for i in range(self.columnCount()):
@@ -1296,12 +1322,12 @@ class XMCDTreeWidget(qt.QTreeWidget):
the identifier given in idx.
"""
if idx not in self.groupList:
- raise ValueError('XMCDTreeWidget: invalid identifer \'%s\'' % idx)
+ raise ValueError("XMCDTreeWidget: invalid identifer '%s'" % idx)
sel = self.selectedItems()
if idx == self.groupList[-1]:
# Last identifier in self.groupList
# is the dummy identifier
- idx = ''
+ idx = ""
for item in sel:
item.setText(self.__colGroup, idx)
if self.color:
@@ -1329,35 +1355,36 @@ class XMCDTreeWidget(qt.QTreeWidget):
root = self.invisibleRootItem()
sel = [root.child(i) for i in range(root.childCount())]
# Try to sort for scanNo
- #self.sortItems(self.__colLegend, qt.Qt.AscendingOrder)
+ # self.sortItems(self.__colLegend, qt.Qt.AscendingOrder)
self.sortItems(self.__colScanNo, qt.Qt.AscendingOrder)
if not seq:
- seq, chk = qt.QInputDialog.\
- getText(None,
- 'Sequence Dialog',
- 'Valid identifiers are: ' + ', '.join(self.groupList),
- qt.QLineEdit.Normal,
- 'Enter sequence')
+ seq, chk = qt.QInputDialog.getText(
+ None,
+ "Sequence Dialog",
+ "Valid identifiers are: " + ", ".join(self.groupList),
+ qt.QLineEdit.Normal,
+ "Enter sequence",
+ )
seq = str(seq).upper()
if not chk:
return
for idx in seq:
if idx not in self.groupList:
invalidMsg = qt.QMessageBox(None)
- invalidMsg.setText('Invalid identifier. Try again.')
+ invalidMsg.setText("Invalid identifier. Try again.")
invalidMsg.setStandardButtons(qt.QMessageBox.Ok)
invalidMsg.exec()
return
if len(sel) != len(seq):
# Assume pattern and repeat
- seq = seq * (len(sel)//len(seq) + 1)
- #invalidMsg = qt.QMessageBox(None)
- #invalidMsg.setText('Sequence length does not match item count.')
- #invalidMsg.setStandardButtons(qt.QMessageBox.Ok)
- #invalidMsg.exec()
- for (idx, item) in zip(seq, sel):
+ seq = seq * (len(sel) // len(seq) + 1)
+ # invalidMsg = qt.QMessageBox(None)
+ # invalidMsg.setText('Sequence length does not match item count.')
+ # invalidMsg.setStandardButtons(qt.QMessageBox.Ok)
+ # invalidMsg.exec()
+ for idx, item in zip(seq, sel):
if idx == self.groupList[-1]:
- idx = ''
+ idx = ""
item.setText(self.__colGroup, idx)
if self.color:
for i in range(self.columnCount()):
@@ -1380,10 +1407,10 @@ class XMCDTreeWidget(qt.QTreeWidget):
root = self.invisibleRootItem()
sel = [root.child(i) for i in range(root.childCount())]
for item in sel:
- item.setText(self.__colGroup,'')
+ item.setText(self.__colGroup, "")
if self.color:
for i in range(self.columnCount()):
- item.setBackground(i, self.colorDict[''])
+ item.setBackground(i, self.colorDict[""])
self.selectionModifiedSignal.emit()
def getSelection(self):
@@ -1397,11 +1424,11 @@ class XMCDTreeWidget(qt.QTreeWidget):
out = dict((group, []) for group in self.groupList)
root = self.invisibleRootItem()
for i in range(root.childCount()):
- item = root.child(i)
- group = str(item.text(0))
+ item = root.child(i)
+ group = str(item.text(0))
legend = str(item.text(1))
- #nCols = item.columnCount()
- #legend = str(item.text(nCols-1))
+ # nCols = item.columnCount()
+ # legend = str(item.text(nCols-1))
if len(group) == 0:
group = self.groupList[-1]
out[group] += [legend]
@@ -1409,21 +1436,13 @@ class XMCDTreeWidget(qt.QTreeWidget):
value.sort()
return out
-class XMCDWidget(qt.QWidget):
- toolbarOptions = {
- 'logx': False,
- 'logy': False,
- 'flip': False,
- 'fit': False
- }
+class XMCDWidget(qt.QWidget):
+ toolbarOptions = {"logx": False, "logy": False, "flip": False, "fit": False}
setSelectionSignal = qt.pyqtSignal(object, object)
- def __init__(self, parent,
- plotWindow,
- beamline,
- nSelectors = 5):
+ def __init__(self, parent, plotWindow, beamline, nSelectors=5):
"""
Input
-----
@@ -1436,184 +1455,196 @@ class XMCDWidget(qt.QWidget):
<Group> <Legend> <ScanNo> <Counter> <Motor 1> ... <Motor5>
"""
qt.QWidget.__init__(self, parent)
- self.setWindowIcon(qt.QIcon(qt.QPixmap(IconDict['peak'])))
+ self.setWindowIcon(qt.QIcon(qt.QPixmap(IconDict["peak"])))
self.plotWindow = plotWindow
self.legendList = []
self.motorsList = []
- self.infoList = []
+ self.infoList = []
# Set self.plotWindow before calling self._setLists!
self._setLists()
- self.motorNamesList = [''] + self._getAllMotorNames()
+ self.motorNamesList = [""] + self._getAllMotorNames()
self.motorNamesList.sort()
self.numCurves = len(self.legendList)
- #self.cBoxList = []
- self.analysisWindow = XMCDScanWindow(origin=plotWindow,
- parent=None)
+ # self.cBoxList = []
+ self.analysisWindow = XMCDScanWindow(origin=plotWindow, parent=None)
+ self.analysisWindow.enableOwnSave(False)
self.optsWindow = XMCDOptions(self, self.motorNamesList)
- helpFileName = pathjoin(PyMcaDataDir.PYMCA_DOC_DIR,
- "HTML",
- "XMCDInfotext.html")
+ helpFileName = pathjoin(PyMcaDataDir.PYMCA_DOC_DIR, "HTML", "XMCDInfotext.html")
self.helpFileBrowser = qt.QTextBrowser()
self.helpFileBrowser.setWindowTitle("XMCD Help")
self.helpFileBrowser.setLineWrapMode(qt.QTextEdit.FixedPixelWidth)
self.helpFileBrowser.setLineWrapColumnOrWidth(500)
- self.helpFileBrowser.resize(520,300)
+ self.helpFileBrowser.resize(520, 300)
try:
helpFileHandle = open(helpFileName)
helpFileHTML = helpFileHandle.read()
helpFileHandle.close()
self.helpFileBrowser.setHtml(helpFileHTML)
except IOError:
- _logger.debug('XMCDWindow -- init: Unable to read help file')
+ _logger.debug("XMCDWindow -- init: Unable to read help file")
self.helpFileBrowser = None
- self.selectionDict = {'D': [],
- 'B': [],
- 'A': []}
- self.setSizePolicy(qt.QSizePolicy.MinimumExpanding,
- qt.QSizePolicy.Expanding)
+ self.selectionDict = {"D": [], "B": [], "A": []}
+ self.setSizePolicy(qt.QSizePolicy.MinimumExpanding, qt.QSizePolicy.Expanding)
self.setWindowTitle("XLD/XMCD Analysis")
- buttonOptions = qt.QPushButton('Options', self)
+ buttonOptions = qt.QPushButton("Options", self)
buttonOptions.setToolTip(
- 'Set normalization and interpolation\n'
- +'method and motors shown')
+ "Set normalization and interpolation\n" + "method and motors shown"
+ )
- buttonInfo = qt.QPushButton('Info')
+ buttonInfo = qt.QPushButton("Info")
buttonInfo.setToolTip(
- 'Shows a describtion of the plugins features\n'
- +'and gives instructions on how to use it')
+ "Shows a describtion of the plugins features\n"
+ + "and gives instructions on how to use it"
+ )
- updatePixmap = qt.QPixmap(IconDict["reload"])
- buttonUpdate = qt.QPushButton(
- qt.QIcon(updatePixmap), '', self)
- buttonUpdate.setIconSize(qt.QSize(21,21))
+ updatePixmap = qt.QPixmap(IconDict["reload"])
+ buttonUpdate = qt.QPushButton(qt.QIcon(updatePixmap), "", self)
+ buttonUpdate.setIconSize(qt.QSize(21, 21))
buttonUpdate.setToolTip(
- 'Update curves in XMCD Analysis\n'
- +'by checking the plot window')
+ "Update curves in XMCD Analysis\n" + "by checking the plot window"
+ )
self.list = XMCDTreeWidget(self)
- labels = ['Group', 'Legend', 'S#','Counter']+\
- (['']*nSelectors)
- ncols = len(labels)
+ labels = ["Group", "Legend", "S#", "Counter"] + ([""] * nSelectors)
+ ncols = len(labels)
self.list.setColumnCount(ncols)
self.list.setHeaderLabels(labels)
self.list.setSortingEnabled(True)
- self.list.setSelectionMode(
- qt.QAbstractItemView.ExtendedSelection)
+ self.list.setSelectionMode(qt.QAbstractItemView.ExtendedSelection)
listContextMenu = XMCDMenu(None)
listContextMenu.setActionList(
- [('Perform analysis', self.triggerXMCD),
- ('$SEPARATOR', None),
- ('Set as A', self.setAsA),
- ('Set as B', self.setAsB),
- ('Enter sequence', self.list._setSelectionToSequenceSlot),
- ('Remove selection', self.list._clearSelectionSlot),
- ('$SEPARATOR', None),
- ('Invert selection', self.list.invertSelection),
- ('Remove curve(s)', self.removeCurve_)])
+ [
+ ("Perform analysis", self.triggerXMCD),
+ ("$SEPARATOR", None),
+ ("Set as A", self.setAsA),
+ ("Set as B", self.setAsB),
+ ("Enter sequence", self.list._setSelectionToSequenceSlot),
+ ("Remove selection", self.list._clearSelectionSlot),
+ ("$SEPARATOR", None),
+ ("Invert selection", self.list.invertSelection),
+ ("Remove curve(s)", self.removeCurve_),
+ ]
+ )
self.list.setContextMenu(listContextMenu)
self.expCBox = qt.QComboBox(self)
- self.expCBox.setToolTip('Select configuration of predefined\n'
- +'experiment or configure new experiment')
+ self.expCBox.setToolTip(
+ "Select configuration of predefined\n"
+ + "experiment or configure new experiment"
+ )
self.experimentsDict = {
- 'Generic Dichroism': {
- 'xrange': 0,
- 'normalization': 0,
- 'normalizationMethod': 'offsetAndArea',
- 'motor0': '',
- 'motor1': '',
- 'motor2': '',
- 'motor3': '',
- 'motor4': ''
+ "Generic Dichroism": {
+ "xrange": 0,
+ "normalization": 0,
+ "normalizationMethod": "offsetAndArea",
+ "motor0": "",
+ "motor1": "",
+ "motor2": "",
+ "motor3": "",
+ "motor4": "",
+ },
+ "ID08: XMCD 9 Tesla Magnet": {
+ "xrange": 0,
+ "normalization": 0,
+ "normalizationMethod": "offsetAndArea",
+ "motor0": "phaseD",
+ "motor1": "magnet",
+ "motor2": "",
+ "motor3": "",
+ "motor4": "",
+ },
+ "ID08: XMCD 5 Tesla Magnet": {
+ "xrange": 0,
+ "normalization": 0,
+ "normalizationMethod": "offsetAndArea",
+ "motor0": "PhaseD",
+ "motor1": "oxPS",
+ "motor2": "",
+ "motor3": "",
+ "motor4": "",
},
- 'ID08: XMCD 9 Tesla Magnet': {
- 'xrange': 0,
- 'normalization': 0,
- 'normalizationMethod': 'offsetAndArea',
- 'motor0': 'phaseD',
- 'motor1': 'magnet',
- 'motor2': '',
- 'motor3': '',
- 'motor4': ''
+ "ID08: XLD 5 Tesla Magnet": {
+ "xrange": 0,
+ "normalization": 0,
+ "normalizationMethod": "offsetAndArea",
+ "motor0": "PhaseD",
+ "motor1": "",
+ "motor2": "",
+ "motor3": "",
+ "motor4": "",
},
- 'ID08: XMCD 5 Tesla Magnet': {
- 'xrange': 0,
- 'normalization': 0,
- 'normalizationMethod': 'offsetAndArea',
- 'motor0': 'PhaseD',
- 'motor1': 'oxPS',
- 'motor2': '',
- 'motor3': '',
- 'motor4': ''
+ "ID08: XLD 9 Tesla Magnet": {
+ "xrange": 0,
+ "normalization": 0,
+ "normalizationMethod": "offsetAndArea",
+ "motor0": "phaseD",
+ "motor1": "",
+ "motor2": "",
+ "motor3": "",
+ "motor4": "",
},
- 'ID08: XLD 5 Tesla Magnet': {
- 'xrange': 0,
- 'normalization': 0,
- 'normalizationMethod': 'offsetAndArea',
- 'motor0': 'PhaseD',
- 'motor1': '',
- 'motor2': '',
- 'motor3': '',
- 'motor4': ''
+ "ID12: XMCD (Flipper)": {
+ "xrange": 0,
+ "normalization": 0,
+ "normalizationMethod": "offsetAndArea",
+ "motor0": "BRUKER",
+ "motor1": "OXFORD",
+ "motor2": "CRYO",
+ "motor3": "",
+ "motor4": "",
},
- 'ID08: XLD 9 Tesla Magnet': {
- 'xrange': 0,
- 'normalization': 0,
- 'normalizationMethod': 'offsetAndArea',
- 'motor0': 'phaseD',
- 'motor1': '',
- 'motor2': '',
- 'motor3': '',
- 'motor4': ''
+ "ID12: XMCD": {
+ "xrange": 0,
+ "normalization": 0,
+ "normalizationMethod": "offsetAndArea",
+ "motor0": "Phase",
+ "motor1": "PhaseA",
+ "motor2": "BRUKER",
+ "motor3": "OXFORD",
+ "motor4": "CRYO",
},
- 'ID12: XMCD (Flipper)': {
- 'xrange': 0,
- 'normalization': 0,
- 'normalizationMethod': 'offsetAndArea',
- 'motor0': 'BRUKER',
- 'motor1': 'OXFORD',
- 'motor2': 'CRYO',
- 'motor3': '',
- 'motor4': ''
+ "ID12: XLD (quater wave plate)": {
+ "xrange": 0,
+ "normalization": 0,
+ "normalizationMethod": "offsetAndArea",
+ "motor0": "",
+ "motor1": "",
+ "motor2": "",
+ "motor3": "",
+ "motor4": "",
},
- 'ID12: XMCD': {
- 'xrange': 0,
- 'normalization': 0,
- 'normalizationMethod': 'offsetAndArea',
- 'motor0': 'Phase',
- 'motor1': 'PhaseA',
- 'motor2': 'BRUKER',
- 'motor3': 'OXFORD',
- 'motor4': 'CRYO'
+ "ID32: XMCD / XMLD": {
+ "xrange": 0,
+ "normalization": 0,
+ "normalizationMethod": "offsetAndArea",
+ "motor0": "HU88CP",
+ "motor1": "HU88AP",
+ "motor2": "magnet",
+ "motor3": "",
+ "motor4": "",
},
- 'ID12: XLD (quater wave plate)': {
- 'xrange': 0,
- 'normalization': 0,
- 'normalizationMethod': 'offsetAndArea',
- 'motor0': '',
- 'motor1': '',
- 'motor2': '',
- 'motor3': '',
- 'motor4': ''
- }
}
self.expCBox.addItems(
- ['Generic Dichroism',
- 'ID08: XLD 9 Tesla Magnet',
- 'ID08: XLD 5 Tesla Magnet',
- 'ID08: XMCD 9 Tesla Magnet',
- 'ID08: XMCD 5 Tesla Magnet',
- 'ID12: XLD (quater wave plate)',
- 'ID12: XMCD (Flipper)',
- 'ID12: XMCD',
- 'Add new configuration'])
+ [
+ "Generic Dichroism",
+ "ID08: XLD 9 Tesla Magnet",
+ "ID08: XLD 5 Tesla Magnet",
+ "ID08: XMCD 9 Tesla Magnet",
+ "ID08: XMCD 5 Tesla Magnet",
+ "ID12: XLD (quater wave plate)",
+ "ID12: XMCD (Flipper)",
+ "ID12: XMCD",
+ "ID32: XMCD / XMLD",
+ "Add new configuration",
+ ]
+ )
self.expCBox.insertSeparator(len(self.experimentsDict))
- topLayout = qt.QHBoxLayout()
+ topLayout = qt.QHBoxLayout()
topLayout.addWidget(buttonUpdate)
topLayout.addWidget(buttonOptions)
topLayout.addWidget(buttonInfo)
@@ -1628,44 +1659,44 @@ class XMCDWidget(qt.QWidget):
leftWidget = qt.QWidget(self)
leftWidget.setLayout(leftLayout)
- self.analysisWindow.setSizePolicy(qt.QSizePolicy.Minimum, qt.QSizePolicy.Minimum)
- #self.splitter = qt.QSplitter(qt.Qt.Horizontal, self)
+ self.analysisWindow.setSizePolicy(
+ qt.QSizePolicy.Minimum, qt.QSizePolicy.Minimum
+ )
+ # self.splitter = qt.QSplitter(qt.Qt.Horizontal, self)
self.splitter = qt.QSplitter(qt.Qt.Vertical, self)
self.splitter.addWidget(leftWidget)
self.splitter.addWidget(self.analysisWindow)
stretch = int(leftWidget.width())
# If window size changes, only the scan window size changes
- self.splitter.setStretchFactor(
- self.splitter.indexOf(self.analysisWindow),1)
+ self.splitter.setStretchFactor(self.splitter.indexOf(self.analysisWindow), 1)
mainLayout = qt.QVBoxLayout()
- mainLayout.setContentsMargins(0,0,0,0)
+ mainLayout.setContentsMargins(0, 0, 0, 0)
mainLayout.addWidget(self.splitter)
self.setLayout(mainLayout)
# Shortcuts
- self.updateShortcut = qt.QShortcut(qt.QKeySequence('F5'), self)
+ self.updateShortcut = qt.QShortcut(qt.QKeySequence("F5"), self)
self.updateShortcut.activated.connect(self.updatePlots)
- self.optionsWindowShortcut = qt.QShortcut(qt.QKeySequence('Alt+O'), self)
+ self.optionsWindowShortcut = qt.QShortcut(qt.QKeySequence("Alt+O"), self)
self.optionsWindowShortcut.activated.connect(self.showOptionsWindow)
- self.helpFileShortcut = qt.QShortcut(qt.QKeySequence('F1'), self)
+ self.helpFileShortcut = qt.QShortcut(qt.QKeySequence("F1"), self)
self.helpFileShortcut.activated.connect(self.showInfoWindow)
- self.expSelectorShortcut = qt.QShortcut(qt.QKeySequence('Tab'), self)
+ self.expSelectorShortcut = qt.QShortcut(qt.QKeySequence("Tab"), self)
self.expSelectorShortcut.activated.connect(self.activateExpCB)
- self.saveShortcut = qt.QShortcut(qt.QKeySequence('Ctrl+S'), self)
- self.saveShortcut.activated.connect(self.analysisWindow._saveIconSignal)
+ self.saveShortcut = qt.QShortcut(qt.QKeySequence("Ctrl+S"), self)
+ self.saveShortcut.activated.connect(self._saveIconSignal)
# Connects
self.expCBox.currentIndexChanged[int].connect(self.updateTree)
if hasattr(self.expCBox, "textActivated"):
- self.expCBox.textActivated[str].connect(\
- self.selectExperiment)
+ self.expCBox.textActivated[str].connect(self.selectExperiment)
else:
- self.expCBox.currentIndexChanged['QString'].connect(\
- self.selectExperiment)
+ self.expCBox.currentIndexChanged["QString"].connect(self.selectExperiment)
self.list.selectionModifiedSignal.connect(self.updateSelectionDict)
self.setSelectionSignal.connect(self.analysisWindow.processSelection)
self.analysisWindow.saveOptionsSignal.connect(self.optsWindow.saveOptions)
+ self.analysisWindow.sigIconSignal.connect(self._saveIconSignal)
self.optsWindow.accepted.connect(self.updateTree)
buttonUpdate.clicked.connect(self.updatePlots)
buttonOptions.clicked.connect(self.showOptionsWindow)
@@ -1677,16 +1708,34 @@ class XMCDWidget(qt.QWidget):
def sizeHint(self):
return self.list.sizeHint() + self.analysisWindow.sizeHint()
+ def _saveIconSignal(self, ddict):
+ key = ddict.get("key", None)
+ if key and key == "save":
+ if not len(self.selectionDict["A"]):
+ msg = qt.QMessageBox()
+ msg.setWindowTitle("XLD/XMCD Error")
+ msg.setText("At lease one scan needs to be as A")
+ msg.exec()
+ return
+
+ # extract the motors from the first curve selected as A
+ legend = self.selectionDict["A"][0]
+ idx = self.legendList.index(legend)
+ motors = self.motorsList[idx]
+ print(motors)
+ self.analysisWindow._saveIconSignalReplacement(motors=motors)
+
def activateExpCB(self):
self.expCBox.setFocus(qt.Qt.TabFocusReason)
def addExperiment(self):
- exp, chk = qt.QInputDialog.\
- getText(self,
- 'Configure new experiment',
- 'Enter experiment title',
- qt.QLineEdit.Normal,
- 'ID00: <Title>')
+ exp, chk = qt.QInputDialog.getText(
+ self,
+ "Configure new experiment",
+ "Enter experiment title",
+ qt.QLineEdit.Normal,
+ "ID00: <Title>",
+ )
if chk and (not exp.isEmpty()):
exp = str(exp)
opts = XMCDOptions(self, self.motorNamesList, False)
@@ -1695,10 +1744,10 @@ class XMCDWidget(qt.QWidget):
cBox = self.expCBox
new = [cBox.itemText(i) for i in range(cBox.count())][0:-2]
new += [exp]
- new.append('Add new configuration')
+ new.append("Add new configuration")
cBox.clear()
cBox.addItems(new)
- cBox.insertSeparator(len(new)-1)
+ cBox.insertSeparator(len(new) - 1)
idx = cBox.findText([exp][0])
if idx < 0:
cBox.setCurrentIndex(0)
@@ -1718,19 +1767,18 @@ class XMCDWidget(qt.QWidget):
def showInfoWindow(self):
if self.helpFileBrowser is None:
msg = qt.QMessageBox()
- msg.setWindowTitle('XLD/XMCD Error')
- msg.setText('No help file found.')
+ msg.setWindowTitle("XLD/XMCD Error")
+ msg.setText("No help file found.")
msg.exec()
return
else:
self.helpFileBrowser.show()
self.helpFileBrowser.raise_()
-
-# Implement new assignment routines here BEGIN
+ # Implement new assignment routines here BEGIN
def selectExperiment(self, exp):
exp = str(exp)
- if exp == 'Add new configuration':
+ if exp == "Add new configuration":
self.addExperiment()
self.updateTree()
elif exp in self.experimentsDict:
@@ -1738,123 +1786,114 @@ class XMCDWidget(qt.QWidget):
# Sets motors 0 to 4 in optsWindow
self.optsWindow.setOptions(self.experimentsDict[exp])
except ValueError:
- self.optsWindow.setOptions(
- self.experimentsDict['Generic Dichroism'])
+ self.optsWindow.setOptions(self.experimentsDict["Generic Dichroism"])
return
# Get motor values from tree
self.updateTree()
- values0 = numpy.array(
- self.list.getColumn(4, convertType=float))
- values1 = numpy.array(
- self.list.getColumn(5, convertType=float))
- values2 = numpy.array(
- self.list.getColumn(6, convertType=float))
- values3 = numpy.array(
- self.list.getColumn(7, convertType=float))
- values4 = numpy.array(
- self.list.getColumn(8, convertType=float))
+ values0 = numpy.array(self.list.getColumn(4, convertType=float))
+ values1 = numpy.array(self.list.getColumn(5, convertType=float))
+ values2 = numpy.array(self.list.getColumn(6, convertType=float))
+ values3 = numpy.array(self.list.getColumn(7, convertType=float))
+ values4 = numpy.array(self.list.getColumn(8, convertType=float))
# Determine p/m selection
- if exp.startswith('ID08: XLD'):
+ if exp.startswith("ID08: XLD"):
values = values0
mask = numpy.where(numpy.isfinite(values))[0]
minmax = values.take(mask)
if len(minmax):
vmin = minmax.min()
vmax = minmax.max()
- vpivot = .5 * (vmax + vmin)
+ vpivot = 0.5 * (vmax + vmin)
else:
- vpivot = 0.
- values = numpy.array(
- [float('NaN')]*len(self.legendList))
- elif exp.startswith('ID08: XMCD'):
+ vpivot = 0.0
+ values = numpy.array([float("NaN")] * len(self.legendList))
+ elif exp.startswith("ID08: XMCD"):
mask = numpy.where(numpy.isfinite(values0))[0]
polarization = values0.take(mask)
values1 = values1.take(mask)
signMagnets = numpy.sign(values1)
- if len(polarization)==0:
- vpivot = 0.
- values = numpy.array(
- [float('NaN')]*len(self.legendList))
- elif numpy.all(signMagnets>=0.) or\
- numpy.all(signMagnets<=0.) or\
- numpy.all(signMagnets==0.):
+ if len(polarization) == 0:
+ vpivot = 0.0
+ values = numpy.array([float("NaN")] * len(self.legendList))
+ elif (
+ numpy.all(signMagnets >= 0.0)
+ or numpy.all(signMagnets <= 0.0)
+ or numpy.all(signMagnets == 0.0)
+ ):
vmin = polarization.min()
vmax = polarization.max()
- vpivot = .5 * (vmax + vmin)
+ vpivot = 0.5 * (vmax + vmin)
values = polarization
else:
- vpivot = 0.
+ vpivot = 0.0
values = polarization * signMagnets
- elif exp.startswith('ID12: XLD (quater wave plate)'):
+ elif exp.startswith("ID12: XLD (quater wave plate)"):
# Extract counters from third column
counters = self.list.getColumn(3, convertType=str)
polarization = []
for counter in counters:
# Relevant counters Ihor, Iver resp. Ihor0, Iver0, etc.
- if 'hor' in counter:
- pol = -1.
- elif 'ver' in counter:
- pol = 1.
+ if "hor" in counter:
+ pol = -1.0
+ elif "ver" in counter:
+ pol = 1.0
else:
- pol = float('nan')
+ pol = float("nan")
polarization += [pol]
values = numpy.asarray(polarization, dtype=float)
- vpivot = 0.
- elif exp.startswith('ID12: XMCD (Flipper)'):
+ vpivot = 0.0
+ elif exp.startswith("ID12: XMCD (Flipper)"):
# Extract counters from third column
counters = self.list.getColumn(1, convertType=str)
polarization = []
for counter in counters:
# Relevant counters: Fminus/Fplus resp. Rminus/Rplus
- if 'minus' in counter:
- pol = 1.
- elif 'plus' in counter:
- pol = -1.
+ if "minus" in counter:
+ pol = 1.0
+ elif "plus" in counter:
+ pol = -1.0
else:
- pol = float('nan')
+ pol = float("nan")
polarization += [pol]
magnets = values0 + values1 + values2
- values = numpy.asarray(polarization, dtype=float)*\
- magnets
- vpivot = 0.
- elif exp.startswith('ID12: XMCD'):
+ values = numpy.asarray(polarization, dtype=float) * magnets
+ vpivot = 0.0
+ elif exp.startswith("ID12: XMCD"):
# Sum over phases..
polarization = values0 + values1
# ..and magnets
magnets = values2 + values3 + values4
signMagnets = numpy.sign(magnets)
- if numpy.all(signMagnets==0.):
+ if numpy.all(signMagnets == 0.0):
values = polarization
else:
- values = numpy.sign(polarization)*\
- numpy.sign(magnets)
- vpivot = 0.
+ values = numpy.sign(polarization) * numpy.sign(magnets)
+ vpivot = 0.0
else:
- values = numpy.array([float('NaN')]*len(self.legendList))
- vpivot = 0.
+ values = numpy.array([float("NaN")] * len(self.legendList))
+ vpivot = 0.0
# Sequence is generate according to values and vpivot
- seq = ''
+ seq = ""
for x in values:
- if str(x) == 'nan':
- seq += 'D'
- elif x<vpivot:
+ if str(x) == "nan":
+ seq += "D"
+ elif x < vpivot:
# Minus group
- seq += 'A'
+ seq += "A"
else:
# Plus group
- seq += 'B'
+ seq += "B"
self.list.setSelectionToSequence(seq)
-# Implement new assignment routines here END
+
+ # Implement new assignment routines here END
def triggerXMCD(self):
- groupA = self.selectionDict['A']
- groupB = self.selectionDict['B']
+ groupA = self.selectionDict["A"]
+ groupB = self.selectionDict["B"]
self.analysisWindow.processSelection(groupA, groupB)
def removeCurve_(self):
- sel = self.list.getColumn(1,
- selectedOnly=True,
- convertType=str)
+ sel = self.list.getColumn(1, selectedOnly=True, convertType=str)
for legend in sel:
self.plotWindow.removeCurve(legend)
for selection in self.selectionDict.values():
@@ -1862,7 +1901,7 @@ class XMCDWidget(qt.QWidget):
selection.remove(legend)
# Remove from XMCDScanWindow.curvesDict
if legend in self.analysisWindow.curvesDict.keys():
- del(self.analysisWindow.curvesDict[legend])
+ del self.analysisWindow.curvesDict[legend]
# Remove from XMCDScanWindow.selectionDict
for selection in self.analysisWindow.selectionDict.values():
if legend in selection:
@@ -1877,89 +1916,79 @@ class XMCDWidget(qt.QWidget):
selDict = self.list.getSelection()
# self.selectionDict -> Uses ScanNumbers instead of legends...
newDict = {}
- for (idx, selList) in selDict.items():
+ for idx, selList in selDict.items():
if idx not in newDict.keys():
newDict[idx] = []
for legend in selList:
newDict[idx] += [legend]
self.selectionDict = newDict
- self.setSelectionSignal.emit(self.selectionDict['A'],
- self.selectionDict['B'])
-
- def updatePlots(self,
- newLegends = None,
- newMotorValues = None):
- # Check if curves in plotWindow changed..
- curves = self.plotWindow.getAllCurves(just_legend=True)
- if curves == self.legendList:
- # ..if not, just replot to account for zoom
- self.triggerXMCD()
- return
- self._setLists()
+ self.setSelectionSignal.emit(self.selectionDict["A"], self.selectionDict["B"])
- self.motorNamesList = [''] + self._getAllMotorNames()
+ def updatePlots(self):
+ self._setLists()
+ self.motorNamesList = [""] + self._getAllMotorNames()
self.motorNamesList.sort()
self.optsWindow.updateMotorList(self.motorNamesList)
self.updateTree()
experiment = str(self.expCBox.currentText())
- if experiment != 'Generic Dichroism':
+ if experiment != "Generic Dichroism":
self.selectExperiment(experiment)
return
def updateTree(self):
- mList = self.optsWindow.getMotors()
- labels = ["Group",'Legend','S#','Counter'] + mList
- items = []
+ mList = self.optsWindow.getMotors()
+ labels = ["Group", "Legend", "S#", "Counter"] + mList
+ items = []
for i in range(len(self.legendList)):
# Loop through rows
# Each row is represented by QStringList
legend = self.legendList[i]
values = self.motorsList[i]
info = self.infoList[i]
- selection = ''
+ selection = ""
# Determine Group from selectionDict
- for (idx, v) in self.selectionDict.items():
- if (legend in v) and (idx != 'D'):
+ for idx, v in self.selectionDict.items():
+ if (legend in v) and (idx != "D"):
selection = idx
break
# Add filename, scanNo, counter
- #sourceName = info.get('SourceName','')
- #if isinstance(sourceName,list):
+ # sourceName = info.get('SourceName','')
+ # if isinstance(sourceName,list):
# filename = basename(sourceName[0])
- #else:
+ # else:
# filename = basename(sourceName)
filename = legend
- scanNo = info.get('Key','')
- counter = info.get('ylabel',None)
+ scanNo = info.get("Key", "")
+ counter = info.get("ylabel", None)
if counter is None:
- selDict = info.get('selection',{})
+ selDict = info.get("selection", {})
if len(selDict) == 0:
- counter = ''
+ counter = ""
else:
# When do multiple selections occur?
try:
- yIdx = selDict['y'][0]
- cntList = selDict['cnt_list']
+ yIdx = selDict["y"][0]
+ cntList = selDict["cnt_list"]
counter = cntList[yIdx]
except Exception:
- counter = ''
+ counter = ""
tmp = QStringList([selection, filename, scanNo, counter])
# Determine value for each motor
for m in mList:
if len(m) == 0:
- tmp.append('')
+ tmp.append("")
else:
- tmp.append(str(values.get(m, '---')))
+ tmp.append(str(values.get(m, "---")))
items.append(tmp)
- self.list.build(items, labels)
+ self.list.build(items, labels)
for idx in range(self.list.columnCount()):
self.list.resizeColumnToContents(idx)
def setAsA(self):
- self.list.setSelectionAs('A')
+ self.list.setSelectionAs("A")
def setAsB(self):
- self.list.setSelectionAs('B')
+ self.list.setSelectionAs("B")
def _getAllMotorNames(self):
names = []
@@ -1970,10 +1999,10 @@ class XMCDWidget(qt.QWidget):
names.sort()
return names
- def _convertInfoDictionary(self, infosList):
+ def _convertInfoDictionary(self, infosList):
ret = []
- for info in infosList :
- motorNames = info.get('MotorNames', None)
+ for info in infosList:
+ motorNames = info.get("MotorNames", None)
if motorNames is not None:
if type(motorNames) == str:
namesList = motorNames.split()
@@ -1983,7 +2012,7 @@ class XMCDWidget(qt.QWidget):
namesList = []
else:
namesList = []
- motorValues = info.get('MotorValues', None)
+ motorValues = info.get("MotorValues", None)
if motorNames is not None:
if type(motorValues) == str:
valuesList = motorValues.split()
@@ -1994,9 +2023,10 @@ class XMCDWidget(qt.QWidget):
else:
valuesList = []
if len(namesList) == len(valuesList):
- ret.append(dict(zip(namesList, valuesList)))
+ ret.append(dict(zip(namesList, valuesList)))
else:
_logger.warning("Number of motors and values does not match!")
+ ret.append({})
return ret
def _setLists(self):
@@ -2016,15 +2046,17 @@ class XMCDWidget(qt.QWidget):
if self.plotWindow is not None:
curves = self.plotWindow.getAllCurves()
else:
- _logger.debug('_setLists -- Set self.plotWindow before calling self._setLists')
+ _logger.debug(
+ "_setLists -- Set self.plotWindow before calling self._setLists"
+ )
return
# nCurves = len(curves)
- self.legendList = [leg for (xvals, yvals, leg, info) in curves]
- self.infoList = [info for (xvals, yvals, leg, info) in curves]
+ self.legendList = [leg for (xvals, yvals, leg, info) in curves]
+ self.infoList = [info for (xvals, yvals, leg, info) in curves]
# Try to recover the scan number from the legend, if not set
# Requires additional import:
- #from re import search as regexpSearch
- #for ddict in self.infoList:
+ # from re import search as regexpSearch
+ # for ddict in self.infoList:
# key = ddict.get('Key','')
# if len(key)== 0:
# selectionlegend = ddict['selectionlegend']
@@ -2034,13 +2066,14 @@ class XMCDWidget(qt.QWidget):
# ddict['Key'] = scanNo
self.motorsList = self._convertInfoDictionary(self.infoList)
+
class XMCDFileDialog(qt.QFileDialog):
def __init__(self, parent, caption, directory, filter):
qt.QFileDialog.__init__(self, parent, caption, directory, filter)
- saveOptsGB = qt.QGroupBox('Save options', self)
- self.appendBox = qt.QCheckBox('Append to existing file', self)
- self.commentBox = qt.QTextEdit('Enter comment', self)
+ saveOptsGB = qt.QGroupBox("Save options", self)
+ self.appendBox = qt.QCheckBox("Append to existing file", self)
+ self.commentBox = qt.QTextEdit("Enter comment", self)
mainLayout = self.layout()
optsLayout = qt.QGridLayout()
@@ -2053,12 +2086,19 @@ class XMCDFileDialog(qt.QFileDialog):
def appendChecked(self, state):
if state == qt.Qt.Unchecked:
- self.setConfirmOverwrite(True)
+ if hasattr(self, "setConfirmOverwrite"):
+ self.setConfirmOverwrite(True)
+ else:
+ self.setOption(qt.QFileDialog.DontConfirmOverwrite, False)
self.setFileMode(qt.QFileDialog.AnyFile)
else:
- self.setConfirmOverwrite(False)
+ if hasattr(self, "setConfirmOverwrite"):
+ self.setConfirmOverwrite(False)
+ else:
+ self.setOption(qt.QFileDialog.DontConfirmOverwrite, True)
self.setFileMode(qt.QFileDialog.ExistingFile)
+
def getSaveFileName(parent, caption, directory, filter):
dial = XMCDFileDialog(parent, caption, directory, filter)
dial.setAcceptMode(qt.QFileDialog.AcceptSave)
@@ -2066,42 +2106,89 @@ def getSaveFileName(parent, caption, directory, filter):
comment = None
files = []
if dial.exec():
- append = dial.appendBox.isChecked()
+ append = dial.appendBox.isChecked()
comment = str(dial.commentBox.toPlainText())
- if comment == 'Enter comment':
- comment = ''
+ if comment == "Enter comment":
+ comment = ""
files = [qt.safe_str(fn) for fn in dial.selectedFiles()]
return (files, append, comment)
+
def main():
# Create dummy ScanWindow
swin = ScanWindow.ScanWindow()
- info0 = {'xlabel': 'foo',
- 'ylabel': 'arb',
- 'MotorNames': 'oxPS PhaseA Phase BRUKER CRYO OXFORD',
- 'MotorValues': '1 -6.27247094 -3.11222732 6.34150808 -34.75892563 21.99607165'}
- info1 = {'MotorNames': 'PhaseD oxPS PhaseA Phase BRUKER CRYO OXFORD',
- 'MotorValues': '0.470746882688 0.25876374531 -0.18515967 -28.31216591 18.54513221 -28.09735532 -26.78833172'}
- info2 = {'MotorNames': 'PhaseD oxPS PhaseA Phase BRUKER CRYO OXFORD',
- 'MotorValues': '-9.45353059 -25.37448851 24.37665651 18.88048044 -0.26018745 2 0.901968648111 '}
- x = numpy.arange(100.,1100.)
- y0 = 10*x + 10000.*numpy.exp(-0.5*(x-500)**2/400) + 1500*numpy.random.random(1000)
- y1 = 10*x + 10000.*numpy.exp(-0.5*(x-600)**2/400) + 1500*numpy.random.random(1000)
- y2 = 10*x + 10000.*numpy.exp(-0.5*(x-400)**2/400) + 1500*numpy.random.random(1000)
-
- swin.newCurve(x, y2, legend="Curve2", xlabel='ene_st2', ylabel='Ihor', info=info2, replot=False, replace=False)
- swin.newCurve(x, y0, legend="Curve0", xlabel='ene_st0', ylabel='Iver', info=info0, replot=False, replace=False)
- swin.newCurve(x, y1, legend="Curve1", xlabel='ene_st1', ylabel='Ihor', info=info1, replot=False, replace=False)
+ info0 = {
+ "xlabel": "foo",
+ "ylabel": "arb",
+ "MotorNames": "oxPS PhaseA Phase BRUKER CRYO OXFORD",
+ "MotorValues": "1 -6.27247094 -3.11222732 6.34150808 -34.75892563 21.99607165",
+ }
+ info1 = {
+ "MotorNames": "PhaseD oxPS PhaseA Phase BRUKER CRYO OXFORD",
+ "MotorValues": "0.470746882688 0.25876374531 -0.18515967 -28.31216591 18.54513221 -28.09735532 -26.78833172",
+ }
+ info2 = {
+ "MotorNames": "PhaseD oxPS PhaseA Phase BRUKER CRYO OXFORD",
+ "MotorValues": "-9.45353059 -25.37448851 24.37665651 18.88048044 -0.26018745 2 0.901968648111 ",
+ }
+ x = numpy.arange(100.0, 1100.0)
+ y0 = (
+ 10 * x
+ + 10000.0 * numpy.exp(-0.5 * (x - 500) ** 2 / 400)
+ + 1500 * numpy.random.random(1000)
+ )
+ y1 = (
+ 10 * x
+ + 10000.0 * numpy.exp(-0.5 * (x - 600) ** 2 / 400)
+ + 1500 * numpy.random.random(1000)
+ )
+ y2 = (
+ 10 * x
+ + 10000.0 * numpy.exp(-0.5 * (x - 400) ** 2 / 400)
+ + 1500 * numpy.random.random(1000)
+ )
+
+ swin.addCurve(
+ x,
+ y2,
+ legend="Curve2",
+ xlabel="ene_st2",
+ ylabel="Ihor",
+ info=info2,
+ replot=False,
+ replace=True,
+ )
+ swin.addCurve(
+ x,
+ y0,
+ legend="Curve0",
+ xlabel="ene_st0",
+ ylabel="Iver",
+ info=info0,
+ replot=False,
+ replace=False,
+ )
+ swin.addCurve(
+ x,
+ y1,
+ legend="Curve1",
+ xlabel="ene_st1",
+ ylabel="Ihor",
+ info=info1,
+ replot=True,
+ replace=False,
+ )
# info['Key'] is overwritten when using newCurve
- swin.dataObjectsDict['Curve2 Ihor'].info['Key'] = '1.1'
- swin.dataObjectsDict['Curve0 Iver'].info['Key'] = '34.1'
- swin.dataObjectsDict['Curve1 Ihor'].info['Key'] = '123.1'
+ swin.dataObjectsDict["Curve2 Ihor"].info["Key"] = "1.1"
+ swin.dataObjectsDict["Curve0 Iver"].info["Key"] = "34.1"
+ swin.dataObjectsDict["Curve1 Ihor"].info["Key"] = "123.1"
- w = XMCDWidget(None, swin, 'ID08', nSelectors = 5)
+ w = XMCDWidget(None, swin, "ID08", nSelectors=5)
w.show()
return w
+
# helpFileBrowser = qt.QTextBrowser()
# helpFileBrowser.setLineWrapMode(qt.QTextEdit.FixedPixelWidth)
# helpFileBrowser.setLineWrapColumnOrWidth(500)
@@ -2113,7 +2200,7 @@ def main():
# helpFileBrowser.show()
-if __name__ == '__main__':
+if __name__ == "__main__":
app = qt.QApplication([])
w = main()
app.exec()
diff --git a/PyMca5/PyMcaIO/TiffIO.py b/PyMca5/PyMcaIO/TiffIO.py
index 6ddb7634..75503eb0 100644
--- a/PyMca5/PyMcaIO/TiffIO.py
+++ b/PyMca5/PyMcaIO/TiffIO.py
@@ -30,7 +30,7 @@ __author__ = "V.A. Sole - ESRF"
__contact__ = "sole@esrf.fr"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
-__date__ = "16/12/2022"
+__date__ = "25/10/2023"
import sys
import os
@@ -143,13 +143,13 @@ class TiffIO(object):
self.fd = fd
# read the order
fd.seek(0)
- order = fd.read(2).decode()
+ order = fd.read(2)
if len(order):
- if order == "II":
+ if order == b"II":
# intel, little endian
fileOrder = "little"
self._structChar = '<'
- elif order == "MM":
+ elif order == b"MM":
# motorola, high endian
fileOrder = "big"
self._structChar = '>'
@@ -1114,9 +1114,7 @@ class TiffIO(object):
info["date"] = date
info["sampleFormat"] = sampleFormat
- outputIFD = ""
- if sys.version > '2.6':
- outputIFD = eval('b""')
+ outputIFD = b""
fmt = st + "H"
outputIFD += struct.pack(fmt, nDirectoryEntries)
diff --git a/PyMca5/PyMcaIO/specfilewrapper.py b/PyMca5/PyMcaIO/specfilewrapper.py
index 51bb3b7b..41e4b63e 100644
--- a/PyMca5/PyMcaIO/specfilewrapper.py
+++ b/PyMca5/PyMcaIO/specfilewrapper.py
@@ -186,7 +186,7 @@ class specfilewrapper(object):
f = open(filename, 'rb')
raw_content = f.read()
f.close()
- expr = '([-+]?\d+)\t\r\n'
+ expr = r'([-+]?\d+)\t\r\n'
self.data = [float(i) for i in re.split(expr,raw_content) if i != '']
self.data = numpy.array(self.data, numpy.float32)
else:
diff --git a/PyMca5/PyMcaMath/mva/NNMAModule.py b/PyMca5/PyMcaMath/mva/NNMAModule.py
index d8c0eb10..6c7a267c 100644
--- a/PyMca5/PyMcaMath/mva/NNMAModule.py
+++ b/PyMca5/PyMcaMath/mva/NNMAModule.py
@@ -63,7 +63,7 @@ The common parameters when calling such a function are:
from numpy or sparse from scipy.sparse
package
- k -- number of componnets to estimate
+ k -- number of components to estimate
Astart
Xstart -- matrices to start iterations. Maybe None
@@ -185,17 +185,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
"""
import numpy
import logging
-try:
- import os
- os.environ["MDP_DISABLE_SKLEARN"] = "yes"
- import mdp
- if mdp.__version__ >= '2.6':
- MDP = True
- else:
- MDP = False
-except Exception:
- MDP = False
-
from . import py_nnma
@@ -223,8 +212,8 @@ def nnma(stack, ncomponents, binning=None,
mask=None, spectral_mask=None,
function=None, eps=5e-5, verbose=VERBOSE,
maxcount=1000, kmeans=False):
- if kmeans and (not MDP):
- raise ValueError("K Means not supported")
+ if kmeans:
+ raise ValueError("K Means not supported by this module")
#I take the defaults for the other parameters
param = dict(alpha=.1, tau=2, regul=1e-2, sparse_par=1e-1, psi=1e-3)
if function is None:
@@ -384,13 +373,9 @@ def nnma(stack, ncomponents, binning=None,
original_intensity = numpy.sum(data)
#final values
- if kmeans:
- n_more = 1
- else:
- n_more = 0
- new_images = numpy.zeros((ncomponents + n_more, r*c), numpy.float32)
- new_vectors = numpy.zeros((X.shape[0]+n_more, X.shape[1]), numpy.float32)
- values = numpy.zeros((ncomponents+n_more,), numpy.float32)
+ new_images = numpy.zeros((ncomponents, r*c), numpy.float32)
+ new_vectors = numpy.zeros((X.shape[0], X.shape[1]), numpy.float32)
+ values = numpy.zeros((ncomponents,), numpy.float32)
for i in range(ncomponents):
idx = sorted_idx[i]
if 1:
@@ -407,17 +392,7 @@ def nnma(stack, ncomponents, binning=None,
new_images[i, maskview] = numpy.sum(numpy.dot(Atmp, Xtmp), axis=1)
new_vectors[i,:] = X[idx,:]
values[i] = 100.*total_nnma_intensity[idx][0]/original_intensity
- new_images.shape = ncomponents + n_more, r, c
- if kmeans:
- classifier = mdp.nodes.KMeansClassifier(ncomponents)
- for i in range(ncomponents):
- classifier.train(new_vectors[i:i+1])
- k = 0
- for i in range(r):
- for j in range(c):
- spectrum = data[k:k+1,:]
- new_images[-1, i,j] = classifier.label(spectrum)[0]
- k += 1
+ new_images.shape = ncomponents, r, c
return new_images, values, new_vectors
if __name__ == "__main__":
diff --git a/PyMca5/PyMcaPhysics/xrf/ClassMcaTheory.py b/PyMca5/PyMcaPhysics/xrf/ClassMcaTheory.py
index 9fb97542..6ef4d59f 100644
--- a/PyMca5/PyMcaPhysics/xrf/ClassMcaTheory.py
+++ b/PyMca5/PyMcaPhysics/xrf/ClassMcaTheory.py
@@ -227,6 +227,8 @@ class McaTheory(object):
energyscatter = []
for i in range(len(self.config['fit']['energy'])):
+ if self.config['fit']['energy'][i] == "None":
+ self.config['fit']['energy'][i] = None
if self.config['fit']['energyflag'][i]:
if self.config['fit']['energy'][i] is not None:
energyflag.append(self.config['fit']['energyflag'][i])
diff --git a/PyMca5/PyMcaPhysics/xrf/FastXRFLinearFit.py b/PyMca5/PyMcaPhysics/xrf/FastXRFLinearFit.py
index cbda7532..1eb68ea6 100644
--- a/PyMca5/PyMcaPhysics/xrf/FastXRFLinearFit.py
+++ b/PyMca5/PyMcaPhysics/xrf/FastXRFLinearFit.py
@@ -736,10 +736,13 @@ class FastXRFLinearFit(object):
@staticmethod
def _fitDtypeResult(data):
if data.dtype not in [numpy.float32, numpy.float64]:
- if data.itemsize < 5:
- return numpy.float32
- else:
- return numpy.float64
+ if hasattr(data, "itemsize"):
+ if data.itemsize < 5:
+ return numpy.float32
+ elif hasattr(data, "nbytes"):
+ if (data.nbytes / data.size) < 5:
+ return numpy.float32
+ return numpy.float64
else:
return data.dtype
diff --git a/PyMca5/PyMcaPhysics/xrf/McaAdvancedFitBatch.py b/PyMca5/PyMcaPhysics/xrf/McaAdvancedFitBatch.py
index 8258496d..bd075916 100644
--- a/PyMca5/PyMcaPhysics/xrf/McaAdvancedFitBatch.py
+++ b/PyMca5/PyMcaPhysics/xrf/McaAdvancedFitBatch.py
@@ -2,7 +2,7 @@
#
# The PyMca X-Ray Fluorescence Toolkit
#
-# Copyright (c) 2004-2022 European Synchrotron Radiation Facility
+# Copyright (c) 2004-2023 European Synchrotron Radiation Facility
#
# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
# the ESRF.
@@ -802,6 +802,8 @@ class McaAdvancedFitBatch(object):
concentrations = None
_logger.error("error in concentrations")
_logger.error(str(sys.exc_info()[0:-1]))
+ self._restoreFitConfig(filename,
+ 'calculating concentrations')
else:
#just images
fitresult = self.mcafit.startfit(digest=0)
diff --git a/PyMca5/PyMcaPhysics/xrf/SingleLayerStrategy.py b/PyMca5/PyMcaPhysics/xrf/SingleLayerStrategy.py
index c077c136..69719896 100644
--- a/PyMca5/PyMcaPhysics/xrf/SingleLayerStrategy.py
+++ b/PyMca5/PyMcaPhysics/xrf/SingleLayerStrategy.py
@@ -2,7 +2,7 @@
#
# The PyMca X-Ray Fluorescence Toolkit
#
-# Copyright (c) 2004-2018 European Synchrotron Radiation Facility
+# Copyright (c) 2004-2023 European Synchrotron Radiation Facility
#
# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
# the ESRF by the Software group.
@@ -118,6 +118,7 @@ class SingleLayerStrategy(object):
CompoundList = []
CompoundFraction = []
materialCounter = -1
+ previousElements = []
for group in strategyConfiguration["peaks"]:
materialCounter += 1
if "-" in group:
@@ -126,6 +127,12 @@ class SingleLayerStrategy(object):
_logger.debug("ignoring %s", group)
continue
ele = group.split()[0]
+ if ele in previousElements:
+ print("Strategy element %s considered twice! Ignoring second entry" % ele)
+ _logger.critical("Strategy element %s considered twice. Ignoring second entry" % ele)
+ continue
+ else:
+ previousElements.append(ele)
material = strategyConfiguration["materials"][materialCounter]
if material in ["-", ele, ele + "1"]:
CompoundList.append(ele)
diff --git a/PyMca5/PyMcaPlugins/AdvancedAlignmentScanPlugin.py b/PyMca5/PyMcaPlugins/AdvancedAlignmentScanPlugin.py
index b7666940..7958aa03 100644
--- a/PyMca5/PyMcaPlugins/AdvancedAlignmentScanPlugin.py
+++ b/PyMca5/PyMcaPlugins/AdvancedAlignmentScanPlugin.py
@@ -61,7 +61,7 @@ The table shows three columns:
While columns one and two can not be edited, shift values
can be entered by hand. Another way of setting the shift values is to load them from a existing
-\*.shift file using the Load button.
+*.shift file using the Load button.
Once the shift values are set, they can either be directly applied to the data present in the
plot window, using the *Apply* button, or the data can be stored in memory. The latter options allow
diff --git a/PyMca5/PyMcaPlugins/ImageAlignmentStackPlugin.py b/PyMca5/PyMcaPlugins/ImageAlignmentStackPlugin.py
index 7d7364b9..901f4211 100644
--- a/PyMca5/PyMcaPlugins/ImageAlignmentStackPlugin.py
+++ b/PyMca5/PyMcaPlugins/ImageAlignmentStackPlugin.py
@@ -156,9 +156,10 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase):
# result[0] contains the string "Exception" in case
# of error. However, direct comparison will raise an
# error
- if type(result[0]) == type('Exception'):
- # exception occurred
- raise Exception(result[1], result[2], result[3])
+ if isinstance(result[0], str):
+ if result[0] == 'Exception':
+ # exception occurred
+ raise Exception(result[1], result[2], result[3])
else:
shifts = result
result = self.__shiftStack(stack,
@@ -306,9 +307,10 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase):
crop=crop, filename=filename)
if result is not None:
if len(result):
- if result[0] == 'Exception':
- # exception occurred
- raise Exception(result[1], result[2], result[3])
+ if isinstance(result[0], str):
+ if result[0] == 'Exception':
+ # exception occurred
+ raise Exception(result[1], result[2], result[3])
if filename is None:
self.setStack(stack)
diff --git a/PyMca5/PyMcaPlugins/NNMAStackPlugin.py b/PyMca5/PyMcaPlugins/NNMAStackPlugin.py
index 76b5dfe2..86b748da 100644
--- a/PyMca5/PyMcaPlugins/NNMAStackPlugin.py
+++ b/PyMca5/PyMcaPlugins/NNMAStackPlugin.py
@@ -138,8 +138,8 @@ class NNMAStackPlugin(StackPluginBase.StackPluginBase):
mcaIndex = stack.info.get('McaIndex')
shape = stack.data.shape
stack = None
- if mcaIndex not in [-1, len(shape) - 1]:
- raise IndexError("NNMA does not support stacks of images yet")
+ if mcaIndex not in [0, -1, len(shape) - 1]:
+ raise IndexError("NNMA only support stacks of images or spectra")
return
if self.configurationWidget is None:
self.configurationWidget = NNMAParametersDialog(None, regions=True)
@@ -225,9 +225,23 @@ class NNMAStackPlugin(StackPluginBase.StackPluginBase):
self._status.setText(text)
oldShape = stack.data.shape
- result = function(stack, **ddict)
- if stack.data.shape != oldShape:
- stack.data.shape = oldShape
+ mcaIndex = stack.info.get('McaIndex')
+ if mcaIndex == 0:
+ # image stack. We need a copy
+ _logger.info("NNMAStackPlugin converting to stack of spectra")
+ data = numpy.zeros(oldShape[1:] + oldShape[0:1], dtype=numpy.float32)
+ data.shape = -1, oldShape[0]
+ for i in range(oldShape[0]):
+ tmpData = stack.data[i]
+ tmpData.shape = -1
+ data[:, i] = tmpData
+ data.shape = oldShape[1:] + oldShape[0:1]
+ result = function(data, **ddict)
+ data = None
+ else:
+ result = function(stack, **ddict)
+ if stack.data.shape != oldShape:
+ stack.data.shape = oldShape
return result
def threadFinished(self):
@@ -237,12 +251,11 @@ class NNMAStackPlugin(StackPluginBase.StackPluginBase):
if type(result) == type((1,)):
#if we receive a tuple there was an error
if len(result):
- if type(result[0]) == type("Exception"):
- if result[0] == "Exception":
- self._status.setText("Ready after calculation error")
- self.configurationWidget.setEnabled(True)
- raise Exception(result[1], result[2])
- return
+ if isinstance(result[0], str) and result[0] == "Exception":
+ self._status.setText("Ready after calculation error")
+ self.configurationWidget.setEnabled(True)
+ raise Exception(result[1], result[2])
+ return
self._status.setText("Ready")
curve = self.configurationWidget.getSpectrum(binned=True)
if curve not in [None, []]:
diff --git a/PyMca5/PyMcaPlugins/PCAStackPlugin.py b/PyMca5/PyMcaPlugins/PCAStackPlugin.py
index 1c24c4bd..7540e92c 100644
--- a/PyMca5/PyMcaPlugins/PCAStackPlugin.py
+++ b/PyMca5/PyMcaPlugins/PCAStackPlugin.py
@@ -259,7 +259,7 @@ class PCAStackPlugin(StackPluginBase.StackPluginBase):
if type(result) == type((1,)):
#if we receive a tuple there was an error
if len(result):
- if result[0] == "Exception":
+ if isinstance(result[0], str) and result[0] == "Exception":
self._status.setText("Ready after calculation error")
self.configurationWidget.setEnabled(True)
raise Exception(result[1], result[2])
diff --git a/PyMca5/PyMcaPlugins/StackROIBatchPlugin.py b/PyMca5/PyMcaPlugins/StackROIBatchPlugin.py
index eecf18bf..1e8627c6 100644
--- a/PyMca5/PyMcaPlugins/StackROIBatchPlugin.py
+++ b/PyMca5/PyMcaPlugins/StackROIBatchPlugin.py
@@ -209,7 +209,7 @@ class StackROIBatchPlugin(StackPluginBase.StackPluginBase):
if type(result) == type((1,)):
#if we receive a tuple there was an error
if len(result):
- if result[0] == "Exception":
+ if isinstance(result[0], str) and result[0] == "Exception":
# somehow this exception is not caught
raise Exception(result[1], result[2])#, result[3])
return
diff --git a/PyMca5/PyMcaPlugins/XASStackBatchPlugin.py b/PyMca5/PyMcaPlugins/XASStackBatchPlugin.py
index 1e03596b..099d81fa 100644
--- a/PyMca5/PyMcaPlugins/XASStackBatchPlugin.py
+++ b/PyMca5/PyMcaPlugins/XASStackBatchPlugin.py
@@ -209,7 +209,7 @@ class XASStackBatchPlugin(StackPluginBase.StackPluginBase):
if type(result) == type((1,)):
#if we receive a tuple there was an error
if len(result):
- if result[0] == "Exception":
+ if isinstance(result[0], str) and result[0] == "Exception":
# somehow this exception is not caught
raise Exception(result[1], result[2])#, result[3])
return
diff --git a/PyMca5/PyMcaPlugins/XASStackNormalizationPlugin.py b/PyMca5/PyMcaPlugins/XASStackNormalizationPlugin.py
index 2215a691..80f249bb 100644
--- a/PyMca5/PyMcaPlugins/XASStackNormalizationPlugin.py
+++ b/PyMca5/PyMcaPlugins/XASStackNormalizationPlugin.py
@@ -187,8 +187,8 @@ class XASStackNormalizationPlugin(StackPluginBase.StackPluginBase):
post_edge_regions=post_edge_regions,
algorithm=algorithm,
algorithm_parameters=algorithm_parameters)
- if result[0] == 'Exception':
- # exception occurred
+ if isinstance(result[0], str) and result[0] == 'Exception':
+ # handled exception occurred
raise Exception(result[1], result[2], result[3])
else:
edges, jumps, errors = result
diff --git a/PyMca5/__init__.py b/PyMca5/__init__.py
index 22ac966c..eb68f953 100644
--- a/PyMca5/__init__.py
+++ b/PyMca5/__init__.py
@@ -27,7 +27,7 @@ __author__ = "V.A. Sole"
__contact__ = "sole@esrf.fr"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
-__version__ = "5.8.7"
+__version__ = "5.9.2"
import os
import sys
diff --git a/PyMca5/tests/FastXRFLinearFitTest.py b/PyMca5/tests/FastXRFLinearFitTest.py
new file mode 100644
index 00000000..6b807634
--- /dev/null
+++ b/PyMca5/tests/FastXRFLinearFitTest.py
@@ -0,0 +1,174 @@
+# /*##########################################################################
+#
+# The PyMca X-Ray Fluorescence Toolkit
+#
+# Copyright (c) 2023 European Synchrotron Radiation Facility
+#
+# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
+# the ESRF.
+#
+# 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.
+#
+#############################################################################*/
+__author__ = "V.A. Sole"
+__contact__ = "sole@esrf.eu"
+__license__ = "MIT"
+__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
+import unittest
+import sys
+import os
+import numpy
+import tempfile
+import shutil
+from PyMca5.tests import XrfData
+from PyMca5.PyMcaPhysics.xrf import FastXRFLinearFit
+from PyMca5.PyMcaPhysics.xrf.XRFBatchFitOutput import OutputBuffer
+
+try:
+ import h5py
+
+ HAS_H5PY = True
+except ImportError:
+ HAS_H5PY = False
+
+class testFastXRFLinearFit(unittest.TestCase):
+ _rtolLegacy = 1e-5
+
+ def setUp(self):
+ self.path = tempfile.mkdtemp(prefix="pymca")
+ super(testFastXRFLinearFit, self).setUp()
+
+ def tearDown(self):
+ shutil.rmtree(self.path)
+
+ @unittest.skipUnless(HAS_H5PY, "h5py not installed")
+ def testCommand(self):
+ from PyMca5.PyMcaIO import HDF5Stack1D
+
+ # generate the data
+ data, livetime = XrfData.generateXRFData()
+ configuration = XrfData.generateXRFConfig()
+ configuration["fit"]["stripalgorithm"] = 1
+
+ # create HDF5 file
+ fname = os.path.join(self.path, "FastXRF.h5")
+ h5 = h5py.File(fname, "w")
+ h5["/data"] = data
+ h5["/data_int32"] = (data * 1000).astype(numpy.int32)
+ h5.flush()
+ h5.close()
+
+ fastFit = FastXRFLinearFit.FastXRFLinearFit()
+ fastFit.setFitConfiguration(configuration)
+
+ outputDir = None
+ outputRoot = ""
+ fileEntry = ""
+ fileProcess = ""
+ refit = None
+ filepattern = None
+ begin = None
+ end = None
+ increment = None
+ backend = None
+ weight = 0
+ tif = 0
+ edf = 0
+ csv = 0
+ h5 = 1
+ dat = 0
+ concentrations = 0
+ diagnostics = 0
+ debug = 0
+ overwrite = 1
+ multipage = 0
+
+ outbuffer = OutputBuffer(
+ outputDir=outputDir,
+ outputRoot=outputRoot,
+ fileEntry=fileEntry,
+ fileProcess=fileProcess,
+ diagnostics=diagnostics,
+ tif=tif,
+ edf=edf,
+ csv=csv,
+ h5=h5,
+ dat=dat,
+ multipage=multipage,
+ overwrite=overwrite,
+ )
+
+ # test standard reading
+ scanlist = None
+ selection = {"y": "/data"}
+ dataStack = HDF5Stack1D.HDF5Stack1D([fname], selection, scanlist=scanlist)
+ with outbuffer.saveContext():
+ fastFit.fitMultipleSpectra(
+ y=dataStack,
+ weight=weight,
+ refit=refit,
+ concentrations=concentrations,
+ outbuffer=outbuffer,
+ )
+ # test dynamic reading
+ h5 = h5py.File(fname, "r")
+ with outbuffer.saveContext():
+ fastFit.fitMultipleSpectra(
+ y=h5["/data"],
+ weight=weight,
+ refit=refit,
+ concentrations=concentrations,
+ outbuffer=outbuffer,
+ )
+ # test dynamic reading of integer data
+ with outbuffer.saveContext():
+ fastFit.fitMultipleSpectra(
+ y=h5["/data_int32"],
+ weight=weight,
+ refit=refit,
+ concentrations=concentrations,
+ outbuffer=outbuffer,
+ )
+
+ h5.close()
+ h5 = None
+
+def getSuite(auto=True):
+ testSuite = unittest.TestSuite()
+ if auto:
+ testSuite.addTest(
+ unittest.TestLoader().loadTestsFromTestCase(testFastXRFLinearFit)
+ )
+ else:
+ # use a predefined order
+ testSuite.addTest(testPyMcaBatch("testCommand"))
+ return testSuite
+
+
+def test(auto=False):
+ return unittest.TextTestRunner(verbosity=2).run(getSuite(auto=auto))
+
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1:
+ auto = False
+ else:
+ auto = True
+ result = test(auto)
+ sys.exit(not result.wasSuccessful())
diff --git a/PyMca5/tests/SpecfileTest.py b/PyMca5/tests/SpecfileTest.py
index 04375597..925dac22 100644
--- a/PyMca5/tests/SpecfileTest.py
+++ b/PyMca5/tests/SpecfileTest.py
@@ -45,7 +45,19 @@ for l in ['de_DE.utf8', 'fr_FR.utf8']:
else:
other_locale = l
break
-locale.setlocale(locale.LC_ALL, current_locale)
+
+try:
+ locale.setlocale(locale.LC_ALL, current_locale)
+except locale.Error:
+ # cleanup python 3.12 issue on same machines
+ if isinstance(current_locale, tuple):
+ # if the returned tuple is (None, 'UTF-8') it cannot restore the locale
+ current_as_list = list(current_locale)
+ for i in range(len(current_as_list)):
+ if current_as_list[i] is None:
+ print(f"Returned locale <{current_locale}> reset to None")
+ current_locale = None
+ locale.setlocale(locale.LC_ALL, current_locale)
class testSpecfile(unittest.TestCase):
def setUp(self):
@@ -90,8 +102,8 @@ class testSpecfile(unittest.TestCase):
# this should free the handle
gc.collect()
# restore saved locale
- locale.setlocale(locale.LC_ALL, current_locale)
-
+ locale.setlocale(locale.LC_ALL, current_locale)
+
if self.specfileClass is not None:
if os.path.exists(self.fname):
os.remove(self.fname)
diff --git a/PyMca5/tests/__init__.py b/PyMca5/tests/__init__.py
index e352fe6a..d9af8578 100644
--- a/PyMca5/tests/__init__.py
+++ b/PyMca5/tests/__init__.py
@@ -2,10 +2,10 @@
#
# The PyMca X-Ray Fluorescence Toolkit
#
-# Copyright (c) 2004-2019 European Synchrotron Radiation Facility
+# Copyright (c) 2004-2023 European Synchrotron Radiation Facility
#
# This file is part of the PyMca X-ray Fluorescence Toolkit developed at
-# the ESRF by the Software group.
+# the ESRF.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
@@ -26,13 +26,14 @@
# THE SOFTWARE.
#
#############################################################################*/
-__author__ = "V. Armando Sole - ESRF Data Analysis"
+__author__ = "V. Armando Sole"
__contact__ = "sole@esrf.fr"
__license__ = "MIT"
__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France"
import os
from PyMca5.tests.ConfigDictTest import test as testConfigDict
+from PyMca5.tests.DataTest import test as testDataTest
from PyMca5.tests.EdfFileTest import test as testEdfFile
from PyMca5.tests.ROIBatchTest import test as testROIBatch
from PyMca5.tests.ElementsTest import test as testElements
@@ -44,6 +45,7 @@ from PyMca5.tests.XrfTest import test as testXrf
from PyMca5.tests.McaStackViewTest import test as testMcaStackView
from PyMca5.tests.NexusUtilsTest import test as testNexusUtils
from PyMca5.tests.StackInfoTest import test as testStackInfo
+from PyMca5.tests.FastXRFLinearFitTest import test as testFastXRFLinearFit
def testAll():
from PyMca5.tests.TestAll import main as testAll
diff --git a/README.rst b/README.rst
index 4c605a2d..6bfc23fc 100644
--- a/README.rst
+++ b/README.rst
@@ -57,7 +57,7 @@ Examples of source installation
You will need the following dependencies installed:
-- `python <https://www.python.org/>`_ (3.7 or higher
+- `python <https://www.python.org/>`_ (3.8 or higher
recommended)
- `numpy <https://www.numpy.org/>`_
- `fisx <https://github.com/vasole/fisx>`_
@@ -75,9 +75,6 @@ running python installation with one of the following combinations:
- ``PySide6`` + ``matplotlib`` (PyMca license will be
`MIT <https://tldrlegal.com/license/mit-license>`_ because PySide6 is
`LGPL <https://www.gnu.org/licenses/lgpl-3.0.en.html>`_)
-- ``PySide2`` + ``matplotlib`` (PyMca license will be
- `MIT <https://tldrlegal.com/license/mit-license>`_ because PySide2 is
- `LGPL <https://www.gnu.org/licenses/lgpl-3.0.en.html>`_)
If you want to embed ``PyMca`` in your own graphical applications, I
recommend you to use the
diff --git a/build-pyinstaller.py b/build-pyinstaller.py
index 9c4b2044..456a0899 100644
--- a/build-pyinstaller.py
+++ b/build-pyinstaller.py
@@ -7,6 +7,17 @@ cmd = r"cd %s; pyinstaller pyinstaller.spec --noconfirm --workpath %s --distpath
os.path.join(".", "build-" + sys.platform),
os.path.join(".", "dist-" + sys.platform))
+if sys.platform.startswith("darwin"):
+ if "arm64" in sys.argv:
+ os.putenv("PYMCA_PYINSTALLER_TARGET_ARCH", "arm64")
+ elif "universal2" in sys.argv:
+ os.putenv("PYMCA_PYINSTALLER_TARGET_ARCH", "universal2")
+ elif "x86_64" in sys.argv:
+ os.putenv("PYMCA_PYINSTALLER_TARGET_ARCH", "x86_64")
+ else:
+ # let PyInstaller choose according to platform
+ pass
+
if sys.platform.startswith("win"):
cmd = cmd.replace(";", "&")
result = os.system(cmd)
diff --git a/changelog.txt b/changelog.txt
index 046e46d1..e05861e6 100644
--- a/changelog.txt
+++ b/changelog.txt
@@ -1,3 +1,42 @@
+VERSION 5.9.2
+-------------
+
+- XRF. Prevent considering twice the same element when using the SingleLayerStrategy.
+
+- Packaging. Compatibility with PyInstaller 6.x
+
+- GUI. Improved PyQt6 compatibility.
+
+- GUI. Compatibility with current silx master branch.
+
+- GUI. Add histogram of pixel intensities to image views (requires silx).
+
+VERSION 5.9.1
+-------------
+
+- ROI Imaging. It is now possible to apply NNMA on stacks of images.
+
+- ROI Imaging. HDF5 stacks of images could not be selected due to a bug introduced in 5.8.2.
+
+- MCA. Correct ID18 calibration issues.
+
+VERSION 5.9.0
+-------------
+
+- IO. Compatibility with h5py 2.10 (Ubuntu 20.04)
+
+- GUI. Compatibility with Matplotlib 3.8
+
+VERSION 5.8.9
+-------------
+
+- FastXRF. Restore compatibility with dynamically loaded HDF5 data
+
+VERSION 5.8.8
+-------------
+
+- GUI. Compatibility with matplotlib 3.7.x
+
VERSION 5.8.7
-------------
diff --git a/debian/changelog b/debian/changelog
index a3eb02fa..88d2afc5 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+pymca (5.9.2+dfsg-1) UNRELEASED; urgency=low
+
+ * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk> Fri, 26 Jan 2024 21:31:08 -0000
+
pymca (5.8.7+dfsg-2) unstable; urgency=medium
* d/rules: Forced python3 shebang
diff --git a/debian/patches/0002-Replace-assert_array_equal-by-assert_allclose-in-tes.patch b/debian/patches/0002-Replace-assert_array_equal-by-assert_allclose-in-tes.patch
index d56523ac..ef94d19a 100644
--- a/debian/patches/0002-Replace-assert_array_equal-by-assert_allclose-in-tes.patch
+++ b/debian/patches/0002-Replace-assert_array_equal-by-assert_allclose-in-tes.patch
@@ -7,11 +7,11 @@ Subject: Replace assert_array_equal by assert_allclose in tests to avoid
PyMca5/tests/McaStackViewTest.py | 26 +++++++++++++-------------
1 file changed, 13 insertions(+), 13 deletions(-)
-diff --git a/PyMca5/tests/McaStackViewTest.py b/PyMca5/tests/McaStackViewTest.py
-index 156a161..4f564bd 100644
---- a/PyMca5/tests/McaStackViewTest.py
-+++ b/PyMca5/tests/McaStackViewTest.py
-@@ -215,7 +215,7 @@ class testMcaStackView(unittest.TestCase):
+Index: pymca.git/PyMca5/tests/McaStackViewTest.py
+===================================================================
+--- pymca.git.orig/PyMca5/tests/McaStackViewTest.py
++++ pymca.git/PyMca5/tests/McaStackViewTest.py
+@@ -215,7 +215,7 @@ class testMcaStackView(unittest.TestCase
shape = range(6, 6+ndim)
data = numpy.random.uniform(size=shape)
self._assertFullView(data)
@@ -20,7 +20,7 @@ index 156a161..4f564bd 100644
@unittest.skipIf(McaStackView is None,
'PyMca5.PyMcaCore.McaStackView cannot be imported')
@unittest.skipIf(h5py is None,
-@@ -274,14 +274,14 @@ class testMcaStackView(unittest.TestCase):
+@@ -274,14 +274,14 @@ class testMcaStackView(unittest.TestCase
for (key, chunk), (addKey, add) in chunks:
chunk += add
for idxFullComplement in dataView.idxFullComplement:
@@ -41,7 +41,7 @@ index 156a161..4f564bd 100644
def _assertMaskedView(self, data):
mcaSlice = slice(2, -1)
-@@ -316,14 +316,14 @@ class testMcaStackView(unittest.TestCase):
+@@ -316,14 +316,14 @@ class testMcaStackView(unittest.TestCase
else:
_data = data
for idxFullComplement in dataView.idxFullComplement:
diff --git a/debian/patches/0002-use-the-local-mathjax.patch b/debian/patches/0002-use-the-local-mathjax.patch
index 17b765d6..4a150069 100644
--- a/debian/patches/0002-use-the-local-mathjax.patch
+++ b/debian/patches/0002-use-the-local-mathjax.patch
@@ -6,10 +6,10 @@ Subject: use the local mathjax
doc/source/conf.py | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
-diff --git a/doc/source/conf.py b/doc/source/conf.py
-index 157758d..edb90dc 100644
---- a/doc/source/conf.py
-+++ b/doc/source/conf.py
+Index: pymca.git/doc/source/conf.py
+===================================================================
+--- pymca.git.orig/doc/source/conf.py
++++ pymca.git/doc/source/conf.py
@@ -39,6 +39,8 @@ import sys, os
extensions = ['sphinx.ext.autodoc', 'sphinx.ext.coverage', 'sphinx.ext.viewcode',
'sphinx.ext.mathjax']
diff --git a/package/cxfreeze/cx_setup.py b/package/cxfreeze/cx_setup.py
index f929f2b8..e72a5f30 100644
--- a/package/cxfreeze/cx_setup.py
+++ b/package/cxfreeze/cx_setup.py
@@ -543,7 +543,7 @@ if not sys.platform.startswith("win"):
# generation of the NSIS executable
-nsis = os.path.join("\Program Files (x86)", "NSIS", "makensis.exe")
+nsis = os.path.join(r"\Program Files (x86)", "NSIS", "makensis.exe")
if sys.platform.startswith("win") and os.path.exists(nsis):
# check if we can perform the packaging
outFile = "nsisscript.nsi"
diff --git a/package/pyinstaller/PyMca.txt b/package/pyinstaller/PyMca.txt
index 50fd6926..fe0a0eea 100644
--- a/package/pyinstaller/PyMca.txt
+++ b/package/pyinstaller/PyMca.txt
@@ -3,7 +3,7 @@
Copyright (C) 2004-2023 European Synchrotron Radiation Facility
- The PyMca X-ray Fluorescence Toolkit is developed at the ESRF by the Software group.
+ The PyMca X-ray Fluorescence Toolkit is developed at the ESRF.
PyMca follows the very permissive MIT license. However, this frozen binary
uses GPL-licensed code and therefore this binary follows the GPL license that follows.
diff --git a/package/pyinstaller/pyinstaller.spec b/package/pyinstaller/pyinstaller.spec
index fb007247..09e3bb0f 100644
--- a/package/pyinstaller/pyinstaller.spec
+++ b/package/pyinstaller/pyinstaller.spec
@@ -1,12 +1,13 @@
# -*- mode: python -*-
import sys
-import os.path
+import os
from pathlib import Path
import shutil
import subprocess
import time
import logging
+import PyInstaller
from PyInstaller.utils.hooks import collect_data_files, collect_submodules
from PyInstaller.config import CONF
@@ -154,21 +155,40 @@ for i in range(len(script_a)):
script_a[i].zipped_data,
cipher=block_cipher))
- script_exe.append(
- EXE(
- script_pyz[i],
- script_a[i].scripts,
- script_a[i].dependencies,
- [],
- exclude_binaries=True,
- name=script_n[i],
- debug=False,
- bootloader_ignore_signals=False,
- strip=False,
- upx=False,
- console=True,
- icon=icon)
- )
+ arch = os.getenv("PYMCA_PYINSTALLER_TARGET_ARCH")
+ if arch:
+ script_exe.append(
+ EXE(
+ script_pyz[i],
+ script_a[i].scripts,
+ script_a[i].dependencies,
+ [],
+ exclude_binaries=True,
+ name=script_n[i],
+ debug=False,
+ bootloader_ignore_signals=False,
+ strip=False,
+ upx=False,
+ console=True,
+ icon=icon,
+ target_arch=arch)
+ )
+ else:
+ script_exe.append(
+ EXE(
+ script_pyz[i],
+ script_a[i].scripts,
+ script_a[i].dependencies,
+ [],
+ exclude_binaries=True,
+ name=script_n[i],
+ debug=False,
+ bootloader_ignore_signals=False,
+ strip=False,
+ upx=False,
+ console=True,
+ icon=icon)
+ )
script_col.append(
COLLECT(
script_exe[i],
@@ -379,7 +399,13 @@ def cleanup_cache(topdir):
def replace_module(name):
dest = os.path.join(DISTDIR, script_n[0])
- target = os.path.join(dest, os.path.basename(name))
+ if sys.platform.startswith("darwin") and PyInstaller.__version__ >= '6.0.0':
+ target = os.path.join(dest, "special_modules")
+ if not os.path.exists(target):
+ os.mkdir(target)
+ target = os.path.join(target, os.path.basename(name))
+ else:
+ target = os.path.join(dest, os.path.basename(name))
print("source = ", name)
print("dest = ", target)
if os.path.exists(target):
@@ -407,19 +433,34 @@ for fname in script_n:
# patch silx
if SILX:
- fname = os.path.join(DISTDIR, script_n[0], "silx", "gui","qt","_qt.py")
- if os.path.exists(fname):
- logger.info("###################################################################")
- logger.info("Patching silx")
- logger.info(fname)
- logger.info("###################################################################")
- f = open(fname, "r")
- content = f.readlines()
- f.close()
- f = open(fname, "w")
- for line in content:
- f.write(line.replace("from PyQt5.uic import loadUi", "pass"))
- f.close()
+ if sys.platform.startswith("darwin") and PyInstaller.__version__ >= '6.0.0':
+ fname_dir = os.path.join(DISTDIR, script_n[0], "special_modules", "silx", "gui","qt")
+ else:
+ fname_dir = os.path.join(DISTDIR, script_n[0], "silx", "gui","qt")
+ for name in ["_qt.py", "__init__.py"]:
+ fname = os.path.join(fname_dir, name)
+ if os.path.exists(fname):
+ logger.info("###################################################################")
+ logger.info("Patching silx")
+ logger.info(fname)
+ logger.info("###################################################################")
+ f = open(fname, "r")
+ content = f.readlines()
+ f.close()
+ f = open(fname, "w")
+ for line in content:
+ #f.write(line.replace("from PyQt5.uic import loadUi", "pass"))
+ #f.write(line.replace("from PyQt6.uic import loadUi", "pass"))
+ if "import loadUi" in line:
+ f.write(line.replace("from ", "pass #"))
+ else:
+ f.write(line)
+ f.close()
+ else:
+ logger.info("###################################################################")
+ logger.info("Cannot patch silx. File not found")
+ logger.info(fname)
+ logger.info("###################################################################")
# patch OpenCL
if OPENCL:
@@ -518,6 +559,24 @@ if sys.platform.startswith("darwin"):
shutil.rmtree(dest)
os.rename(source, dest)
+ # relocate the special modules
+ special_modules_dir = os.path.join(dest, "Contents", "MacOS", "special_modules")
+ if os.path.exists(special_modules_dir):
+ source = os.path.join(special_modules_dir, "*")
+ dest = os.path.join(dest, "Contents", "Frameworks")
+ cmd = "cp -Rf %s %s" % (source, dest)
+ print(cmd)
+ os.system(cmd)
+ source = source[:-1]
+ print("deleting %s" % source)
+ shutil.rmtree(source)
+ # remove the duplicated _internal directory
+ internal_modules_dir = os.path.join( \
+ DISTDIR, "PyMca%s.app" % version, "Contents", "MacOS", "_internal")
+ if os.path.exists(internal_modules_dir):
+ print("deleting %s" % internal_modules_dir)
+ shutil.rmtree(internal_modules_dir)
+
# Pack the application
destination = os.path.join(SPECPATH, "artifacts")
if os.path.exists(destination):
@@ -610,7 +669,7 @@ else:
frozenDir = target
# generation of the NSIS executable
- nsis = os.path.join("\Program Files (x86)", "NSIS", "makensis.exe")
+ nsis = os.path.join(r"\Program Files (x86)", "NSIS", "makensis.exe")
if sys.platform.startswith("win") and os.path.exists(nsis):
# check if we can perform the packaging
outFile = os.path.join(SPECPATH, "nsisscript.nsi")