diff options
author | Picca Frédéric-Emmanuel <picca@debian.org> | 2018-09-20 09:58:07 +0200 |
---|---|---|
committer | Picca Frédéric-Emmanuel <picca@debian.org> | 2018-09-20 09:58:07 +0200 |
commit | 184bbc8e7e31657479978aff481f9d96d344e376 (patch) | |
tree | cd7182afcc53e0cd730ecdd13f579b4abc29bdd8 | |
parent | 0c737c7331790e9825a69d65b26d08075aded68a (diff) |
New upstream version 5.4.0+dfsg
275 files changed, 9502 insertions, 7645 deletions
diff --git a/MANIFEST.in b/MANIFEST.in index b7d32b2..3bde16e 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -46,14 +46,5 @@ recursive-include PyMca5/scripts * recursive-include PyMca5/tests *.py recursive-include third-party/qhull *.c *.h *.txt recursive-include third-party/khronos_headers *.h -include third-party/fisx/changelog.txt -include third-party/fisx/LICENSE -include third-party/fisx/MANIFEST.in -include third-party/fisx/README.rst -include third-party/fisx/setup.py -include third-party/fisx/TODO -recursive-include third-party/fisx/fisx_data *.dat -recursive-include third-party/fisx/python *.py *.pyx *.pxd *.cpp -recursive-include third-party/fisx/src *.h *.cpp recursive-include scripts *.py *.bat recursive-include package * @@ -1,12 +1,12 @@ Metadata-Version: 1.1 Name: PyMca5 -Version: 5.3.2 +Version: 5.4.0 Summary: Mapping and X-Ray Fluorescence Analysis Home-page: http://pymca.sourceforge.net Author: V. Armando Sole Author-email: sole@esrf.fr License: MIT -Download-URL: https://github.com/vasole/pymca/archive/v5.3.2.tar.gz +Download-URL: https://github.com/vasole/pymca/archive/v5.4.0.tar.gz Description: 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 Platform: any diff --git a/PyMca5.egg-info/PKG-INFO b/PyMca5.egg-info/PKG-INFO index d997002..cf22011 100644 --- a/PyMca5.egg-info/PKG-INFO +++ b/PyMca5.egg-info/PKG-INFO @@ -1,12 +1,12 @@ Metadata-Version: 1.1 Name: PyMca5 -Version: 5.3.2 +Version: 5.4.0 Summary: Mapping and X-Ray Fluorescence Analysis Home-page: http://pymca.sourceforge.net Author: V. Armando Sole Author-email: sole@esrf.fr License: MIT -Download-URL: https://github.com/vasole/pymca/archive/v5.3.2.tar.gz +Download-URL: https://github.com/vasole/pymca/archive/v5.4.0.tar.gz Description: 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 Platform: any diff --git a/PyMca5.egg-info/SOURCES.txt b/PyMca5.egg-info/SOURCES.txt index 40702b0..950923c 100644 --- a/PyMca5.egg-info/SOURCES.txt +++ b/PyMca5.egg-info/SOURCES.txt @@ -99,9 +99,11 @@ PyMca5/PyMcaCore/EdfFileDataSource.py PyMca5/PyMcaCore/EdfFileLayer.py PyMca5/PyMcaCore/EventHandler.py PyMca5/PyMcaCore/HtmlIndex.py +PyMca5/PyMcaCore/LoggingLevel.py PyMca5/PyMcaCore/NexusDataSource.py PyMca5/PyMcaCore/NexusTools.py PyMca5/PyMcaCore/Plugin1DBase.py +PyMca5/PyMcaCore/Plugin2DBase.py PyMca5/PyMcaCore/PyMcaBatchBuildOutput.py PyMca5/PyMcaCore/PyMcaDirs.py PyMca5/PyMcaCore/PyMcaLogo.py @@ -389,6 +391,7 @@ PyMca5/PyMcaGui/math/PCAWindow.py PyMca5/PyMcaGui/math/SGWindow.py PyMca5/PyMcaGui/math/SIFTAlignmentWindow.py PyMca5/PyMcaGui/math/SNIPWindow.py +PyMca5/PyMcaGui/math/SimpleActions.py PyMca5/PyMcaGui/math/StripBackgroundWidget.py PyMca5/PyMcaGui/math/__init__.py PyMca5/PyMcaGui/math/fitting/CheckField.py @@ -460,6 +463,7 @@ PyMca5/PyMcaGui/plotting/LegendSelector.py PyMca5/PyMcaGui/plotting/MaskImageTools.py PyMca5/PyMcaGui/plotting/MaskImageWidget.py PyMca5/PyMcaGui/plotting/MaskScatterWidget.py +PyMca5/PyMcaGui/plotting/MaskToolBar.py PyMca5/PyMcaGui/plotting/McaROIWidget.py PyMca5/PyMcaGui/plotting/ObjectPrintConfigurationDialog.py PyMca5/PyMcaGui/plotting/PlotWidget.py @@ -479,9 +483,11 @@ PyMca5/PyMcaGui/pymca/ChangeLog.py PyMca5/PyMcaGui/pymca/EdfFileSimpleViewer.py PyMca5/PyMcaGui/pymca/ExternalImagesWindow.py PyMca5/PyMcaGui/pymca/Fit2Spec.py +PyMca5/PyMcaGui/pymca/LegacyScanWindow.py PyMca5/PyMcaGui/pymca/Mca2Edf.py PyMca5/PyMcaGui/pymca/McaCalibrationControlGUI.py PyMca5/PyMcaGui/pymca/McaCustomEvent.py +PyMca5/PyMcaGui/pymca/McaLegendselector.py PyMca5/PyMcaGui/pymca/McaSimpleFit.py PyMca5/PyMcaGui/pymca/McaWindow.py PyMca5/PyMcaGui/pymca/Median2DBrowser.py @@ -511,6 +517,7 @@ PyMca5/PyMcaGui/pymca/RGBCorrelatorTable.py PyMca5/PyMcaGui/pymca/RGBCorrelatorWidget.py PyMca5/PyMcaGui/pymca/RGBImageCalculator.py PyMca5/PyMcaGui/pymca/ScanFit.py +PyMca5/PyMcaGui/pymca/ScanFitToolButton.py PyMca5/PyMcaGui/pymca/ScanWindow.py PyMca5/PyMcaGui/pymca/ScanWindowInfoWidget.py PyMca5/PyMcaGui/pymca/SilxExternalImagesWindow.py @@ -715,6 +722,7 @@ PyMca5/PyMcaPlugins/ImageAlignmentStackPlugin.py PyMca5/PyMcaPlugins/KineticsPlugin.py PyMca5/PyMcaPlugins/LoadPositionersStackPlugin.py PyMca5/PyMcaPlugins/MathPlugins.py +PyMca5/PyMcaPlugins/Medfilt2DPlugin.py PyMca5/PyMcaPlugins/MedianFilterScanDeglitchPlugin.py PyMca5/PyMcaPlugins/MedianFilterScanPlugin.py PyMca5/PyMcaPlugins/MedianFilterStackPlugin.py @@ -767,6 +775,7 @@ PyMca5/tests/SpecfileTest.py PyMca5/tests/StackBaseTest.py PyMca5/tests/StackInfoTest.py PyMca5/tests/TestAll.py +PyMca5/tests/WidgetsTest.py PyMca5/tests/XrfTest.py PyMca5/tests/__init__.py PyMca5/tests/specfilewrapperTest.py @@ -786,20 +795,55 @@ doc/source/index.rst doc/source/install.rst doc/source/license.rst doc/source/overview.rst +doc/source/recipes.rst doc/source/tutorials.rst doc/source/_templates/localtoc.html doc/source/_templates/pagesource.html doc/source/customization/index.rst -doc/source/customization/settings.rst doc/source/customization/plugins/index.rst doc/source/customization/plugins/plugins1d.rst doc/source/customization/plugins/stackplugins.rst +doc/source/customization/settings/index.rst +doc/source/customization/settings/img/settings_01.png +doc/source/customization/settings/img/settings_02.png doc/source/hdf5/index.rst doc/source/img/PyMca.ico doc/source/img/PyMca_256x256.png doc/source/modules/index.rst doc/source/modules/core/index.rst doc/source/modules/core/nexustools.rst +doc/source/recipes/xrfembedpyqt.rst +doc/source/recipes/xrfhdf5stack.rst +doc/source/recipes/recipescode/GenerateHDF5Stack.py +doc/source/training/matrix/index.rst +doc/source/training/matrix/img/matrix_01.png +doc/source/training/matrix/img/matrix_02.png +doc/source/training/matrix/img/matrix_03.png +doc/source/training/matrix/img/matrix_04.png +doc/source/training/matrix/img/tertiary_01.png +doc/source/training/matrix/img/tertiary_03.png +doc/source/training/matrix/img/tertiary_04.png +doc/source/training/quantification/index.rst +doc/source/training/quantification/img/quantification_01.png +doc/source/training/quantification/img/quantification_02.png +doc/source/training/quantification/img/quantification_03.png +doc/source/training/quantification/img/quantification_04.png +doc/source/training/quantification/img/quantification_05.png +doc/source/training/quantification/img/quantification_06.png +doc/source/training/quantification/img/quantification_07.png +doc/source/training/quantification/img/quantification_08.png +doc/source/training/quantification/img/quantification_09.png +doc/source/training/quantification/img/quantification_10.png +doc/source/training/quantification/img/quantification_11.png +doc/source/training/quantification/img/quantification_12.png +doc/source/training/tertiary/index.rst +doc/source/training/tertiary/img/tertiary_01.png +doc/source/training/tertiary/img/tertiary_02.png +doc/source/training/tertiary/img/tertiary_03.png +doc/source/training/tertiary/img/tertiary_04.png +doc/source/training/xraydata/index.rst +doc/source/training/xraydata/img/xraydata_01.png +doc/source/training/xraydata/img/xraydata_02.png doc/source/xrf/material-definition/index.rst doc/source/xrf/material-definition/img/materials_01.jpg doc/source/xrf/material-definition/img/materials_02.jpg @@ -853,97 +897,6 @@ scripts/pymcabatch.bat scripts/pymcapostbatch.bat scripts/pymcaroitool.bat scripts/rgbcorrelator.bat -third-party/fisx/LICENSE -third-party/fisx/MANIFEST.in -third-party/fisx/README.rst -third-party/fisx/TODO -third-party/fisx/changelog.txt -third-party/fisx/setup.py -third-party/fisx/fisx_data/BindingEnergies.dat -third-party/fisx/fisx_data/EADL97_BindingEnergies.dat -third-party/fisx/fisx_data/EADL97_KShellConstants.dat -third-party/fisx/fisx_data/EADL97_KShellNonradiativeRates.dat -third-party/fisx/fisx_data/EADL97_KShellRadiativeRates.dat -third-party/fisx/fisx_data/EADL97_LShellConstants.dat -third-party/fisx/fisx_data/EADL97_LShellNonradiativeRates.dat -third-party/fisx/fisx_data/EADL97_LShellRadiativeRates.dat -third-party/fisx/fisx_data/EADL97_MShellConstants.dat -third-party/fisx/fisx_data/EADL97_MShellNonradiativeRates.dat -third-party/fisx/fisx_data/EADL97_MShellRadiativeRates.dat -third-party/fisx/fisx_data/EPDL97_CrossSections.dat -third-party/fisx/fisx_data/KShellConstants.dat -third-party/fisx/fisx_data/KShellRates.dat -third-party/fisx/fisx_data/LShellConstants.dat -third-party/fisx/fisx_data/LShellRates.dat -third-party/fisx/fisx_data/MShellConstants.dat -third-party/fisx/fisx_data/MShellRates.dat -third-party/fisx/fisx_data/XCOM_CrossSections.dat -third-party/fisx/python/cython/Detector.pxd -third-party/fisx/python/cython/EPDL97.pxd -third-party/fisx/python/cython/Element.pxd -third-party/fisx/python/cython/Elements.pxd -third-party/fisx/python/cython/Layer.pxd -third-party/fisx/python/cython/Material.pxd -third-party/fisx/python/cython/Math.pxd -third-party/fisx/python/cython/PyDetector.pyx -third-party/fisx/python/cython/PyEPDL97.pyx -third-party/fisx/python/cython/PyElement.pyx -third-party/fisx/python/cython/PyElements.pyx -third-party/fisx/python/cython/PyLayer.pyx -third-party/fisx/python/cython/PyMaterial.pyx -third-party/fisx/python/cython/PyMath.pyx -third-party/fisx/python/cython/PyShell.pyx -third-party/fisx/python/cython/PySimpleIni.pyx -third-party/fisx/python/cython/PySimpleSpecfile.pyx -third-party/fisx/python/cython/PyVersion.pyx -third-party/fisx/python/cython/PyXRF.pyx -third-party/fisx/python/cython/Shell.pxd -third-party/fisx/python/cython/SimpleIni.pxd -third-party/fisx/python/cython/SimpleSpecfile.pxd -third-party/fisx/python/cython/Version.pxd -third-party/fisx/python/cython/XRF.pxd -third-party/fisx/python/cython/_fisx.pyx -third-party/fisx/python/cython/default/_fisx.cpp -third-party/fisx/python/fisx/DataDir.py -third-party/fisx/python/fisx/FisxCythonTools.py -third-party/fisx/python/fisx/__init__.py -third-party/fisx/python/fisx/tests/__init__.py -third-party/fisx/python/fisx/tests/testAll.py -third-party/fisx/python/fisx/tests/testDataDir.py -third-party/fisx/python/fisx/tests/testEPDL97.py -third-party/fisx/python/fisx/tests/testElements.py -third-party/fisx/python/fisx/tests/testSimpleSpecfile.py -third-party/fisx/python/fisx/tests/testXRF.py -third-party/fisx/src/fisx_beam.cpp -third-party/fisx/src/fisx_beam.h -third-party/fisx/src/fisx_defaultelementsinfo.h -third-party/fisx/src/fisx_detector.cpp -third-party/fisx/src/fisx_detector.h -third-party/fisx/src/fisx_element.cpp -third-party/fisx/src/fisx_element.h -third-party/fisx/src/fisx_elements.cpp -third-party/fisx/src/fisx_elements.h -third-party/fisx/src/fisx_epdl97.cpp -third-party/fisx/src/fisx_epdl97.h -third-party/fisx/src/fisx_layer.cpp -third-party/fisx/src/fisx_layer.h -third-party/fisx/src/fisx_material.cpp -third-party/fisx/src/fisx_material.h -third-party/fisx/src/fisx_math.cpp -third-party/fisx/src/fisx_math.h -third-party/fisx/src/fisx_multilayer.cpp -third-party/fisx/src/fisx_shell.cpp -third-party/fisx/src/fisx_shell.h -third-party/fisx/src/fisx_simpleini.cpp -third-party/fisx/src/fisx_simpleini.h -third-party/fisx/src/fisx_simplespecfile.cpp -third-party/fisx/src/fisx_simplespecfile.h -third-party/fisx/src/fisx_version.cpp -third-party/fisx/src/fisx_version.h -third-party/fisx/src/fisx_xrf.cpp -third-party/fisx/src/fisx_xrf.h -third-party/fisx/src/fisx_xrfconfig.cpp -third-party/fisx/src/fisx_xrfconfig.h third-party/khronos_headers/GL/glcorearb.h third-party/khronos_headers/GL/glext.h third-party/khronos_headers/GL/glxext.h diff --git a/PyMca5.egg-info/requires.txt b/PyMca5.egg-info/requires.txt index 265c771..409ddbf 100644 --- a/PyMca5.egg-info/requires.txt +++ b/PyMca5.egg-info/requires.txt @@ -1,4 +1,4 @@ -fisx>=1.1.4 +fisx>=1.1.6 h5py matplotlib>1.0 numpy diff --git a/PyMca5/EPDL97/EADLParser.py b/PyMca5/EPDL97/EADLParser.py index 7c22e57..72fb885 100644 --- a/PyMca5/EPDL97/EADLParser.py +++ b/PyMca5/EPDL97/EADLParser.py @@ -32,6 +32,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os +import logging __doc__ =\ """ The 1997 release of the Evaluated Atomic Data Library (EADL97) @@ -159,7 +160,8 @@ SHELL_LIST = EADLSubshells.SHELL_LIST getSubshellFromValue = EADLSubshells.getSubshellFromValue getValueFromSubshell = EADLSubshells.getValueFromSubshell -DEBUG = 0 +_logger = logging.getLogger(__name__) + AVOGADRO_NUMBER = 6.02214179E23 # @@ -414,14 +416,14 @@ def parseHeader1(line): ddict['subshell_code'] = 0 ddict['subshell'] = 'none' else: - print("Inconsistent data") - print("X1 = ", X1, "S = ", S) + _logger.error("Inconsistent data") + _logger.error("X1 = %s; S = %s", X1, S) sys.exit(1) return ddict def parseHeader(line0, line1): - #print "line0 = ", line0 - #print "line1 = ", line1 + #_logger.info "line0 = ", line0 + #_logger.info "line1 = ", line1 ddict = parseHeader0(line0) ddict.update(parseHeader1(line1)) return ddict @@ -429,12 +431,12 @@ def parseHeader(line0, line1): if 0: ddict = parseHeader0(EADL97_DATA[0]) for key in ddict.keys(): - print(key, ddict[key]) + _logger.info("%s: %s", key, ddict[key]) if 0: ddict = parseHeader1(EADL97_DATA[1]) for key in ddict.keys(): - print(key, ddict[key]) + _logger.info("%s: %s", key, ddict[key]) def getDataLineIndex(lines, z, Yi, C, S, X1, Yo, I): @@ -459,59 +461,46 @@ def getDataLineIndex(lines, z, Yi, C, S, X1, Yo, I): try: ddict = parseHeader(lines[i], lines[i+1]) except: - print("Error with lines") - print("line index = %d" % i) - print(lines[i]) - print(lines[i+1]) - print(sys.exc_info()) + _logger.error("Error with lines") + _logger.error("line index = %d", i) + _logger.error(lines[i]) + _logger.error(lines[i+1]) + _logger.error(sys.exc_info()) raise if 0: - print(ddict['Z'], z) - print(ddict['Yi'], Yi) - print(ddict['C'], C) - print(ddict['S'], S) - print(ddict['X1'], X1) - print(ddict['Yo'], Yo) - print(ddict['I'], I) - if DEBUG: - if ddict['Z'] == z: - print("Z found") - if ddict['Yi'] == Yi: - print("Yi found") - if ddict['C'] == C: - print("C found") - if ddict['S'] == S: - print("S found with X1 = ", ddict['X1']) - print("Requested X1 = ", X1) - print(lines[i]) - print(lines[i+1]) - if ddict['X1'] == X1: - print("Requested Yo = ", Yo) - print("Found Yo = ", ddict['Yo']) - if ddict['Yo'] == Yo: - print("Requested I = ",I) - if ddict['I'] == I: - print("FOUND!") - print(lines[i]) - print(lines[i+1]) - LAST_INDEX = i - 1 - return i - break - else: - if ddict['Z'] == z: - if ddict['Yi'] == Yi: - if ddict['C'] == C: - if ddict['S'] == S: - if ddict['X1'] == X1: - if ddict['Yo'] == Yo: - if ddict['I'] == I: - LAST_INDEX = i - 1 - return i - break + _logger.info("%s, %s", ddict['Z'], z) + _logger.info("%s, %s", ddict['Yi'], Yi) + _logger.info("%s, %s", ddict['C'], C) + _logger.info("%s, %s", ddict['S'], S) + _logger.info("%s, %s", ddict['X1'], X1) + _logger.info("%s, %s", ddict['Yo'], Yo) + _logger.info("%s, %s", ddict['I'], I) + + if ddict['Z'] == z: + _logger.debug("Z found") + if ddict['Yi'] == Yi: + _logger.debug("Yi found") + if ddict['C'] == C: + _logger.debug("C found") + if ddict['S'] == S: + _logger.debug("S found with X1 = %s", ddict['X1']) + _logger.debug("Requested X1 = %s", X1) + _logger.debug(lines[i]) + _logger.debug(lines[i+1]) + if ddict['X1'] == X1: + _logger.debug("Requested Yo = %s", Yo) + _logger.debug("Found Yo = %s", ddict['Yo']) + if ddict['Yo'] == Yo: + _logger.debug("Requested I = %s", I) + if ddict['I'] == I: + _logger.debug("FOUND!") + _logger.debug(lines[i]) + _logger.debug(lines[i+1]) + LAST_INDEX = i - 1 + return i i += 1 if LAST_INDEX > 0: - if DEBUG: - print("REPEATING") + _logger.debug("REPEATING") LAST_INDEX = -1 return getDataLineIndex(lines, z, Yi, C, S, X1, Yo, I) return -1 @@ -524,13 +513,12 @@ def getActualDataFromLinesAndOffset(lines, index): data_end += 1 end_line = lines[data_end + 1] data_end += 1 - if DEBUG: - print("COMPLETE DATA SET") - print(lines[index:data_end]) - print("END DATA SET") - print("ADDITIONAL LINE") - print(lines[data_end]) - print("END ADDITIONAL LINE") + _logger.debug("COMPLETE DATA SET") + _logger.debug(lines[index:data_end]) + _logger.debug("END DATA SET") + _logger.debug("ADDITIONAL LINE") + _logger.debug(lines[data_end]) + _logger.debug("END ADDITIONAL LINE") ndata = data_end - data_begin energy = numpy.zeros((ndata,), numpy.float) t = lines[data_begin].split() @@ -614,8 +602,7 @@ def getRadiativeWidths(z, lines=None): if index < 0: raise IOError("Requested data not found") shell_codes, value = getActualDataFromLinesAndOffset(lines, index) - if DEBUG: - print("shell_codes, value ",shell_codes, value) + _logger.debug("shell_codes %s, value %s", shell_codes, value) i = 0 ddict = getBaseShellDict() for code in shell_codes: @@ -636,8 +623,7 @@ def getNonradiativeWidths(z, lines=None): if index < 0: raise IOError("Requested data not found") shell_codes, value = getActualDataFromLinesAndOffset(lines, index) - if DEBUG: - print("shell_codes, value ",shell_codes, value) + _logger.debug("shell_codes %s, value %s", shell_codes, value) i = 0 ddict = getBaseShellDict() for code in shell_codes: @@ -673,8 +659,7 @@ def getRadiativeTransitionProbabilities(z, shell='K', lines=None): #this error may happen when requesting non existing data too raise IOError("Requested data not found") shell_codes, values = getActualDataFromLinesAndOffset(lines, index) - if DEBUG: - print("shell_codes, values ",shell_codes, values) + _logger.debug("shell_codes %s, values %s", shell_codes, values) i = 0 ddict = getBaseShellDict(nvalues=2) for code in shell_codes: @@ -711,8 +696,7 @@ def getNonradiativeTransitionProbabilities(z, shell='K', lines=None): #this error may happen when requesting non existing data too raise IOError("Requested data not found") shell_codes, values = getActualDataFromLinesAndOffset(lines, index) - if DEBUG: - print("shell_codes, values ",shell_codes, values) + _logger.debug("shell_codes %s, values %s", shell_codes, values) i = 0 ddict = {}#getBaseShellDict() for code in shell_codes: @@ -737,8 +721,7 @@ def getBindingEnergies(z, lines=None): if index < 0: raise IOError("Requested data not found") shell_codes, value = getActualDataFromLinesAndOffset(lines, index) - if DEBUG: - print("shell_codes, value ",shell_codes, value) + _logger.debug("shell_codes %s, value %s", shell_codes, value) i = 0 ddict = getBaseShellDict() for code in shell_codes: @@ -841,41 +824,41 @@ if __name__ == "__main__": element = sys.argv[1] else: element = 'Pb' - print("Getting binding energies for element %s" % element) + _logger.info("Getting binding energies for element %s", element) ddict = getBindingEnergies(Elements.index(element)+1) for key in getBaseShellList(): if ddict[key] > 0.0: - print("Shell = %s Energy (keV) = %.7E" % (key, ddict[key] * 1000.)) - print("Getting fluorescence yields for element %s" % element) + _logger.info("Shell = %s Energy (keV) = %.7E", key, ddict[key] * 1000.) + _logger.info("Getting fluorescence yields for element %s", element) ddict = getFluorescenceYields(Elements.index(element)+1) for key in getBaseShellList(): if key in ddict: if ddict[key] > 0.0: - print("Shell = %s Yield = %.7E" % (key, ddict[key])) + _logger.info("Shell = %s Yield = %.7E", key, ddict[key]) #total_emission = 0.0 for shell in ['K', 'L1', 'L2', 'L3', 'M1', 'M2', 'M3', 'M4', 'M5']: try: ddict = getRadiativeTransitionProbabilities(Elements.index(element)+1, - shell=shell) - print("%s Shell radiative emission probabilities " % shell) + shell=shell) + _logger.info("%s Shell radiative emission probabilities ", shell) except IOError: continue total = 0.0 for key in getBaseShellList(): if key in ddict: if ddict[key][0] > 0.0: - print("Shell = %s Yield = %.7E Energy = %.7E" % (key, ddict[key][0], - ddict[key][1] * 1000.)) + _logger.info("Shell = %s Yield = %.7E Energy = %.7E", + key, ddict[key][0], ddict[key][1] * 1000.) total += ddict[key][0] - print("Total %s-shell emission probability = %.7E" % (shell, total)) + _logger.info("Total %s-shell emission probability = %.7E", shell, total) #total_emission += total - #print "total_emission = ", total_emission + #_logger.info "total_emission = ", total_emission for shell in ['K', 'L1', 'L2', 'L3', 'M1', 'M2', 'M3', 'M4', 'M5']: try: ddict = getNonradiativeTransitionProbabilities(Elements.index(element)+1, - shell=shell) - print("%s Shell Nonradiative emission probabilities " % shell) + shell=shell) + _logger.info("%s Shell Nonradiative emission probabilities ", shell) except IOError: continue total = 0.0 @@ -885,12 +868,13 @@ if __name__ == "__main__": key = "%s-%s%s" % (shell, key0.split()[0], key1.split()[0]) if key in ddict: if ddict[key][0] > 0.0: - print("Shell = %s Yield = %.7E Energy = %.7E" %\ - (key, ddict[key][0], ddict[key][1] * 1000.)) + _logger.info("Shell = %s Yield = %.7E Energy = %.7E", + key, ddict[key][0], ddict[key][1] * 1000.) total += ddict[key][0] - print("Total %s-shell non-radiative emission probability = %.7E" % (shell, total)) + _logger.info("Total %s-shell non-radiative emission probability = %.7E", + shell, total) if shell in ['K']: - for key0 in ['L1', 'L2' ,'L3']: + for key0 in ['L1', 'L2', 'L3']: subtotal = 0.0 for key1 in shell_list: tmpKey = key1.split()[0] @@ -900,10 +884,10 @@ if __name__ == "__main__": subtotal += ddict[key][0] if tmpKey == key0: subtotal += ddict[key][0] - print("%s vacancies for nonradiative transition to %s shell = %.7E"%\ - (key0, shell, subtotal)) + _logger.info("%s vacancies for nonradiative transition to %s shell = %.7E", + key0, shell, subtotal) - #print(getNonradiativeTransitionProbabilities(Elements.index(element)+1, 'L1')) - print(getMShellCosterKronigYields(Elements.index(element)+1)) - print("atomic weight = ", getAtomicWeights()[Elements.index(element)]) + #_logger.info(getNonradiativeTransitionProbabilities(Elements.index(element)+1, 'L1')) + _logger.info(getMShellCosterKronigYields(Elements.index(element)+1)) + _logger.info("atomic weight = %s", getAtomicWeights()[Elements.index(element)]) sys.exit(0) diff --git a/PyMca5/EPDL97/EPDL97Parser.py b/PyMca5/EPDL97/EPDL97Parser.py index 887d2ff..d6805a7 100644 --- a/PyMca5/EPDL97/EPDL97Parser.py +++ b/PyMca5/EPDL97/EPDL97Parser.py @@ -32,6 +32,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os +import logging __doc__ =\ """ The 1997 release of the Evaluated Photon Data Library (EPDL97) @@ -213,7 +214,8 @@ SHELL_LIST = EADLSubshells getSubshellFromValue = EADLSubshells.getSubshellFromValue getValueFromSubshell = EADLSubshells.getValueFromSubshell -DEBUG = 0 +_logger = logging.getLogger(__name__) + AVOGADRO_NUMBER = 6.02214179E23 #Read the EPDL library @@ -463,8 +465,8 @@ def parseHeader1(line): ddict['subshell_code'] = 0 ddict['subshell'] = 'none' else: - print("Inconsistent data") - print("X1 = ", X1, "S = ", S) + _logger.error("Inconsistent data") + _logger.error("X1 = %s; S = %s", X1, S) sys.exit(1) return ddict @@ -478,12 +480,12 @@ def parseHeader(line0, line1): if 0: ddict = parseHeader0(EPDL97_DATA[0]) for key in ddict.keys(): - print(key, ddict[key]) + _logger.info("%s: %s", key, ddict[key]) if 0: ddict = parseHeader1(EPDL97_DATA[1]) for key in ddict.keys(): - print (key, ddict[key]) + _logger.info("%s: %s", key, ddict[key]) def getDataLineIndex(lines, z, Yi, C, S, X1, Yo, I, getmode=True): @@ -500,61 +502,46 @@ def getDataLineIndex(lines, z, Yi, C, S, X1, Yo, I, getmode=True): try: ddict = parseHeader(lines[i], lines[i+1]) except: - print("Error with lines") - print(lines[i]) - print(lines[i+1]) - print(sys.exc_info()) + _logger.error("Error with lines") + _logger.error(lines[i]) + _logger.error(lines[i+1]) + _logger.error(sys.exc_info()) raise if 0: - print(ddict['Z'], z) - print(ddict['Yi'], Yi) - print(ddict['C'], C) - print(ddict['S'], S) - print(ddict['X1'], X1) - print(ddict['Yo'], Yo) - print(ddict['I'], I) - if DEBUG: - if ddict['Z'] == z: - print("Z found") - if ddict['Yi'] == Yi: - print("Yi found") - if ddict['C'] == C: - print("C found") - if ddict['S'] == S: - print("S found with X1 = ", ddict['X1']) - print("Requested X1 = ", X1) - print(lines[i]) - print(lines[i+1]) - if ddict['X1'] == X1: - if ddict['Yo'] == Yo: - if ddict['I'] == I: - print("FOUND!") - print(lines[i]) - print(lines[i+1]) - LAST_INDEX = i - 1 - if getmode: - return i, ddict['interpolation_type'] - else: - return i - break - else: - if ddict['Z'] == z: - if ddict['Yi'] == Yi: - if ddict['C'] == C: - if ddict['S'] == S: - if ddict['X1'] == X1: - if ddict['Yo'] == Yo: - if ddict['I'] == I: - LAST_INDEX = i - 1 - if getmode: - return i, ddict['interpolation_type'] - else: - return i - break + _logger.info("%s, %s", ddict['Z'], z) + _logger.info("%s, %s", ddict['Yi'], Yi) + _logger.info("%s, %s", ddict['C'], C) + _logger.info("%s, %s", ddict['S'], S) + _logger.info("%s, %s", ddict['X1'], X1) + _logger.info("%s, %s", ddict['Yo'], Yo) + _logger.info("%s, %s", ddict['I'], I) + + if ddict['Z'] == z: + _logger.debug("Z found") + if ddict['Yi'] == Yi: + _logger.debug("Yi found") + if ddict['C'] == C: + _logger.debug("C found") + if ddict['S'] == S: + _logger.debug("S found with X1 = %s", ddict['X1']) + _logger.debug("Requested X1 = %s", X1) + _logger.debug(lines[i]) + _logger.debug(lines[i+1]) + if ddict['X1'] == X1: + if ddict['Yo'] == Yo: + if ddict['I'] == I: + _logger.debug("FOUND!") + _logger.debug(lines[i]) + _logger.debug(lines[i+1]) + LAST_INDEX = i - 1 + if getmode: + return i, ddict['interpolation_type'] + else: + return i + i += 1 if LAST_INDEX > 0: - if DEBUG: - print("REPEATING") + _logger.debug("REPEATING") LAST_INDEX = -1 return getDataLineIndex(lines, z, Yi, C, S, X1, Yo, I, getmode=getmode) if getmode: @@ -567,12 +554,11 @@ def getActualDataFromLinesAndOffset(lines, index): data_end = index + 2 while len(lines[data_end].split()) == 2: data_end += 1 - if DEBUG: - print("COMPLETE DATA SET") - print(lines[index:data_end]) - print("END DATA SET") - print(lines[data_end]) - print("ADDITIONAL LINE") + _logger.debug("COMPLETE DATA SET") + _logger.debug(lines[index:data_end]) + _logger.debug("END DATA SET") + _logger.debug(lines[data_end]) + _logger.debug("ADDITIONAL LINE") ndata = data_end - data_begin energy = numpy.zeros((ndata,), numpy.float) value = numpy.zeros((ndata,), numpy.float) @@ -714,45 +700,45 @@ if __name__ == "__main__": else: Z = 82 energy, value, mode = getTotalCoherentCrossSection(Z, EPDL97_DATA, getmode=True) - print("TOTAL COHERENT ", mode) + _logger.info("TOTAL COHERENT %s", mode) for i in range(len(energy)): if energy[i] > 0.010: if energy[i] < 0.020: - print(energy[i], value[i]) + _logger.info("%s, %s", energy[i], value[i]) energy, value, mode = getTotalIncoherentCrossSection(Z, EPDL97_DATA , getmode=True) - print("TOTAL INCOHERENT ", mode) + _logger.info("TOTAL INCOHERENT %s", mode) for i in range(len(energy)): if energy[i] > 0.010: if energy[i] < 0.020: - print(energy[i], value[i]) + _logger.info("%s, %s", energy[i], value[i]) energy, value, mode = getTotalPhotoelectricCrossSection(Z, EPDL97_DATA, getmode=True) - print("TOTAL PHOTOELECTRIC ", mode) + _logger.info("TOTAL PHOTOELECTRIC %s", mode) for i in range(len(energy)): if energy[i] > 0.010: if energy[i] < 0.020: - print(energy[i], value[i]) + _logger.info("%s, %s", energy[i], value[i]) energy, value, mode = getTotalPairCrossSection(Z, EPDL97_DATA, getmode=True) - print(" TOTAL PAIR ", mode) + _logger.info(" TOTAL PAIR %s", mode) for i in range(len(energy)): if energy[i] > 0.010: if energy[i] < 0.020: - print(energy[i], value[i]) + _logger.info("%s, %s", energy[i], value[i]) energy, value, mode = getPartialPhotoelectricCrossSection(Z, 'L1', EPDL97_DATA, getmode=True) - print("L1 SHELL PARTIAL PHOTOELECTRIC IDX") + _logger.info("L1 SHELL PARTIAL PHOTOELECTRIC IDX") for i in range(len(energy)): if energy[i] > 0.010: if energy[i] < 0.020: - print(energy[i], value[i], mode) + _logger.info("%s, %s, %s", energy[i], value[i], mode) energy, value, mode = getPartialPhotoelectricCrossSection(Z, 'K', EPDL97_DATA, getmode=True) - print("K SHELL PARTIAL PHOTOELECTRIC") + _logger.info("K SHELL PARTIAL PHOTOELECTRIC") for i in range(len(energy)): if energy[i] > 0.088: if energy[i] < 0.090: - print(energy[i], value[i], mode) + _logger.info("%s, %s, %s", energy[i], value[i], mode) - print("atomic weight = ", getAtomicWeights()[Z-1]) + _logger.info("atomic weight = %s", getAtomicWeights()[Z-1]) diff --git a/PyMca5/Object3D/ClippingPlaneConfiguration.py b/PyMca5/Object3D/ClippingPlaneConfiguration.py index 6443752..092c1dc 100644 --- a/PyMca5/Object3D/ClippingPlaneConfiguration.py +++ b/PyMca5/Object3D/ClippingPlaneConfiguration.py @@ -30,8 +30,11 @@ from . import Object3DQt as qt from . import Object3DSlider from .Object3DMovement import Object3DRotationWidget, Object3DTranslationWidget import numpy +import logging + + +_logger = logging.getLogger(__name__) -DEBUG = 0 class ClippingPlaneConfiguration(qt.QGroupBox): sigClippingPlaneSignal = qt.pyqtSignal(object) @@ -107,8 +110,7 @@ class ClippingPlaneConfiguration(qt.QGroupBox): def _sliderSlot(self, *var): if self.__disconnected: return - if DEBUG: - print("sliderSlot") + _logger.debug("sliderSlot") for i in range(3): value = self.planeList[i][5].value() self.planeList[i][4].setText("%f" % value) @@ -116,8 +118,7 @@ class ClippingPlaneConfiguration(qt.QGroupBox): self._signal() def _lineSlot(self): - if DEBUG: - print("lineSlot") + _logger.debug("lineSlot") for i in range(3): oldValue = self.planeList[i][5].value() value = float(str(self.planeList[i][4].text())) @@ -219,8 +220,7 @@ class UserClippingPlaneWidget(qt.QWidget): def _emitSignal(self, event=None): if self.__disconnected: return - if DEBUG: - print("Emitting UserClippingPlaneSignal") + _logger.debug("Emitting UserClippingPlaneSignal") if event is None: event="U0PlaneUpdated" ddict = self.getParameters() @@ -319,8 +319,7 @@ class ClippingPlaneWidget(qt.QWidget): return [A, B, C, D] def _emitSignal(self, event = None): - if DEBUG: - print("Emitting ClippingPlaneWidgetSignal") + _logger.debug("Emitting ClippingPlaneWidgetSignal") if event is None: event = 'ClippingPlaneWidgetUpdated' ddict = self.standardClippingPlane.getParameters() @@ -337,7 +336,7 @@ if __name__ == "__main__": app = qt.QApplication(sys.argv) def myslot(ddict): print("Signal received") - print("ddict = ", ddict) + print("ddict = %s" % ddict) if 0: w = ClippingPlaneConfiguration() w.sigClippingPlaneSignal.connect(myslot) diff --git a/PyMca5/Object3D/GLToolBar.py b/PyMca5/Object3D/GLToolBar.py index 987574c..d843c0f 100644 --- a/PyMca5/Object3D/GLToolBar.py +++ b/PyMca5/Object3D/GLToolBar.py @@ -31,7 +31,6 @@ from . import Object3DQt as qt from . import Object3DIcons from .HorizontalSpacer import HorizontalSpacer -DEBUG = 0 class GLToolBar(qt.QWidget): diff --git a/PyMca5/Object3D/GLWidgetCachePixmap.py b/PyMca5/Object3D/GLWidgetCachePixmap.py index bff365a..d82399d 100644 --- a/PyMca5/Object3D/GLWidgetCachePixmap.py +++ b/PyMca5/Object3D/GLWidgetCachePixmap.py @@ -32,7 +32,10 @@ except ImportError: raise ImportError("OpenGL must be installed to use these functionalities") from . import Object3DQt as qt import numpy -DEBUG = 0 +import logging + +_logger = logging.getLogger(__name__) + class GLWidgetCachePixmap(object): def __init__(self, name="Unnamed"): @@ -47,8 +50,7 @@ class GLWidgetCachePixmap(object): return self.__textureId def openGLCleanup(self): - if DEBUG: - print("CLEANING OPENGL") + _logger.debug("CLEANING OPENGL") if self.drawList <= 0: GL.glDeleteLists(self.drawList, 1) self.drawList = 0 @@ -162,7 +164,7 @@ class GLWidgetCachePixmap(object): GL.glDeleteTextures([self.__textureId]) self.__textureId = GL.glGenTextures(1) if self.__textureId is None: - print("no valid texture id?") + _logger.info("no valid texture id?") return if self._useNewTexture: GL.glBindTexture(GL.GL_TEXTURE_2D, self.__textureId) diff --git a/PyMca5/Object3D/Object3DBase.py b/PyMca5/Object3D/Object3DBase.py index 1031f87..c77579b 100644 --- a/PyMca5/Object3D/Object3DBase.py +++ b/PyMca5/Object3D/Object3DBase.py @@ -26,6 +26,7 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "LGPL2+" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import logging import numpy import weakref import sys @@ -37,7 +38,8 @@ try: except ImportError: raise ImportError("OpenGL must be installed to use these functionalities") -DEBUG = 0 +_logger = logging.getLogger(__name__) + DRAW_MODES = ['NONE', 'POINT', @@ -97,9 +99,8 @@ class Object3D(object): if GL is not None: if self.boundingBoxList != 0: GL.glDeleteLists(self.boundingBoxList, 1) - if DEBUG: - print("DELETING Object3d base") - print(self.name(), "DELETED") + _logger.debug("DELETING Object3d base") + _logger.debug("%s DELETED", self.name()) def name(self): return self.__name diff --git a/PyMca5/Object3D/Object3DColormap.py b/PyMca5/Object3D/Object3DColormap.py index 2ffe3ea..9ae37ac 100644 --- a/PyMca5/Object3D/Object3DColormap.py +++ b/PyMca5/Object3D/Object3DColormap.py @@ -27,10 +27,13 @@ __contact__ = "sole@esrf.fr" __license__ = "LGPL2+" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys +import logging from . import Object3DQt as qt from . import Object3DSlider -DEBUG = 0 + +_logger = logging.getLogger(__name__) + class Object3DColormap(qt.QGroupBox): sigObject3DColormapSignal = qt.pyqtSignal(object) @@ -200,8 +203,7 @@ class Object3DColormap(qt.QGroupBox): def sliderChanged(self, value): if self.__disconnected: return - if DEBUG: - print("sliderChanged") + _logger.debug("sliderChanged") value0 = self.sliderList[0].value() value1 = self.sliderList[1].value() self.maxText.setText("%f" % max(value0, value1)) @@ -212,8 +214,7 @@ class Object3DColormap(qt.QGroupBox): self._emitSignal() def _update(self): - if DEBUG: - print("colormap _update called") + _logger.debug("colormap _update called") self.__disconnected = True delta = (self.dataMax - self.dataMin)/ 200. self.sliderList[0].setRange(self.dataMin, self.dataMax, delta) @@ -233,15 +234,13 @@ class Object3DColormap(qt.QGroupBox): return if event is None: event = 'ColormapChanged' - if DEBUG: - print("sending colormap") + _logger.debug("sending colormap") ddict = self.getParameters() ddict['event'] = event self.sigObject3DColormapSignal.emit(ddict) def setAutoscale(self, val): - if DEBUG: - print("setAutoscale called", val) + _logger.debug("setAutoscale called %s", val) if val: self.autoScaleButton.setChecked(True) self.autoScale90Button.setChecked(False) diff --git a/PyMca5/PyMcaCore/EdfFileDataSource.py b/PyMca5/PyMcaCore/EdfFileDataSource.py index 35ecef3..72b3cf3 100644 --- a/PyMca5/PyMcaCore/EdfFileDataSource.py +++ b/PyMca5/PyMcaCore/EdfFileDataSource.py @@ -36,9 +36,12 @@ import types import sys import os import numpy +import logging SOURCE_TYPE = "EdfFile" -DEBUG = 0 + +_logger = logging.getLogger(__name__) + class EdfFileDataSource(object): def __init__(self,nameInput, fastedf=False): @@ -60,7 +63,7 @@ class EdfFileDataSource(object): #self._fastedf = True self._fastedf = fastedf if fastedf: - print("fastedf is unsafe!") + _logger.warning("fastedf is unsafe!") self.refresh() def refresh(self): @@ -98,8 +101,7 @@ class EdfFileDataSource(object): return self.__getKeyInfo(key) else: #should we raise a KeyError? - if DEBUG: - print("Error key not in list ") + _logger.debug("Error key not in list ") return {} def __getKeyInfo(self,key): @@ -109,8 +111,7 @@ class EdfFileDataSource(object): image = int(image)-1 except: #should we rise an error? - if DEBUG: - print("Error trying to interpret key =",key) + _logger.debug("Error trying to interpret key = %s", key) return {} sourceObject = self._sourceObjectList[index] @@ -179,8 +180,7 @@ class EdfFileDataSource(object): image = int(image)-1 MCAIMP = 0 if len(key_split) == 4: - if DEBUG: - print("mca like selection") + _logger.debug("mca like selection") #print data.info if 1: MCAIMP = 1 @@ -192,8 +192,7 @@ class EdfFileDataSource(object): size = (1,int(data.info['Dim_2'])) data.info['selectiontype'] = "1D" else: - if DEBUG: - print("mca like selection not yet implemented") + _logger.debug("mca like selection not yet implemented") pos = None size = None data.info['selectiontype'] = "1D" @@ -263,7 +262,7 @@ if __name__ == "__main__": sourcename=sys.argv[1] key =sys.argv[2] except: - print("Usage: EdfFileDataSource <file> <key>") + _logger.error("Usage: EdfFileDataSource <file> <key>") sys.exit() #one can use this: obj = EdfFileDataSource(sourcename) diff --git a/PyMca5/PyMcaCore/EdfFileLayer.py b/PyMca5/PyMcaCore/EdfFileLayer.py index de0fc14..4e8adbf 100644 --- a/PyMca5/PyMcaCore/EdfFileLayer.py +++ b/PyMca5/PyMcaCore/EdfFileLayer.py @@ -38,12 +38,14 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" ################################################################################ +import logging #import fast_EdfFile as EdfFile from PyMca5.PyMcaIO import EdfFile ################################################################################ +_logger = logging.getLogger(__name__) SOURCE_TYPE = "EdfFile" -DEBUG = 0 + class EdfFileLayer(object): """ @@ -87,20 +89,19 @@ class EdfFileLayer(object): self.Source= source_obj else: if (type(source_name) == type([])): - if DEBUG: - print("List of files") + _logger.debug("List of files") self.Source=[] for name in source_name: try: self.Source.append(EdfFile.EdfFile(name,fastedf=self.fastedf)) except: - #print("EdfFileLayer.SetSource: Error trying to read EDF file %s" % name) + # _logger.info("EdfFileLayer.SetSource: Error trying to read EDF file %s", name) self.Source.append( None) else: try: self.Source = EdfFile.EdfFile(source_name, fastedf=self.fastedf) except: - #print("EdfFileLayer.SetSource: Error trying to read EDF file") + # _logger.info("EdfFileLayer.SetSource: Error trying to read EDF file") self.Source=None else: self.Source=None @@ -200,7 +201,7 @@ class EdfFileLayer(object): index = int(index)-1 image = int(image)-1 except: - print("Error trying to interpret key = %s" % key) + _logger.error("Error trying to interpret key = %s", key) return {} source = self.Source[index] NumImages=source.GetNumImages() @@ -379,7 +380,8 @@ class EdfFileLayer(object): """ #AS if append==0: Data.Delete(self) numimages=self.Source.GetNumImages() - if key_list == "ALL": key_list=range(numimages) + if key_list == "ALL": + key_list=range(numimages) elif type(key_list) != type([]): key_list=[key_list] #AS elif type(key_list) is types.IntType: key_list=[key_list] if pos is not None: @@ -445,11 +447,11 @@ if __name__ == "__main__": fast = int(sys.argv[3]) obj=EdfFileLayer(fastedf=fast) if not obj.SetSource([filename]): - print("ERROR: cannot open file %s" % filename) + _logger.error("ERROR: cannot open file %s" % filename) sys.exit() #obj.LoadSource(key) except: - print("Usage: EdfFileData.py <filename> <image> <fastflag>") + _logger.error("Usage: EdfFileData.py <filename> <image> <fastflag>") sys.exit() print(obj.GetSourceInfo()) for i in range(1): diff --git a/PyMca5/PyMcaCore/EventHandler.py b/PyMca5/PyMcaCore/EventHandler.py index 9741351..3749051 100644 --- a/PyMca5/PyMcaCore/EventHandler.py +++ b/PyMca5/PyMcaCore/EventHandler.py @@ -78,6 +78,11 @@ Full event names: A string with the event name fully specified (i.e. a.b.c) """ __version__ = '0.1Beta' +import logging + +_logger = logging.getLogger(__name__) + + class Event(object): pass @@ -212,7 +217,7 @@ class EventHandler(object): for cb in self.callbacks[event]: cb(*args, **kw) else: - print("Warning: missing event ",event) + _logger.warning("Warning: missing event: %s", event) def getfullevents(self): """ return a list with fully specified event names (a.b.c) """ diff --git a/PyMca5/PyMcaCore/HtmlIndex.py b/PyMca5/PyMcaCore/HtmlIndex.py index 3a75c6f..7f0d4f2 100644 --- a/PyMca5/PyMcaCore/HtmlIndex.py +++ b/PyMca5/PyMcaCore/HtmlIndex.py @@ -30,11 +30,14 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import logging import os import sys import time from . import PyMcaLogo +_logger = logging.getLogger(__name__) + class HtmlIndex(object): def __init__(self, htmldir): if htmldir is None:htmldir = "/tmp/HTML" @@ -150,7 +153,7 @@ class HtmlIndex(object): try: os.remove(index) except: - print("cannot delete file %s" % index) + _logger.error("cannot delete file %s", index) continue def _getHtmlFileList(self, directory): @@ -166,7 +169,7 @@ class HtmlIndex(object): try: os.remove(index) except: - print("cannot delete file %s" % index) + _logger.error("cannot delete file %s", index) return filelist = self._getHtmlFileList(directory) text = "" @@ -185,7 +188,7 @@ class HtmlIndex(object): try: os.remove(index) except: - print("cannot delete file %s" % index) + _logger.error("cannot delete file %s", index) return directorylist = self._getHtmlDirList(directory) text = "" diff --git a/PyMca5/PyMcaCore/LoggingLevel.py b/PyMca5/PyMcaCore/LoggingLevel.py new file mode 100644 index 0000000..eca68d7 --- /dev/null +++ b/PyMca5/PyMcaCore/LoggingLevel.py @@ -0,0 +1,89 @@ + +#!/usr/bin/env python +#/*########################################################################## +# Copyright (C) 2004-2014 V.A. Sole, European Synchrotron Radiation Facility +# +# This file is part of the PyMca X-ray Fluorescence Toolkit developed at +# the ESRF by the Software group. +# +# 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. +# +#############################################################################*/ +"""Module for parsing command line options related to the logging level.""" +__author__ = "P. Knobel" +__license__ = "MIT" +__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" + +import logging + + +DEFAULT_LOGGING_LEVEL = logging.WARNING + + +def getLoggingLevel(opts): + """Find logging level from the output of `getopt.getopt()`. + This level can be specified via one of two long options: + --debug or --logging. If both are specified, --logging overrules + --debug. + + When specifying the level with --logging, the level can be + specified explicitly as a string (debug, info, warning, error, critical), + or as an integer in the range 0--4, in increasing order of verbosity + (0 is "critical", 4 is "debug"). + + The option --debug only allows to chose between the default logging level + (--debug=0) or debugging mode with maximum verbosity (--debug=1). + + :param opts: Command line options as a list of 2-tuples of strings + (e.g. ``[('--logging', 'debug'), ('--cfg', 'config.ini')]``). + :returns: logging level + :rtype: int""" + logging_level = None + for opt, arg in opts: + if opt == '--logging': + levels_dict = { + # Explicit args + 'debug': logging.DEBUG, + 'info': logging.INFO, + 'warning': logging.WARNING, + 'error': logging.ERROR, + 'critical': logging.CRITICAL, + # int args sorted by increasing verbosity + '0': logging.CRITICAL, + '1': logging.ERROR, + '2': logging.WARNING, + '3': logging.INFO, + '4': logging.DEBUG} + + logging_level = levels_dict.get(arg.lower()) + if logging_level is None: + raise ValueError("Unknown logging level <%s>" % arg) + # if --logging is specified, ignore --debug + return logging_level + if opt == '--debug': + # simpler option to choose between the default logging or DEBUG + if arg.lower() in ["0", "false"]: + logging_level = logging.INFO + elif arg.lower() in ["1", "true"]: + logging_level = logging.DEBUG + else: + raise ValueError("Incorrect debug parameter <%s> (should be 0 or 1)" % arg) + if logging_level is None: + return DEFAULT_LOGGING_LEVEL + return logging_level diff --git a/PyMca5/PyMcaCore/NexusDataSource.py b/PyMca5/PyMcaCore/NexusDataSource.py index 80daa9d..6cc5dc8 100644 --- a/PyMca5/PyMcaCore/NexusDataSource.py +++ b/PyMca5/PyMcaCore/NexusDataSource.py @@ -37,6 +37,7 @@ import h5py from operator import itemgetter import re import posixpath +import logging phynx = h5py if sys.version_info >= (3,): @@ -46,7 +47,8 @@ from . import DataObject from . import NexusTools SOURCE_TYPE = "HDF5" -DEBUG = 0 + +_logger = logging.getLogger(__name__) #sorting method def h5py_sorting(object_list): @@ -89,8 +91,8 @@ def h5py_sorting(object_list): #The only way to reach this point is to have different #structures among the different entries. In that case #defaults to the unfiltered case - print("WARNING: Default ordering") - print("Probably all entries do not have the key %s" % sorting_key) + _logger.warning("Default ordering") + _logger.warning("Probably all entries do not have the key %s", sorting_key) return object_list def _get_number_list(txt): @@ -262,8 +264,7 @@ class NexusDataSource(object): return self.__getKeyInfo(key) else: #should we raise a KeyError? - if DEBUG: - print("Error key not in list ") + _logger.debug("Error key not in list ") return {} def __getKeyInfo(self,key): @@ -273,8 +274,7 @@ class NexusDataSource(object): entry = int(entry)-1 except: #should we rise an error? - if DEBUG: - print("Error trying to interpret key = %s" % key) + _logger.debug("Error trying to interpret key = %s", key) return {} sourceObject = self._sourceObjectList[index] @@ -293,8 +293,7 @@ class NexusDataSource(object): starting by 1. selection: a dictionnary generated via QNexusWidget """ - if DEBUG: - print("getDataObject selection = ", selection) + _logger.debug("getDataObject selection = %s", selection) if selection is not None: if 'sourcename' in selection: filename = selection['sourcename'] @@ -314,7 +313,7 @@ class NexusDataSource(object): actual_key = "%d.%d" % (fileIndex+1, entryIndex+1) if actual_key != key: if entry != "/": - print("Warning selection keys do not match") + _logger.warning("selection keys do not match") else: #Probably I should find the acual entry following h5py_ordering output #and search for an NXdata plot. @@ -355,9 +354,8 @@ class NexusDataSource(object): if path in h5: dataset = h5[path].value if dataset is None: - if DEBUG: - print("Broken link? Ignoring key %s = % s" % \ - (key, mcaDatasetObjectPath )) + _logger.debug("Broken link? Ignoring key %s = %s", + key, mcaDatasetObjectPath) del mcaObjectPaths[key] else: mcaObjectPaths[key] = dataset @@ -405,9 +403,9 @@ class NexusDataSource(object): if selection['mcaselectiontype'].lower() != "sum": output.info["McaLiveTime"] /= divider except: - import traceback - print("exception", sys.exc_info()) - print(("%s " % value) + ''.join(traceback.format_tb(trace))) + # import traceback + _logger.error("%s", sys.exc_info()) + # print(("%s " % value) + ''.join(traceback.format_tb(trace))) return output output.x = [mcaChannels] output.y = [mcaData] @@ -444,8 +442,7 @@ class NexusDataSource(object): output.info['MotorValues'].append(value) except: # I cannot affort to fail here for something probably not used - if DEBUG: - print("Error reading positioners ", sys.exc_info()) + _logger.debug("Error reading positioners\n%s", sys.exc_info()) for cnt in ['y', 'x', 'm']: if not cnt in selection: continue @@ -471,16 +468,15 @@ class NexusDataSource(object): nSpectra *= iDummy if mcaselectiontype == "sum": # sum already calculated - if DEBUG: - print("SUM") + _logger.debug("SUM") elif mcaselectiontype in ["avg", "average"]: # calculate the average - if DEBUG: - print("AVERAGE") + _logger.debug("AVERAGE") data /= nSpectra else: - print("Unsupported selection type %s" % mcaselectiontype) - print("Calculating average") + _logger.warning("Unsupported selection type %s", + mcaselectiontype) + _logger.warning("Calculating average") data /= nSpectra elif len(data.shape) == 2: if min(data.shape) == 1: diff --git a/PyMca5/PyMcaCore/NexusTools.py b/PyMca5/PyMcaCore/NexusTools.py index 9385920..55d2c69 100644 --- a/PyMca5/PyMcaCore/NexusTools.py +++ b/PyMca5/PyMcaCore/NexusTools.py @@ -35,7 +35,9 @@ from operator import itemgetter import re import posixpath from h5py import File, Dataset, Group -DEBUG = 0 +import logging + +_logger = logging.getLogger(__name__) #sorting method def h5py_sorting(object_list): @@ -54,8 +56,7 @@ def h5py_sorting(object_list): posixNames = [item[1].name for item in object_list] except AttributeError: # Typical of broken external links - if DEBUG: - print("HDF5Widget: Cannot get posixNames") + _logger.debug("HDF5Widget: Cannot get posixNames") return object_list # This implementation only sorts entries @@ -91,8 +92,8 @@ def h5py_sorting(object_list): #The only way to reach this point is to have different #structures among the different entries. In that case #defaults to the unfiltered case - print("WARNING: Default ordering") - print("Probably all entries do not have the key %s" % sorting_key) + _logger.warning("Default ordering. " + "Probably all entries do not have the key %s", sorting_key) return object_list def _get_number_list(txt): @@ -421,7 +422,7 @@ def getInstrumentGroup(h5file, path): return None else: if n > 1: - print("WARNING: More than one instrument associated to the same entry") + _logger.warning("More than one instrument associated to the same entry") return groups[0] def getScannedPositioners(h5file, path): diff --git a/PyMca5/PyMcaCore/Plugin1DBase.py b/PyMca5/PyMcaCore/Plugin1DBase.py index 2cae8d6..b27cb99 100644 --- a/PyMca5/PyMcaCore/Plugin1DBase.py +++ b/PyMca5/PyMcaCore/Plugin1DBase.py @@ -40,7 +40,7 @@ Plugins can be automatically installed provided they are in the appropriate plac or *${HOME}/PyMca5/plugins* (older PyMca installation) - In *"My Documents\\\\PyMca\\\\plugins"* (Windows) -A plugin inherit the :class:`Plugin1DBase` class and implement the methods: +Plugins inherit the :class:`Plugin1DBase` class and implement the methods: - :meth:`Plugin1DBase.getMethods` - :meth:`Plugin1DBase.getMethodToolTip` (optional but convenient) @@ -50,6 +50,9 @@ A plugin inherit the :class:`Plugin1DBase` class and implement the methods: and modify the static module variable :const:`MENU_TEXT` and the static module function :func:`getPlugin1DInstance` according to the defined plugin. +Optionally, plugins may also implement :meth:`Plugin1DBase.activeCurveChanged` +to react to data selection in the plot. + These plugins will be compatible with any 1D-plot window that implements the Plot1D interface. The plot window interface is described in the Plot1DBase class. @@ -162,14 +165,19 @@ class Plugin1DBase(object): """ self._plotWindow = weakref.proxy(plotWindow) - self._legacy = True - """The plot window can be a legacy PyMca plot, in which case + self._legacy = False + """ + In the transition phase from PyMca plot to silx plot, + the plot window could be a legacy PyMca plot, in which case :attr:`_legacy` is set to *True*, or a :class:`PluginsToolButton` acting as proxy for a *silx* PlotWindow (*legacy=False*). + But now we don't expect to see PyMca plots any longer. """ - if hasattr(plotWindow, "plot"): # PluginsToolButton.plot -> silx plot - self._legacy = False + # PyMcaGraph.Plot has a PLUGINS_DIR class attribute, + # PluginsToolButton does not + if hasattr(plotWindow, "PLUGINS_DIR"): + self._legacy = True # Window related functions def windowTitle(self): @@ -480,6 +488,17 @@ class Plugin1DBase(object): print("applyMethod not implemented") return + def activeCurveChanged(self, prev, new): + """A plugin may implement this method which is called + when the active curve changes in the plot. + + :param prev: Legend of the previous active curve, + or None if no curve was active. + :param new: Legend of the new active curve, + or None if no curve is currently active. + """ + pass + MENU_TEXT = "Plugin1D Base" """This is the name of the plugin, as it appears in the plugins menu.""" diff --git a/PyMca5/PyMcaCore/Plugin2DBase.py b/PyMca5/PyMcaCore/Plugin2DBase.py new file mode 100644 index 0000000..5cc71df --- /dev/null +++ b/PyMca5/PyMcaCore/Plugin2DBase.py @@ -0,0 +1,124 @@ +#/*########################################################################## +# +# The PyMca X-Ray Fluorescence Toolkit +# +# Copyright (c) 2004-2016 European Synchrotron Radiation Facility +# +# This file is part of the PyMca X-ray Fluorescence Toolkit developed at +# the ESRF by the Software group. +# +# 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. +# +#############################################################################*/ +""" +A 2D plugin is a module that can be added to the PyMca 2D window in order to +perform user defined operations of the plotted 2D data. + +Plugins can be automatically installed provided they are in the appropriate place: + + - In the user home directory (POSIX systems): *${HOME}/.pymca/plugins* + or *${HOME}/PyMca5/plugins* (older PyMca installation) + - In *"My Documents\\\\PyMca\\\\plugins"* (Windows) + +A plugin inherits the :class:`Plugin2DBase` class and implements the methods: + + - :meth:`Plugin2DBase.getMethods` + - :meth:`Plugin2DBase.getMethodToolTip` (optional but convenient) + - :meth:`Plugin2DBase.getMethodPixmap` (optional) + - :meth:`Plugin2DBase.applyMethod` + +and modifies the static module variable :const:`MENU_TEXT` and the static module function +:func:`getPlugin2DInstance` according to the defined plugin. + +It may also optionally implement :meth:`Plugin2DBase.activeImageChanged`. + +""" +__license__ = "MIT" +__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" + +import weakref + + +class Plugin2DBase(object): + def __init__(self, plotWindow, **kw): + """ + plotWindow is the plot on which the plugin operates. + + + """ + self._plotWindow = weakref.proxy(plotWindow) + + # Methods to be implemented by the plugin + def getMethods(self, plottype=None): + """ + + :return: A list with the NAMES associated to the callable methods + that are applicable to the specified type plot. The list can be empty. + :rtype: list[string] + """ + print("getMethods not implemented") + return [] + + def getMethodToolTip(self, name): + """ + Returns the help associated to the particular method name or None. + + :param name: The method for which a tooltip is asked + :rtype: string + """ + return None + + def getMethodPixmap(self, name): + """ + :param name: The method for which a pixmap is asked + :rtype: QPixmap or None + """ + return None + + def applyMethod(self, name): + """ + The plugin is asked to apply the method associated to name. + """ + print("applyMethod not implemented") + return + + def activeImageChanged(self, prev, new): + """A plugin may implement this method which is called + when the active image changes in the plot. + + :param prev: Legend of the previous active image, + or None if no image was active. + :param new: Legend of the new active curve, + or None if no image is currently active. + """ + pass + + +MENU_TEXT = "Plugin2D Base" +"""This is the name of the plugin, as it appears in the plugins menu.""" + + +def getPlugin2DInstance(plotWindow, **kw): + """ + This function will be called by the plot window instantiating and calling + the plugins. It passes itself as first argument, but the default implementation + of the base class only keeps a weak reference to prevent circular references. + """ + ob = Plugin2DBase(plotWindow) + return ob diff --git a/PyMca5/PyMcaCore/PyMcaBatchBuildOutput.py b/PyMca5/PyMcaCore/PyMcaBatchBuildOutput.py index 85baefc..3a619d2 100644 --- a/PyMca5/PyMcaCore/PyMcaBatchBuildOutput.py +++ b/PyMca5/PyMcaCore/PyMcaBatchBuildOutput.py @@ -33,9 +33,10 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import os import numpy +import logging from PyMca5.PyMcaIO import EdfFile -DEBUG = 0 +_logger = logging.getLogger(__name__) class PyMcaBatchBuildOutput(object): def __init__(self, inputdir=None, outputdir=None): @@ -50,8 +51,7 @@ class PyMcaBatchBuildOutput(object): if delete is None: if outputdir == inputdir: delete = True - if DEBUG: - print("delete option = ", delete) + _logger.debug("delete option = %s", delete) allfiles = os.listdir(inputdir) partialedflist = [] partialdatlist = [] @@ -64,8 +64,7 @@ class PyMcaBatchBuildOutput(object): #IMAGES edfoutlist = [] for filename in partialedflist: - if DEBUG: - print("Dealing with filename %s" % filename) + _logger.debug("Dealing with filename %s", filename) edflist = self.getIndexedFileList(os.path.join(inputdir, filename)) i = 0 for edfname in edflist: @@ -83,11 +82,9 @@ class PyMcaBatchBuildOutput(object): i += 1 edfname = filename.replace('_000000_partial.edf',".edf") edfoutname = os.path.join(outputdir, edfname) - if DEBUG: - print("Dealing with output filename %s" % edfoutname) + _logger.debug("Dealing with output filename %s", edfoutname) if os.path.exists(edfoutname): - if DEBUG: - print("Output file already exists, trying to delete it") + _logger.debug("Output file already exists, trying to delete it") os.remove(edfoutname) edfout = EdfFile.EdfFile(edfoutname, access="wb") edfout.WriteImage (header , data, Append=0) @@ -98,7 +95,7 @@ class PyMcaBatchBuildOutput(object): try: os.remove(filename) except: - print("Cannot delete file %s" % filename) + _logger.warning("Cannot delete file %s" % filename) #DAT IMAGES datoutlist = [] @@ -215,8 +212,8 @@ class PyMcaBatchBuildOutput(object): prefix = name[0:n-i+1] prefix = os.path.join(os.path.dirname(filename),prefix) if not os.path.exists(prefix + number + suffix): - print("Internal error in EDFStack") - print("file should exist: %s " % (prefix + number + suffix)) + _logger.error("Internal error in EDFStack") + _logger.error("file should exist: %s " % (prefix + number + suffix)) return i = 0 if begin is None: diff --git a/PyMca5/PyMcaCore/PyMcaDirs.py b/PyMca5/PyMcaCore/PyMcaDirs.py index e4b2135..a1ef8b8 100644 --- a/PyMca5/PyMcaCore/PyMcaDirs.py +++ b/PyMca5/PyMcaCore/PyMcaDirs.py @@ -32,8 +32,10 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os +import logging + +_logger = logging.getLogger(__name__) -DEBUG = 0 inputDir = None outputDir = None nativeFileDialogs = False @@ -43,8 +45,7 @@ class __ModuleWrapper: self.__dict__["_ModuleWrapper__wrapped"] = wrapped def __getattr__(self, name): - if DEBUG: - print("getting ", name) + _logger.debug("getting %s", name) if name == "inputDir": if self.__wrapped.__dict__[name] is None: if self.__wrapped.__dict__['outputDir'] is not None: @@ -63,13 +64,11 @@ class __ModuleWrapper: if not os.path.isdir(value): value = os.getcwd() self.__setattr__('outputDir', value) - if DEBUG: - print("got ", name, getattr(self.__wrapped, name)) + _logger.debug("got %s %s", name, getattr(self.__wrapped, name)) return getattr(self.__wrapped, name) def __setattr__(self, name, value): - if DEBUG: - print("setting ", name, value) + _logger.debug("setting %s %s", name, value) if name == "inputDir": if os.path.isdir(value): self.__wrapped.__dict__[name]=value diff --git a/PyMca5/PyMcaCore/PyMcaMatplotlibSave.py b/PyMca5/PyMcaCore/PyMcaMatplotlibSave.py index 860a263..724078a 100644 --- a/PyMca5/PyMcaCore/PyMcaMatplotlibSave.py +++ b/PyMca5/PyMcaCore/PyMcaMatplotlibSave.py @@ -33,6 +33,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import os import numpy +import logging from matplotlib import cm from matplotlib import __version__ as matplotlib_version from matplotlib.font_manager import FontProperties @@ -41,7 +42,8 @@ from matplotlib.figure import Figure from matplotlib.colors import LinearSegmentedColormap, LogNorm, Normalize from matplotlib.ticker import MaxNLocator, AutoLocator -DEBUG = 0 + +_logger = logging.getLogger(__name__) colordict = {} colordict['blue'] = '#0000ff' @@ -171,8 +173,7 @@ class PyMcaMatplotlibSave(FigureCanvas): n = self._filterData(x, y) if n == 0: #nothing to plot - if DEBUG: - print("nothing to plot") + _logger.debug("nothing to plot") return style = None if color is None: @@ -473,7 +474,8 @@ class PyMcaMatplotlibSaveImage: elif self.config['colormap'] == 'ylgnbu_r': cmap = cm.YlGnBu_r else: - print("Unsupported colormap %s" % self.config['colormap']) + _logger.warning("Unsupported colormap %s", self.config['colormap']) + _logger.warning("Defaulting to grayscale.") if self.config['extent'] is None: h, w = self.imageData.shape diff --git a/PyMca5/PyMcaCore/SpecFileDataSource.py b/PyMca5/PyMcaCore/SpecFileDataSource.py index e1cc082..da1f3a2 100644 --- a/PyMca5/PyMcaCore/SpecFileDataSource.py +++ b/PyMca5/PyMcaCore/SpecFileDataSource.py @@ -34,11 +34,14 @@ import sys import os import numpy import types +import logging from . import DataObject from PyMca5.PyMcaIO import specfilewrapper as specfile +_logger = logging.getLogger(__name__) + + SOURCE_TYPE = "SpecFile" -DEBUG = 0 # Scan types # ---------- @@ -115,8 +118,7 @@ class SpecFileDataSource(object): try: self.__fileHeaderList[0] = sel.fileheader('') except: - if DEBUG: - print("getSourceInfo %s" % sys.exc_info()[1]) + _logger.debug("getSourceInfo %s", sys.exc_info()[1]) self.__fileHeaderList[0] = None try: n = sel.nbmca() @@ -219,8 +221,7 @@ class SpecFileDataSource(object): try: self.__fileHeaderList[index] = scandata.fileheader('') except: - if DEBUG: - print("getScanInfo %s" % sys.exc_info()[1]) + _logger.debug("getScanInfo %s", sys.exc_info()[1]) self.__fileHeaderList[index] = None info["FileHeader"] = self.__fileHeaderList[index] try: info["Number"] = scandata.number() @@ -284,8 +285,7 @@ class SpecFileDataSource(object): if len(calib) == info["NbMcaDet"]: calib = [calib[mcainfo["McaDet"]-1]] else: - if DEBUG: - print("Warning","Number of calibrations does not match number of MCAs") + _logger.debug("Number of calibrations does not match number of MCAs") if len(calib) == 1: pass else: @@ -304,8 +304,7 @@ class SpecFileDataSource(object): if len(ctime) == info["NbMcaDet"]: ctime = [ctime[mcainfo["McaDet"]-1]] else: - if DEBUG: - print("Warning","Number of counting times does not match number of MCAs") + _logger.debug("Number of counting times does not match number of MCAs") if len(ctime) == 1: pass else: @@ -324,8 +323,7 @@ class SpecFileDataSource(object): if len(chann) == info["NbMcaDet"]: chann = [chann[mcainfo["McaDet"] - 1]] else: - if DEBUG: - print("Warning","Number of @CHANN information does not match number of MCAs") + _logger.debug("Number of @CHANN information does not match number of MCAs") if len(chann) == 1: pass else: @@ -408,9 +406,8 @@ class SpecFileDataSource(object): raise KeyError("Key %s not in source keys" % key) mca3D = False - if DEBUG: - print("SELECTION = ", selection) - print("key_type = ", key_type) + _logger.debug("SELECTION = %s", selection) + _logger.debug("key_type = %s", key_type) if key_type == "scan": if selection is not None: if 'mcalist' in selection: @@ -704,9 +701,8 @@ class SpecFileDataSource(object): #mca_no= 1 + int(key_split[2]) + int(key_split[3])*mot1 mca_no = (int(key_split[2])-1) * scan_info["NbMcaDet"] + \ int(key_split[3]) - if DEBUG: - print("try to read mca number = ",mca_no) - print("total number of mca = ",scan_info["NbMca"]) + _logger.debug("try to read mca number = %s", mca_no) + _logger.debug("total number of mca = %s", scan_info["NbMca"]) scan_data = scan_obj.mca(mca_no) except: raise IOError("SF_MESH+SF_MCA read failed") diff --git a/PyMca5/PyMcaCore/SpsDataSource.py b/PyMca5/PyMcaCore/SpsDataSource.py index 3e1706f..e437b54 100644 --- a/PyMca5/PyMcaCore/SpsDataSource.py +++ b/PyMca5/PyMcaCore/SpsDataSource.py @@ -31,10 +31,13 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import copy +import logging from . import DataObject from PyMca5.PyMcaIO import spswrap as sps -DEBUG = 0 +_logger = logging.getLogger(__name__) + + SOURCE_TYPE = 'SPS' @@ -186,8 +189,7 @@ class SpsDataSource(object): if (arrayflag & sps.TAG_ARRAY) == sps.TAG_ARRAY: arraylist.append(array) continue - if DEBUG: - print("array not added %s" % array) + _logger.debug("array not added %s", array) source_info = {} source_info["Size"] = len(arraylist) source_info["KeyList"] = arraylist @@ -226,7 +228,7 @@ class SpsDataSource(object): try: labels = [(int(x),x) for x in labels] except: - print("SpsDataSource error reverting to old behavior") + _logger.warning("SpsDataSource error reverting to old behavior") labels = [(x, x) for x in labels] labels.sort() if len(labels): @@ -292,8 +294,7 @@ class SpsDataSource(object): return info def _buildLabelsList(self, instr): - if DEBUG: - print('SpsDataSource : building counter list') + _logger.debug('SpsDataSource : building counter list') state = 0 llist = [''] for letter in instr: diff --git a/PyMca5/PyMcaCore/StackBase.py b/PyMca5/PyMcaCore/StackBase.py index f7f9352..a253d27 100644 --- a/PyMca5/PyMcaCore/StackBase.py +++ b/PyMca5/PyMcaCore/StackBase.py @@ -40,7 +40,11 @@ import time import os import sys import glob -DEBUG = 0 +import logging + +logger = logging.getLogger(__name__) + + PLUGINS_DIR = None try: import PyMca5 @@ -182,8 +186,8 @@ class StackBase(object): sys.modules[plugin].getStackPluginInstance(self) self.pluginList.append(plugin) except: - if DEBUG: - print("Problem importing module %s" % plugin) + logger.debug("Problem importing module %s", plugin) + if logger.getEffectiveLevel() == logging.DEBUG: raise return len(self.pluginList) @@ -264,17 +268,15 @@ class StackBase(object): axis=self.mcaIndex, dtype=numpy.float) #original ICR mca - if DEBUG: - print("(self.otherIndex, self.fileIndex) = (%d, %d)" %\ - (self.otherIndex, self.fileIndex)) + logger.debug("(self.otherIndex, self.fileIndex) = (%d, %d)", + self.otherIndex, self.fileIndex) i = max(self.otherIndex, self.fileIndex) j = min(self.otherIndex, self.fileIndex) mcaData0 = numpy.sum(numpy.sum(self._stack.data, axis=i, dtype=numpy.float), j) else: - if DEBUG: - t0 = time.time() + t0 = time.time() shape = self._stack.data.shape if self.mcaIndex in [2, -1]: self._stackImageData = numpy.zeros((shape[0], shape[1]), @@ -308,11 +310,9 @@ class StackBase(object): mcaData0[i] = tmpData.sum() else: raise ValueError("Unhandled case 1D index = %d" % self.mcaIndex) - if DEBUG: - print("Print dynamic loading elapsed = %f" % (time.time() - t0)) + logger.debug("Print dynamic loading elapsed = %f", time.time() - t0) - if DEBUG: - print("__stackImageData.shape = ", self._stackImageData.shape) + logger.debug("__stackImageData.shape = %s", self._stackImageData.shape) if previousStackImageSize != self._stackImageData.size: self._clearPositioners() @@ -386,7 +386,7 @@ class StackBase(object): def handleNonFiniteData(self): text = "Your data contain infinite values or nans.\n" text += "Pixels containing those values will be ignored." - print(text) + logger.info(text) def updateROIImages(self, ddict=None): if ddict is None: @@ -411,15 +411,13 @@ class StackBase(object): if len(i1): i1 = min(i1) else: - if DEBUG: - print("updateROIImages: nothing to be made") + logger.debug("updateROIImages: nothing to be made") return i2 = numpy.nonzero(xw <= ddict['to'])[0] if len(i2): i2 = max(i2) + 1 else: - if DEBUG: - print("updateROIImages: nothing to be made") + logger.debug("updateROIImages: nothing to be made") return pos = 0.5 * (ddict['from'] + ddict['to']) imiddle = max(numpy.nonzero(xw <= pos)[0]) @@ -428,15 +426,13 @@ class StackBase(object): if len(i2): i2 = max(i2) else: - if DEBUG: - print("updateROIImages: nothing to be made") + logger.debug("updateROIImages: nothing to be made") return i1 = numpy.nonzero(xw <= ddict['to'])[0] if len(i1): i1 = min(i1) + 1 else: - if DEBUG: - print("updateROIImages: nothing to be made") + logger.debug("updateROIImages: nothing to be made") return pos = 0.5 * (ddict['from'] + ddict['to']) imiddle = min(numpy.nonzero(xw <= pos)[0]) @@ -505,12 +501,10 @@ class StackBase(object): self.showROIImageList(imageList, image_names=imageNames) def showOriginalImage(self): - if DEBUG: - print("showOriginalImage to be implemented") + logger.debug("showOriginalImage to be implemented") def showOriginalMca(self): - if DEBUG: - print("showOriginalMca to be implemented") + logger.debug("showOriginalMca to be implemented") def showROIImageList(self, imageList, image_names=None): self._ROIImageList = imageList @@ -533,12 +527,10 @@ class StackBase(object): return mcaData = None goodData = numpy.isfinite(self._mcaData0.y[0].sum()) - if DEBUG: - print("Stack data is not finite") + logger.debug("Stack data is not finite") if (self._selectionMask is None) and goodData: if normalize: - if DEBUG: - print("Case 1") + logger.debug("Case 1") npixels = self._stackImageData.shape[0] *\ self._stackImageData.shape[1] * 1.0 dataObject = DataObject.DataObject() @@ -548,8 +540,7 @@ class StackBase(object): if "McaLiveTime" in dataObject.info: dataObject.info["McaLiveTime"] /= float(npixels) else: - if DEBUG: - print("Case 2") + logger.debug("Case 2") dataObject = self._mcaData0 return dataObject @@ -570,8 +561,7 @@ class StackBase(object): npixels = actualSelectionMask.sum() if (npixels == 0) and goodData: if normalize: - if DEBUG: - print("Case 3") + logger.debug("Case 3") npixels = self._stackImageData.shape[0] * self._stackImageData.shape[1] * 1.0 dataObject = DataObject.DataObject() dataObject.info.update(self._mcaData0.info) @@ -580,8 +570,7 @@ class StackBase(object): if "McaLiveTime" in dataObject.info: dataObject.info["McaLiveTime"] /= float(npixels) else: - if DEBUG: - print("Case 4") + logger.debug("Case 4") dataObject = self._mcaData0 return dataObject @@ -597,28 +586,22 @@ class StackBase(object): else: arrayMask = (actualSelectionMask > 0) - if DEBUG: - print("Reached MCA calculation") + logger.debug("Reached MCA calculation") cleanMask = numpy.nonzero(arrayMask) - if DEBUG: - print("self.fileIndex, self.mcaIndex = %d , %d" %\ - (self.fileIndex, self.mcaIndex)) - if DEBUG: - t0 = time.time() + logger.debug("self.fileIndex, self.mcaIndex = %d , %d", + self.fileIndex, self.mcaIndex) + t0 = time.time() if len(cleanMask[0]) and len(cleanMask[1]): - if DEBUG: - print("USING MASK") + logger.debug("USING MASK") cleanMask = numpy.array(cleanMask).transpose() if self.fileIndex == 2: if self.mcaIndex == 0: if isinstance(self._stack.data, numpy.ndarray): - if DEBUG: - print("In memory case 0") + logger.debug("In memory case 0") for r, c in cleanMask: mcaData += self._stack.data[:, r, c] else: - if DEBUG: - print("Dynamic loading case 0") + logger.debug("Dynamic loading case 0") #no other choice than to read all images #for the time being, one by one rMin = cleanMask[0][0] @@ -644,13 +627,11 @@ class StackBase(object): elif self.fileIndex == 1: if self.mcaIndex == 0: if isinstance(self._stack.data, numpy.ndarray): - if DEBUG: - print("In memory case 2") + logger.debug("In memory case 2") for r, c in cleanMask: mcaData += self._stack.data[:, r, c] else: - if DEBUG: - print("Dynamic loading case 2") + logger.debug("Dynamic loading case 2") #no other choice than to read all images #for the time being, one by one if 1: @@ -675,13 +656,11 @@ class StackBase(object): mcaData[i] = (tmpData[0] * arrayMask).sum(dtype=numpy.float) elif self.mcaIndex == 2: if isinstance(self._stack.data, numpy.ndarray): - if DEBUG: - print("In memory case 3") + logger.debug("In memory case 3") for r, c in cleanMask: mcaData += self._stack.data[r, c, :] else: - if DEBUG: - print("Dynamic loading case 3") + logger.debug("Dynamic loading case 3") #try to minimize access to the file row_list = [] row_dict = {} @@ -699,21 +678,18 @@ class StackBase(object): elif self.fileIndex == 0: if self.mcaIndex == 1: if isinstance(self._stack.data, numpy.ndarray): - if DEBUG: - print("In memory case 4") + logger.debug("In memory case 4") for r, c in cleanMask: mcaData += self._stack.data[r, :, c] else: raise IndexError("Dynamic loading case 4") elif self.mcaIndex in [2, -1]: if isinstance(self._stack.data, numpy.ndarray): - if DEBUG: - print("In memory case 5") + logger.debug("In memory case 5") for r, c in cleanMask: mcaData += self._stack.data[r, c, :] else: - if DEBUG: - print("Dynamic loading case 5") + logger.debug("Dynamic loading case 5") #try to minimize access to the file row_list = [] row_dict = {} @@ -731,11 +707,9 @@ class StackBase(object): else: raise IndexError("File index undefined") else: - if DEBUG: - print("NOT USING MASK !") + logger.debug("NOT USING MASK !") - if DEBUG: - print("Mca sum elapsed = %f" % (time.time() - t0)) + logger.debug("Mca sum elapsed = %f", time.time() - t0) if goodData: if n_nonselected < npixels: mcaData = self._mcaData0.y[0] - mcaData @@ -764,8 +738,7 @@ class StackBase(object): return dataObject def calculateROIImages(self, index1, index2, imiddle=None, energy=None): - if DEBUG: - print("Calculating ROI images") + logger.debug("Calculating ROI images") i1 = min(index1, index2) i2 = max(index1, index2) if imiddle is None: @@ -797,8 +770,7 @@ class StackBase(object): minImage = energy[(numpy.argmin(dataImage, axis=1) + i1)] isUsingSuppliedEnergyAxis = True else: - if DEBUG: - t0 = time.time() + t0 = time.time() if self._tryNumpy and\ isinstance(self._stack.data, numpy.ndarray): leftImage = self._stack.data[:, :, i1] @@ -810,9 +782,8 @@ class StackBase(object): maxImage = energy[numpy.argmax(dataImage, axis=2) + i1] minImage = energy[numpy.argmin(dataImage, axis=2) + i1] isUsingSuppliedEnergyAxis = True - if DEBUG: - print("Case 1 ROI image calculation elapsed = %f " %\ - (time.time() - t0)) + logger.debug("Case 1 ROI image calculation elapsed = %f ", + time.time() - t0) else: shape = self._stack.data.shape roiImage = numpy.zeros(self._stackImageData.shape, @@ -841,13 +812,11 @@ class StackBase(object): isUsingSuppliedEnergyAxis = True minImage = energy[minImage] maxImage = energy[maxImage] - if DEBUG: - print("2 Dynamic ROI image calculation elapsed = %f " %\ - (time.time() - t0)) + logger.debug("2 Dynamic ROI image calculation elapsed = %f ", + time.time() - t0) elif self.fileIndex == 1: if self.mcaIndex == 0: - if DEBUG: - t0 = time.time() + t0 = time.time() if isinstance(self._stack.data, numpy.ndarray) and\ self._tryNumpy: leftImage = self._stack.data[i1, :, :] @@ -884,9 +853,8 @@ class StackBase(object): isUsingSuppliedEnergyAxis = True background = 0.5 * (i2 - i1) * (leftImage + rightImage) roiImage = numpy.sum(dataImage, axis=0, dtype=numpy.float) - if DEBUG: - print("Case 3 ROI image calculation elapsed = %f " %\ - (time.time() - t0)) + logger.debug("Case 3 ROI image calculation elapsed = %f ", + time.time() - t0) else: shape = self._stack.data.shape roiImage = numpy.zeros(self._stackImageData.shape, @@ -928,12 +896,10 @@ class StackBase(object): maxImage = energy[maxImage] if i2 > i1: background = (leftImage + rightImage) * 0.5 * (i2 - i1) - if DEBUG: - print("Case 4 Dynamic ROI elapsed = %f" %\ - (time.time() - t0)) + logger.debug("Case 4 Dynamic ROI elapsed = %f", + time.time() - t0) else: - if DEBUG: - t0 = time.time() + t0 = time.time() if self._tryNumpy and\ isinstance(self._stack.data, numpy.ndarray): leftImage = self._stack.data[:, :, i1] @@ -945,9 +911,8 @@ class StackBase(object): maxImage = energy[numpy.argmax(dataImage, axis=2) + i1] minImage = energy[numpy.argmin(dataImage, axis=2) + i1] isUsingSuppliedEnergyAxis = True - if DEBUG: - print("Case 5 ROI Image elapsed = %f" %\ - (time.time() - t0)) + logger.debug("Case 5 ROI Image elapsed = %f", + time.time() - t0) else: shape = self._stack.data.shape roiImage = numpy.zeros(self._stackImageData.shape, @@ -974,13 +939,11 @@ class StackBase(object): middleImage[i:i+step, :] += tmpData[:, :, imiddle-i1] rightImage[i:i+step, :] += tmpData[:, :,-1] background = 0.5*(i2-i1)*(leftImage+rightImage) - if DEBUG: - print("Case 6 Dynamic ROI image calculation elapsed = %f" %\ - (time.time() - t0)) + logger.debug("Case 6 Dynamic ROI image calculation elapsed = %f", + time.time() - t0) else: #self.fileIndex = 2 - if DEBUG: - t0 = time.time() + t0 = time.time() if self.mcaIndex == 0: leftImage = self._stack.data[i1] middleImage = self._stack.data[imiddle] @@ -991,9 +954,8 @@ class StackBase(object): minImage = energy[numpy.argmin(dataImage, axis=0) + i1] maxImage = energy[numpy.argmax(dataImage, axis=0) + i1] isUsingSuppliedEnergyAxis = True - if DEBUG: - print("Case 7 Default ROI image calculation elapsed = %f" %\ - (time.time() - t0)) + logger.debug("Case 7 Default ROI image calculation elapsed = %f", + time.time() - t0) else: leftImage = self._stack.data[:, i1, :] middleImage = self._stack.data[:, imiddle, :] @@ -1004,9 +966,8 @@ class StackBase(object): minImage = energy[numpy.argmin(dataImage, axis=1) + i1] maxImage = energy[numpy.argmax(dataImage, axis=1) + i1] isUsingSuppliedEnergyAxis = True - if DEBUG: - print("Case 8 Default ROI image calculation elapsed = %f" %\ - (time.time() - t0)) + logger.debug("Case 8 Default ROI image calculation elapsed = %f", + time.time() - t0) imageDict = {'ROI': roiImage, 'Maximum': maxImage, @@ -1016,13 +977,11 @@ class StackBase(object): 'Right': rightImage, 'Background': background} self.__ROIImageCalculationIsUsingSuppliedEnergyAxis = isUsingSuppliedEnergyAxis - if DEBUG: - print("ROI images calculated") + logger.debug("ROI images calculated") return imageDict def setSelectionMask(self, mask): - if DEBUG: - print("setSelectionMask called") + logger.debug("setSelectionMask called") goodData = numpy.isfinite(self._mcaData0.y[0].sum()) if goodData: @@ -1038,8 +997,7 @@ class StackBase(object): self.pluginInstanceDict[key].selectionMaskUpdated() def getSelectionMask(self): - if DEBUG: - print("getSelectionMask called") + logger.debug("getSelectionMask called") return self._selectionMask def addImage(self, image, name, info=None, replace=False, replot=True): @@ -1092,8 +1050,7 @@ class StackBase(object): If just_legend is True: The legend of the active curve (or None) is returned. """ - if DEBUG: - print("getActiveCurve default implementation") + logger.debug("getActiveCurve default implementation") info = {} info['xlabel'] = 'Channel' info['ylabel'] = 'Counts' @@ -1101,13 +1058,11 @@ class StackBase(object): return self._mcaData0.x[0], self._mcaData0.y[0], legend, info def getGraphXLimits(self): - if DEBUG: - print("getGraphXLimits default implementation") + logger.debug("getGraphXLimits default implementation") return self._mcaData0.x[0].min(), self._mcaData0.x[0].max() def getGraphYLimits(self): - if DEBUG: - print("getGraphYLimits default implementation") + logger.debug("getGraphYLimits default implementation") return self._mcaData0.y[0].min(), self._mcaData0.y[0].max() def getStackDataObject(self): @@ -1163,12 +1118,11 @@ class StackBase(object): "Wrong type for positioner %s. " % motorName + "Valid types are numpy arrays, scalars or list of scalars.") - if DEBUG and len(stackPositioners) != len(positioners): + if len(stackPositioners) != len(positioners): ignored_motors = list(set(positioners.keys()) - set(stackPositioners.keys())) - - print("Ignored motors due to mismatch in number of values: " + - ', '.join(ignored_motors)) + logger.debug("Ignored motors due to mismatch in number of values: %s", + ', '.join(ignored_motors)) self._stack.info["positioners"] = stackPositioners diff --git a/PyMca5/PyMcaCore/StackPluginBase.py b/PyMca5/PyMcaCore/StackPluginBase.py index d586a1f..5f1909f 100644 --- a/PyMca5/PyMcaCore/StackPluginBase.py +++ b/PyMca5/PyMcaCore/StackPluginBase.py @@ -116,7 +116,10 @@ These plugins will be compatible with any stack window that provides the followi """ import weakref -DEBUG = 0 +import logging + +pluginBaseLogger = logging.getLogger(__name__) + class StackPluginBase(object): def __init__(self, stackWindow, **kw): @@ -218,12 +221,10 @@ class StackPluginBase(object): return self._stackWindow.getGraphYLabel() def stackUpdated(self): - if DEBUG: - print("stackUpdated(self) not implemented") + pluginBaseLogger.debug("stackUpdated(self) not implemented") def stackROIImageListUpdated(self): - if DEBUG: - print("stackROIImageListUpdated(self) not implemented") + pluginBaseLogger.debug("stackROIImageListUpdated(self) not implemented") return def stackClosed(self): @@ -240,8 +241,7 @@ class StackPluginBase(object): self._widget.close() def selectionMaskUpdated(self): - if DEBUG: - print("selectionMaskUpdated(self) not implemented") + pluginBaseLogger.debug("selectionMaskUpdated(self) not implemented") #Methods to be implemented by the plugin def getMethods(self): @@ -249,7 +249,7 @@ class StackPluginBase(object): A list with the NAMES associated to the callable methods that are applicable to the specified stack. """ - print("BASE STACK getMethods not implemented") + pluginBaseLogger.debug("BASE STACK getMethods not implemented") return [] def getMethodToolTip(self, name): @@ -268,7 +268,7 @@ class StackPluginBase(object): """ The plugin is asked to apply the method associated to name. """ - print("applyMethod not implemented") + pluginBaseLogger.debug("applyMethod not implemented") return diff --git a/PyMca5/PyMcaCore/StackROIBatch.py b/PyMca5/PyMcaCore/StackROIBatch.py index 0f10331..3b66421 100644 --- a/PyMca5/PyMcaCore/StackROIBatch.py +++ b/PyMca5/PyMcaCore/StackROIBatch.py @@ -37,8 +37,10 @@ import os import numpy from PyMca5.PyMcaIO import ConfigDict import time +import logging + +_logger = logging.getLogger(__name__) -DEBUG = 0 class StackROIBatch(object): def __init__(self): @@ -98,7 +100,7 @@ class StackROIBatch(object): except AttributeError: txt = "%s" % type(data) if 'h5py' in txt: - print("Implementing h5py workaround") + _logger.info("Implementing h5py workaround") import h5py data = h5py.Dataset(data.id) else: @@ -108,8 +110,6 @@ class StackROIBatch(object): if x is None: x = numpy.arange(data.shape[index]).astype(numpy.float32) - if DEBUG: - t0 = time.time() if configuration is not None: self.setConfiguration(configuration) @@ -252,12 +252,12 @@ def getFileListFromPattern(pattern, begin, end, increment=None): return fileList if __name__ == "__main__": - DEBUG = True import glob import sys from PyMca5.PyMca import EDFStack from PyMca5.PyMca import ArraySave import getopt + _logger.setLevel(logging.DEBUG) options = '' longoptions = ['cfg=', 'outdir=', 'tif=', #'listfile=', @@ -269,7 +269,7 @@ if __name__ == "__main__": options, longoptions) except: - print(sys.exc_info()[1]) + _logger.error(sys.exc_info()[1]) sys.exit(1) fileRoot = "" outputDir = None diff --git a/PyMca5/PyMcaData/HTML/AdvancedAlignmentScanPlugin.html b/PyMca5/PyMcaData/HTML/AdvancedAlignmentScanPlugin.html index 40c4d44..8154a4b 100644 --- a/PyMca5/PyMcaData/HTML/AdvancedAlignmentScanPlugin.html +++ b/PyMca5/PyMcaData/HTML/AdvancedAlignmentScanPlugin.html @@ -177,14 +177,14 @@ switches between two sets of data where one set aligns the other one it is highl to consult the table in the Alignment window to check if every element in the two different sets of data is assigned to its correct counterpart before applying the shift.</br> </br> -If the data in the plot window is zoomed in to a distinct feature, only of the data is used to -calculate the shift. +If the data in the plot window is zoomed in to a distinct feature, only the visible data range +is used to calculate the shift. </div> <br><a href=#Up>up</a> <H3><a NAME=Methods> -1. Usage and Description +2. Methods used by the plug-in </a></H3> <div class='main'> @@ -194,30 +194,30 @@ FIT DRV. <div class=elem><i>FFT</i></div> <div class='indent'> - Uses the Fourier Transform of the curves to calculated their cross-correlation. The maximum - of the correlation is determined and yields the shift value. This method is the default option, - since it is not affected by the peak shape, fast and numerically robust. Notice: the shifts + Uses the Fourier Transform of the curves to calculate their cross-correlation. The maximum + of the correlation is determined and yields the shift value. This method is the default option. + Since it is not affected by the peak shape, it is fast and numerically robust. Notice: the shifts are given in real space values. </div> <div class=elem><i>MAX</i></div> <div class='indent'> - Determines the maximum of each curve, the shift is given by its the differences in the x-position + Determines the maximum of each curve. The shift is given by the differences in the x-position of the maxima. Notice that this method is highly vulnerable to noise in the data and spikes. </div> <div class=elem><i>FIT</i></div> <div class='indent'> - Method subtracts a background from the data using the SNIP algorithm (c.f. plug-in section, + This method subtracts a background from the data using the SNIP algorithm (c.f. plug-in section, Background subtraction tools) and searches for peaks in the data. For every curve, the single - most pronounced feature is selected. The peak is fitted by a Gaussian model, the shifts are then + most pronounced feature is selected. The peak is fitted by a Gaussian model. The shifts are then given by differences in the x-offsets of the fitted Gaussians. </div> -<div class=elem><i>FIT</i></div> +<div class=elem><i>FIT DRV</i></div> <div class='indent'> - Uses the same procedure as the FIT method, however the fit is applied to the first derivative of - the data. This method only recommended for X-ray absorption data. + Uses the same procedure as the FIT method. However the fit is applied to the first derivative of + the data. This method is only recommended for X-ray absorption data. </div> <div class='main'> diff --git a/PyMca5/PyMcaGraph/Colormap.py b/PyMca5/PyMcaGraph/Colormap.py index 4d7dbd9..d2d735a 100644 --- a/PyMca5/PyMcaGraph/Colormap.py +++ b/PyMca5/PyMcaGraph/Colormap.py @@ -38,6 +38,15 @@ __doc__ = """Convert data to a RGBA colormap.""" import numpy as np from . import ctools +import logging +import traceback +_logger = logging.getLogger(__name__) + +_logger.warning("%s is deprecated, you are advised to use " + "silx.gui.plot.matplotlib.Colormap instead", + __name__) +for line in traceback.format_stack(limit=4): + _logger.warning(line.rstrip()) # default colormaps ########################################################### diff --git a/PyMca5/PyMcaGraph/Colors.py b/PyMca5/PyMcaGraph/Colors.py index 0d65881..349f40f 100644 --- a/PyMca5/PyMcaGraph/Colors.py +++ b/PyMca5/PyMcaGraph/Colors.py @@ -33,6 +33,16 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" __doc__ = """ Dictionary of common colors. """ +import logging +import traceback +_logger = logging.getLogger(__name__) + +_logger.warning("%s is deprecated, you are advised to use " + "silx.gui.plot.Colors instead", + __name__) +for line in traceback.format_stack(limit=4): + _logger.warning(line.rstrip()) + COLORDICT = {} COLORDICT['b'] = COLORDICT['blue'] = '#0000ff' diff --git a/PyMca5/PyMcaGraph/Plot.py b/PyMca5/PyMcaGraph/Plot.py index 4f25cd4..aca2d36 100644 --- a/PyMca5/PyMcaGraph/Plot.py +++ b/PyMca5/PyMcaGraph/Plot.py @@ -45,9 +45,21 @@ from . import PlotBase from . import PlotBackend from . import Colors +import logging +import traceback +_logger = logging.getLogger(__name__) + DEBUG = 0 if DEBUG: PlotBase.DEBUG = True + _logger.setLevel(logging.DEBUG) + +_logger.warning("%s is deprecated, you are advised to use " + "silx.gui.plot.PlotWidget instead", + __name__) +for line in traceback.format_stack(limit=4): + _logger.warning(line.rstrip()) + _COLORDICT = Colors.COLORDICT _COLORLIST = [_COLORDICT['black'], diff --git a/PyMca5/PyMcaGraph/PlotBackend.py b/PyMca5/PyMcaGraph/PlotBackend.py index ee7dad5..7700dc6 100644 --- a/PyMca5/PyMcaGraph/PlotBackend.py +++ b/PyMca5/PyMcaGraph/PlotBackend.py @@ -177,6 +177,16 @@ DEBUG = 0 from . import Colors +import logging +import traceback +_logger = logging.getLogger(__name__) + +_logger.warning("%s is deprecated, you are advised to use " + "silx.gui.plot.backends.BackendBase instead", + __name__) +for line in traceback.format_stack(limit=4): + _logger.warning(line.rstrip()) + class PlotBackend(object): COLORDICT = Colors.COLORDICT diff --git a/PyMca5/PyMcaGraph/PlotBase.py b/PyMca5/PyMcaGraph/PlotBase.py index bdd72f9..b42e20e 100644 --- a/PyMca5/PyMcaGraph/PlotBase.py +++ b/PyMca5/PyMcaGraph/PlotBase.py @@ -58,9 +58,6 @@ The plugins will be compatible with any plot window that provides the methods: The simplest way to achieve that is to inherit from Plot """ -import os -import sys -import glob try: from numpy import argsort, nonzero, take except ImportError: @@ -69,6 +66,16 @@ except ImportError: from . import PlotBackend from . import PluginLoader +import logging +import traceback +_logger = logging.getLogger(__name__) + +_logger.warning("%s is deprecated, you are advised to use " + "silx.gui.plot.Colors instead", + __name__) +for line in traceback.format_stack(limit=4): + _logger.warning(line.rstrip()) + DEBUG = 0 class PlotBase(PlotBackend.PlotBackend, PluginLoader.PluginLoader): diff --git a/PyMca5/PyMcaGraph/PluginLoader.py b/PyMca5/PyMcaGraph/PluginLoader.py index 443c37a..2c1b2c6 100644 --- a/PyMca5/PyMcaGraph/PluginLoader.py +++ b/PyMca5/PyMcaGraph/PluginLoader.py @@ -41,10 +41,12 @@ directory and stores them into the attributes pluginList and pluginInstanceDict import os import sys import glob +import logging PLUGINS_DIR = None -DEBUG = 0 +_logger = logging.getLogger(__name__) + class PluginLoader(object): def __init__(self, method=None, directoryList=None): @@ -89,9 +91,8 @@ class PluginLoader(object): directoryList = self._pluginDirList if directoryList in [None, []]: directoryList = [PLUGINS_DIR] - if DEBUG: - print("method: %s" % targetMethod) - print("directoryList: %s" % directoryList) + _logger.debug("method: %s", targetMethod) + _logger.debug("directoryList: %s", directoryList) exceptionMessage = "" self._pluginDirList = directoryList self.pluginList = [] @@ -118,8 +119,7 @@ class PluginLoader(object): for module in moduleList: try: pluginName = os.path.basename(module)[:-3] - if DEBUG: - print("pluginName %s" % pluginName) + _logger.debug("pluginName %s", pluginName) plugin = pluginName if directory not in sys.path: sys.path.insert(0, directory) @@ -148,9 +148,8 @@ class PluginLoader(object): exceptionMessage += "%s\n" % sys.exc_info()[1] exceptionMessage += "%s\n" % sys.exc_info()[2] - if len(exceptionMessage): - if DEBUG: - raise IOError(exceptionMessage) + if len(exceptionMessage) and _logger.getEffectiveLevel() == logging.DEBUG: + raise IOError(exceptionMessage) if exceptions: return len(self.pluginList), exceptionMessage else: diff --git a/PyMca5/PyMcaGraph/backends/__init__.py b/PyMca5/PyMcaGraph/backends/__init__.py index e69de29..9205aa4 100644 --- a/PyMca5/PyMcaGraph/backends/__init__.py +++ b/PyMca5/PyMcaGraph/backends/__init__.py @@ -0,0 +1,9 @@ +import logging +import traceback +_logger = logging.getLogger(__name__) + +_logger.warning("%s is deprecated, you are advised to use " + "silx.gui.plot.backends instead", + __name__) +for line in traceback.format_stack(limit=4): + _logger.warning(line.rstrip()) diff --git a/PyMca5/PyMcaGui/PluginsToolButton.py b/PyMca5/PyMcaGui/PluginsToolButton.py index 8cf9066..77ddae4 100644 --- a/PyMca5/PyMcaGui/PluginsToolButton.py +++ b/PyMca5/PyMcaGui/PluginsToolButton.py @@ -74,21 +74,25 @@ class PluginsToolButton(qt.QToolButton, PluginLoader): :param parent: Parent QWidget widget """ - def __init__(self, plot, parent=None): + def __init__(self, plot, parent=None, method="getPlugin1DInstance"): qt.QToolButton.__init__(self, parent) self.setIcon(qt.QIcon(qt.QPixmap(IconDict["plugin"]))) - self.setToolTip("Call/Load 1D Plugins") + if method == "getPlugin1DInstance": + self.setToolTip("Call/Load 1D Plugins") + elif method == "getPlugin2DInstance": + self.setToolTip("Call/Load 2D Plugins") # fill attr pluginList and pluginInstanceDict with existing plugins - PluginLoader.__init__(self, method='getPlugin1DInstance') + PluginLoader.__init__(self, method=method) # plugins expect a legacy API, not the silx Plot API self.plot = weakref.proxy(plot, self._ooPlotDestroyed) + self._plotType = getattr(self.plot, "_plotType", None) self.clicked.connect(self._pluginClicked) - def _ooPlotDestroyed(self): + def _ooPlotDestroyed(self, obj=None): self.setEnabled(False) def __getattr__(self, attr): @@ -101,6 +105,30 @@ class PluginsToolButton(qt.QToolButton, PluginLoader): raise AttributeError( self.plot.__class__.__name__ + " has no attribute " + attr) + def _connectPlotSignals(self): + for name, plugin in self.pluginInstanceDict.items(): + if hasattr(plugin, "activeCurveChanged") and callable(plugin.activeCurveChanged): + # Can we just assume it has the proper signature? + self.plot.sigActiveCurveChanged.connect(plugin.activeCurveChanged) + if hasattr(plugin, "activeImageChanged") and callable(plugin.activeImageChanged): + # Can we just assume it has the proper signature? + self.plot.sigActiveImageChanged.connect(plugin.activeImageChanged) + + def _disconnectPlotSignals(self): + for name, plugin in self.pluginInstanceDict.items(): + if hasattr(plugin, "activeCurveChanged") and callable(plugin.activeCurveChanged): + # Can we just assume it has the proper signature? + self.plot.sigActiveCurveChanged.disconnect(plugin.activeCurveChanged) + if hasattr(plugin, "activeImageChanged") and callable(plugin.activeImageChanged): + # Can we just assume it has the proper signature? + self.plot.sigActiveImageChanged.disconnect(plugin.activeImageChanged) + + def getPlugins(self, method=None, directoryList=None, exceptions=False): + """method overloaded to update signal connections when loading plugins""" + self._disconnectPlotSignals() + PluginLoader.getPlugins(self, method, directoryList, exceptions) + self._connectPlotSignals() + def _pluginClicked(self): actionNames = [] menu = qt.QMenu(self) @@ -133,7 +161,7 @@ class PluginsToolButton(qt.QToolButton, PluginLoader): text = text[:-3] methods = pluginInstances[pluginName].getMethods( - plottype=self.plot._plotType) + plottype=self._plotType) if not len(methods): continue elif len(methods) == 1: @@ -178,10 +206,11 @@ class PluginsToolButton(qt.QToolButton, PluginLoader): return if "Toggle DEBUG mode" in a.text(): _toggleLogger() + return key = callableKeys[idx] methods = pluginInstances[key].getMethods( - plottype=self.plot._plotType) + plottype=self._plotType) if len(methods) == 1: idx = 0 else: diff --git a/PyMca5/PyMcaGui/PyMcaQt.py b/PyMca5/PyMcaGui/PyMcaQt.py index 3adbf8e..72d1db8 100644 --- a/PyMca5/PyMcaGui/PyMcaQt.py +++ b/PyMca5/PyMcaGui/PyMcaQt.py @@ -65,8 +65,13 @@ else: # Try the different bindings if sys.version_info < (3,): try: import sip - sip.setapi("QString", 2) - sip.setapi("QVariant", 2) + sip.setapi('QString', 2) + sip.setapi('QVariant', 2) + sip.setapi('QDate', 2) + sip.setapi('QDateTime', 2) + sip.setapi('QTextStream', 2) + sip.setapi('QTime', 2) + sip.setapi('QUrl', 2) except: print("Cannot set sip API") # Console widget not available import PyQt4 @@ -108,8 +113,13 @@ elif BINDING == "PyQt4": if sys.version_info < (3,): try: import sip - sip.setapi("QString", 2) - sip.setapi("QVariant", 2) + sip.setapi('QString', 2) + sip.setapi('QVariant', 2) + sip.setapi('QDate', 2) + sip.setapi('QDateTime', 2) + sip.setapi('QTextStream', 2) + sip.setapi('QTime', 2) + sip.setapi('QUrl', 2) except: print("Cannot set sip API") # Console widget not available from PyQt4.QtCore import * diff --git a/PyMca5/PyMcaGui/__init__.py b/PyMca5/PyMcaGui/__init__.py index 396fb96..121b4d7 100644 --- a/PyMca5/PyMcaGui/__init__.py +++ b/PyMca5/PyMcaGui/__init__.py @@ -42,6 +42,10 @@ def getPackages(directory): from .plotting import PyMca_Icons from .plotting.PyMca_Icons import IconDict +## legacy (not used within PyMca) +#import silx.gui.widgets.PrintPreview as PyMcaPrintPreview +#PyMcaPrintPreview.PyMcaPrintPreview = PyMcaPrintPreview.SingletonPrintPreviewDialog + # this is the package level directory PyMcaGui baseDirectory = os.path.dirname(__file__) __path__ += [baseDirectory] @@ -51,3 +55,4 @@ for directory in ["io", "math", "misc", if os.path.exists(os.path.join(tmpDir, "__init__.py")): __path__ += [tmpDir] __path__ += getPackages(tmpDir) + diff --git a/PyMca5/PyMcaGui/io/QEdfFileWidget.py b/PyMca5/PyMcaGui/io/QEdfFileWidget.py index 7b9819d..4a5d963 100644 --- a/PyMca5/PyMcaGui/io/QEdfFileWidget.py +++ b/PyMca5/PyMcaGui/io/QEdfFileWidget.py @@ -30,9 +30,18 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os.path import numpy +import logging from PyMca5.PyMcaGui import PyMcaQt as qt -from PyMca5.PyMcaGui import PlotWidget +from silx.gui.plot import PlotWidget +from silx.gui.plot.PrintPreviewToolButton import SingletonPrintPreviewToolButton +from silx.gui import icons as silx_icons + +if sys.version_info[0] == 3: + from io import BytesIO +else: + import cStringIO as _StringIO + BytesIO = _StringIO.StringIO if not hasattr(qt, 'QString'): QString = qt.safe_str @@ -46,19 +55,43 @@ from PyMca5.PyMcaGui import QPyMcaMatplotlibSave MATPLOTLIB = True from PyMca5.PyMcaGui import IconDict from PyMca5.PyMcaGui import ColormapDialog -from PyMca5.PyMcaGui import PyMcaPrintPreview from PyMca5.PyMcaIO import ArraySave from PyMca5 import PyMcaDirs from . import SpecFileDataInfo from PyMca5 import spslut + +_logger = logging.getLogger(__name__) + + COLORMAPLIST = [spslut.GREYSCALE, spslut.REVERSEGREY, spslut.TEMP, spslut.RED, spslut.GREEN, spslut.BLUE, spslut.MANY] -DEBUG = 0 SOURCE_TYPE = 'EdfFile' __revision__ = "$Revision: 1.35 $" + +def convertToRowAndColumn(x, y, shape, xScale=None, yScale=None, safe=True): + if xScale is None: + c = x + else: + c = (x - xScale[0]) / xScale[1] + if yScale is None: + r = y + else: + r = ( y - yScale[0]) / yScale[1] + + if safe: + c = min(int(c), shape[1] - 1) + c = max(c, 0) + r = min(int(r), shape[0] - 1) + r = max(r, 0) + else: + c = int(c) + r = int(r) + return r, c + + class EdfFile_StandardArray(qt.QWidget): sigWidgetSignal = qt.pyqtSignal(object) @@ -228,30 +261,28 @@ class QEdfFileWidget(qt.QWidget): self.lastInputDir = None self.colormapDialog = None self.colormap = None - self.printPreview = PyMcaPrintPreview.PyMcaPrintPreview(modal = 0) - if DEBUG: - print("printPreview id = %d" % id(self.printPreview)) #self.selectPixmap= qt.QPixmap(icons.selected) #self.unselectPixamp= qt.QPixmap(icons.unselected) self.mapComboName= {} - self.mainLayout= qt.QVBoxLayout(self) - self.toolBar = None - self._buildToolBar() - # --- splitter - self.splitter= qt.QSplitter(self) + self.splitter = qt.QSplitter(self) self.splitter.setOrientation(qt.Qt.Vertical) # --- graph - self.graph=PlotWidget.PlotWidget(self.splitter, backend=None) + self.graph = PlotWidget(self.splitter, backend=None) self.graph.setGraphTitle('') self.graph.setGraphXLabel('Columns') self.graph.setGraphYLabel('Rows') self.graph.sigPlotSignal.connect(self.widgetSignal) self._x1Limit = self.graph.getGraphXLimits()[-1] self._y1Limit = self.graph.getGraphYLimits()[-1] + + self.mainLayout = qt.QVBoxLayout(self) + self.toolBar = None + self._buildToolBar() + #self.graph.hide() # --- array parameter self.__dummyW = qt.QWidget(self.splitter) @@ -305,6 +336,7 @@ class QEdfFileWidget(qt.QWidget): self.zoomResetIcon = qt.QIcon(qt.QPixmap(IconDict["zoomreset"])) self.printIcon = qt.QIcon(qt.QPixmap(IconDict["fileprint"])) self.saveIcon = qt.QIcon(qt.QPixmap(IconDict["filesave"])) + self.copyIcon = silx_icons.getQIcon("edit-copy") try: self.infoIcon = qt.QApplication.style().\ standardIcon(qt.QStyle.SP_MessageBoxInformation) @@ -346,8 +378,8 @@ class QEdfFileWidget(qt.QWidget): #save if MATPLOTLIB: tb = self._addToolButton(self.saveIcon, - self.__saveIconSignal, - 'Export Graph') + self.__saveIconSignal, + 'Export Graph') self._saveMenu = qt.QMenu() self._saveMenu.addAction(QString("Standard"), self._saveIconSignal) self._saveMenu.addAction(QString("Matplotlib") , self._saveMatplotlibImage) @@ -356,6 +388,18 @@ class QEdfFileWidget(qt.QWidget): self._saveIconSignal, 'Export Graph') + self.copyToolButton = self._addToolButton(self.copyIcon, + self._copyIconSignal, + "Copy graph to clipboard") + + self.printPreview = SingletonPrintPreviewToolButton(parent=self, + plot=self.graph) + self.printPreview.setIcon(self.printIcon) + self.toolBarLayout.addWidget(self.printPreview) + + _logger.debug("printPreview id = %d", + id(self.printPreview.printPreviewDialog)) + #info self.infoText = qt.QLabel(self.toolBar) self.infoText.setText(" X = ???? Y = ???? Z = ????") @@ -363,22 +407,12 @@ class QEdfFileWidget(qt.QWidget): self.toolBarLayout.addWidget(qt.HorizontalSpacer(self.toolBar)) - # ---print - tb = self._addToolButton(self.printIcon, - self.printGraph, - 'Print the Graph') def _hFlipIconSignal(self): - if DEBUG: - print("_hFlipIconSignal called") - if self.graph.isYAxisInverted(): - self.graph.invertYAxis(False) - else: - self.graph.invertYAxis(True) - self.graph.replot() + _logger.debug("_hFlipIconSignal called") + self.graph.setYAxisInverted(not self.graph.isYAxisInverted()) def _aspectButtonSignal(self): - if DEBUG: - print("_aspectButtonSignal") + _logger.debug("_aspectButtonSignal") if self._keepDataAspectRatioFlag: self.keepDataAspectRatio(False) else: @@ -386,15 +420,13 @@ class QEdfFileWidget(qt.QWidget): def keepDataAspectRatio(self, flag=True): if flag: - self._keepDataAspectRatioFlag = True self.aspectButton.setIcon(self.solidEllipseIcon) self.aspectButton.setToolTip("Set free data aspect ratio") else: - self._keepDataAspectRatioFlag = False self.aspectButton.setIcon(self.solidCircleIcon) self.aspectButton.setToolTip("Keep data aspect ratio") - self.graph.keepDataAspectRatio(self._keepDataAspectRatioFlag) - + self._keepDataAspectRatioFlag = flag + self.graph.setKeepDataAspectRatio(flag) def _addToolButton(self, icon, action, tip, toggle=None): tb = qt.QToolButton(self.toolBar) @@ -435,10 +467,19 @@ class QEdfFileWidget(qt.QWidget): self._dataInfoClosed(ddict) def _zoomReset(self): - if DEBUG: - print("_zoomReset") + _logger.debug("_zoomReset") self.graph.resetZoom() + def _copyIconSignal(self): + pngFile = BytesIO() + self.graph.saveGraph(pngFile, fileFormat='png') + pngFile.flush() + pngFile.seek(0) + pngData = pngFile.read() + pngFile.close() + image = qt.QImage.fromData(pngData, 'png') + qt.QApplication.clipboard().setImage(image) + def _saveMatplotlibImage(self): if self._matplotlibSaveImage is None: if (self.currentArray is None) or \ @@ -553,22 +594,25 @@ class QEdfFileWidget(qt.QWidget): else: self.saveGraphWidget(outputFile) - def saveGraphImage(self, filename,original=True): - fformat = filename[-3:].upper() - #This is the whole image, not the zoomed one ... - rgbData, legend, info, pixmap = self.graph.getActiveImage() + def saveGraphImage(self, filename, original=False): + format_ = filename[-3:].upper() + activeImage = self.graph.getActiveImage() + rgbdata = activeImage.getRgbaImageData() + # silx to pymca scale convention (a + b x) + xScale = activeImage.getOrigin()[0], activeImage.getScale()[0] + yScale = activeImage.getOrigin()[1], activeImage.getScale()[1] if original: # save whole image - bgrData = numpy.array(rgbData, copy=True) - bgrData[:,:,0] = rgbData[:, :, 2] - bgrData[:,:,2] = rgbData[:, :, 0] + bgradata = numpy.array(rgbdata, copy=True) + bgradata[:, :, 0] = rgbdata[:, :, 2] + bgradata[:, :, 2] = rgbdata[:, :, 0] else: - shape = rgbData.shape[:2] + shape = rgbdata.shape[:2] xmin, xmax = self.graph.getGraphXLimits() ymin, ymax = self.graph.getGraphYLimits() # save zoomed image, for that we have to get the limits - r0, c0 = ymin, xmin - r1, c1 = ymax, xmax + r0, c0 = convertToRowAndColumn(xmin, ymin, shape, xScale=xScale, yScale=yScale, safe=True) + r1, c1 = convertToRowAndColumn(xmax, ymax, shape, xScale=xScale, yScale=yScale, safe=True) row0 = int(min(r0, r1)) row1 = int(max(r0, r1)) col0 = int(min(c0, c1)) @@ -577,26 +621,27 @@ class QEdfFileWidget(qt.QWidget): row1 += 1 if col1 < shape[1]: col1 += 1 - tmpArray = rgbData[row0:row1, col0:col1, :] - bgrData = numpy.array(tmpArray, copy=True, dtype=rgbData.dtype) - bgrData[:,:,0] = tmpArray[:, :, 2] - bgrData[:,:,2] = tmpArray[:, :, 0] + tmpArray = rgbdata[row0:row1, col0:col1, :] + bgradata = numpy.array(tmpArray, copy=True, dtype=rgbdata.dtype) + bgradata[:, :, 0] = tmpArray[:, :, 2] + bgradata[:, :, 2] = tmpArray[:, :, 0] if self.graph.isYAxisInverted(): - qImage = qt.QImage(bgrData, bgrData.shape[1], bgrData.shape[0], - qt.QImage.Format_RGB32) + qImage = qt.QImage(bgradata, bgradata.shape[1], bgradata.shape[0], + qt.QImage.Format_ARGB32) else: - qImage = qt.QImage(bgrData, bgrData.shape[1], bgrData.shape[0], - qt.QImage.Format_RGB32).mirrored(False, True) + qImage = qt.QImage(bgradata, bgradata.shape[1], bgradata.shape[0], + qt.QImage.Format_ARGB32).mirrored(False, True) pixmap = qt.QPixmap.fromImage(qImage) - if pixmap.save(filename, fformat): + if pixmap.save(filename, format_): return else: - qt.QMessageBox.critical(self, "Save Error", "%s" % sys.exc_info()[1]) + qt.QMessageBox.critical(self, "Save Error", + "%s" % sys.exc_info()[1]) return def saveGraphWidget(self, filename): fformat = filename[-3:].upper() - if hasattr(qt.QPixmap, "graphWidget"): + if hasattr(qt.QPixmap, "grabWidget"): # Qt4 pixmap = qt.QPixmap.grabWidget(self.graph) else: @@ -615,18 +660,6 @@ class QEdfFileWidget(qt.QWidget): else: return False - def printGraph(self): - if hasattr(qt.QPixmap, "graphWidget"): - # Qt4 - pixmap = qt.QPixmap.grabWidget(self.graph) - else: - #Qt5 - pixmap = self.graph.grab() - self.printPreview.addPixmap(pixmap) - if self.printPreview.isHidden(): - self.printPreview.show() - self.printPreview.raise_() - def _buildActions(self): self.buttonBox = qt.QWidget(self) buttonBox = self.buttonBox @@ -794,17 +827,14 @@ class QEdfFileWidget(qt.QWidget): else: self.removeSelection([nsel]) elif dict['event'] == 'imageChanged': - if DEBUG: - print("Image changed") + _logger.debug("Image changed") if dict['index'] != self.currentArray: self.currentArray = dict['index'] self.refresh() - if DEBUG: - print("self.currentArray = ",self.currentArray) + _logger.debug("self.currentArray = %s", self.currentArray) def openFile(self, filename=None,justloaded=None): - if DEBUG: - print("openfile = %s" % filename) + _logger.debug("openfile = %s", filename) if justloaded is None:justloaded = 0 if filename is None: self.lastInputDir = PyMcaDirs.inputDir @@ -1023,10 +1053,9 @@ class QEdfFileWidget(qt.QWidget): var[3], var[4], var[5]] - #self.graph.invertYAxis(True) + #self.graph.setYAxisInverted(True) pixmap = self.getPixmapFromData(self.lastData, self.colormap) self.graph.addImage(pixmap, legend="QEdfFileWidget") - self.graph.replot() def closeFile(self, filename=None): if filename is None: @@ -1051,8 +1080,7 @@ class QEdfFileWidget(qt.QWidget): try: self.sigDelSelection.emit((self.data.SourceName, mcakeys)) except: - if DEBUG: - print("sigDelSelection is to be implemented") + _logger.debug("sigDelSelection is to be implemented") for idx in range(self.fileCombo.count()): itext = self.fileCombo.itemText(idx) @@ -1085,22 +1113,18 @@ class QEdfFileWidget(qt.QWidget): self.graph.removeImage(legend="QEdfFileWidget") self.oldsource = None self.graph.clearMarkers() - self.graph.replot() wid = self.__getParamWidget('array') wid.setImages(1) - wid.setDataSize(0,0) - + wid.setDataSize(0, 0) def setDataSource(self,data=None): - if DEBUG: - print("setData(self, data) called") - print("data = ",data) + _logger.debug("setData(self, data) called") + _logger.debug("data = %s", data) self.data= data self.refresh() def refresh(self): - if DEBUG: - print("refresh method called") + _logger.debug("refresh method called") if self.data is None: self._reset() #wid = self.__getParamWidget('array') @@ -1110,10 +1134,9 @@ class QEdfFileWidget(qt.QWidget): return self.currentFile = self.data.sourceName #this gives the number of images in the file - infoSource= self.data.getSourceInfo() - if DEBUG: - print("info :") - print(infoSource) + infoSource = self.data.getSourceInfo() + _logger.debug("info :") + _logger.debug("%s", infoSource) nimages=len(infoSource['KeyList']) #print self.data.SourceName,"nimages = ",nimages @@ -1127,19 +1150,16 @@ class QEdfFileWidget(qt.QWidget): #print "SUM = ",loadsum, infoSource['KeyList'] #print self.currentArray if (self.oldsource != self.currentFile) or (self.oldcurrentArray != self.currentArray): - if DEBUG: - print("I have to read again ... ") + _logger.debug("I have to read again ... ") if not loadsum: - if DEBUG: - print("Not Loading the sum") + _logger.debug("Not Loading the sum") dataObject = self.data.getDataObject(infoSource['KeyList']\ [self.currentArray]) info = dataObject.info data = dataObject.data imageinfo = infoSource['KeyList'] else: - if DEBUG: - print("Loading the sum") + _logger.debug("Loading the sum") dataObject = self.data.getDataObject('0.0') info = dataObject.info data = dataObject.data @@ -1157,9 +1177,9 @@ class QEdfFileWidget(qt.QWidget): if 'Title' in header: imageinfo[i] += "- " + header['Title'] i+=1 - if DEBUG: - print("NOT ADDING 0.0 - SUM KEY") - wid.setImages(nimages+1,info = imageinfo+["0.0 - SUM"]) + _logger.debug("NOT ADDING 0.0 - SUM KEY") + if _logger.getEffectiveLevel() == logging.DEBUG: + wid.setImages(nimages+1, info = imageinfo+["0.0 - SUM"]) wid.setImages(nimages,info = imageinfo) else: if 'Title' in info: @@ -1168,10 +1188,9 @@ class QEdfFileWidget(qt.QWidget): wid.setCurrentImage(self.currentArray) #P.B. -> pointer(a,d1,d2,i1,i2) = a+ (i1+i2 * d1) wid.setDataSize(int(info["Dim_2"]), int(info["Dim_1"])) - if DEBUG: - print("Image size = %d x %d" % (int(info["Dim_2"]), - int(info["Dim_1"]))) - print("data size = ", data.shape) + _logger.debug("Image size = %d x %d", + int(info["Dim_2"]), int(info["Dim_1"])) + _logger.debug("data size = %s", data.shape) if self.graph.isHidden(): self.graph.show() @@ -1205,7 +1224,6 @@ class QEdfFileWidget(qt.QWidget): pixmap = self.getPixmapFromData(data, self.colormap) self.graph.addImage(pixmap, legend="QEdfFileWidget") self.__refreshSelection() - self.graph.replot() self.oldsource = "%s" % self.data.sourceName self.oldcurrentArray = self.currentArray * 1 @@ -1213,13 +1231,11 @@ class QEdfFileWidget(qt.QWidget): return self.paramWidget def _replaceClicked(self): - if DEBUG: - print("replace clicked") + _logger.debug("replace clicked") selkeys= self.__getSelectedKeys() if len(selkeys): #self.eh.event(self.repEvent, selkeys) - if DEBUG: - print("Replace event") + _logger.debug("Replace event") if self.allImages: arraynamelist = self.data.getSourceInfo()['KeyList'] else: @@ -1268,8 +1284,7 @@ class QEdfFileWidget(qt.QWidget): self.sigReplaceSelection.emit(signalsellist) def _add2DClicked(self, replace=False, emit=True): - if DEBUG: - print("ADD 2D clicked") + _logger.debug("ADD 2D clicked") if (self.data is None) or \ (self.currentArray is None): return @@ -1299,8 +1314,7 @@ class QEdfFileWidget(qt.QWidget): return [sel] def _remove2DClicked(self): - if DEBUG: - print("REMOVE 2D clicked") + _logger.debug("REMOVE 2D clicked") infoSource= self.data.getSourceInfo() sel = {} sel['SourceType'] = infoSource['SourceType'] @@ -1318,8 +1332,7 @@ class QEdfFileWidget(qt.QWidget): self.sigRemoveSelection.emit([sel]) def _replace2DClicked(self): - if DEBUG: - print("REPLACE 2D clicked") + _logger.debug("REPLACE 2D clicked") self._add2DClicked(replace=True) def currentSelectionList(self): @@ -1329,15 +1342,12 @@ class QEdfFileWidget(qt.QWidget): return a def _addClicked(self, emit=True): - if DEBUG: - print("select clicked") + _logger.debug("select clicked") selkeys= self.__getSelectedKeys() - if DEBUG: - print("selected keys = ",selkeys) + _logger.debug("selected keys = %s", selkeys) if len(selkeys): #self.eh.event(self.addEvent, selkeys) - if DEBUG: - print("Select event") + _logger.debug("Select event") if self.allImages: arraynamelist = self.data.getSourceInfo()['KeyList'] else: @@ -1416,16 +1426,14 @@ class QEdfFileWidget(qt.QWidget): return selkeys def _removeClicked(self): - if DEBUG: - print("remove clicked") + _logger.debug("remove clicked") selkeys= self.__getSelectedKeys() returnedselection=[] signalsellist = [] if len(selkeys): #self.eh.event(self.delEvent, selkeys) - if DEBUG: - print("Remove Event") - print("self.selection before = ",self.selection) + _logger.debug("Remove Event") + _logger.debug("self.selection before = %s", self.selection) if self.allImages: arraynamelist = self.data.getSourceInfo()['KeyList'] else: @@ -1453,17 +1461,13 @@ class QEdfFileWidget(qt.QWidget): if selection['plot'] == 'rows': sel[arrayname]['rows'].append({'x':selection['x'],'y':selection['y']}) if self.selection is not None: - if DEBUG: - print("step 1") + _logger.debug("step 1") if sel['SourceName'] in self.selection: - if DEBUG: - print("step 2") + _logger.debug("step 2") if arrayname in self.selection[sel['SourceName']]: - if DEBUG: - print("step 3") + _logger.debug("step 3") if 'rows' in self.selection[sel['SourceName']][arrayname]: - if DEBUG: - print("step 4") + _logger.debug("step 4") for couple in sel[arrayname]['rows']: if couple in self.selection[sel['SourceName']][arrayname]['rows']: index= self.selection[sel['SourceName']][arrayname]['rows'].index(couple) @@ -1512,17 +1516,13 @@ class QEdfFileWidget(qt.QWidget): for sel in selection: arrayname = sel['Key'] if self.selection is not None: - if DEBUG: - print("step 1") + _logger.debug("step 1") if sel['SourceName'] in self.selection: - if DEBUG: - print("step 2") + _logger.debug("step 2") if arrayname in self.selection[sel['SourceName']]: - if DEBUG: - print("step 3") + _logger.debug("step 3") if 'rows' in self.selection[sel['SourceName']][arrayname]: - if DEBUG: - print("step 4") + _logger.debug("step 4") for couple in sel[arrayname]['rows']: if couple in self.selection[sel['SourceName']][arrayname]['rows']: index= self.selection[sel['SourceName']][arrayname]['rows'].index(couple) @@ -1564,11 +1564,10 @@ class QEdfFileWidget(qt.QWidget): self.sigRemoveSelection.emit(signalsellist) def setSelected(self,sellist,reset=1): - if DEBUG: - print("setSelected(self,sellist,reset=1) called") - print("sellist = ",sellist) - print("selection before = ",self.selection) - print("reset = ",reset) + _logger.debug("setSelected(self,sellist,reset=1) called") + _logger.debug("sellist = %s", sellist) + _logger.debug("selection before = %s", self.selection) + _logger.debug("reset = %s", reset) if reset: self.selection = {} elif self.selection is None: @@ -1598,8 +1597,7 @@ class QEdfFileWidget(qt.QWidget): for rowsel in sel[selkey]['cols']: if rowsel not in self.selection[specname][selkey]['cols']: self.selection[specname][selkey]['cols'].append(rowsel) - if DEBUG: - print("self.selection after = ",self.selection) + _logger.debug("self.selection after = %s", self.selection) self.__refreshSelection() def getSelection(self): @@ -1621,10 +1619,9 @@ class QEdfFileWidget(qt.QWidget): def __refreshSelection(self): - if DEBUG: - print("__refreshSelection(self) called") - print(self.selection) - print("self.data.SourceName = ",self.data.sourceName) + _logger.debug("__refreshSelection(self) called") + _logger.debug(self.selection) + _logger.debug("self.data.SourceName = %s", self.data.sourceName) if self.selection is not None: if self.data is None: return @@ -1650,9 +1647,9 @@ class QEdfFileWidget(qt.QWidget): for key in sel.keys(): if (sel[key]['rows'] != []) or (sel[key]['cols'] != []): selkeys.append(key) - if DEBUG: - print("selected images =",selkeys,"but self.selection = ",self.selection) - print("and self.selection.get(self.data.SourceName, {}) =",sel) + _logger.debug("selected images = %s but self.selection = %s", + selkeys, self.selection) + _logger.debug("and self.selection.get(self.data.SourceName, {}) = %s ", sel) wid = self.__getParamWidget("array") wid.markImageSelected(selkeys) @@ -1681,17 +1678,16 @@ class QEdfFileWidget(qt.QWidget): self.graph.clearMarkers() for i in rows: label = "R%d" % i - marker=self.graph.insertYMarker(i, - label, - text=label, - color='white') + marker = self.graph.addYMarker(i, + label, + text=label, + color='white') for i in cols: label = "C%d" % i - marker=self.graph.insertXMarker(i, - label, - text=label, - color='white') - self.graph.replot() + marker = self.graph.addXMarker(i, + label, + text=label, + color='white') return def closeEvent(self, event): @@ -1720,9 +1716,12 @@ def test(): def addSelection(sel): print("addSelection", sel) - a= qt.QApplication(sys.argv) - a.lastWindowClosed.connect(a.quit) - + if qt.QApplication.instance() is None: + a = qt.QApplication(sys.argv) + a.lastWindowClosed.connect(a.quit) + sys.excepthook = qt.exceptionHandler + else: + a = None w = QEdfFileWidget() #print w if len(sys.argv) > 1: @@ -1738,8 +1737,11 @@ def test(): w.sigRemoveSelection.connect(removeSelection) w.sigReplaceSelection.connect(replaceSelection) w.show() - a.exec_() + if a is not None: + a.exec_() + else: + return w -if __name__=="__main__": +if __name__ == "__main__": test() diff --git a/PyMca5/PyMcaGui/io/QSelectorWidget.py b/PyMca5/PyMcaGui/io/QSelectorWidget.py index 148d6f9..5d7b6fe 100644 --- a/PyMca5/PyMcaGui/io/QSelectorWidget.py +++ b/PyMca5/PyMcaGui/io/QSelectorWidget.py @@ -27,11 +27,13 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import sys + +import logging from PyMca5.PyMcaGui import PyMcaQt as qt QTVERSION = qt.qVersion() -DEBUG = 0 +_logger = logging.getLogger(__name__) + class QSelectorWidget(qt.QWidget): def __init__(self, parent=None): @@ -44,8 +46,7 @@ class QSelectorWidget(qt.QWidget): """ Method to be overwritten to build the main widget """ - if DEBUG: - print("_build():Method to be overwritten") + _logger.debug("_build():Method to be overwritten") pass def _buildActions(self): @@ -74,16 +75,13 @@ class QSelectorWidget(qt.QWidget): self._addClicked() def _addClicked(self): - if DEBUG: - print("_addClicked()") + _logger.debug("_addClicked()") def _removeClicked(self): - if DEBUG: - print("_removeClicked()") + _logger.debug("_removeClicked()") def _replaceClicked(self): - if DEBUG: print( - "_replaceClicked()") + _logger.debug("_replaceClicked()") def test(): diff --git a/PyMca5/PyMcaGui/io/QSourceSelector.py b/PyMca5/PyMcaGui/io/QSourceSelector.py index 8d16946..e7be450 100644 --- a/PyMca5/PyMcaGui/io/QSourceSelector.py +++ b/PyMca5/PyMcaGui/io/QSourceSelector.py @@ -29,6 +29,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os +import logging from PyMca5.PyMcaGui import PyMcaQt as qt QTVERSION = qt.qVersion() from PyMca5.PyMcaGui import PyMca_Icons as icons @@ -36,7 +37,7 @@ from PyMca5.PyMcaIO import spswrap as sps from PyMca5 import PyMcaDirs from PyMca5.PyMcaGui.io import PyMcaFileDialogs -DEBUG = 0 +_logger = logging.getLogger(__name__) class QSourceSelector(qt.QWidget): sigSourceSelectorSignal = qt.pyqtSignal(object) @@ -116,8 +117,7 @@ class QSourceSelector(qt.QWidget): self.mainLayout.addWidget(self.fileWidget) def _reload(self): - if DEBUG: - print("_reload called") + _logger.debug("_reload called") qstring = self.fileCombo.currentText() if not len(qstring): return @@ -141,8 +141,7 @@ class QSourceSelector(qt.QWidget): self.openFile(sourcename, specsession=specsession) def openFile(self, filename=None, justloaded=None, specsession = False): - if DEBUG: - print("openfile = ",filename) + _logger.debug("openfile = %s", filename) staticDialog = False if not specsession: if justloaded is None: @@ -218,8 +217,7 @@ class QSourceSelector(qt.QWidget): self.sigSourceSelectorSignal.emit(ddict) def closeFile(self): - if DEBUG: - print("closeFile called") + _logger.debug("closeFile called") #get current combobox key qstring = self.fileCombo.currentText() if not len(qstring): @@ -244,9 +242,7 @@ class QSourceSelector(qt.QWidget): "No SPEC Shared Memory Found", "No shared memory source available") return - if QTVERSION < '4.0.0': - print("should I keep Qt3 version?") - return + menu = qt.QMenu() for spec in speclist: if hasattr(qt, "QString"): @@ -258,8 +254,7 @@ class QSourceSelector(qt.QWidget): menu.exec_(self.cursor().pos()) def _fileSelection(self, qstring): - if DEBUG: - print("file selected ", qstring) + _logger.debug("file selected %s", qstring) key = str(qstring) ddict = {} ddict["event"] = "SourceSelected" diff --git a/PyMca5/PyMcaGui/io/QSpecFileWidget.py b/PyMca5/PyMcaGui/io/QSpecFileWidget.py index 4f868c9..3aacfc3 100644 --- a/PyMca5/PyMcaGui/io/QSpecFileWidget.py +++ b/PyMca5/PyMcaGui/io/QSpecFileWidget.py @@ -29,6 +29,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui.io import QSelectorWidget from PyMca5.PyMcaGui.io import SpecFileDataInfo @@ -36,9 +37,10 @@ from PyMca5.PyMcaGui.io import SpecFileCntTable OBJECT3D = SpecFileCntTable.OBJECT3D from PyMca5.PyMcaGui.io import SpecFileMcaTable +_logger = logging.getLogger(__name__) + QTVERSION = qt.qVersion() -DEBUG = 0 if QTVERSION > '4.2.0': class MyQTreeWidgetItem(qt.QTreeWidgetItem): def __lt__(self, other): @@ -264,9 +266,8 @@ class QSpecFileWidget(QSelectorWidget.QSelectorWidget): # #NEW data management def setDataSource(self, datasource): - if DEBUG: - print("setDataSource(self, datasource) called") - print("datasource = ", datasource) + _logger.debug("setDataSource(self, datasource) called") + _logger.debug("datasource = %s", datasource) self.data = datasource self.refresh() @@ -282,9 +283,8 @@ class QSpecFileWidget(QSelectorWidget.QSelectorWidget): #OLD data management def setData(self, specfiledata): - if DEBUG: - print("setData(self, specfiledata) called") - print("specfiledata = ",specfiledata) + _logger.debug("setData(self, specfiledata) called") + _logger.debug("specfiledata = %s", specfiledata) self.data= specfiledata self.refresh() @@ -335,8 +335,7 @@ class QSpecFileWidget(QSelectorWidget.QSelectorWidget): item.setText(0, "") def _autoReplace(self, scanlist=None): - if DEBUG: - print("autoreplace called with ",scanlist) + _logger.debug("autoreplace called with %s", scanlist) if self.autoReplaceBox.isChecked(): self._replaceClicked() elif self.autoAddBox.isChecked(): @@ -353,12 +352,10 @@ class QSpecFileWidget(QSelectorWidget.QSelectorWidget): def __selectionChanged(self): - if DEBUG: - print("__selectionChanged") + _logger.debug("__selectionChanged") itemlist = self.list.selectedItems() sel = [str(item.text(1)) for item in itemlist] - if DEBUG: - print("selection = ",sel) + _logger.debug("selection = %s", sel) if not len(sel): return info = self.data.getKeyInfo(sel[0]) @@ -412,10 +409,8 @@ class QSpecFileWidget(QSelectorWidget.QSelectorWidget): self.list.sortItems(index, qt.Qt.AscendingOrder) #print "index = ", index - def __doubleClicked(self, item): - if DEBUG: - print("__doubleClicked") + _logger.debug("__doubleClicked") if item is not None: sn = str(item.text(1)) ddict={} @@ -429,8 +424,7 @@ class QSpecFileWidget(QSelectorWidget.QSelectorWidget): self._addClicked() def __contextMenu(self, point): - if DEBUG: - print("__contextMenu",point) + _logger.debug("__contextMenu %s", point) item = self.list.itemAt(point) if item is not None: sn= str(item.text(1)) @@ -474,8 +468,7 @@ class QSpecFileWidget(QSelectorWidget.QSelectorWidget): if idx is None: if QTVERSION > '4.0.0': idx = self.menu_idx - if DEBUG: - print("Scan information:") + _logger.debug("Scan information:") try: info = self.data.getDataObject(self.scans[idx]).info @@ -485,7 +478,7 @@ class QSpecFileWidget(QSelectorWidget.QSelectorWidget): text = "Error: %s\n accessing scan information." % (sys.exc_info()[1]) msg.setText(text) msg.exec_() - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise return @@ -508,8 +501,7 @@ class QSpecFileWidget(QSelectorWidget.QSelectorWidget): self._dataInfoClosed(ddict) def _addClicked(self, emit=True): - if DEBUG: - print("Overwritten _addClicked method") + _logger.debug("Overwritten _addClicked method") #get selected scan keys if QTVERSION < '4.0.0': @@ -574,10 +566,8 @@ class QSpecFileWidget(QSelectorWidget.QSelectorWidget): def currentSelectionList(self): return self._addClicked(emit=False) - def _removeClicked(self): - if DEBUG: - print("Overwritten _removeClicked method") + _logger.debug("Overwritten _removeClicked method") #get selected scan keys itemlist = self.list.selectedItems() @@ -625,8 +615,7 @@ class QSpecFileWidget(QSelectorWidget.QSelectorWidget): self.sigRemoveSelection.emit(sel_list) def _replaceClicked(self): - if DEBUG: - print("Overwritten _replaceClicked method") + _logger.debug("Overwritten _replaceClicked method") #get selected scan keys itemlist = self.list.selectedItems() scan_sel = [str(item.text(1)) for item in itemlist] @@ -682,8 +671,7 @@ class QSpecFileWidget(QSelectorWidget.QSelectorWidget): self.sigReplaceSelection.emit(sel_list) def _tabChanged(self, value): - if DEBUG: - print("self._tabChanged(value), value = ",value) + _logger.debug("self._tabChanged(value), value = %s", value) text = str(self.mainTab.tabText(value)) if self.data is None: return diff --git a/PyMca5/PyMcaGui/io/QSpsWidget.py b/PyMca5/PyMcaGui/io/QSpsWidget.py index 1ef3733..5c667b1 100644 --- a/PyMca5/PyMcaGui/io/QSpsWidget.py +++ b/PyMca5/PyMcaGui/io/QSpsWidget.py @@ -27,14 +27,16 @@ __author__ = "E. Papillon, V.A. Sole - ESRF Software Group" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import sys +import logging from PyMca5.PyMcaIO import spswrap as sps from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui.io import SpecFileCntTable from PyMca5.PyMcaGui import MaskImageWidget QTVERSION = qt.qVersion() from PyMca5.PyMcaGui import PyMca_Icons as icons -DEBUG = 0 + +_logger = logging.getLogger(__name__) + SOURCE_TYPE = 'SPS' SCAN_MODE = True @@ -111,8 +113,7 @@ class SPSFramesMcaWidget(qt.QWidget): class SPSScanArrayWidget(SpecFileCntTable.SpecFileCntTable): def setInfo(self, info): - if DEBUG: - print("info = ", info) + _logger.debug("info = %s", info) if "LabelNames" in info: # new style cntList = info.get("LabelNames", []) @@ -124,10 +125,8 @@ class SPSScanArrayWidget(SpecFileCntTable.SpecFileCntTable): #We have environment information if "datafile" in info["envdict"]: if info["envdict"]["datafile"] != "/dev/null": - if DEBUG: - print("I should send a signal, either from here or from the parent to the dispatcher") - if DEBUG: - print("SPEC data file = %s" % datafile) + _logger.debug("I should send a signal, either from here or from the parent to the dispatcher") + _logger.debug("SPEC data file = %s", info["envdict"]["datafile"]) #usefull keys = ["datafile", "scantype", "axistitles","plotlist", "xlabel", "ylabel"] # #info = self.data.getKeyInfo(sel[0]) @@ -244,8 +243,7 @@ class SPSXiaArrayWidget(qt.QWidget): layout.addWidget(self.title, 0, 0) layout.setAlignment(self.title, qt.Qt.AlignCenter) ##layout.addRowSpacing(0, 40) - if DEBUG: - print("row spacing") + _logger.debug("row spacing") layout.addWidget(self.detList, 1, 0) def setTitle(self, title): @@ -554,17 +552,15 @@ class QSpsWidget(qt.QWidget): mainLayout.addWidget(butWidget) def setData(self,data=None): - if DEBUG: - print("setData(self, data) called") - print("spec data = ",data) + _logger.debug("setData(self, data) called") + _logger.debug("spec data = %s", data) self.data= data self.refreshSpecList() self.refreshDataSelection() def setDataSource(self,data=None): - if DEBUG: - print("setDataSource(self, data) called") - print("spec data = ",data) + _logger.debug("setDataSource(self, data) called") + _logger.debug("spec data = %s", data) self.data= data self.refreshSpecList() self.refreshDataSelection() @@ -639,10 +635,9 @@ class QSpsWidget(qt.QWidget): cols= info[1] type= info[2] flag= info[3] - if DEBUG: - print(" array = ", array) - print(" flag = ", flag) - print(" type = ", type) + _logger.debug(" array = %s", array) + _logger.debug(" flag = %s", flag) + _logger.debug(" type = %s", type) if type!=sps.STRING: if (flag & sps.TAG_ARRAY) == sps.TAG_ARRAY: arraylist[array]= (rows, cols) @@ -736,13 +731,11 @@ class QSpsWidget(qt.QWidget): return wid def __replaceClicked(self): - if DEBUG: - print("replace clicked") + _logger.debug("replace clicked") selkeys= self.__getSelectedKeys() if len(selkeys): #self.eh.event(self.repEvent, selkeys) - if DEBUG: - print("Replace event") + _logger.debug("Replace event") sel = {} sel['SourceType'] = SOURCE_TYPE sellistsignal = [] @@ -841,15 +834,12 @@ class QSpsWidget(qt.QWidget): return self._addClicked() def _addClicked(self, emit=True): - if DEBUG: - print("select clicked") + _logger.debug("select clicked") selkeys= self.__getSelectedKeys() - if DEBUG: - print("selected keys = ",selkeys ) + _logger.debug("selected keys = %s", selkeys ) if len(selkeys): #self.eh.event(self.addEvent, selkeys) - if DEBUG: - print("Select event") + _logger.debug("Select event") sel = {} sel['SourceType'] = SOURCE_TYPE sellistsignal = [] @@ -962,14 +952,12 @@ class QSpsWidget(qt.QWidget): return selkeys def __removeClicked(self): - if DEBUG: - print("remove clicked") + _logger.debug("remove clicked") selkeys= self.__getSelectedKeys() if len(selkeys): #self.eh.event(self.delEvent, selkeys) - if DEBUG: - print("Remove Event") - print("self.selection before = ",self.selection) + _logger.debug("Remove Event") + _logger.debug("self.selection before = %s", self.selection) returnedselection=[] sellistsignal = [] for selection in selkeys: @@ -1043,17 +1031,13 @@ class QSpsWidget(qt.QWidget): sellistsignal.append(selsignal) returnedselection.append(sel) if self.selection is not None: - if DEBUG: - print("step 1") + _logger.debug("step 1") if sel['SourceName'] in self.selection: - if DEBUG: - print("step 2") + _logger.debug("step 2") if arrayname in self.selection[sel['SourceName']]: - if DEBUG: - print("step 3") + _logger.debug("step 3") if 'rows' in self.selection[sel['SourceName']][arrayname]: - if DEBUG: - print("step 4") + _logger.debug("step 4") for couple in sel[arrayname]['rows']: if couple in self.selection[sel['SourceName']][arrayname]['rows']: index= self.selection[sel['SourceName']][arrayname]['rows'].index(couple) @@ -1077,17 +1061,13 @@ class QSpsWidget(qt.QWidget): for sel in selection: arrayname = sel['Key'] if self.selection is not None: - if DEBUG: - print("step 1") + _logger.debug("step 1") if sel['SourceName'] in self.selection: - if DEBUG: - print("step 2") + _logger.debug("step 2") if arrayname in self.selection[sel['SourceName']]: - if DEBUG: - print("step 3") + _logger.debug("step 3") if 'rows' in self.selection[sel['SourceName']][arrayname]: - if DEBUG: - print("step 4") + _logger.debug("step 4") for couple in sel[arrayname]['rows']: if couple in self.selection[sel['SourceName']][arrayname]['rows']: index= self.selection[sel['SourceName']][arrayname]['rows'].index(couple) @@ -1105,11 +1085,10 @@ class QSpsWidget(qt.QWidget): self.sigRemoveSelection.emit((selection)) def setSelected(self,sellist,reset=1): - if DEBUG: - print("setSelected(self,sellist,reset=1) called") - print("sellist = ",sellist) - print("selection before = ",self.selection) - print("reset = ",reset) + _logger.debug("setSelected(self,sellist,reset=1) called") + _logger.debug("sellist = %s", sellist) + _logger.debug("selection before = %s", self.selection) + _logger.debug("reset = %s", reset) if reset: self.selection = {} elif self.selection is None: @@ -1133,8 +1112,7 @@ class QSpsWidget(qt.QWidget): for rowsel in sel[selkey]['cols']: if rowsel not in self.selection[specname][selkey]['cols']: self.selection[specname][selkey]['cols'].append(rowsel) - if DEBUG: - print("self.selection after = ",self.selection) + _logger.debug("self.selection after = %s", self.selection) self.__refreshSelection() def getSelection(self): @@ -1157,19 +1135,17 @@ class QSpsWidget(qt.QWidget): def __refreshSelection(self): return - if DEBUG: - print("__refreshSelection(self) called") - print(self.selection) + _logger.debug("__refreshSelection(self) called") + _logger.debug(selection) if self.selection is not None: sel = self.selection.get(self.data.SourceName, {}) selkeys = [] for key in sel.keys(): if (sel[key]['mca'] != []) or (sel[key]['scan']['Ycnt'] != []): selkeys.append(key) - if DEBUG: - print("selected scans =",selkeys) - print("but self.selection = ",self.selection) - print("and self.selection.get(self.data.SourceName, {}) =",sel) + _logger.debug("selected scans = %s", selkeys) + _logger.debug("but self.selection = %s", self.selection) + _logger.debug("and self.selection.get(self.data.SourceName, {}) = %s", sel) self.scanList.markScanSelected(selkeys) scandict = sel.get(self.currentScan, {}) if 'mca' in scandict: diff --git a/PyMca5/PyMcaGui/io/SpecFileCntTable.py b/PyMca5/PyMcaGui/io/SpecFileCntTable.py index ecc675c..84cb0e9 100644 --- a/PyMca5/PyMcaGui/io/SpecFileCntTable.py +++ b/PyMca5/PyMcaGui/io/SpecFileCntTable.py @@ -27,10 +27,9 @@ __author__ = "E. Papillon, V.A. Sole - ESRF Software Group" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import sys + from PyMca5.PyMcaGui import PyMcaQt as qt QTVERSION = qt.qVersion() -DEBUG = 0 try: import PyMca5.Object3D diff --git a/PyMca5/PyMcaGui/io/SpecFileMcaTable.py b/PyMca5/PyMcaGui/io/SpecFileMcaTable.py index 63f8221..f31b148 100644 --- a/PyMca5/PyMcaGui/io/SpecFileMcaTable.py +++ b/PyMca5/PyMcaGui/io/SpecFileMcaTable.py @@ -27,12 +27,13 @@ __author__ = "E. Papillon, V.A. Sole - ESRF Software Group" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import sys +import logging from PyMca5.PyMcaGui import PyMcaQt as qt QTVERSION = qt.qVersion() -DEBUG = 0 +_logger = logging.getLogger(__name__) + class SpecFileMcaTable(qt.QWidget): sigMcaDeviceSelected = qt.pyqtSignal(object) @@ -131,16 +132,14 @@ class SpecFileMcaTable(qt.QWidget): item.setText("X") def _cellClicked(self, row, col): - if DEBUG: - print("_cellClicked %d %d " % (row, col)) + _logger.debug("_cellClicked %d %d ", row, col) item = self.table.item(row, col) if item is None: item = qt.QTableWidgetItem('',qt.QTableWidgetItem.Type) self.table.setItem(row, col, item) def _cellDoubleClicked(self, row, col): - if DEBUG: - print("_cellDoubleClicked %d %d" % (row, col)) + _logger.debug("_cellDoubleClicked %d %d", (row, col)) #self._toggleCell(row, col) pass diff --git a/PyMca5/PyMcaGui/io/hdf5/HDF5CounterTable.py b/PyMca5/PyMcaGui/io/hdf5/HDF5CounterTable.py index af1cddd..9ea31b8 100644 --- a/PyMca5/PyMcaGui/io/hdf5/HDF5CounterTable.py +++ b/PyMca5/PyMcaGui/io/hdf5/HDF5CounterTable.py @@ -28,10 +28,12 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import posixpath +import logging from PyMca5.PyMcaGui import PyMcaQt as qt safe_str = qt.safe_str -DEBUG = 0 +_logger = logging.getLogger(__name__) + class HDF5CounterTable(qt.QTableWidget): @@ -256,8 +258,7 @@ class HDF5CounterTable(qt.QTableWidget): return ddict def setCounterSelection(self, ddict): - if DEBUG: - print("HDF5CounterTable.setCounterSelection", ddict) + _logger.debug("HDF5CounterTable.setCounterSelection %s", ddict) keys = ddict.keys() if 'cntlist' in keys: cntlist = ddict['cntlist'] diff --git a/PyMca5/PyMcaGui/io/hdf5/HDF5McaTable.py b/PyMca5/PyMcaGui/io/hdf5/HDF5McaTable.py index d66aafe..4dcc4a2 100644 --- a/PyMca5/PyMcaGui/io/hdf5/HDF5McaTable.py +++ b/PyMca5/PyMcaGui/io/hdf5/HDF5McaTable.py @@ -28,10 +28,12 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import posixpath +import logging from PyMca5.PyMcaGui import PyMcaQt as qt safe_str = qt.safe_str -DEBUG = 0 +_logger = logging.getLogger(__name__) + class McaSelectionType(qt.QWidget): sigMcaSelectionTypeSignal = qt.pyqtSignal(object) @@ -204,8 +206,7 @@ class HDF5McaTable(qt.QTableWidget): self._update(row, col) def _mySlot(self, ddict): - if DEBUG: - print("HDF5McaTable._mySlot", ddict) + _logger.debug("HDF5McaTable._mySlot %s", ddict) row = ddict["row"] col = ddict["column"] if col == 1: diff --git a/PyMca5/PyMcaGui/io/hdf5/HDF5Selection.py b/PyMca5/PyMcaGui/io/hdf5/HDF5Selection.py index 78bd6c2..3b10de8 100644 --- a/PyMca5/PyMcaGui/io/hdf5/HDF5Selection.py +++ b/PyMca5/PyMcaGui/io/hdf5/HDF5Selection.py @@ -27,10 +27,10 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" + from PyMca5.PyMcaGui import PyMcaQt as qt safe_str = qt.safe_str -DEBUG = 0 class HDF5Selection(qt.QWidget): def __init__(self, parent=None): diff --git a/PyMca5/PyMcaGui/io/hdf5/HDF5Widget.py b/PyMca5/PyMcaGui/io/hdf5/HDF5Widget.py index 6775387..97d10cc 100644 --- a/PyMca5/PyMcaGui/io/hdf5/HDF5Widget.py +++ b/PyMca5/PyMcaGui/io/hdf5/HDF5Widget.py @@ -34,6 +34,7 @@ import posixpath import gc import re from operator import itemgetter +import logging import h5py import weakref @@ -60,7 +61,8 @@ else: return x -DEBUG = 0 +_logger = logging.getLogger(__name__) + QVERSION = qt.qVersion() @@ -81,8 +83,7 @@ def h5py_sorting(object_list): posixNames = [item[1].name for item in object_list] except AttributeError: # Typical of broken external links - if DEBUG: - print("HDF5Widget: Cannot get posixNames") + _logger.debug("HDF5Widget: Cannot get posixNames") return object_list # This implementation only sorts entries @@ -223,7 +224,7 @@ class H5NodeProxy(object): except: #one cannot afford any error, so I revert to the old # method where values where used instead of items - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise else: # tmpList = list(self.getNode(self.name).values()) @@ -581,8 +582,7 @@ class FileModel(qt.QAbstractItemModel): self.sigFileAppended.emit(ddict) def clear(self): - if DEBUG: - print("Clear called") + _logger.debug("Clear called") # reset is considered obsolete under Qt 5. if hasattr(self, "reset"): self.reset() diff --git a/PyMca5/PyMcaGui/io/hdf5/Hdf5NodeView.py b/PyMca5/PyMcaGui/io/hdf5/Hdf5NodeView.py index c042515..d1c3c6a 100644 --- a/PyMca5/PyMcaGui/io/hdf5/Hdf5NodeView.py +++ b/PyMca5/PyMcaGui/io/hdf5/Hdf5NodeView.py @@ -46,7 +46,7 @@ import silx from silx.gui.data.DataViewerFrame import DataViewerFrame from silx.gui.data import DataViews from silx.gui.data import NXdataWidgets -from silx.gui.plot import Plot1D +from silx.gui.plot import Plot1D, Plot2D from silx.gui import icons @@ -134,6 +134,35 @@ class Plot1DViewWithPlugins(DataViews._Plot1dView): return Plot1DWithPlugins(parent=parent) +class Plot2DWithPlugins(Plot2D): + """Add a plugin toolbutton to a Plot2D""" + def __init__(self, parent=None): + Plot2D.__init__(self, parent) + + self._toolbar = qt.QToolBar(self) + self.addToolBar(self._toolbar) + pluginsToolButton = PluginsToolButton(plot=self, parent=self, + method="getPlugin2DInstance") + + if PLUGINS_DIR: + pluginsToolButton.getPlugins( + method="getPlugin2DInstance", + directoryList=PLUGINS_DIR) + self._toolbar.addWidget(pluginsToolButton) + + +class Plot2DViewWithPlugins(DataViews._Plot2dView): + def createWidget(self, parent): + widget = Plot2DWithPlugins(parent=parent) + widget.setDefaultColormap(self.defaultColormap()) + widget.getColormapAction().setColorDialog(self.defaultColorDialog()) + widget.getIntensityHistogramAction().setVisible(True) + widget.setKeepDataAspectRatio(True) + widget.getXAxis().setLabel('X') + widget.getYAxis().setLabel('Y') + return widget + + class ArrayCurvePlotWithPlugins(NXdataWidgets.ArrayCurvePlot): """Adds a plugin toolbutton to an ArrayCurvePlot widget""" def __init__(self, parent=None): @@ -159,43 +188,30 @@ class NXdataCurveViewWithPlugins(DataViews._NXdataCurveView): return ArrayCurvePlotWithPlugins(parent=parent) -class NXdataViewWithPlugins(DataViews.CompositeDataView): - """Re-implement DataViews._NXdataView to use the 1D view with - a plugin toolbutton in the composite view.""" - # This widget is needed only for silx < 0.7. - def __init__(self, parent): - super(NXdataViewWithPlugins, self).__init__( - parent=parent, - label="NXdata", - icon=icons.getQIcon("view-nexus")) - - if silx.version >= "0.7.0": - self.addView(DataViews._InvalidNXdataView(parent)) - self.addView(DataViews._NXdataScalarView(parent)) - self.addView(NXdataCurveViewWithPlugins(parent)) - self.addView(DataViews._NXdataXYVScatterView(parent)) - self.addView(DataViews._NXdataImageView(parent)) - self.addView(DataViews._NXdataStackView(parent)) - - -class DataViewerFrameWithPlugins(DataViewerFrame): - """Overloaded DataViewerFrame with the 1D view replaced by - Plot1DViewWithPlugins""" - # This widget is needed only for silx < 0.7. - def createDefaultViews(self, parent=None): - views = list(DataViewerFrame.createDefaultViews(self, parent=parent)) +class ArrayImagePlotWithPlugins(NXdataWidgets.ArrayImagePlot): + """Adds a plugin toolbutton to an ArrayImagePlot widget""" + def __init__(self, parent=None): + NXdataWidgets.ArrayImagePlot.__init__(self, parent) - # replace 1d view - oldView = [v for v in views if v.modeId() == DataViews.PLOT1D_MODE][0] - newView = Plot1DViewWithPlugins(parent=parent) - views[views.index(oldView)] = newView + self._toolbar = qt.QToolBar(self) + self.getPlot().addToolBar(self._toolbar) + pluginsToolButton = PluginsToolButton(plot=self.getPlot(), + parent=self, + method="getPlugin2DInstance") + if PLUGINS_DIR: + pluginsToolButton.getPlugins( + method="getPlugin2DInstance", + directoryList=PLUGINS_DIR) + self._toolbar.addWidget(pluginsToolButton) - # replace NXdataView - oldView = [v for v in views if isinstance(v, DataViews._NXdataView)][0] - newView = NXdataViewWithPlugins(parent=parent) - views[views.index(oldView)] = newView - return views +class NXdataImageViewWithPlugins(DataViews._NXdataImageView): + """Use the widget with a :class:`PluginsToolButton`""" + def createWidget(self, parent): + widget = ArrayImagePlotWithPlugins(parent) + widget.getPlot().setDefaultColormap(self.defaultColormap()) + widget.getPlot().getColormapAction().setColorDialog(self.defaultColorDialog()) + return widget class Hdf5NodeView(CloseEventNotifyingWidget.CloseEventNotifyingWidget): @@ -213,14 +229,15 @@ class Hdf5NodeView(CloseEventNotifyingWidget.CloseEventNotifyingWidget): self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.setSpacing(0) - if silx.hexversion >= 0x000700f0: # 0.7.0 final - self.viewWidget = DataViewerFrame(self) - self.viewWidget.replaceView(DataViews.PLOT1D_MODE, - Plot1DViewWithPlugins(self)) - self.viewWidget.replaceView(DataViews.NXDATA_CURVE_MODE, - NXdataCurveViewWithPlugins(self)) - else: - self.viewWidget = DataViewerFrameWithPlugins(self) + self.viewWidget = DataViewerFrame(self) + self.viewWidget.replaceView(DataViews.PLOT1D_MODE, + Plot1DViewWithPlugins(self)) + self.viewWidget.replaceView(DataViews.PLOT2D_MODE, + Plot2DViewWithPlugins(self)) + self.viewWidget.replaceView(DataViews.NXDATA_CURVE_MODE, + NXdataCurveViewWithPlugins(self)) + self.viewWidget.replaceView(DataViews.NXDATA_IMAGE_MODE, + NXdataImageViewWithPlugins(self)) self.mainLayout.addWidget(self.viewWidget) diff --git a/PyMca5/PyMcaGui/io/hdf5/QNexusWidget.py b/PyMca5/PyMcaGui/io/hdf5/QNexusWidget.py index ba3ddbc..dda5514 100644 --- a/PyMca5/PyMcaGui/io/hdf5/QNexusWidget.py +++ b/PyMca5/PyMcaGui/io/hdf5/QNexusWidget.py @@ -33,6 +33,7 @@ import posixpath import weakref import gc import h5py +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaCore import NexusTools @@ -57,7 +58,8 @@ from PyMca5.PyMcaIO import ConfigDict if "PyMcaDirs" in sys.modules: from PyMca5 import PyMcaDirs -DEBUG=0 +_logger = logging.getLogger(__name__) + class Buttons(qt.QWidget): @@ -296,8 +298,7 @@ class QNexusWidget(qt.QWidget): else: ddict['HDF5'] ={'WidgetConfiguration':\ self.getWidgetConfiguration()} - if DEBUG: - print("TODO - Add selection options") + _logger.debug("TODO - Add selection options") ddict.write(fname) def _deleteAllCountersFromTable(self): @@ -401,8 +402,7 @@ class QNexusWidget(qt.QWidget): if type(self._aliasList) == type(""): self._aliasList = [ddict['aliases']] self.cntTable.build(self._cntList, self._aliasList) - if DEBUG: - print("TODO - Add selection options") + _logger.debug("TODO - Add selection options") def setDataSource(self, dataSource): self.data = dataSource @@ -480,7 +480,7 @@ class QNexusWidget(qt.QWidget): try: widget.w.setDataset(dataset) except: - print("Error filling table") + _logger.error("Error filling table") widget.addTab(widget.w, 'DataView') widget.setCurrentWidget(widget.w) elif Hdf5NodeView is not None: @@ -661,19 +661,16 @@ class QNexusWidget(qt.QWidget): self.tableTab.insertTab(2, self.mcaTable, "MCA") elif (len(mcaList)==0) and (nTabs > 2): self.tableTab.removeTab(2) - if DEBUG: - print("currentTab = ", currentTab) + _logger.debug("currentTab = %s", currentTab) if currentTab != "USER": if (len(mcaList) > 0) and (len(cntList) == 0): idx = self.tableTabOrder.index("MCA") self.tableTab.setCurrentIndex(idx) - if DEBUG: - print("setting tab = ", idx, "MCA") + _logger.debug("setting tab = %s MCA", idx) elif (len(mcaList) == 0) and (len(cntList) > 0): idx = self.tableTabOrder.index("AUTO") self.tableTab.setCurrentIndex(idx) - if DEBUG: - print("setting tab = ", idx, "AUTO") + _logger.debug("setting tab = %s AUTO", idx) self._lastEntry = currentEntry if ddict['event'] == 'itemClicked': if ddict['mouse'] == "right": @@ -697,8 +694,7 @@ class QNexusWidget(qt.QWidget): else: self.tableTab.setCurrentIndex(0) if not self._isNumericType(ddict['dtype']): - if DEBUG: - print("string like %s" % ddict['dtype']) + _logger.debug("string like %s", ddict['dtype']) else: root = ddict['name'].split('/') root = "/" + root[1] @@ -737,8 +733,7 @@ class QNexusWidget(qt.QWidget): root = "/" + root[1] cnt = ddict['name'].split(root)[-1] if cnt not in self._cntList: - if DEBUG: - print("USING SECOND WAY") + _logger.debug("USING SECOND WAY") self._cntList.append(cnt) basename = posixpath.basename(cnt) if basename not in self._aliasList: @@ -747,36 +742,30 @@ class QNexusWidget(qt.QWidget): self._aliasList.append(cnt) self.cntTable.build(self._cntList, self._aliasList) return - if DEBUG: - print("Unhandled item type: %s" % ddict['dtype']) + _logger.debug("Unhandled item type: %s", ddict['dtype']) def _addMcaAction(self): - if DEBUG: - print("_addMcaAction received") + _logger.debug("_addMcaAction received") self.mcaAction("ADD") def _removeMcaAction(self): - if DEBUG: - print("_removeMcaAction received") + _logger.debug("_removeMcaAction received") self.mcaAction("REMOVE") def _replaceMcaAction(self): - if DEBUG: - print("_replaceMcaAction received") + _logger.debug("_replaceMcaAction received") self.mcaAction("REPLACE") def mcaAction(self, action="ADD"): - if DEBUG: - print("mcaAction %s" % action) + _logger.debug("mcaAction %s", action) self.mcaTable.getMcaSelection() ddict = {} ddict['action'] = "%s MCA" % action self.buttonsSlot(ddict, emit=True) def _addAction(self): - if DEBUG: - print("_addAction received") + _logger.debug("_addAction received") # formerly we had action and selection type text = qt.safe_str(self.tableTab.tabText(self.tableTab.currentIndex())) if text.upper() == "MCA": @@ -791,8 +780,7 @@ class QNexusWidget(qt.QWidget): self.buttonsSlot(ddict, emit=True) def _removeAction(self): - if DEBUG: - print("_removeAction received") + _logger.debug("_removeAction received") text = qt.safe_str(self.tableTab.tabText(self.tableTab.currentIndex())) if text.upper() == "MCA": self._removeMcaAction() @@ -806,8 +794,7 @@ class QNexusWidget(qt.QWidget): self.buttonsSlot(ddict, emit=True) def _replaceAction(self): - if DEBUG: - print("_replaceAction received") + _logger.debug("_replaceAction received") text = qt.safe_str(self.tableTab.tabText(self.tableTab.currentIndex())) if text.upper() == "MCA": self._replaceMcaAction() @@ -821,8 +808,7 @@ class QNexusWidget(qt.QWidget): self.buttonsSlot(ddict, emit=True) def _configurationChangedAction(self, ddict): - if DEBUG: - print("_configurationChangedAction received", ddict) + _logger.debug("_configurationChangedAction received %s", ddict) if ddict["3d"]: self.autoTable.set3DEnabled(True, emit=False) self.cntTable.set3DEnabled(True, emit=False) @@ -834,8 +820,7 @@ class QNexusWidget(qt.QWidget): self.cntTable.set2DEnabled(False, emit=False) def _autoTableUpdated(self, ddict): - if DEBUG: - print("_autoTableUpdated(self, ddict) ", ddict) + _logger.debug("_autoTableUpdated(self, ddict) %s", ddict) text = qt.safe_str(self.tableTab.tabText(self.tableTab.currentIndex())) if text.upper() == "AUTO": actions = self.actions.getConfiguration() @@ -846,8 +831,7 @@ class QNexusWidget(qt.QWidget): self._replaceAction() def _userTableUpdated(self, ddict): - if DEBUG: - print("_userTableUpdated(self, ddict) ", ddict) + _logger.debug("_userTableUpdated(self, ddict) %s", ddict) text = qt.safe_str(self.tableTab.tabText(self.tableTab.currentIndex())) if text.upper() == "USER": actions = self.actions.getConfiguration() @@ -858,8 +842,7 @@ class QNexusWidget(qt.QWidget): self._replaceAction() def _mcaTableUpdated(self, ddict): - if DEBUG: - print("_mcaTableUpdated(self, ddict) ", ddict) + _logger.debug("_mcaTableUpdated(self, ddict) %s", ddict) text = qt.safe_str(self.tableTab.tabText(self.tableTab.currentIndex())) if text.upper() == "MCA": actions = self.actions.getConfiguration() @@ -870,8 +853,7 @@ class QNexusWidget(qt.QWidget): self._replaceAction() def buttonsSlot(self, ddict, emit=True): - if DEBUG: - print("buttonsSlot(self, ddict,emit=True)", ddict, "emit = ", emit) + _logger.debug("buttonsSlot(self, %s,emit=%s)", ddict, emit) if self.data is None: return action, selectionType = ddict['action'].split() @@ -1057,10 +1039,10 @@ class QNexusWidget(qt.QWidget): if 'event' in ddict: if ddict['event'] == "closeEventSignal": if ddict['id'] in self._widgetDict: - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: try: widget = self._widgetDict[ddict['id']] - print("DELETING %s" % widget.windowTitle()) + _logger.debug("DELETING %s", widget.windowTitle()) except: pass del self._widgetDict[ddict['id']] diff --git a/PyMca5/PyMcaGui/io/hdf5/QNexusWidgetActions.py b/PyMca5/PyMcaGui/io/hdf5/QNexusWidgetActions.py index 18dda50..178a41c 100644 --- a/PyMca5/PyMcaGui/io/hdf5/QNexusWidgetActions.py +++ b/PyMca5/PyMcaGui/io/hdf5/QNexusWidgetActions.py @@ -28,8 +28,11 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys +import logging from PyMca5.PyMcaGui import PyMcaQt as qt -DEBUG = 0 + +_logger = logging.getLogger(__name__) + class QNexusWidgetActions(qt.QWidget): sigAddSelection = qt.pyqtSignal() @@ -173,23 +176,19 @@ class QNexusWidgetActions(qt.QWidget): self._addClicked() def _addClicked(self): - if DEBUG: - print("_addClicked()") + _logger.debug("_addClicked()") self.sigAddSelection.emit() def _removeClicked(self): - if DEBUG: - print("_removeClicked()") + _logger.debug("_removeClicked()") self.sigRemoveSelection.emit() def _replaceClicked(self): - if DEBUG: - print("_replaceClicked()") + _logger.debug("_replaceClicked()") self.sigReplaceSelection.emit() def configurationChanged(self): - if DEBUG: - print("configurationChanged(object)") + _logger.debug("configurationChanged(object)") ddict = self.getConfiguration() self.sigActionsConfigurationChanged.emit(ddict) diff --git a/PyMca5/PyMcaGui/math/FFTAlignmentWindow.py b/PyMca5/PyMcaGui/math/FFTAlignmentWindow.py index 11ebabb..9d9d125 100644 --- a/PyMca5/PyMcaGui/math/FFTAlignmentWindow.py +++ b/PyMca5/PyMcaGui/math/FFTAlignmentWindow.py @@ -33,7 +33,6 @@ import numpy from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import ExternalImagesWindow from PyMca5.PyMcaGui import PyMcaFileDialogs -DEBUG = 0 class ParametersWidget(qt.QWidget): parametersWidgetSignal = qt.pyqtSignal(object) diff --git a/PyMca5/PyMcaGui/math/NNMADialog.py b/PyMca5/PyMcaGui/math/NNMADialog.py index 9cb8b79..a71c7d8 100644 --- a/PyMca5/PyMcaGui/math/NNMADialog.py +++ b/PyMca5/PyMcaGui/math/NNMADialog.py @@ -31,10 +31,14 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import numpy import time +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from . import NNMAWindow NNMA = True -DEBUG = 0 + + +_logger = logging.getLogger(__name__) + class SimpleThread(qt.QThread): def __init__(self, function, *var, **kw): @@ -46,13 +50,12 @@ class SimpleThread(qt.QThread): self._result = None def run(self): - if DEBUG: + try: self._result = self._function(*self._var, **self._kw ) - else: - try: - self._result = self._function(*self._var, **self._kw ) - except: - self._result = ("Exception",) + sys.exc_info() + except: + if _logger.getEffectiveLevel() == logging.DEBUG: + raise + self._result = ("Exception",) + sys.exc_info() class NNMADialog(qt.QDialog): def __init__(self, parent=None, rgbwidget=None, selection=False): @@ -106,8 +109,7 @@ class NNMADialog(qt.QDialog): self.nnmaParametersDialog.setParameters(ddict) ret = self.nnmaParametersDialog.exec_() if ret: - if DEBUG: - t0 = time.time() + t0 = time.time() nnmaParameters = self.nnmaParametersDialog.getParameters() self.nnmaParametersDialog.close() function = nnmaParameters['function'] @@ -116,7 +118,7 @@ class NNMADialog(qt.QDialog): kw = nnmaParameters['kw'] data = self._data old_shape = self._data.shape - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: images, eigenvalues, eigenvectors = function(data, npc, binning=binning, @@ -143,8 +145,7 @@ class NNMADialog(qt.QDialog): return if isinstance(self._data, numpy.ndarray): self._data.shape = old_shape - if DEBUG: - print("NNMA Elapsed = ", time.time() - t0) + _logger.debug("NNMA Elapsed = %s", time.time() - t0) self.nnmaWindow.setPCAData(images, eigenvalues, eigenvectors) diff --git a/PyMca5/PyMcaGui/math/PCADialog.py b/PyMca5/PyMcaGui/math/PCADialog.py index b3b57b8..aa26ad8 100644 --- a/PyMca5/PyMcaGui/math/PCADialog.py +++ b/PyMca5/PyMcaGui/math/PCADialog.py @@ -31,6 +31,7 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import time import numpy +import logging from PyMca5.PyMcaGui import PyMcaQt as qt try: from . import PCAWindow @@ -39,7 +40,10 @@ try: except ImportError: PCA = False MDP = False -DEBUG = 0 + + +_logger = logging.getLogger(__name__) + class SimpleThread(qt.QThread): @@ -53,13 +57,12 @@ class SimpleThread(qt.QThread): self._result = None def run(self): - if DEBUG: + try: self._result = self._function(*self._var, **self._kw) - else: - try: - self._result = self._function(*self._var, **self._kw) - except: - self._result = ("Exception",) + sys.exc_info() + except: + if _logger.getEffectiveLevel() == logging.DEBUG: + raise + self._result = ("Exception",) + sys.exc_info() class PCADialog(qt.QDialog): @@ -120,8 +123,7 @@ class PCADialog(qt.QDialog): self.pcaParametersDialogInitialized = True ret = self.pcaParametersDialog.exec_() if ret: - if DEBUG: - t0 = time.time() + t0 = time.time() pcaParameters = self.pcaParametersDialog.getParameters() self.pcaParametersDialog.close() function = pcaParameters['function'] @@ -139,7 +141,7 @@ class PCADialog(qt.QDialog): msg.setText("Number of components too high") msg.exec_() return - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: images, eigenvalues, eigenvectors = function(data, npc, binning=binning, @@ -169,8 +171,7 @@ class PCADialog(qt.QDialog): return if isinstance(self._data, numpy.ndarray): self._data.shape = old_shape - if DEBUG: - print("PCA Elapsed = ", time.time() - t0) + _logger.debug("PCA Elapsed = %s", time.time() - t0) methodlabel = pcaParameters.get('methodlabel', "") imagenames = None vectornames = None @@ -279,7 +280,7 @@ class PCADialog(qt.QDialog): return result if __name__ == "__main__": - DEBUG = 1 + _logger.setLevel(logging.DEBUG) import os from PyMca5.PyMcaIO import EdfFile app = qt.QApplication([]) diff --git a/PyMca5/PyMcaGui/math/PCAWindow.py b/PyMca5/PyMcaGui/math/PCAWindow.py index 59e8ec1..a6004f0 100644 --- a/PyMca5/PyMcaGui/math/PCAWindow.py +++ b/PyMca5/PyMcaGui/math/PCAWindow.py @@ -162,7 +162,7 @@ class PCAParametersDialog(qt.QDialog): self.graph.sigPlotSignal.connect(self._graphSlot) if not self.__regions: #I am adding after instantiation - self.mainLayout.insertWidget(2,self.regionsWidget) + self.mainLayout.insertWidget(2, self.regionsWidget) self.mainLayout.addWidget(self.graph) self.__regions = True @@ -172,17 +172,16 @@ class PCAParametersDialog(qt.QDialog): toValue = ddict['to'] self.graph.setEnabled(True) self.graph.clearMarkers() - self.graph.insertXMarker(fromValue, - 'From', - text='From', - color='blue', - draggable=True) - self.graph.insertXMarker(toValue, - 'To', - text= 'To', - color='blue', - draggable=True) - self.graph.replot() + self.graph.addXMarker(fromValue, + 'From', + text='From', + color='blue', + draggable=True) + self.graph.addXMarker(toValue, + 'To', + text='To', + color='blue', + draggable=True) else: self.graph.clearMarkers() self.graph.setEnabled(False) diff --git a/PyMca5/PyMcaGui/math/SGWindow.py b/PyMca5/PyMcaGui/math/SGWindow.py index 1a59e96..0449645 100644 --- a/PyMca5/PyMcaGui/math/SGWindow.py +++ b/PyMca5/PyMcaGui/math/SGWindow.py @@ -27,11 +27,10 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import sys + from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import PyMca_Icons IconDict = PyMca_Icons.IconDict -from PyMca5.PyMcaGui import MaskImageWidget from PyMca5.PyMcaGui import ScanWindow from PyMca5.PyMcaMath import SGModule @@ -111,8 +110,9 @@ class SGWindow(qt.QWidget): self.spectrum = spectrum self.parametersWidget = SGParametersWidget(self, length=len(spectrum)) self.graph = ScanWindow.ScanWindow(self) - self.graph.newCurve(self.xValues, - spectrum, "Spectrum", replace=True) + self.graph.addCurve(self.xValues, + spectrum, "Spectrum", + replace=True) self.mainLayout.addWidget(self.parametersWidget) self.mainLayout.addWidget(self.graph) self.getParameters = self.parametersWidget.getParameters @@ -129,13 +129,13 @@ class SGWindow(qt.QWidget): degree=degree, order=order) if order > 0: - maptoy2 = True + yaxis = "right" else: - maptoy2 = False - self.graph.newCurve(self.xValues, - self.background, "Filtered Spectrum", - replace=False, - maptoy2=maptoy2) + yaxis = "left" + self.graph.addCurve(self.xValues, + self.background, "Filtered Spectrum", + replace=False, + yaxis=yaxis) #Force information update legend = self.graph.getActiveCurve(just_legend=True) @@ -190,10 +190,11 @@ if __name__ == "__main__": import numpy app = qt.QApplication([]) if 1: - noise = numpy.random.randn(1000.) - y=numpy.arange(1000.) - w = SGDialog(None, y+numpy.sqrt(y)* noise) + noise = numpy.random.randn(1000) + y = numpy.arange(1000.) + w = SGDialog(None, + y + numpy.sqrt(y) * noise) w.show() - ret=w.exec_() + ret = w.exec_() if ret: print(w.getParameters()) diff --git a/PyMca5/PyMcaGui/math/SIFTAlignmentWindow.py b/PyMca5/PyMcaGui/math/SIFTAlignmentWindow.py index 2e963cc..d164ec5 100644 --- a/PyMca5/PyMcaGui/math/SIFTAlignmentWindow.py +++ b/PyMca5/PyMcaGui/math/SIFTAlignmentWindow.py @@ -29,20 +29,20 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import os -import sys import numpy from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import ExternalImagesWindow from PyMca5.PyMcaGui import PyMcaFileDialogs -from PyMca5.PyMcaMath import sift +import silx.opencl +from silx.image import sift DEBUG = 0 if DEBUG: print("SIFT coming from %s" % os.path.abspath(sift.__file__)) -__doc__ ="""The SIFT algorithm belongs to the University of British Columbia. It is -protected by patent US6711293. If you are on a country where this pattent +__doc__ = """The SIFT algorithm belongs to the University of British Columbia. It is +protected by patent US6711293. If you are in a country where this patent applies (like the USA), please check if you are allowed to use it. The University of British Columbia does not require a license for its use for non-commercial research applications. @@ -50,7 +50,7 @@ non-commercial research applications. This SIFT implementation uses the code developed by Jerome Kieffer and Pierre Paleo. The project is hosted at: -https://github.com/kif/sift_pyocl +https://github.com/silx-kit/silx/tree/master/silx/opencl/sift This algorithm should provide better results than FFT based algorithms provided the images to be aligned provide enough registration points @@ -72,7 +72,7 @@ Please note that introduces an additional dependency of PyMca on PyOpenCL. sift_pyocl license follows: -Copyright(C) 2013 European Synchrotron Radiation Facility, Grenoble, France +Copyright (C) 2013-2017 European Synchrotron Radiation Facility, Grenoble, France Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation @@ -146,8 +146,8 @@ class ParametersWidget(qt.QWidget): def getOpenCLDevices(self): devices = [] - if sift.opencl.ocl is not None: - for platformid, platform in enumerate(sift.opencl.ocl.platforms): + if silx.opencl.ocl is not None: + for platformid, platform in enumerate(silx.opencl.ocl.platforms): for deviceid, dev in enumerate(platform.devices): devices.append((platformid, deviceid, dev.name)) return devices diff --git a/PyMca5/PyMcaGui/math/SNIPWindow.py b/PyMca5/PyMcaGui/math/SNIPWindow.py index ece019a..0e5a612 100644 --- a/PyMca5/PyMcaGui/math/SNIPWindow.py +++ b/PyMca5/PyMcaGui/math/SNIPWindow.py @@ -244,8 +244,8 @@ class SNIPWindow(qt.QWidget): length=len(spectrum), smooth=smooth) self.graph = ScanWindow.ScanWindow(self) - self.graph.newCurve(self.xValues, - spectrum, "Spectrum", replace=True) + self.graph.addCurve(self.xValues, + spectrum, "Spectrum", replace=True) self.mainLayout.addWidget(self.parametersWidget) self.mainLayout.addWidget(self.graph) self.xMarkers = [] @@ -267,31 +267,31 @@ class SNIPWindow(qt.QWidget): yMin, yMax = self.graph.getGraphYLimits() xMean = 0.5 * (xMin + xMax) yMean = 0.5 * (yMin + yMax) - self.xMarkers.append(self.graph.insertXMarker(roi_min[1], - legend='C Min', - text='C Min')) - self.xMarkers.append(self.graph.insertXMarker(roi_max[1], - legend='C Max', - text='C Max')) - self.yMarkers.append(self.graph.insertYMarker(roi_min[0], - legend='R Min', - text='R Min')) - self.yMarkers.append(self.graph.insertYMarker(roi_max[0], - legend='R Max', - text='R Max')) + self.xMarkers.append(self.graph.addXMarker(roi_min[1], + legend='C Min', + text='C Min')) + self.xMarkers.append(self.graph.addXMarker(roi_max[1], + legend='C Max', + text='C Max')) + self.yMarkers.append(self.graph.addYMarker(roi_min[0], + legend='R Min', + text='R Min')) + self.yMarkers.append(self.graph.addYMarker(roi_max[0], + legend='R Max', + text='R Max')) else: - self.graph.insertXMarker(roi_min[1], - legend='C Min', - text='C Min') - self.graph.insertXMarker(roi_max[1], - legend='C Max', - text='C Max') - self.graph.insertYMarker(roi_min[0], - legend='R Min', - text='R Min') - self.graph.insertYMarker(roi_max[0], - legend='R Max', - text='R Max') + self.graph.addXMarker(roi_min[1], + legend='C Min', + text='C Min') + self.graph.addXMarker(roi_max[1], + legend='C Max', + text='C Max') + self.graph.addYMarker(roi_min[0], + legend='R Min', + text='R Min') + self.graph.addYMarker(roi_max[0], + legend='R Max', + text='R Max') self.background = SNIPModule.getImageBackground(self.image, width, roi_min=roi_min, roi_max=roi_max, @@ -319,8 +319,8 @@ class SNIPWindow(qt.QWidget): legend0 = "Smoothed Spectrum" else: legend0 = "Background" - self.graph.addCurve(self.xValues, - self.background, legend0, replace=False) + self.graph.addCurve(self.xValues, self.background, + legend0, replace=False) #Force information update legend = self.graph.getActiveCurve(just_legend=True) @@ -398,8 +398,8 @@ if __name__ == "__main__": import numpy app = qt.QApplication([]) if 0: - noise = numpy.random.randn(1000.) - y=numpy.arange(1000.) + noise = numpy.random.randn(1000) + y = numpy.arange(1000.) w = SNIPDialog(None, y+numpy.sqrt(y)* noise) elif len(sys.argv) > 1: from PyMca5.PyMcaIO import EdfFile @@ -413,6 +413,6 @@ if __name__ == "__main__": 100 * numpy.exp(-(1./20) * ((x-64)*(x-64) + (y-128)*(y-128))) w = SNIPDialog(None, data) w.show() - ret=w.exec_() + ret = w.exec_() if ret: print(w.getParameters()) diff --git a/PyMca5/PyMcaGui/math/SimpleActions.py b/PyMca5/PyMcaGui/math/SimpleActions.py new file mode 100644 index 0000000..a94119a --- /dev/null +++ b/PyMca5/PyMcaGui/math/SimpleActions.py @@ -0,0 +1,322 @@ +#/*########################################################################## +# Copyright (C) 2004-2017 V.A. Sole, European Synchrotron Radiation Facility +# +# This file is part of the PyMca X-ray Fluorescence Toolkit developed at +# the ESRF by the Software group. +# +# 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. +# +#############################################################################*/ +"""This module defines a set of simple plot actions processing one or all +plotted curves in a ScanWindow: + + - :class:`AverageAction` + - :class:`DerivativeAction` + - :class:`SmoothAction` + - :class:`SwapSignAction` + - :class:`SubtractAction` + - :class:`YMinToZeroAction` + +""" +import copy + +from silx.gui.plot.actions import PlotAction + +from PyMca5.PyMcaGui import PyMcaQt as qt +from PyMca5.PyMcaMath import SimpleMath +from PyMca5.PyMcaGui.plotting.PyMca_Icons import IconDict + +if hasattr(qt, 'QString'): + QString = qt.QString +else: + QString = qt.safe_str + +_simpleMath = SimpleMath.SimpleMath() + + +def _getOneCurve(plot, qwarning=True): + """Return active curve if any, + else if there is a single curve return it, + else return None. + + :param plot: Plot instance + :param bool qwarning: If True, display a warning popup to + inform that a curve must be selected when function is not + successful. + """ + curve = plot.getActiveCurve() + if curve is None: + curves = plot.getAllCurves() + if not curves or len(curves) > 1: + if qwarning: + _QWarning(msg="You must select a curve.", + parent=plot) + return None + return curves[0] + return curve + + +def _QWarning(msg, parent=None): + """Print a warning message in a QMessageBox""" + mb = qt.QMessageBox(parent) + mb.setIcon(qt.QMessageBox.Warning) + mb.setText(msg) + mb.exec_() + +# +# def _isActive(legend, plot): +# """ +# +# :param legend: curve legend +# :param plot: plot instance +# :return: True or False +# """ +# active_legend = plot.getActiveCurve(just_legend=True) +# if active_legend is None: +# # No active curve +# return False +# return legend == active_legend + + +def _updated_info(info0, sourcename, operation): + info1 = copy.deepcopy(info0) + if not 'operations' in info1: + info1['operations'] = [] + info1['operations'].append(operation) + info1['SourceName'] = sourcename + if 'selectionlegend' in info1: + del info1['selectionlegend'] + return info1 + + +class AverageAction(PlotAction): + """Average all curves, clear plot, add average curve + """ + def __init__(self, plot, parent=None): + self.icon = qt.QIcon(qt.QPixmap(IconDict["average16"])) + PlotAction.__init__(self, + plot, + icon=self.icon, + text='Average Plotted Curves', + tooltip='Replace all curves by the average curve', + triggered=self._averageAllCurves, + parent=parent) + + def _averageAllCurves(self): + curves = self.plot.getAllCurves() + if not curves: + return + x0, y0, legend0, info0, _params0 = curves[0] + avg_legend = legend0 + all_x = [x0] + all_y = [y0] + for x, y, legend, info, params in curves[1:]: + avg_legend += " + " + legend + all_x.append(x) + all_y.append(y) + + avg_info = _updated_info(info0, avg_legend, "average") + + xavg, yavg = _simpleMath.average(all_x, all_y) + avg_legend = "(%s)/%d" % (avg_legend, len(curves)) + + self.plot.clearCurves() + self.plot.addCurve(xavg, yavg, avg_legend, + info=avg_info) + + +class SmoothAction(PlotAction): + """Plot smooth of the active curve if any, + else plot smooth of the only existing curve if any. + """ + def __init__(self, plot, parent=None): + self.icon = qt.QIcon(qt.QPixmap(IconDict["smooth"])) + PlotAction.__init__(self, + plot, + icon=self.icon, + text='Smooth Active Curve', + tooltip='Smooth Active Curve', + triggered=self._smoothActiveCurve, + parent=parent) + + def _smoothActiveCurve(self): + curve = _getOneCurve(self.plot) + if curve is None: + return + x0, y0, legend0, info0, _params = curve + + x1 = x0 * 1 + y1 = _simpleMath.smooth(y0) + + if info0.get("operations") is None or \ + info0["operations"][-1] != "smooth": + legend1 = "%s Smooth" % legend0 + else: + # don't repeat "smooth" + legend1 = legend0 + + info1 = _updated_info(info0, legend0, "smooth") + + self.plot.addCurve(x1, y1, legend1, + info=info1) + + +class DerivativeAction(PlotAction): + """Plot derivative of the active curve if any, + else the derivative of the only existing curve. + """ + def __init__(self, plot, parent=None): + self.icon = qt.QIcon(qt.QPixmap(IconDict["derive"])) + PlotAction.__init__(self, + plot, + icon=self.icon, + tooltip='Plot Derivative of Active Curve', + text='Derivate Active Curve', + triggered=self._derivateActiveCurve, + parent=parent) + + def _derivateActiveCurve(self): + curve = _getOneCurve(self.plot) + if curve is None: + return + x0, y0, legend0, info0, params0 = curve + + x1, y1 = _simpleMath.derivate(x0, y0) + legend1 = legend0 + "'" + + info1 = _updated_info(info0, legend0, "derivate") + info1['plot_yaxis'] = "right" + + ylabel1 = params0.get("ylabel") + if ylabel1 is None: + ylabel1 = "Y" + + self.plot.addCurve(x1, y1, legend1, + ylabel=ylabel1 + "'", + info=info1, + yaxis="right") + + +class SwapSignAction(PlotAction): + """Plot the active curve multiplied by -1 + """ + def __init__(self, plot, parent=None): + self.icon = qt.QIcon(qt.QPixmap(IconDict["swapsign"])) + PlotAction.__init__(self, + plot, + icon=self.icon, + text='Multiply Active Curve by -1', + tooltip='Multiply Active Curve by -1', + triggered=self._swapSignCurve, + parent=parent) + + def _swapSignCurve(self): + curve = _getOneCurve(self.plot) + if curve is None: + return + x0, y0, legend0, info0, _params = curve + + x1 = 1 * x0 + y1 = -y0 + legend1 = "-(%s)" % legend0 + + info1 = _updated_info(info0, legend0, "swapsign") + + self.plot.addCurve(x1, y1, legend1, + info=info1) + + +class YMinToZeroAction(PlotAction): + """ + + """ + def __init__(self, plot, parent=None): + self.icon = qt.QIcon(qt.QPixmap(IconDict["ymintozero"])) + PlotAction.__init__(self, + plot, + icon=self.icon, + text='Y Min to Zero', + tooltip='Shift curve vertically to put min value at 0', + triggered=self._yMinToZeroCurve, + parent=parent) + + def _yMinToZeroCurve(self): + curve = _getOneCurve(self.plot) + if curve is None: + return + x0, y0, legend0, info0, _params = curve + + x1 = x0 * 1 + y1 = y0 - min(y0) + legend1 = "(%s) - ymin" % legend0 + + info1 = _updated_info(info0, legend0, "forceymintozero") + + self.plot.addCurve(x1, y1, legend1, + info=info1) + + +class SubtractAction(PlotAction): + """Subtract active curve from all curves. + + """ + def __init__(self, plot, parent=None): + self.icon = qt.QIcon(qt.QPixmap(IconDict["subtract"])) + PlotAction.__init__(self, + plot, + icon=self.icon, + text='Subtract Active Curve', + tooltip='Subtract active curve from all curves', + triggered=self._subtractCurve, + parent=parent) + + def _subtractCurve(self): + active_curve = _getOneCurve(self.plot) + all_curves = self.plot.getAllCurves() + + ############################################################# + if active_curve is None: + return + + x0, y0, legend0, info0, params0 = active_curve + + ylabel0 = params0.get("ylabel", "Y0") + if ylabel0 is None: + ylabel0 = "Y0" + + for x, y, legend, info, params in all_curves: + # (y1 - y0) is equivalent to 2 * average(-y0, y1) + XX = [x0, x] + YY = [-y0, y] + xplot, yplot = _simpleMath.average(XX, YY) + yplot *= 2 + legend1 = "(%s - %s)" % (legend, legend0) + + ylabel = params0.get("ylabel", "Y") + if ylabel is None: + ylabel = "Y" + + ylabel1 = "(%s - %s)" % (ylabel, ylabel0) + + info1 = _updated_info(info, legend, "subtract") + info1['LabelNames'] = [legend1] + + self.plot.removeCurve(legend) + self.plot.addCurve(xplot, yplot, legend1, + info=info1, ylabel=ylabel1) diff --git a/PyMca5/PyMcaGui/math/StripBackgroundWidget.py b/PyMca5/PyMcaGui/math/StripBackgroundWidget.py index c3fe56a..4c2f104 100644 --- a/PyMca5/PyMcaGui/math/StripBackgroundWidget.py +++ b/PyMca5/PyMcaGui/math/StripBackgroundWidget.py @@ -1,5 +1,5 @@ #/*########################################################################## -# Copyright (C) 2004-2014 V.A. Sole, European Synchrotron Radiation Facility +# Copyright (C) 2004-2018 V.A. Sole, European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -30,9 +30,10 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import numpy from PyMca5.PyMcaGui import PyMcaQt as qt -from PyMca5.PyMcaGui import PlotWindow from PyMca5.PyMcaMath.fitting import SpecfitFuns +from silx.gui.plot import PlotWindow + class StripParametersWidget(qt.QWidget): sigStripParametersWidgetSignal = qt.pyqtSignal(object) @@ -235,10 +236,13 @@ class StripBackgroundWidget(qt.QWidget): self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.setSpacing(2) self.parametersWidget = StripParametersWidget(self) - self.graphWidget = PlotWindow.PlotWindow(self, - newplot=False, - plugins=False, - fit=False) + self.graphWidget = PlotWindow(self, position=False, aspectRatio=False, + colormap=False, yInverted=False, + roi=False, mask=False, fit=False) + toolBar = self.graphWidget.getInteractiveModeToolBar() + toolBar.getZoomModeAction().setVisible(False) + toolBar.getPanModeAction().setVisible(False) + self.mainLayout.addWidget(self.parametersWidget) self.mainLayout.addWidget(self.graphWidget) self.getParameters = self.parametersWidget.getParameters @@ -252,6 +256,7 @@ class StripBackgroundWidget(qt.QWidget): self._x = x self._y = y self.update() + self.graphWidget.resetZoom() def _slot(self, ddict): self.update() @@ -319,16 +324,16 @@ class StripBackgroundWidget(qt.QWidget): snipBackground[lastAnchor:] =\ SpecfitFuns.snip1d(ysmooth[lastAnchor:], width, 0) - self.graphWidget.addCurve(x, y, \ - legend='Input Data',\ - replace=True, - replot=False) - self.graphWidget.addCurve(x, stripBackground,\ - legend='Strip Background',\ - replot=False) - self.graphWidget.addCurve(x, snipBackground,\ - legend='SNIP Background', - replot=True) + self.graphWidget.addCurve(x, y, + legend='Input Data', + resetzoom=False) + self.graphWidget.addCurve(x, stripBackground, + resetzoom=False, + legend='Strip Background') + self.graphWidget.addCurve(x, snipBackground, + resetzoom=False, + legend='SNIP Background') + self.graphWidget.setActiveCurve('Input Data') class StripBackgroundDialog(qt.QDialog): def __init__(self, parent=None): diff --git a/PyMca5/PyMcaGui/math/fitting/McaTable.py b/PyMca5/PyMcaGui/math/fitting/McaTable.py index 3e606ef..7e83c94 100644 --- a/PyMca5/PyMcaGui/math/fitting/McaTable.py +++ b/PyMca5/PyMcaGui/math/fitting/McaTable.py @@ -27,6 +27,7 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import logging from PyMca5.PyMcaGui import PyMcaQt as qt if hasattr(qt, "QString"): QString = qt.QString @@ -37,7 +38,8 @@ QTVERSION = qt.qVersion() QTable = qt.QTableWidget -DEBUG=0 +_logger = logging.getLogger(__name__) + class McaTable(QTable): sigMcaTableSignal = qt.pyqtSignal(object) @@ -72,8 +74,8 @@ class McaTable(QTable): self.regionlist=[] self.regiondict={} - if DEBUG: - print("MCATABLE click on vertical header items?") + if _logger.getEffectiveLevel() == logging.DEBUG: + _logger.debug("MCATABLE click on vertical header items?") self.verticalHeader().sectionClicked[int].connect(self.__myslot) self.cellClicked[int, int].connect(self.__myslot) self.itemSelectionChanged[()].connect(self.__myslot) diff --git a/PyMca5/PyMcaGui/math/fitting/MultiParameters.py b/PyMca5/PyMcaGui/math/fitting/MultiParameters.py index bb0069c..0f8b7c9 100644 --- a/PyMca5/PyMcaGui/math/fitting/MultiParameters.py +++ b/PyMca5/PyMcaGui/math/fitting/MultiParameters.py @@ -29,12 +29,14 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import os import sys +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from . import Parameters QTVERSION = qt.qVersion() from . import McaTable -DEBUG = 0 +_logger = logging.getLogger(__name__) + class ParametersTab(qt.QTabWidget): sigMultiParametersSignal = qt.pyqtSignal(object) @@ -77,7 +79,6 @@ class ParametersTab(qt.QTabWidget): self.addTab(table,str(name)) if fitparameterslist is not None: table.fillfromfit(fitparameterslist) - #print "SHowing page ",name if QTVERSION < '4.0.0': self.showPage(self.views[name]) else: @@ -188,8 +189,7 @@ class ParametersTab(qt.QTabWidget): hb = table.horizontalHeader().paletteBackgroundColor() hcolor = ("#%x%x%x" % (hb.red(), hb.green(), hb.blue())).upper() else: - if DEBUG: - print("Actual color to ge got") + _logger.debug("Actual color to ge got") hcolor = ("#%x%x%x" % (230,240,249)).upper() text="" text+=("<nobr>") diff --git a/PyMca5/PyMcaGui/math/fitting/Parameters.py b/PyMca5/PyMcaGui/math/fitting/Parameters.py index 101a116..4ec094e 100644 --- a/PyMca5/PyMcaGui/math/fitting/Parameters.py +++ b/PyMca5/PyMcaGui/math/fitting/Parameters.py @@ -28,6 +28,7 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys +import logging from PyMca5.PyMcaGui import PyMcaQt as qt if not hasattr(qt, "QString"): QString = str @@ -42,6 +43,9 @@ else: QTVERSION = qt.qVersion() QTable = qt.QTableWidget +_logger = logging.getLogger(__name__) + + class QComboTableItem(qt.QComboBox): sigCellChanged = qt.pyqtSignal(int, int) def __init__(self, parent=None, row=None, col=None): @@ -51,8 +55,7 @@ class QComboTableItem(qt.QComboBox): self.activated[int].connect(self._cellChanged) def _cellChanged(self, idx): - if DEBUG: - print("cell changed", idx) + _logger.debug("cell changed %s", idx) self.sigCellChanged.emit(self._row, self._col) class QCheckBoxItem(qt.QCheckBox): @@ -66,8 +69,6 @@ class QCheckBoxItem(qt.QCheckBox): def _cellChanged(self): self.sigCellChanged.emit(self._row, self._col) -DEBUG = 0 - class Parameters(QTable): def __init__(self, parent=None, allowBackgroundAdd=False, **kw): @@ -77,7 +78,7 @@ class Parameters(QTable): self.setColumnCount(1) self.labels = ['Parameter', 'Estimation', 'Fit Value', 'Sigma', 'Constraints', 'Min/Parame', 'Max/Factor/Delta/'] - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: self.code_options = ["FREE", "POSITIVE", "QUOTED", "FIXED", "FACTOR", "DELTA", "SUM", "IGNORE", "ADD", "SHOW"] @@ -267,9 +268,8 @@ class Parameters(QTable): return fitparameterslist def myslot(self, row, col): - if DEBUG: - print("Passing by myslot(%d, %d)" % (row, col)) - print("current(%d, %d)" % (self.currentRow(), self.currentColumn())) + _logger.debug("Passing by myslot(%d, %d)", row, col) + _logger.debug("current(%d, %d)", self.currentRow(), self.currentColumn()) if (col != 4) and (col != -1): if row != self.currentRow(): return @@ -290,17 +290,14 @@ class Parameters(QTable): #this is the combobox widget = self.cellWidget(row, col) newvalue = widget.currentText() - if DEBUG: - print("old value = ", oldvalue) - print("new value = ", newvalue) + _logger.debug("old value = %s", oldvalue) + _logger.debug("new value = %s", newvalue) if self.validate(param, field, oldvalue, newvalue): - if DEBUG: - print("Change is valid") + _logger.debug("Change is valid") exec("self.configure(name=param,%s=newvalue)" % field) else: - if DEBUG: - print("Change is not valid") - print("oldvalue ", oldvalue) + _logger.debug("Change is not valid") + _logger.debug("oldvalue %s", oldvalue) if field == 'code': index = self.code_options.index(oldvalue) self.__configuring = True @@ -460,15 +457,14 @@ class Parameters(QTable): self.addgroup(i, group) return 0 elif str(newvalue) == 'SHOW': - print(self.cget(workparam)) + _logger.info(self.cget(workparam)) return 0 else: - print("None of the others!") + _logger.info("None of the others!") def addgroup(self, newg, gtype): - if DEBUG: - print("addgroup called") - print("newg = ", newg, "gtype = ", gtype) + _logger.debug("addgroup called") + _logger.debug("newg = %s gtype = %s", newg, gtype) line = 0 newparam = [] oldparamlist = list(self.paramlist) @@ -545,10 +541,9 @@ class Parameters(QTable): return candidates[0], candidates def setReadOnly(self, parameter, fields): - if DEBUG: - print("parameter ", parameter) - print("fields = ", fields) - print("asked to be read only") + _logger.debug("parameter %s", parameter) + _logger.debug("fields = %s", fields) + _logger.debug("asked to be read only") if QTVERSION < '4.0.0': self.setfield(parameter, fields, qttable.QTableItem.Never) else: @@ -556,10 +551,9 @@ class Parameters(QTable): self.setfield(parameter, fields, editflags) def setReadWrite(self, parameter, fields): - if DEBUG: - print("parameter ", parameter) - print("fields = ", fields) - print("asked to be read write") + _logger.debug("parameter %s", parameter) + _logger.debug("fields = %s", fields) + _logger.debug("asked to be read write") if QTVERSION < '4.0.0': self.setfield(parameter, fields, qttable.QTableItem.OnTyping) else: @@ -569,9 +563,8 @@ class Parameters(QTable): self.setfield(parameter, fields, editflags) def setfield(self, parameter, fields, EditType): - if DEBUG: - print("setfield. parameter =", parameter) - print("fields = ", fields) + _logger.debug("setfield. parameter = %s", parameter) + _logger.debug("fields = %s", fields) if isinstance(parameter, list) or \ isinstance(parameter, tuple): paramlist = parameter @@ -613,8 +606,7 @@ class Parameters(QTable): self.__configuring = _oldvalue def configure(self, *vars, **kw): - if DEBUG: - print("configure called with **kw = ", kw) + _logger.debug("configure called with **kw = %s", kw) name = None error = 0 if 'name' in kw: @@ -655,8 +647,7 @@ class Parameters(QTable): elif key in self.parameters[name].keys(): newvalue = QString(str(kw[key])) self.parameters[name][key] = newvalue - if DEBUG: - print("error = ", error) + _logger.debug("error = %s", error) if 'code' in kw.keys(): newvalue = QString(kw['code']) self.parameters[name]['code'] = newvalue @@ -756,7 +747,7 @@ class Parameters(QTable): float(str(self.parameters[name]['val2'])) except: error = 1 - print("Forcing factor to 1") + _logger.warning("Forcing factor to 1") self.parameters[name]['cons2'] = 1.0 self.setReadWrite(name, ['estimation', 'val1', 'val2']) self.setReadOnly(name, ['fitresult', 'sigma']) @@ -770,7 +761,7 @@ class Parameters(QTable): float(str(self.parameters[name]['val2'])) except: error = 1 - print("Forcing delta to 0") + _logger.warning("Forcing delta to 0") self.parameters[name]['cons2'] = 0.0 self.setReadWrite(name, ['estimation', 'val1', 'val2']) self.setReadOnly(name, ['fitresult', 'sigma']) @@ -784,7 +775,7 @@ class Parameters(QTable): float(str(self.parameters[name]['val2'])) except: error = 1 - print("Forcing sum to 0") + _logger.warning("Forcing sum to 0") self.parameters[name]['cons2'] = 0.0 self.setReadWrite(name, ['estimation', 'val1', 'val2']) self.setReadOnly(name, ['fitresult', 'sigma']) diff --git a/PyMca5/PyMcaGui/math/fitting/RateLawWindow.py b/PyMca5/PyMcaGui/math/fitting/RateLawWindow.py index 461e45d..73094ba 100644 --- a/PyMca5/PyMcaGui/math/fitting/RateLawWindow.py +++ b/PyMca5/PyMcaGui/math/fitting/RateLawWindow.py @@ -30,17 +30,16 @@ __author__ = "V. Armando Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import os + import sys import numpy -import traceback -import copy from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import PyMca_Icons IconDict = PyMca_Icons.IconDict -from PyMca5.PyMcaGui import PlotWindow from PyMca5.PyMcaMath.fitting import RateLaw -DEBUG = 0 +from PyMca5.PyMcaGui.PluginsToolButton import PluginsToolButton + +from silx.gui.plot import PlotWindow class RateLawWindow(qt.QMainWindow): def __init__(self, parent=None, backend=None): @@ -67,9 +66,12 @@ class RateLawMdiArea(qt.QMdiArea): "First", "Second"] self._windowList.reverse() for title in self._windowList: - plot = PlotWindow.PlotWindow(self, - position=True, - backend=backend) + plot = PlotWindow(self, + position=True, backend=backend, + colormap=False, aspectRatio=False, + yInverted=False, roi=False, mask=False) + self.pluginsToolButton = PluginsToolButton(plot=plot) + plot.toolBar().addWidget(self.pluginsToolButton) plot.setWindowTitle(title) self.addSubWindow(plot) self._windowDict[title] = plot @@ -89,16 +91,25 @@ class RateLawMdiArea(qt.QMdiArea): ylabel=ylabel, yerror=sigmay, symbol="o") + self._windowDict["Original"].setActiveCurve(legend) self.update() def update(self): plot = self._windowDict["Original"] activeCurve = plot.getActiveCurve() - if not len(activeCurve): - return - [x, y, legend, info] = activeCurve[:4] - xmin, xmax = plot.getGraphXLimits() - ymin, ymax = plot.getGraphYLimits() + if activeCurve is None: + allCurves = plot.getAllCurves() + if len(allCurves): + activeCurve = allCurves[0] + else: + return + x = activeCurve.getXData(copy=False) + y = activeCurve.getYData(copy=False) + xlabel = activeCurve.getXLabel() + ylabel = activeCurve.getYLabel() + + # xmin, xmax = plot.getGraphXLimits() + # ymin, ymax = plot.getGraphYLimits() result = RateLaw.rateLaw(x, y, sigmay=None) labels = ["Zero", "First", "Second"] @@ -117,8 +128,6 @@ class RateLawMdiArea(qt.QMdiArea): stderr = workingResult["stderr"] xw = workingResult["x"] yw = workingResult["y"] - xlabel = info["ylabel"] - ylabel = info["ylabel"] title = "r = %.5f slope = %.3E +/- %.2E" % (r_value, slope, sigma_slope) fit_legend = "%.3g * x + %.3g" % (slope, intercept) if key == "First": @@ -127,7 +136,7 @@ class RateLawMdiArea(qt.QMdiArea): ylabel = "1 / %s" % ylabel plot.addCurve(xw, yw, legend="Data", - replace=True, replot=False, + replace=True, resetzoom=False, symbol="o", linestyle=" ", ylabel=ylabel) @@ -135,10 +144,11 @@ class RateLawMdiArea(qt.QMdiArea): plot.addCurve(xw, intercept + slope * xw, legend=fit_legend, replace=False, - replot=True, + resetzoom=True, symbol=None, color="red", ylabel=ylabel) + plot.setActiveCurve("Data") plot.resetZoom() self.sigRateLawMdiAreaSignal.emit(result) diff --git a/PyMca5/PyMcaGui/math/fitting/SimpleFitConfigurationGui.py b/PyMca5/PyMcaGui/math/fitting/SimpleFitConfigurationGui.py index 9be0054..1091a79 100644 --- a/PyMca5/PyMcaGui/math/fitting/SimpleFitConfigurationGui.py +++ b/PyMca5/PyMcaGui/math/fitting/SimpleFitConfigurationGui.py @@ -30,6 +30,7 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import traceback import os.path +import logging from . import SimpleFitControlWidget if sys.version_info < (3,): @@ -54,7 +55,8 @@ if h5py is not None: from . import Parameters from PyMca5.PyMcaGui.math.StripBackgroundWidget import StripBackgroundDialog -DEBUG = 0 +_logger = logging.getLogger(__name__) + class DummyWidget(qt.QWidget): def __init__(self, parent=None, text="Automatically estimated function"): @@ -194,8 +196,7 @@ class SimpleFitConfigurationGui(qt.QDialog): self.initDir = None def _fitControlSlot(self, ddict): - if DEBUG: - print("FitControlSignal", ddict) + _logger.debug("FitControlSignal %s", ddict) event = ddict['event'] if event == "stripSetupCalled": if self._stripDialog is None: @@ -351,17 +352,15 @@ class SimpleFitConfigurationGui(qt.QDialog): fileName = ddict['functions'][functionName]['file'] if fileName not in currentFiles: try: - if DEBUG: - print("Adding file %s" % fileName) + _logger.debug("Adding file %s", fileName) self.simpleFitInstance.importFunctions(fileName) currentFiles.append(fileName) except: if "library.zip" in fileName: - if DEBUG: - print("Assuming PyMca supplied fit function") + _logger.debug("Assuming PyMca supplied fit function") continue - print("Cannot import file %s" % fileName) - print(sys.exc_info()[1]) + _logger.warning("Cannot import file %s", fileName) + _logger.warning(sys.exc_info()[1]) if 'fit' in ddict: self.fitControlWidget.setConfiguration(ddict['fit']) @@ -493,7 +492,7 @@ class SimpleFitConfigurationGui(qt.QDialog): self.initDir = os.path.dirname(filename) self.setConfiguration(cfg) except: - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise msg = qt.QMessageBox(self) msg.setIcon(qt.QMessageBox.Critical) @@ -530,17 +529,15 @@ class SimpleFitConfigurationGui(qt.QDialog): def saveConfiguration(self, filename): cfg = ConfigDict.ConfigDict(self.getConfiguration()) - if DEBUG: + try: cfg.write(filename) self.initDir = os.path.dirname(filename) - else: - try: - cfg.write(filename) - self.initDir = os.path.dirname(filename) - except: - qt.QMessageBox.critical(self, "Save Parameters", - "ERROR while saving parameters to\n%s"%filename, - qt.QMessageBox.Ok, qt.QMessageBox.NoButton, qt.QMessageBox.NoButton) + except: + if _logger.getEffectiveLevel() == logging.DEBUG: + raise + qt.QMessageBox.critical(self, "Save Parameters", + "ERROR while saving parameters to\n%s"%filename, + qt.QMessageBox.Ok, qt.QMessageBox.NoButton, qt.QMessageBox.NoButton) def test(): @@ -558,5 +555,5 @@ def test(): sys.exit() if __name__=="__main__": - DEBUG = 1 + _logger.setLevel(logging.DEBUG) test() diff --git a/PyMca5/PyMcaGui/math/fitting/SimpleFitGui.py b/PyMca5/PyMcaGui/math/fitting/SimpleFitGui.py index 19d73b2..2009690 100644 --- a/PyMca5/PyMcaGui/math/fitting/SimpleFitGui.py +++ b/PyMca5/PyMcaGui/math/fitting/SimpleFitGui.py @@ -29,14 +29,16 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaMath.fitting import SimpleFitModule from . import SimpleFitConfigurationGui from PyMca5.PyMcaMath.fitting import SimpleFitUserEstimatedFunctions from . import Parameters -from PyMca5.PyMcaGui import PlotWindow +from silx.gui.plot import PlotWindow + +_logger = logging.getLogger(__name__) -DEBUG = 0 class TopWidget(qt.QWidget): def __init__(self, parent=None): @@ -141,11 +143,15 @@ class SimpleFitGui(qt.QWidget): self.fitModule = fit if graph is None: self.__useTab = True - self.graph = PlotWindow.PlotWindow(newplot=False, - plugins=False, - fit=False, - control=True, - position=True) + self.graph = PlotWindow(self, + aspectRatio=False, colormap=False, + yInverted=False, roi=False, mask=False, + fit=False, control=True, position=True) + self.graph.getInteractiveModeToolBar().setVisible(False) + # No context menu by default, execute zoomBack on right click + plotArea = self.graph.getWidgetHandle() + plotArea.setContextMenuPolicy(qt.Qt.CustomContextMenu) + plotArea.customContextMenuRequested.connect(self._zoomBack) else: self.__useTab = False self.graph = graph @@ -221,14 +227,14 @@ class SimpleFitGui(qt.QWidget): functionsfile= qt.safe_str(fn) if not len(functionsfile): return - if DEBUG: + + try: self.fitModule.importFunctions(functionsfile) - else: - try: - self.fitModule.importFunctions(functionsfile) - except: - qt.QMessageBox.critical(self, "ERROR", - "Function not imported") + except: + if _logger.getEffectiveLevel() == logging.DEBUG: + raise + qt.QMessageBox.critical(self, "ERROR", + "Function not imported") config = self.fitModule.getConfiguration() self.topWidget.setFunctions(config['fit']['functions']) @@ -253,8 +259,7 @@ class SimpleFitGui(qt.QWidget): SimpleFitConfigurationGui.SimpleFitConfigurationGui() self._configurationDialog.setSimpleFitInstance(self.fitModule) if not self._configurationDialog.exec_(): - if DEBUG: - print("NOT UPDATING CONFIGURATION") + _logger.debug("NOT UPDATING CONFIGURATION") oldConfig = self.fitModule.getConfiguration() self._configurationDialog.setConfiguration(oldConfig) return @@ -276,8 +281,7 @@ class SimpleFitGui(qt.QWidget): idx = newConfig['fit']['functions'].index(fname) + 1 idx = self.topWidget.backgroundCombo.findText(fname) self.topWidget.backgroundCombo.setCurrentIndex(idx) - if DEBUG: - print("TABLE TO BE CLEANED") + _logger.debug("TABLE TO BE CLEANED") #self.estimate() def setFitFunction(self, fname): @@ -298,16 +302,18 @@ class SimpleFitGui(qt.QWidget): returnValue = self.fitModule.setData(*var, **kw) if self.__useTab: if hasattr(self.graph, "addCurve"): + self.graph.clear() self.graph.addCurve(self.fitModule._x, self.fitModule._y, - legend='Data', - replace=True) + legend='Data') + self.graph.setActiveCurve('Data') elif hasattr(self.graph, "newCurve"): + # TODO: remove if not used self.graph.clearCurves() self.graph.newCurve('Data', self.fitModule._x, self.fitModule._y) - self.graph.replot() + self.graph.replot() return returnValue def estimate(self): @@ -318,11 +324,12 @@ class SimpleFitGui(qt.QWidget): y = self.fitModule._y self.graph.clear() self.graph.addCurve(x, y, 'Data') + self.graph.setActiveCurve('Data') self.fitModule.estimate() self.setStatus() self.parametersTable.fillTableFromFit(self.fitModule.paramlist) except: - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise text = "%s:%s" % (sys.exc_info()[0], sys.exc_info()[1]) msg = qt.QMessageBox(self) @@ -331,7 +338,6 @@ class SimpleFitGui(qt.QWidget): msg.exec_() self.setStatus("Ready (after estimate error)") - def setStatus(self, text=None): if text is None: text = "%s" % self.fitModule.getStatus() @@ -345,7 +351,7 @@ class SimpleFitGui(qt.QWidget): values,chisq,sigma,niter,lastdeltachi = self.fitModule.startFit() self.setStatus() except: - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise text = "%s:%s" % (sys.exc_info()[0], sys.exc_info()[1]) msg = qt.QMessageBox(self) @@ -376,10 +382,9 @@ class SimpleFitGui(qt.QWidget): #ddict['yfit'] = self.evaluateDefinedFunction() #ddict['background'] = self.fitModule._evaluateBackground() self.graph.clear() - self.graph.addCurve(ddict['x'], ddict['y'], 'Data', replot=False) - self.graph.addCurve(ddict['x'], ddict['yfit'], 'Fit', replot=False) - self.graph.addCurve(ddict['x'], ddict['background'], 'Background', - replot=False) + self.graph.addCurve(ddict['x'], ddict['y'], 'Data') + self.graph.addCurve(ddict['x'], ddict['yfit'], 'Fit') + self.graph.addCurve(ddict['x'], ddict['background'], 'Background') contributions = ddict['contributions'] if len(contributions) > 1: background = ddict['background'] @@ -387,9 +392,8 @@ class SimpleFitGui(qt.QWidget): for contribution in contributions: i += 1 self.graph.addCurve(ddict['x'], background + contribution, - legend='Contribution %d' % i, - replot=False) - self.graph.replot() + legend='Contribution %d' % i) + self.graph.setActiveCurve('Data') self.graph.show() def dismiss(self): @@ -401,6 +405,9 @@ class SimpleFitGui(qt.QWidget): def evaluateContributions(self, x=None): return self.fitModule.evaluateContributions(x) + def _zoomBack(self, pos): + self.graph.getLimitsHistory().pop() + def test(): import numpy @@ -433,7 +440,7 @@ def test(): return w if __name__=="__main__": - DEBUG = 0 + _logger.setLevel(logging.DEBUG) app = qt.QApplication([]) w = test() app.exec_() diff --git a/PyMca5/PyMcaGui/math/fitting/SpecfitGui.py b/PyMca5/PyMcaGui/math/fitting/SpecfitGui.py index aef8fe8..2fd140e 100644 --- a/PyMca5/PyMcaGui/math/fitting/SpecfitGui.py +++ b/PyMca5/PyMcaGui/math/fitting/SpecfitGui.py @@ -30,6 +30,7 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import traceback +import logging from PyMca5.PyMcaCore import EventHandler from PyMca5.PyMcaMath.fitting import Specfit from PyMca5.PyMcaGui import PyMcaQt as qt @@ -41,7 +42,9 @@ from . import MultiParameters from . import FitActionsGui from . import FitStatusGui from . import QScriptOption -DEBUG = 0 + +_logger = logging.getLogger(__name__) + class SpecfitGui(qt.QWidget): sigSpecfitGuiSignal = qt.pyqtSignal(object) @@ -173,8 +176,8 @@ class SpecfitGui(qt.QWidget): self.guiconfig.FunComBox.setCurrentIndex(i) self.funevent(self.specfit.fitconfig['fittheory']) except: - print("Function not in list %s" %\ - self.specfit.fitconfig['fittheory']) + _logger.warning("Function not in list %s", + self.specfit.fitconfig['fittheory']) self.funevent(self.specfit.theorylist[0]) #current background try: @@ -182,8 +185,8 @@ class SpecfitGui(qt.QWidget): i=1+list(self.specfit.bkgdict.keys()).index(self.specfit.fitconfig['fitbkg']) self.guiconfig.BkgComBox.setCurrentIndex(i) except: - print("Background not in list %s" %\ - self.specfit.fitconfig['fitbkg']) + _logger.warning("Background not in list %s", + self.specfit.fitconfig['fitbkg']) self.bkgevent(list(self.specfit.bkgdict.keys())[0]) #and all the rest if configuration['McaMode']: @@ -261,7 +264,7 @@ class SpecfitGui(qt.QWidget): ddict={} ddict['event'] = 'FitError' self._emitSignal(ddict) - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise return self.guiparameters.fillfrommca(mcaresult) @@ -285,7 +288,7 @@ class SpecfitGui(qt.QWidget): msg.exec_() return except: - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise msg = qt.QMessageBox(self) msg.setIcon(qt.QMessageBox.Critical) @@ -313,7 +316,7 @@ class SpecfitGui(qt.QWidget): msg.setIcon(qt.QMessageBox.Critical) msg.setText("Error on mcafit: %s" % sys.exc_info()[1]) msg.exec_() - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise return self.guiparameters.fillfrommca(mcaresult) @@ -326,11 +329,11 @@ class SpecfitGui(qt.QWidget): #for param in self.specfit.paramlist: # print param['name'],param['group'],param['estimation'] self.specfit.paramlist=self.guiparameters.fillfitfromtable() - if DEBUG: - for param in self.specfit.paramlist: - print(param['name'],param['group'],param['estimation']) - print("TESTING") - self.specfit.startfit() + for param in self.specfit.paramlist: + _logger.debug("name %s; group %s; estimation %s", + param['name'], param['group'], param['estimation']) + _logger.debug("TESTING") + try: self.specfit.startfit() except: @@ -338,7 +341,7 @@ class SpecfitGui(qt.QWidget): msg.setIcon(qt.QMessageBox.Critical) msg.setText("Error on Fit") msg.exec_() - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise return self.guiparameters.fillfromfit(self.specfit.paramlist,current='Fit') diff --git a/PyMca5/PyMcaGui/math/fitting/TextField.py b/PyMca5/PyMcaGui/math/fitting/TextField.py index 1327a9e..ecbc7d1 100644 --- a/PyMca5/PyMcaGui/math/fitting/TextField.py +++ b/PyMca5/PyMcaGui/math/fitting/TextField.py @@ -27,11 +27,12 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import sys +import logging from PyMca5.PyMcaGui import PyMcaQt as qt QTVERSION = qt.qVersion() -DEBUG = 0 +_logger = logging.getLogger(__name__) + class TextField(qt.QWidget): def __init__(self,parent = None,name = None,fl = 0): @@ -40,8 +41,7 @@ class TextField(qt.QWidget): try: self.setSizePolicy(qt.QSizePolicy(1,1,0,0,self.sizePolicy().hasHeightForWidth())) except: - if DEBUG: - print("TextField Bad Size policy") + _logger.warning("TextField Bad Size policy") TextFieldLayout = qt.QHBoxLayout(self) Layout2 = qt.QHBoxLayout(None) @@ -55,8 +55,7 @@ class TextField(qt.QWidget): try: self.TextLabel.setSizePolicy(qt.QSizePolicy(7,1,0,0,self.TextLabel.sizePolicy().hasHeightForWidth())) except: - if DEBUG: - print("TextField Bad Size policy") + _logger.warning("TextField Bad Size policy") self.TextLabel.setText(str("TextLabel")) Layout2.addWidget(self.TextLabel) diff --git a/PyMca5/PyMcaGui/misc/DoubleSlider.py b/PyMca5/PyMcaGui/misc/DoubleSlider.py index 4d85105..36d2b7b 100644 --- a/PyMca5/PyMcaGui/misc/DoubleSlider.py +++ b/PyMca5/PyMcaGui/misc/DoubleSlider.py @@ -27,11 +27,12 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" - +import logging from PyMca5.PyMcaGui import PyMcaQt as qt QTVERSION = qt.qVersion() -DEBUG = 0 +_logger = logging.getLogger(__name__) + class DoubleSlider(qt.QWidget): sigDoubleSliderValueChanged = qt.pyqtSignal(object) @@ -68,8 +69,7 @@ class DoubleSlider(qt.QWidget): return ddict def _sliderChanged(self, value): - if DEBUG: - print("DoubleSlider._sliderChanged()") + _logger.debug("DoubleSlider._sliderChanged()") ddict = self.__getDict() self.sigDoubleSliderValueChanged.emit(ddict) diff --git a/PyMca5/PyMcaGui/misc/SelectionTable.py b/PyMca5/PyMcaGui/misc/SelectionTable.py index f121eb3..4332964 100644 --- a/PyMca5/PyMcaGui/misc/SelectionTable.py +++ b/PyMca5/PyMcaGui/misc/SelectionTable.py @@ -38,7 +38,6 @@ Each time one of the contained widgets changes, a sigSelectionTableSignal is emitted indicating the current selection and the triggering cell. """ from PyMca5.PyMcaGui import PyMcaQt as qt -DEBUG = 0 class SelectionTable(qt.QTableWidget): diff --git a/PyMca5/PyMcaGui/physics/xas/XASFourierTransformParameters.py b/PyMca5/PyMcaGui/physics/xas/XASFourierTransformParameters.py index 32b3aae..5a7cab4 100644 --- a/PyMca5/PyMcaGui/physics/xas/XASFourierTransformParameters.py +++ b/PyMca5/PyMcaGui/physics/xas/XASFourierTransformParameters.py @@ -30,13 +30,13 @@ __author__ = "V. Armando Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import os -import sys +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import PyMca_Icons IconDict = PyMca_Icons.IconDict -DEBUG = 0 +_logger = logging.getLogger(__name__) + class XASFourierTransformParameters(qt.QGroupBox): sigFTParametersSignal = qt.pyqtSignal(object) @@ -164,8 +164,7 @@ class XASFourierTransformParameters(qt.QGroupBox): self.pointsSelector.activated[int].connect(self._pointsChanged) def _windowChanged(self, value): - if DEBUG: - print("_windowChanged ", value) + _logger.debug("_windowChanged %s", value) current = str(self.windowSelector.currentText()) if current.lower() in ["gaussian", "gauss", "tukey", "papul"]: self.apodizationBox.setEnabled(False) @@ -177,20 +176,17 @@ class XASFourierTransformParameters(qt.QGroupBox): self.emitSignal("FTWindowChanged") def _apodizationChanged(self, value): - if DEBUG: - print("_apodizationChanged ", value) + _logger.debug("_apodizationChanged %s", value) if self.__connected: self.emitSignal("FTApodizationChanged") def _kMinChanged(self, value): - if DEBUG: - print("Current kMin Value =", value) + _logger.debug("Current kMin Value = %s", value) if self.__connected: self.emitSignal("FTKMinChanged") def _kMaxChanged(self, value): - if DEBUG: - print("Current kMax Value =", value) + _logger.debug("Current kMax Value = %s", value) if self.__connected: if value > self.kMinBox.value(): self.emitSignal("FTKMaxChanged") @@ -201,20 +197,17 @@ class XASFourierTransformParameters(qt.QGroupBox): pass def _kStepChanged(self, value): - if DEBUG: - print("Current kStep value = ", value) + _logger.debug("Current kStep value = %s", value) if self.__connected: self.emitSignal("FTKStepChanged") def _rMaxChanged(self, value): - if DEBUG: - print("Current rMax Value =", value) + _logger.debug("Current rMax Value = %s", value) if self.__connected: self.emitSignal("FTRMaxChanged") def _pointsChanged(self, value): - if DEBUG: - print("_pointsChanged ", value) + _logger.debug("_pointsChanged %s", value) if self.__connected: self.emitSignal("FTPointsChanged") @@ -234,8 +227,8 @@ class XASFourierTransformParameters(qt.QGroupBox): return ddict def setParameters(self, ddict, signal=True): - if DEBUG: - print("setParameters called", ddict, signal) + _logger.debug("setParameters called, ddict %s, signal %s", + ddict, signal) if "FT" in ddict: ddict = ddict["FT"] try: @@ -295,7 +288,7 @@ class XASFourierTransformParameters(qt.QGroupBox): self.setStyleSheet("QGroupBox {color: %s;}" % color) if __name__ == "__main__": - DEBUG = 1 + _logger.setLevel(logging.DEBUG) app = qt.QApplication([]) def mySlot(ddict): print("Signal received: ", ddict) diff --git a/PyMca5/PyMcaGui/physics/xas/XASNormalizationParameters.py b/PyMca5/PyMcaGui/physics/xas/XASNormalizationParameters.py index 7643541..8fbafdd 100644 --- a/PyMca5/PyMcaGui/physics/xas/XASNormalizationParameters.py +++ b/PyMca5/PyMcaGui/physics/xas/XASNormalizationParameters.py @@ -32,13 +32,15 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import os import sys +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import PyMca_Icons from PyMca5.PyMcaGui import XASNormalizationWindow from PyMca5.PyMca import XASNormalization IconDict = PyMca_Icons.IconDict -DEBUG = 0 +_logger = logging.getLogger(__name__) + class XASNormalizationParameters(qt.QGroupBox): sigNormalizationParametersSignal = qt.pyqtSignal(object) @@ -172,8 +174,7 @@ class XASNormalizationParameters(qt.QGroupBox): self.postEdgeEndBox.valueChanged[float].connect(self._postEdgeEndChanged) def _normalizationChanged(self, value): - if DEBUG: - print("_normalizationChanged ", value) + _logger.debug("_normalizationChanged, %s ", value) if self.__connected: self._emitSignal("JumpNormalizationChanged") @@ -204,8 +205,7 @@ class XASNormalizationParameters(qt.QGroupBox): self.e0SpinBox.setEnabled(True) def _e0Changed(self, value): - if DEBUG: - print("E0 CHANGED", value) + _logger.debug("E0 CHANGED, %s", value) if self.__connected: try: self.__connected = False @@ -215,14 +215,12 @@ class XASNormalizationParameters(qt.QGroupBox): self._emitSignal("E0Changed") def _preEdgeChanged(self, value): - if DEBUG: - print("Current pre-edge value = ", value) + _logger.debug("Current pre-edge value = %s", value) if self.__connected: self._emitSignal("PreEdgeChanged") def _preEdgeStartChanged(self, value): - if DEBUG: - print("pre start changed", value) + _logger.debug("pre start changed: %s", value) if self.__connected: try: self.__connected = False @@ -232,8 +230,7 @@ class XASNormalizationParameters(qt.QGroupBox): self._emitSignal("PreEdgeChanged") def _preEdgeEndChanged(self, value): - if DEBUG: - print("pre end changed", value) + _logger.debug("pre end changed: %s", value) if self.__connected: try: self.__connected = False @@ -243,14 +240,12 @@ class XASNormalizationParameters(qt.QGroupBox): self._emitSignal("PreEdgeChanged") def _postEdgeChanged(self, value): - if DEBUG: - print("post-edge changed", value) + _logger.debug("post-edge changed: %s", value) if self.__connected: self._emitSignal("PostEdgeChanged") def _postEdgeStartChanged(self, value): - if DEBUG: - print("post-edge start changed", value) + _logger.debug("post-edge start changed: %s", value) if self.__connected: try: self.__connected = False @@ -260,8 +255,7 @@ class XASNormalizationParameters(qt.QGroupBox): self._emitSignal("PostEdgeChanged") def _postEdgeEndChanged(self, value): - if DEBUG: - print("post-edge changed", value) + _logger.debug("post-edge changed: %s", value) if self.__connected: try: self.__connected = False @@ -343,8 +337,7 @@ class XASNormalizationParameters(qt.QGroupBox): return ddict def setParameters(self, ddict, signal=True): - if DEBUG: - print("setParameters called", ddict, signal) + _logger.debug("setParameters called, %s %s", ddict, signal) if "Normalization" in ddict: ddict = ddict["Normalization"] try: @@ -408,7 +401,7 @@ class XASNormalizationParameters(qt.QGroupBox): self.setStyleSheet("QGroupBox {color: %s;}" % color) if __name__ == "__main__": - DEBUG = 1 + _logger.setLevel(logging.DEBUG) app = qt.QApplication([]) def mySlot(ddict): print("Signal received: ", ddict) diff --git a/PyMca5/PyMcaGui/physics/xas/XASNormalizationWindow.py b/PyMca5/PyMcaGui/physics/xas/XASNormalizationWindow.py index 882e1b8..60e97aa 100644 --- a/PyMca5/PyMcaGui/physics/xas/XASNormalizationWindow.py +++ b/PyMca5/PyMcaGui/physics/xas/XASNormalizationWindow.py @@ -37,10 +37,8 @@ import copy from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import PyMca_Icons IconDict = PyMca_Icons.IconDict -from PyMca5.PyMcaGraph.backends.MatplotlibBackend \ - import MatplotlibBackend as backend -from PyMca5.PyMcaGui import PlotWindow from PyMca5.PyMcaPhysics import XASNormalization +from silx.gui.plot import PlotWindow POLYNOM_OPTIONS = ['Modif. Victoreen', 'Victoreen', @@ -278,12 +276,17 @@ class XASNormalizationWindow(qt.QWidget): self.energy = energy self.spectrum = spectrum self.parametersWidget = XASNormalizationParametersWidget(self) - self.graph = PlotWindow.PlotWindow(self, backend=backend, - plugins=False, newplot=False) + self.graph = PlotWindow(self, position=False, + aspectRatio=False, colormap=False, + yInverted=False, roi=False, mask=False, + fit=False) + self.graph.zoomModeAction.setVisible(False) + self.graph.panModeAction.setVisible(False) self.__lastDict = {} self.graph.sigPlotSignal.connect(self._handleGraphSignal) self.graph.addCurve(self.energy, - spectrum, legend="Spectrum", replace=True) + spectrum, legend="Spectrum") + self.graph.setActiveCurve("Spectrum") self.mainLayout.addWidget(self.parametersWidget) self.mainLayout.addWidget(self.graph) # initialize variables @@ -307,11 +310,10 @@ class XASNormalizationWindow(qt.QWidget): else: self.energy = energy self.graph.clearMarkers() + self.graph.clearCurves() self.graph.addCurve(self.energy, self.spectrum, - legend="Spectrum", - replot=True, - replace=True) + legend="Spectrum") edgeEnergy = XASNormalization.estimateXANESEdge(self.spectrum, energy=self.energy, full=False) @@ -379,13 +381,10 @@ class XASNormalizationWindow(qt.QWidget): yPost = postEdgeFunction(postEdgeParameters, x) self.graph.addCurve(x, yPre, - legend="Pre-edge Polynomial", - replace=False) + legend="Pre-edge Polynomial") self.graph.addCurve(x, yPost+yPre, - legend="Post-edge Polynomial", - replace=False, - replot=True) + legend="Post-edge Polynomial") def updateMarkers(self, edgeEnergy, preEdgeRegions, postEdgeRegions, edge_auto=True): if edge_auto: @@ -393,38 +392,33 @@ class XASNormalizationWindow(qt.QWidget): else: draggable = True #self.graph.clearMarkers() - self.graph.insertXMarker(edgeEnergy, - 'EDGE', - text='EDGE', - color='pink', - draggable=draggable, - replot=False) + self.graph.addXMarker(edgeEnergy, + 'EDGE', + text='EDGE', + color='pink', + draggable=draggable) for i in range(2): x = preEdgeRegions[0][i] + edgeEnergy if i == 0: label = 'MIN' else: label = 'MAX' - self.graph.insertXMarker(x, - 'Pre-'+ label, - text=label, - color='blue', - draggable=True, - replot=False) + self.graph.addXMarker(x, + 'Pre-' + label, + text=label, + color='blue', + draggable=True) for i in range(2): x = postEdgeRegions[0][i] + edgeEnergy if i == 0: label = 'MIN' - replot=False else: label = 'MAX' - replot=True - self.graph.insertXMarker(x, - 'Post-'+ label, - text=label, - color='blue', - draggable=True, - replot=replot) + self.graph.addXMarker(x, + 'Post-' + label, + text=label, + color='blue', + draggable=True) def _handleGraphSignal(self, ddict): #print("ddict = ", ddict) @@ -532,7 +526,7 @@ if __name__ == "__main__": w = XASNormalizationDialog(None, spectrum, energy=energy) else: from PyMca5 import SpecfitFuns - noise = numpy.random.randn(1500.) + noise = numpy.random.randn(1500) x = 8000. + numpy.arange(1500.) y = SpecfitFuns.upstep([100, 8500., 50], x) w = XASNormalizationDialog(None, y + numpy.sqrt(y)* noise, energy=x) diff --git a/PyMca5/PyMcaGui/physics/xas/XASParameters.py b/PyMca5/PyMcaGui/physics/xas/XASParameters.py index acdfbfc..7a1055f 100644 --- a/PyMca5/PyMcaGui/physics/xas/XASParameters.py +++ b/PyMca5/PyMcaGui/physics/xas/XASParameters.py @@ -31,6 +31,7 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import PyMca_Icons IconDict = PyMca_Icons.IconDict @@ -40,7 +41,8 @@ from PyMca5.PyMcaGui import XASFourierTransformParameters from PyMca5.PyMcaGui import PyMcaFileDialogs from PyMca5.PyMcaIO import ConfigDict -DEBUG = 0 +_logger = logging.getLogger(__name__) + class XASParameters(qt.QWidget): sigXASParametersSignal = qt.pyqtSignal(object) @@ -145,8 +147,7 @@ class XASParameters(qt.QWidget): self._emitSignal(ddict["event"]) def _postEdgeParameterSlot(self, ddict): - if DEBUG: - print("_postEdgeParameterSlot ", ddict) + _logger.debug("_postEdgeParameterSlot: %s", ddict) # Should I change the event to "EXAFSChanged"? self.fourierTransformWidget.setKRange([ddict["KMin"], ddict["KMax"]]) self._emitSignal(ddict["event"]) @@ -213,10 +214,10 @@ class XASParameters(qt.QWidget): self.postEdgeWidget.setTitleColor(color) self.fourierTransformWidget.setTitleColor(color) except: - print("Error setting title color", sys.exc_info()) + _logger.error("Error setting title color: %s", sys.exc_info()) if __name__ == "__main__": - DEBUG = 1 + _logger.setLevel(logging.DEBUG) app = qt.QApplication([]) def testSlot(ddict): print("Emitted signal = ", ddict) diff --git a/PyMca5/PyMcaGui/physics/xas/XASPostEdgeParameters.py b/PyMca5/PyMcaGui/physics/xas/XASPostEdgeParameters.py index 5bfe43b..6f537f5 100644 --- a/PyMca5/PyMcaGui/physics/xas/XASPostEdgeParameters.py +++ b/PyMca5/PyMcaGui/physics/xas/XASPostEdgeParameters.py @@ -30,11 +30,13 @@ __author__ = "V. Armando Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import PyMca_Icons IconDict = PyMca_Icons.IconDict -DEBUG = 0 +_logger = logging.getLogger(__name__) + def myFloat(x): try: @@ -161,8 +163,7 @@ class XASPostEdgeParameters(qt.QGroupBox): self.__connected = True def _knotNumberChanged(self, value): - if DEBUG: - print("Current number of knots = ", value) + _logger.debug("Current number of knots = %s", value) oldValue = self.__connected self.__connected = False try: @@ -181,8 +182,7 @@ class XASPostEdgeParameters(qt.QGroupBox): self.emitSignal("KnotNumberChanged") def _kMinChanged(self, value): - if DEBUG: - print("Current kMin Value =", value) + _logger.debug("Current kMin Value = %s", value) oldValue = self.__connected self.__connected = False try: @@ -193,8 +193,7 @@ class XASPostEdgeParameters(qt.QGroupBox): self.emitSignal("KMinChanged") def _kMaxChanged(self, value): - if DEBUG: - print("Current kMax Value =", value) + _logger.debug("Current kMax Value = %s", value) if value <= self.kMinBox.value(): # I should check if we have the focus prior to # raise any error. @@ -210,14 +209,12 @@ class XASPostEdgeParameters(qt.QGroupBox): self.emitSignal("KMaxChanged") def _kWeightChanged(self, value): - if DEBUG: - print("Current kWeight Value =", value) + _logger.debug("Current kWeight Value = %s", value) if self.__connected: self.emitSignal("KWeightChanged") def _knotChanged(self, value): - if DEBUG: - print("One knot has been changed = ", value) + _logger.debug("One knot has been changed = %s", value) # adjust limits oldValue = self.__connected self.__connected = False @@ -242,8 +239,7 @@ class XASPostEdgeParameters(qt.QGroupBox): self.emitSignal("KnotPositionChanged") def _degreeChanged(self, value): - if DEBUG: - print("One knot polynomial degree changed", value) + _logger.debug("One knot polynomial degree changed: %s", value) if self.__connected: self.emitSignal("KnotOrderChanged") @@ -267,8 +263,7 @@ class XASPostEdgeParameters(qt.QGroupBox): return ddict def setParameters(self, ddict, signal=True): - if DEBUG: - print("setParameters called", ddict, signal) + _logger.debug("setParameters called: ddict %s, signal%s", ddict, signal) if "EXAFS" in ddict: ddict = ddict["EXAFS"] elif "PostEdge" in ddict: @@ -362,7 +357,7 @@ class XASPostEdgeParameters(qt.QGroupBox): self.setStyleSheet("QGroupBox {color: %s;}" % color) if __name__ == "__main__": - DEBUG = 1 + _logger.setLevel(logging.DEBUG) app = qt.QApplication([]) def testSlot(ddict): print("Emitted signal = ", ddict) diff --git a/PyMca5/PyMcaGui/physics/xas/XASSelfattenuationWindow.py b/PyMca5/PyMcaGui/physics/xas/XASSelfattenuationWindow.py index 0d0f387..bdb0b6b 100644 --- a/PyMca5/PyMcaGui/physics/xas/XASSelfattenuationWindow.py +++ b/PyMca5/PyMcaGui/physics/xas/XASSelfattenuationWindow.py @@ -31,8 +31,7 @@ __author__ = "V. Armando Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import copy -import numpy +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import PyMcaFileDialogs from PyMca5.PyMcaPhysics.xrf import Elements @@ -45,7 +44,8 @@ if hasattr(qt, "QString"): else: qstring = str -DEBUG = 0 +_logger = logging.getLogger(__name__) + class SampleConfiguration(qt.QWidget): def __init__(self, parent=None,orientation="vertical"): @@ -121,16 +121,13 @@ class SampleConfiguration(qt.QWidget): def materialSignal(self, txt): txt = str(txt) if Elements.isValidFormula(txt): - if DEBUG: - print("validFormula") + _logger.debug("validFormula") elementDict = Elements.getMaterialMassFractions([txt], [1.0]) elif Elements.isValidMaterial(txt): - if DEBUG: - print("ValidMaterial") + _logger.debug("ValidMaterial") elementDict = Elements.getMaterialMassFractions([txt], [1.0]) else: - if DEBUG: - print("to be defined") + _logger.debug("to be defined") msg=qt.QMessageBox.information(self, "Invalid Material %s" % txt, "The material %s is not a valid Formula " \ @@ -160,12 +157,10 @@ class SampleConfiguration(qt.QWidget): qstring(ele + "(%d)" % (z[i]))) if currentElement in elementsList: #selection does not need to be changed - if DEBUG: - print("Element widget up to date") + _logger.debug("Element widget up to date") else: #selection needs to be changed - if DEBUG: - print("Setting the highest Z as default") + _logger.debug("Setting the highest Z as default") self.elementSignal(qstring(elementsList[iMaxZ])) diff --git a/PyMca5/PyMcaGui/physics/xas/XASWindow.py b/PyMca5/PyMcaGui/physics/xas/XASWindow.py index b4fa9a4..bea81ef 100644 --- a/PyMca5/PyMcaGui/physics/xas/XASWindow.py +++ b/PyMca5/PyMcaGui/physics/xas/XASWindow.py @@ -32,16 +32,17 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import os import sys -import numpy -import traceback -import copy +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import PyMca_Icons IconDict = PyMca_Icons.IconDict -from PyMca5.PyMcaGui import PlotWindow from PyMca5.PyMcaGui import XASParameters from PyMca5.PyMca import XASClass -DEBUG = 0 +from silx.gui.plot import PlotWindow +from PyMca5.PyMcaGui.PluginsToolButton import PluginsToolButton + +_logger = logging.getLogger(__name__) + class XASDialog(qt.QDialog): def __init__(self, parent=None, analyzer=None, backend=None): @@ -116,15 +117,13 @@ class XASWindow(qt.QMainWindow): return self.parametersWidget.getParameters() def _parametersSlot(self, ddict): - if DEBUG: - print("XASWindow.parametersSlot", ddict) + _logger.debug("XASWindow.parametersSlot: %s", ddict) analyzer = self.mdiArea.analyzer if "XASParameters" in ddict: ddict = ddict["XASParameters"] analyzer.setConfiguration(ddict) - if DEBUG: - print("ANALYZER CONFIGURATION FINAL") - print(analyzer.getConfiguration()) + _logger.debug("ANALYZER CONFIGURATION FINAL") + _logger.debug(analyzer.getConfiguration()) self.update() def update(self, ddict=None): @@ -156,10 +155,14 @@ class XASMdiArea(qt.QMdiArea): self._windowList = ["Spectrum", "Post-edge", "Signal", "FT"] self._windowList.reverse() for title in self._windowList: - plot = PlotWindow.PlotWindow(self, - #control=True, - position=True, - backend=backend) + plot = PlotWindow(self, position=True, aspectRatio=False, + colormap=False, yInverted=False, + roi=False, mask=False, fit=False, + backend=backend) + plot.zoomModeAction.setVisible(False) + plot.panModeAction.setVisible(False) + pluginsToolButton = PluginsToolButton(plot=plot) + plot.toolBar().addWidget(pluginsToolButton) plot.setWindowTitle(title) self.addSubWindow(plot) self._windowDict[title] = plot @@ -190,6 +193,7 @@ class XASMdiArea(qt.QMdiArea): legend="Spectrum", xlabel="Energy (eV)", ylabel="Absorption (a.u.)") + self._windowDict["Spectrum"].setActiveCurve("Spectrum") return self.analyzer.setSpectrum(energy, mu) def update(self, ddict=None): @@ -200,19 +204,17 @@ class XASMdiArea(qt.QMdiArea): plot = self._windowDict["Spectrum"] e0 = ddict["Edge"] plot.addCurve(ddict["Energy"] - e0, ddict["Mu"], legend="Spectrum", - xlabel="Energy (eV)", ylabel="Absorption (a.u.)", - replot=False, replace=True) - plot.addCurve(ddict["NormalizedEnergy"][idx] - e0, + xlabel="Energy (eV)", ylabel="Absorption (a.u.)") + plot.addCurve(ddict["NormalizedEnergy"][idx] - e0, ddict["NormalizedMu"][idx], legend="Normalized", xlabel="Energy (eV)", ylabel="Absorption (a.u.)", - yaxis="right", - replot=False) + yaxis="right") plot.addCurve(ddict["NormalizedEnergy"] - e0, - ddict["NormalizedSignal"], legend="Post", replot=False) + ddict["NormalizedSignal"], legend="Post", resetzoom=False) plot.addCurve(ddict["NormalizedEnergy"] - e0, - ddict["NormalizedBackground"], legend="Pre",replot=False) + ddict["NormalizedBackground"], legend="Pre", resetzoom=False) plot.resetZoom() #idxK = ddict["EXAFSKValues"] >= 0 idx = (ddict["EXAFSKValues"] >= ddict["KMin"]) & \ @@ -222,32 +224,28 @@ class XASMdiArea(qt.QMdiArea): ddict["EXAFSSignal"][idx], legend="EXAFSSignal", xlabel="K", - ylabel="Normalized Units", - replace=True, - replot=False) + ylabel="Normalized Units") + plot.setActiveCurve("EXAFSSignal") plot.addCurve(ddict["EXAFSKValues"][idx], ddict["PostEdgeB"][idx], legend="PostEdge", xlabel="K", ylabel="Normalized Units", - color="blue", - replot=False) + color="blue") if 0: plot.clearMarkers() for i in range(len(ddict["KnotsX"])): - plot.insertMarker(ddict["KnotsX"][i], - ddict["KnotsY"][i], - legend="Knot %d" % (i+1), - text="Knot %d" % (i+1), - replot=False, - draggable=False, - selectable=False, - color="orange") + plot.addMarker(ddict["KnotsX"][i], + ddict["KnotsY"][i], + legend="Knot %d" % (i+1), + text="Knot %d" % (i+1), + draggable=False, + selectable=False, + color="orange") else: plot.addCurve(ddict["KnotsX"], ddict["KnotsY"], legend="Knots", - replot=False, linestyle="", symbol="o", color="orange") @@ -264,50 +262,41 @@ class XASMdiArea(qt.QMdiArea): ddict["EXAFSNormalized"][idx], legend="Normalized EXAFS", xlabel="K", - ylabel=ylabel, - replace=True, - replot=False) + ylabel=ylabel) + plot.setActiveCurve("Normalized EXAFS") plot.addCurve(ddict["FT"]["K"], ddict["FT"]["WindowWeight"], legend="FT Window", xlabel="K", ylabel="Weight", yaxis="right", - color="red", - replace=False, - replot=False) + color="red") plot.resetZoom() plot = self._windowDict["FT"] plot.addCurve(ddict["FT"]["FTRadius"], ddict["FT"]["FTIntensity"], legend="FT Intensity", xlabel="R (Angstrom)", - ylabel="Arbitrary Units", - replace=True, - replot=False) + ylabel="Arbitrary Units") + plot.setActiveCurve("FT Intensity") """ plot.addCurve(ddict["FT"]["FTRadius"], ddict["FT"]["FTReal"], legend="FT Real", xlabel="R (Angstrom)", ylabel="Arbitrary Units", - color="green", - replace=False, - replot=False) + color="green") """ plot.addCurve(ddict["FT"]["FTRadius"], ddict["FT"]["FTImaginary"], legend="FT Imaginary", xlabel="R (Angstrom)", ylabel="Arbitrary Units", - color="red", - replace=False, - replot=False) - plot.resetZoom() + color="red") self.sigXASMdiAreaSignal.emit(ddict) if __name__ == "__main__": - DEBUG = 1 + _logger.setLevel(logging.DEBUG) app = qt.QApplication([]) from PyMca5.PyMcaIO import specfilewrapper as specfile from PyMca5.PyMcaDataDir import PYMCA_DATA_DIR diff --git a/PyMca5/PyMcaGui/physics/xrf/AttenuatorsTable.py b/PyMca5/PyMcaGui/physics/xrf/AttenuatorsTable.py index d230a68..370ff06 100644 --- a/PyMca5/PyMcaGui/physics/xrf/AttenuatorsTable.py +++ b/PyMca5/PyMcaGui/physics/xrf/AttenuatorsTable.py @@ -31,6 +31,7 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys +import logging from PyMca5.PyMcaGui import PyMcaQt as qt QTVERSION = qt.qVersion() @@ -46,7 +47,8 @@ from PyMca5.PyMcaPhysics import Elements from . import MaterialEditor from . import MatrixEditor import re -DEBUG = 0 + +_logger = logging.getLogger(__name__) class MyQLabel(qt.QLabel): @@ -342,9 +344,8 @@ class AttenuatorsTableWidget(QTable): self.matrixMode = matrixmode self.attenuators = attenuators self.verticalHeader().hide() - if DEBUG: - print("margin to adjust") - print("focus style") + _logger.debug("margin to adjust") + _logger.debug("focus style") self.setFrameShape(qt.QTableWidget.NoFrame) self.setSelectionMode(qt.QTableWidget.NoSelection) self.setColumnCount(len(labels)) @@ -544,14 +545,12 @@ class AttenuatorsTableWidget(QTable): self.setCellWidget(idx, 2, self.combo) self.combo.sigMaterialComboBoxSignal.connect(self._comboSlot) - def mySlot(self,row,col): - if DEBUG: - print("Value changed row = %d cole = &d" % (row, col)) - print("Text = %s" % self.text(row, col)) + def mySlot(self, row, col): + _logger.debug("Value changed row = %d cole = &d", row, col) + _logger.debug("Text = %s", self.text(row, col)) def _comboSlot(self, ddict): - if DEBUG: - print("_comboSlot", ddict) + _logger.debug("_comboSlot %s", ddict) row = ddict['row'] col = ddict['col'] text = ddict['text'] @@ -564,8 +563,8 @@ class AttenuatorsTableWidget(QTable): return self.cellWidget(row, col).currentText() else: if col not in [1, 3, 4, 5]: - print("row, col = %d, %d" % (row, col)) - print("I should not be here") + _logger.info("row, col = %d, %d", row, col) + _logger.info("I should not be here") else: item = self.item(row, col) return item.text() @@ -575,7 +574,7 @@ class AttenuatorsTableWidget(QTable): self.cellWidget(row, 0).setText(text) return if col not in [1, 3, 4, 5]: - print("only compatible columns 1, 3 and 4") + _logger.debug("only compatible columns 1, 3 and 4") raise ValueError("method for column > 2") item = self.item(row, col) if item is None: diff --git a/PyMca5/PyMcaGui/physics/xrf/ConcentrationsWidget.py b/PyMca5/PyMcaGui/physics/xrf/ConcentrationsWidget.py index 8d31dd9..5392291 100644 --- a/PyMca5/PyMcaGui/physics/xrf/ConcentrationsWidget.py +++ b/PyMca5/PyMcaGui/physics/xrf/ConcentrationsWidget.py @@ -31,6 +31,7 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys +import logging from PyMca5.PyMcaGui import PyMcaQt as qt if hasattr(qt, 'QString'): @@ -49,9 +50,10 @@ QTable = qt.QTableWidget from PyMca5.PyMcaPhysics.xrf import ConcentrationsTool from PyMca5.PyMcaPhysics.xrf import Elements import time -DEBUG = 0 -if DEBUG: - print("ConcentrationsWidget is in debug mode") + +_logger = logging.getLogger(__name__) + +_logger.debug("ConcentrationsWidget is in debug mode") class Concentrations(qt.QWidget): @@ -127,7 +129,7 @@ class Concentrations(qt.QWidget): if 'addinfo' in kw: if kw['addinfo']: addInfo = True - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: if addInfo: ddict, info = self.concentrationsTool.processFitResult(*var, **kw) self.concentrationsTable.fillFromResult(ddict) @@ -217,13 +219,12 @@ class SimpleThread(qt.QThread): self._result = None def run(self): - if DEBUG: + try: self._result = self._function(*self._var, **self._kw) - else: - try: - self._result = self._function(*self._var, **self._kw) - except: - self._result = ("Exception",) + sys.exc_info() + except: + if _logger.getEffectiveLevel() == logging.DEBUG: + raise + self._result = ("Exception",) + sys.exc_info() class ConcentrationsWidget(qt.QWidget): diff --git a/PyMca5/PyMcaGui/physics/xrf/ElementsInfo.py b/PyMca5/PyMcaGui/physics/xrf/ElementsInfo.py index e16fd12..aa7b7a6 100644 --- a/PyMca5/PyMcaGui/physics/xrf/ElementsInfo.py +++ b/PyMca5/PyMcaGui/physics/xrf/ElementsInfo.py @@ -30,6 +30,7 @@ __author__ = "V. Armando Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaPhysics.xrf import ElementHtml from PyMca5.PyMcaPhysics.xrf import Elements @@ -37,7 +38,8 @@ from PyMca5.PyMcaGui.physics.xrf.QPeriodicTable import QPeriodicTable __revision__ = "$Revision: 1.15 $" -DEBUG = 0 +_logger = logging.getLogger(__name__) + CLOSE_ICON =[ "16 16 18 1", ". c None", @@ -193,8 +195,7 @@ class ElementsInfo(qt.QWidget): self.infoWidget.setFocus() def infoToggle(self,**kw): - if DEBUG: - print("toggleSource called") + _logger.debug("toggleSource called") if self.infoWidget.isHidden(): self.infoWidget.show() self.infoWidget.raiseW() @@ -238,8 +239,7 @@ class ElementsInfo(qt.QWidget): class Line(qt.QFrame): sigLineDoubleClickEvent = qt.pyqtSignal(object) def mouseDoubleClickEvent(self,event): - if DEBUG: - print("Double Click Event") + _logger.debug("Double Click Event") ddict={} ddict['event']="DoubleClick" ddict['data'] = event @@ -248,8 +248,7 @@ class Line(qt.QFrame): class PixmapLabel(qt.QLabel): sigPixmapLabelMousePressEvent = qt.pyqtSignal(object) def mousePressEvent(self,event): - if DEBUG: - print("Mouse Press Event") + _logger.debug("Mouse Press Event") ddict={} ddict['event']="MousePress" ddict['data'] = event @@ -276,6 +275,7 @@ class MyQLineEdit(qt.QLineEdit): self.sigFocusOut.emit() def main(): + logging.basicConfig(level=logging.INFO) app = qt.QApplication([]) winpalette = qt.QPalette(qt.QColor(230,240,249),qt.QColor(238,234,238)) app.setPalette(winpalette) diff --git a/PyMca5/PyMcaGui/physics/xrf/EnergyTable.py b/PyMca5/PyMcaGui/physics/xrf/EnergyTable.py index 7c07cb9..fac83c8 100644 --- a/PyMca5/PyMcaGui/physics/xrf/EnergyTable.py +++ b/PyMca5/PyMcaGui/physics/xrf/EnergyTable.py @@ -2,7 +2,7 @@ # # The PyMca X-Ray Fluorescence Toolkit # -# Copyright (c) 2004-2017 European Synchrotron Radiation Facility +# Copyright (c) 2004-2018 European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -33,6 +33,7 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import numpy +import logging from . import QXTube from PyMca5.PyMcaCore import PyMcaDirs from PyMca5.PyMcaGui import PyMca_Icons as Icons @@ -41,7 +42,9 @@ qt = QXTube.qt QTVERSION = qt.qVersion() -DEBUG=0 +_logger = logging.getLogger(__name__) + + class EnergyTab(qt.QWidget): def __init__(self,parent=None, name="Energy Tab"): qt.QWidget.__init__(self, parent) @@ -304,26 +307,25 @@ class EnergyTable(QTable): self.setNumCols(3 * self.dataColumns) self.setFocusStyle(qttable.QTable.FollowStyle) else: - if DEBUG: - print("margin") - print("frame shape") - print("selection mode") - print("focus style") - print("all of them missing") - self.setColumnCount(3 * self.dataColumns) + _logger.debug("margin\n" + "frame shape\n" + "selection mode\n" + "focus style\n" + "all of them missing") + self.setColumnCount(3 * self.dataColumns) labels = [] for i in range(self.dataColumns): - labels.append("Use" + i * " ") - labels.append("Energy" + i * " ") - labels.append("Weight" + i * " ") + labels.append("Use") + labels.append("Energy") + labels.append("Weight") if QTVERSION < '4.0.0': - for label in labels: - self.horizontalHeader().setLabel(labels.index(label),label) + for i in range(len(labels)): + label = labels[i] + self.horizontalHeader().setLabel(i, label) else: - if DEBUG: - print("margin to addjust") - print("focus style") + _logger.debug("margin to adjust") + _logger.debug("focus style") self.setFrameShape(qt.QTableWidget.NoFrame) self.setSelectionMode(qt.QTableWidget.NoSelection) self.setColumnCount(len(labels)) @@ -337,8 +339,7 @@ class EnergyTable(QTable): self.__build(self.dataColumns * 20) self.__disconnected = False for i in range(self.dataColumns): - if DEBUG: - print("column adjustment missing") + _logger.debug("column adjustment missing") self.cellChanged[int, int].connect(self.mySlot) def _itemSlot(self, *var): @@ -505,8 +506,7 @@ class EnergyTable(QTable): if QTVERSION < '4.0.0': self.adjustColumn(0 + 3*i) else: - if DEBUG: - print("column adjustment missing") + _logger.debug("column adjustment missing") except: self.__disconnected = False raise @@ -520,9 +520,8 @@ class EnergyTable(QTable): def mySlot(self,row,col): if self.__disconnected:return - if DEBUG: - print("Value changed row = %d col = %d" % (row, col)) - print("Text = %s" % self.text(row,col)) + _logger.debug("Value changed row = %d col = %d", row, col) + _logger.debug("Text = %s", self.text(row, col)) if (col != 0) and (col !=3) and (col != 6) and (col != 9): try: s = str(self.text(row, col)) @@ -561,8 +560,7 @@ class EnergyTable(QTable): else: item.setText(text) else: - if DEBUG: - print("checkbox can be called?") + _logger.debug("checkbox can be called?") pass def _getDict(self): diff --git a/PyMca5/PyMcaGui/physics/xrf/FastXRFLinearFitWindow.py b/PyMca5/PyMcaGui/physics/xrf/FastXRFLinearFitWindow.py index dc7a169..71b0bf8 100644 --- a/PyMca5/PyMcaGui/physics/xrf/FastXRFLinearFitWindow.py +++ b/PyMca5/PyMcaGui/physics/xrf/FastXRFLinearFitWindow.py @@ -30,14 +30,12 @@ __author__ = "V. Armando Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import sys -import numpy + from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import PyMca_Icons IconDict = PyMca_Icons.IconDict from PyMca5.PyMcaGui import PyMcaFileDialogs -DEBUG = 0 class FastXRFLinearFitWindow(qt.QWidget): def __init__(self, parent=None): diff --git a/PyMca5/PyMcaGui/physics/xrf/FitParam.py b/PyMca5/PyMcaGui/physics/xrf/FitParam.py index 0235258..d998e53 100644 --- a/PyMca5/PyMcaGui/physics/xrf/FitParam.py +++ b/PyMca5/PyMcaGui/physics/xrf/FitParam.py @@ -32,6 +32,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import traceback +import logging from PyMca5.PyMcaGui import PyMcaQt as qt QTVERSION = qt.qVersion() @@ -48,19 +49,19 @@ from . import EnergyTable from PyMca5.PyMcaCore import PyMcaDirs from PyMca5.PyMcaGui import PyMcaFileDialogs XRFMC_FLAG = False +_logger = logging.getLogger(__name__) try: from . import XRFMCPyMca XRFMC_FLAG = True except ImportError: - print("XRFMC_TO_BE_IMPORTED") + _logger.warning("XRFMC_TO_BE_IMPORTED") # no XRFMC support pass from PyMca5.PyMcaGui.math import StripBackgroundWidget -from PyMca5.PyMcaGui import PlotWindow from PyMca5.PyMcaGui.physics.xrf import StrategyHandler +from silx.gui.plot import PlotWindow import numpy -DEBUG = 0 FitParamSections= ["fit", "detector", "peaks", "peakshape", "attenuators","concentrations"] FitParamHeaders= ["FIT", "DETECTOR","BEAM","PEAKS", "PEAK SHAPE", "ATTENUATORS","MATRIX","CONCENTRATIONS"] @@ -84,12 +85,15 @@ class FitParamWidget(FitParamForm): self.graphDialog.mainLayout = qt.QVBoxLayout(self.graphDialog) self.graphDialog.mainLayout.setContentsMargins(0, 0, 0, 0) self.graphDialog.mainLayout.setSpacing(0) - self.graphDialog.graph = PlotWindow.PlotWindow(self.graphDialog, - newplot=False, - plugins=False, fit=False) + self.graphDialog.graph = PlotWindow(self.graphDialog, + position=False, colormap=False, + aspectRatio=False, yInverted=False, + roi=False, mask=False, fit=False) self.graph = self.graphDialog.graph - self.graph._togglePointsSignal() - self.tabAttenuators = AttenuatorsTable.AttenuatorsTab(self.tabAtt, + self.graph.getInteractiveModeToolBar().getZoomModeAction().setVisible(False) + self.graph.getInteractiveModeToolBar().getPanModeAction().setVisible(False) + self.graph.setDefaultPlotPoints(True) + self.tabAttenuators = AttenuatorsTable.AttenuatorsTab(self.tabAtt, graph=self.graphDialog) self.graphDialog.mainLayout.addWidget(self.graph) self.graphDialog.okButton = qt.QPushButton(self.graphDialog) @@ -315,11 +319,11 @@ class FitParamWidget(FitParamForm): efficiency *= (1.0 - numpy.exp(-coeffs)) self.graph.setGraphTitle('Filter (not beam filter) and detector correction') - self.graph.addCurve(energies, efficiency, - legend='Ta * (1.0 - Td)', + legend = 'Ta * (1.0 - Td)' + self.graph.addCurve(energies, efficiency, legend, xlabel='Energy (keV)', - ylabel='Efficiency Term', - replace=True) + ylabel='Efficiency Term') + self.graph.setActiveCurve(legend) self.graphDialog.exec_() def __contComboActivated(self, idx): @@ -365,8 +369,8 @@ class FitParamWidget(FitParamForm): msg.setText("Error configuring strategy") msg.setInformativeText("You need to specify incident beam energy") msg.exec_() - #print("TO check for matrix composition") - #print("TO check for peaks") + #_logger.debug("TO check for matrix composition") + #_logger.debug("TO check for peaks") def _strategySetupButtonClicked(self): maxEnergy = qt.safe_str(self.peakTable.energy.text()) @@ -617,7 +621,7 @@ class FitParamWidget(FitParamForm): combo.setOptions(matlist) combo.lineEdit().setText(str(attpar[1])) else: - print("ERROR in __setAttPar") + _logger.warning("ERROR in __setAttPar") if len(attpar) == 4: attpar.append(1.0) self.attTable.setText(row, 3, str(attpar[2])) @@ -686,7 +690,7 @@ class FitParamWidget(FitParamForm): combo.setOptions(matlist) combo.lineEdit().setText(str(attpar[1])) else: - print("ERROR in __setAttPar") + _logger.warning("ERROR in __setAttPar") self.multilayerTable.setText(row, 3, str(attpar[2])) self.multilayerTable.setText(row, 4, str(attpar[3])) @@ -1039,8 +1043,7 @@ class SectionFileDialog(qt.QFileDialog): else: self.setDir(qt.safe_str(initdir)) - if DEBUG: - print("right to be added") + _logger.debug("right to be added") if 0: self.sectionWidget= SectionFileWidget(self, sections=sections, @@ -1362,6 +1365,7 @@ class FitParamDialog(qt.QDialog): self.saveParameters(filename, None) self.initDir = os.path.dirname(filename) + def openWidget(): app= qt.QApplication(sys.argv) app.lastWindowClosed.connect(app.quit) @@ -1370,6 +1374,7 @@ def openWidget(): wid.show() app.exec_loop() + def openDialog(): app= qt.QApplication(sys.argv) app.lastWindowClosed.connect(app.quit) @@ -1381,6 +1386,8 @@ def openDialog(): del wid app.quit() + if __name__=="__main__": + logging.basicConfig(level=logging.INFO) #openWidget() openDialog() diff --git a/PyMca5/PyMcaGui/physics/xrf/FitPeakSelect.py b/PyMca5/PyMcaGui/physics/xrf/FitPeakSelect.py index bbcb55a..c63e74d 100644 --- a/PyMca5/PyMcaGui/physics/xrf/FitPeakSelect.py +++ b/PyMca5/PyMcaGui/physics/xrf/FitPeakSelect.py @@ -2,7 +2,7 @@ # # The PyMca X-Ray Fluorescence Toolkit # -# Copyright (c) 2004-2014 European Synchrotron Radiation Facility +# Copyright (c) 2004-2018 European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -32,6 +32,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import copy +import logging from . import EnergyTable from PyMca5.PyMcaPhysics import Elements @@ -39,8 +40,8 @@ from .QPeriodicTable import QPeriodicTable from PyMca5.PyMcaGui import PyMcaQt as qt +_logger = logging.getLogger(__name__) -DEBUG = 0 QTVERSION = qt.qVersion() ElementList = Elements.ElementList __revision__ = "$Revision: 1.12 $" @@ -374,9 +375,7 @@ class FitPeakSelect(qt.QWidget): self._energyClicked() def _energyTableAction(self, ddict): - if DEBUG: - print("_energyTableAction called",) - print("ddict = ",ddict.dict) + _logger.debug("_energyTableAction called, ddict = %s", ddict) elist, wlist, flist, slist= self.energyTable.getParameters() maxenergy = 0.0 for i in range(len(flist)): diff --git a/PyMca5/PyMcaGui/physics/xrf/MaterialEditor.py b/PyMca5/PyMcaGui/physics/xrf/MaterialEditor.py index 079e200..737189b 100644 --- a/PyMca5/PyMcaGui/physics/xrf/MaterialEditor.py +++ b/PyMca5/PyMcaGui/physics/xrf/MaterialEditor.py @@ -33,19 +33,20 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import copy +import logging import numpy import traceback from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaPhysics import Elements -from PyMca5.PyMcaGui import PlotWindow -ScanWindow = PlotWindow.PlotWindow +from silx.gui.plot import PlotWindow if hasattr(qt, "QString"): QString = qt.QString else: QString = str -DEBUG = 0 +_logger = logging.getLogger(__name__) + class MaterialEditor(qt.QWidget): def __init__(self, parent=None, name="Material Editor", @@ -105,13 +106,10 @@ class MaterialEditor(qt.QWidget): if self.__toolMode: self.materialGUI.setCurrent(a[0]) if (self.graph is None): - self.graph = ScanWindow(self, newplot=False, - fit=False, - plugins=False, - control=True, - position=True) - self.graph._togglePointsSignal() - self.graph.enableOwnSave(True) + self.graph = PlotWindow(self, control=True, position=True, + colormap=False, aspectRatio=False, + yInverted=False, roi=False, mask=False) + self.graph.setDefaultPlotPoints(True) layout.addWidget(self.materialGUI) layout.addWidget(self.graph) else: @@ -146,7 +144,7 @@ class MaterialEditor(qt.QWidget): #no message? error = 1 del Elements.Material[material] - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise continue return error @@ -174,6 +172,8 @@ class MaterialEditor(qt.QWidget): density=density, thickness=thickness, listoutput=False) addButton = False if self.graph is None: + # probably dead code (ScanWindow.ScanWindow not imported) + # TODO: if needed, this should be updated for silx based ScanWindow self.graphDialog = qt.QDialog(self) self.graphDialog.mainLayout = qt.QVBoxLayout(self.graphDialog) self.graphDialog.mainLayout.setContentsMargins(0, 0, 0, 0) @@ -216,6 +216,8 @@ class MaterialEditor(qt.QWidget): energy) addButton = False if self.graph is None: + # probably dead code (ScanWindow.ScanWindow not imported) + # TODO: if needed, this should be updated for silx based ScanWindow self.graphDialog = qt.QDialog(self) self.graphDialog.mainLayout = qt.QVBoxLayout(self.graphDialog) self.graphDialog.mainLayout.setContentsMargins(0, 0, 0, 0) @@ -233,15 +235,13 @@ class MaterialEditor(qt.QWidget): legend=legend, xlabel='Energy (keV)', ylabel='Mass Att. (cm2/g)', - replace=True, - replot=False) + replace=True) for legend in ['Compton', 'Photo','Total']: self.graph.addCurve(energy, numpy.array(data[legend.lower()]), - legend=legend, - xlabel='Energy (keV)', - ylabel='Mass Att. (cm2/g)', - replace=False, - replot=False) + legend=legend, + xlabel='Energy (keV)', + ylabel='Mass Att. (cm2/g)', + replace=False) self.graph.setActiveCurve(legend+' '+'Mass Att. (cm2/g)') self.graph.setGraphTitle(ddict['Comment']) if self.graphDialog is not None: @@ -670,7 +670,7 @@ class MaterialGUI(qt.QWidget): self.__massAttButton.clicked.connect(self.__massAttSlot) def setCurrent(self, matkey0): - if DEBUG:"setCurrent(self, matkey0) ", matkey0 + _logger.debug("setCurrent(self, matkey0=%s)", matkey0) matkey = Elements.getMaterialKey(matkey0) if matkey is not None: if self.__toolMode: @@ -694,8 +694,7 @@ class MaterialGUI(qt.QWidget): self.__fillingValues = False def _fillValues(self): - if DEBUG: - print("fillValues(self)") + _logger.debug("fillValues(self)") self.__fillingValues = True if self.__comments: self.__nameLine.setText("%s" % self._current['Comment']) @@ -733,9 +732,8 @@ class MaterialGUI(qt.QWidget): # http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=666503 def _updateCurrent(self): - if DEBUG: - print("updateCurrent(self)") - print("self._current before = ", self._current) + _logger.debug("updateCurrent(self)") + _logger.debug("self._current before = %s", self._current) self._current['CompoundList'] = [] self._current['CompoundFraction'] = [] @@ -755,8 +753,7 @@ class MaterialGUI(qt.QWidget): self._current['CompoundFraction'].append(float(txt1)) self.__densitySlot(silent=True) self.__thicknessSlot(silent=True) - if DEBUG: - print("self._current after = ", self._current) + _logger.debug("self._current after = %s", self._current) def __densitySlot(self, silent=False): try: @@ -804,8 +801,7 @@ class MaterialGUI(qt.QWidget): self.sigMaterialMassAttenuationSignal.emit(ddict) def __nameLineSlot(self): - if DEBUG: - print("__nameLineSlot(self)") + _logger.debug("__nameLineSlot(self)") qstring = self.__nameLine.text() text = str(qstring) if self.__toolMode: @@ -857,8 +853,7 @@ class MaterialGUI(qt.QWidget): return item = self.__table.item(row, col) if item is not None: - if DEBUG: - print("table item is None") + _logger.debug("table item is None") qstring = item.text() else: qstring = "" diff --git a/PyMca5/PyMcaGui/physics/xrf/McaAdvancedFit.py b/PyMca5/PyMcaGui/physics/xrf/McaAdvancedFit.py index 7d67dfb..6e6b9f9 100644 --- a/PyMca5/PyMcaGui/physics/xrf/McaAdvancedFit.py +++ b/PyMca5/PyMcaGui/physics/xrf/McaAdvancedFit.py @@ -2,7 +2,7 @@ # # The PyMca X-Ray Fluorescence Toolkit # -# Copyright (c) 2004-2017 European Synchrotron Radiation Facility +# Copyright (c) 2004-2018 European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -35,6 +35,7 @@ import os import numpy import time import copy +import logging import tempfile import shutil import traceback @@ -66,7 +67,6 @@ from . import McaAdvancedTable from . import QtMcaAdvancedFitReport from . import ConcentrationsWidget from PyMca5.PyMcaPhysics.xrf import ConcentrationsTool -from PyMca5.PyMcaGui import PlotWindow from PyMca5.PyMcaGui import PyMca_Icons IconDict = PyMca_Icons.IconDict from . import McaCalWidget @@ -75,25 +75,31 @@ from PyMca5.PyMcaGui import SubprocessLogWidget from . import ElementsInfo Elements = ElementsInfo.Elements #import McaROIWidget -from PyMca5.PyMcaGui import PyMcaPrintPreview from PyMca5.PyMcaCore import PyMcaDirs from PyMca5.PyMcaIO import ConfigDict from PyMca5.PyMcaGui import CalculationThread -DEBUG = 0 -if DEBUG: - print("############################################") - print("# McaAdvancedFit is in DEBUG mode %s #" % DEBUG) - print("############################################") +from PyMca5.PyMcaGui.plotting import PyMca_Icons + +_logger = logging.getLogger(__name__) + +_logger.debug("############################################\n" + "# McaAdvancedFit is in DEBUG mode #\n" + "############################################") XRFMC_FLAG = False try: from PyMca5.PyMcaPhysics.xrf.XRFMC import XRFMCHelper XRFMC_FLAG = True except ImportError: - print("Cannot import XRFMCHelper module") - if DEBUG: + _logger.warning("Cannot import XRFMCHelper module") + if _logger.getEffectiveLevel() == logging.DEBUG: raise USE_BOLD_FONT = True +import silx +from silx.gui.plot import PlotWindow +from silx.gui.plot.PrintPreviewToolButton import SingletonPrintPreviewToolButton + + class McaAdvancedFit(qt.QWidget): """ This class inherits QWidget. @@ -162,7 +168,7 @@ class McaAdvancedFit(qt.QWidget): self.graph = self.graphWindow self.graph.setGraphXLabel('Channel') self.graph.setGraphYLabel('Counts') - self.mainTab.addTab(self.tabGraph,"GRAPH") + self.mainTab.addTab(self.tabGraph, "GRAPH") self.graphWindow.sigPlotSignal.connect(self._mcaGraphSignalSlot) #table self.tabMca = qt.QWidget() @@ -449,7 +455,6 @@ class McaAdvancedFit(qt.QWidget): #del dialog self.graph.clearMarkers() - self.graph.replot() self.__fitdone = False self._concentrationsDict = None self._concentrationsInfo = None @@ -478,7 +483,7 @@ class McaAdvancedFit(qt.QWidget): else: self.matrixXRFMCSpectrumButton.show() - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: self.mcafit.configure(config) elif 1: try: @@ -550,8 +555,7 @@ class McaAdvancedFit(qt.QWidget): self.mainTab.setCurrentIndex(0) def __configureFromConcentrations(self,ddict): - if DEBUG: - print("McaAdvancedFit.__configureFromConcentrations", ddict) + _logger.debug("McaAdvancedFit.__configureFromConcentrations %s", ddict) config = self.concentrationsWidget.getParameters() self.mcafit.config['concentrations'].update(config) if ddict['event'] == 'updated': @@ -570,7 +574,6 @@ class McaAdvancedFit(qt.QWidget): ele = dict['current'] items = [] if not (ele in dict): - self.graph.replot() return for rays in dict[ele]: for transition in Elements.Element[ele][rays +" xrays"]: @@ -590,18 +593,15 @@ class McaAdvancedFit(qt.QWidget): if (x < xmin) or (x > xmax):continue if not self._energyAxis: if abs(calib[1]) > 0.0000001: - marker=self.graph.insertXMarker(x, - legend=transition, - text=transition, - color='orange', - replot=False) + marker=self.graph.addXMarker(x, + legend=transition, + text=transition, + color='orange') else: - marker=self.graph.insertXMarker(energy, - legend=transition, - text=transition, - color='orange', - replot=False) - self.graph.replot() + marker=self.graph.addXMarker(energy, + legend=transition, + text=transition, + color='orange') def _updateTop(self): config = {} @@ -619,11 +619,10 @@ class McaAdvancedFit(qt.QWidget): def __updatefromtop(self,ndict): config = self.mcafit.configure() for key in ndict.keys(): - if DEBUG: - keylist = ['stripflag','hypermetflag','sumflag','escapeflag', - 'fitfunction', 'continuum'] - if key not in keylist: - print("UNKNOWN key ",key) + if key not in ['stripflag', 'hypermetflag', + 'sumflag', 'escapeflag', + 'fitfunction', 'continuum']: + _logger.debug("UNKNOWN key %s", key) config['fit'][key] = ndict[key] self.__fitdone = False #erase table @@ -644,7 +643,7 @@ class McaAdvancedFit(qt.QWidget): self.graph.removeCurve(key) self.plot() - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: self.mcafit.configure(config) elif 1: try: @@ -689,8 +688,7 @@ class McaAdvancedFit(qt.QWidget): return def _tabChanged(self, value): - if DEBUG: - print("_tabChanged(self, value) called") + _logger.debug("_tabChanged(self, value) called") if str(self.mainTab.tabText(self.mainTab.currentIndex())).upper() == "CONCENTRATIONS": self.printButton.setEnabled(False) w = self.concentrationsWidget @@ -701,21 +699,19 @@ class McaAdvancedFit(qt.QWidget): self.printButton.setEnabled(True) #do not calculate again. It should be already updated return - if DEBUG: + try: self.concentrations() self.printButton.setEnabled(True) - else: - try: - self.concentrations() - self.printButton.setEnabled(True) - except: - #print "try to set" - self.printButton.setEnabled(False) - msg = qt.QMessageBox(self) - msg.setIcon(qt.QMessageBox.Critical) - msg.setText("Concentrations error: %s" % sys.exc_info()[1]) - msg.exec_() - self.mainTab.setCurrentIndex(0) + except: + if _logger.getEffectiveLevel() == logging.DEBUG: + raise + #print "try to set" + self.printButton.setEnabled(False) + msg = qt.QMessageBox(self) + msg.setIcon(qt.QMessageBox.Critical) + msg.setText("Concentrations error: %s" % sys.exc_info()[1]) + msg.exec_() + self.mainTab.setCurrentIndex(0) elif str(self.mainTab.tabText(self.mainTab.currentIndex())).upper() == "TABLE": self.printButton.setEnabled(True) w = self.mcatable @@ -804,7 +800,8 @@ class McaAdvancedFit(qt.QWidget): def printActiveTab(self): txt = str(self.mainTab.tabText(self.mainTab.currentIndex())).upper() if txt == "GRAPH": - self.graph.printps() + # trigger the 2nd action in the PrintPreviewToolButton drop-down menu + self.graph.printPreviewTB.menu().actions()[1].trigger() elif txt == "TABLE": self.printps(True) elif txt == "CONCENTRATIONS": @@ -992,25 +989,21 @@ class McaAdvancedFit(qt.QWidget): ddict = {} ddict.update(config['concentrations']) tool.setParameters(ddict, signal=False) - if DEBUG: - ddict, info = tool.processFitResult(config=ddict,fitresult=fitresult, + try: + ddict, info = tool.processFitResult(config=ddict, fitresult=fitresult, elementsfrommatrix=False, - fluorates = self.mcafit._fluoRates, + fluorates=self.mcafit._fluoRates, addinfo=True) - else: - try: - ddict, info = tool.processFitResult(config=ddict,fitresult=fitresult, - elementsfrommatrix=False, - fluorates = self.mcafit._fluoRates, - addinfo=True) - except: - msg = qt.QMessageBox(self) - msg.setIcon(qt.QMessageBox.Critical) - msg.setText("Error processing fit result: %s" % (sys.exc_info()[1])) - msg.exec_() - if str(self.mainTab.tabText(self.mainTab.currentIndex())).upper() == 'CONCENTRATIONS': - self.mainTab.setCurrentIndex(0) - return + except: + if _logger.getEffectiveLevel() == logging.DEBUG: + raise + msg = qt.QMessageBox(self) + msg.setIcon(qt.QMessageBox.Critical) + msg.setText("Error processing fit result: %s" % (sys.exc_info()[1])) + msg.exec_() + if str(self.mainTab.tabText(self.mainTab.currentIndex())).upper() == 'CONCENTRATIONS': + self.mainTab.setCurrentIndex(0) + return self._concentrationsDict = ddict self._concentrationsInfo = info tool.show() @@ -1043,7 +1036,7 @@ class McaAdvancedFit(qt.QWidget): else: delcurves.append(key) for key in delcurves: - self.graph.removeCurve(key, replot=False) + self.graph.removeCurve(key) def matrixSpectrum(self): @@ -1066,17 +1059,17 @@ class McaAdvancedFit(qt.QWidget): ddict = {} ddict.update(config['concentrations']) tool.configure(ddict) - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: ddict, info = tool.processFitResult(fitresult=fitresult, - elementsfrommatrix=True, - addinfo=True) + elementsfrommatrix=True, + addinfo=True) elif 1: try: - thread = CalculationThread.CalculationThread( \ - calculation_method = tool.processFitResult, - calculation_kw = {'fitresult':fitresult, - 'elementsfrommatrix':True, - 'addinfo':True}, + thread = CalculationThread.CalculationThread( + calculation_method=tool.processFitResult, + calculation_kw={'fitresult': fitresult, + 'elementsfrommatrix': True, + 'addinfo': True}, expand_vars=True, expand_kw=True) thread.start() @@ -1174,27 +1167,23 @@ class McaAdvancedFit(qt.QWidget): self.dict['result']['ymatrix']= ddict['result']['ymatrix'] * 1.0 """ if self.graph is not None: - if self._logY: - logfilter = 1 - else: - logfilter = 0 if self._energyAxis: xdata = dict['result']['energy'][:] else: xdata = dict['result']['xdata'][:] - self.graph.newCurve("Matrix",xdata,dict['result']['ymatrix'],logfilter=logfilter) + self.graph.addCurve(xdata, dict['result']['ymatrix'], "Matrix") """ try: self.__anasignal(ddict) except: - print("Error generating matrix output. ") - print("Try to perform your fit again. ") - print(sys.exc_info()) - print("If error persists, please report this error.") - print("ymatrix shape = ", ddict['result']['ymatrix'].shape) - print("xmatrix shape = ", xmatrix.shape) - print("continuum shape = ", ddict['result']['continuum'].shape) - print("zz shape = ", self.mcafit.zz.shape) + _logger.warning("Error generating matrix output. ") + _logger.warning("Try to perform your fit again. ") + _logger.warning("%s", sys.exc_info()) + _logger.warning("If error persists, please report this error.") + _logger.warning("ymatrix shape = %s", ddict['result']['ymatrix'].shape) + _logger.warning("xmatrix shape = %s", xmatrix.shape) + _logger.warning("continuum shape = %s", ddict['result']['continuum'].shape) + _logger.warning("zz shape = %s", self.mcafit.zz.shape) def fisxSpectrum(self): if not self.__fitdone: @@ -1327,7 +1316,7 @@ class McaAdvancedFit(qt.QWidget): fileNamesDict = XRFMCHelper.getOutputFileNames(newFile, outputDir=self.__tmpMatrixSpectrumDir) if newFile != fileNamesDict['fit']: - removeDirectory(self.__tmpMatrixSpectrumDir) + self.removeDirectory(self.__tmpMatrixSpectrumDir) raise ValueError("Inconsistent internal behaviour!") self._xrfmcFileNamesDict = fileNamesDict @@ -1434,8 +1423,8 @@ class McaAdvancedFit(qt.QWidget): try: self.__anasignal(ddict) except: - print("Error generating Monte Carlo matrix output. ") - print(sys.exc_info()) + _logger.warning("Error generating Monte Carlo matrix output. ") + _logger.warning(sys.exc_info()) def peaksSpectrum(self): if not self.__fitdone: @@ -1477,14 +1466,14 @@ class McaAdvancedFit(qt.QWidget): try: self.__anasignal(ddict) except: - print("Error generating peaks output. ") - print("Try to perform your fit again. ") - print(sys.exc_info()) - print("If error persists, please report this error.") - print("ymatrix shape = ", ddict['result']['ymatrix'].shape) - print("xmatrix shape = ", xmatrix.shape) - print("continuum shape = ", ddict['result']['continuum'].shape) - print("zz shape = ", self.mcafit.zz.shape) + _logger.warning("Error generating peaks output. ") + _logger.warning("Try to perform your fit again. ") + _logger.warning("%s", sys.exc_info()) + _logger.warning("If error persists, please report this error.") + _logger.warning("ymatrix shape = %s", ddict['result']['ymatrix'].shape) + _logger.warning("xmatrix shape = %s", xmatrix.shape) + _logger.warning("continuum shape = %s", ddict['result']['continuum'].shape) + _logger.warning("zz shape = %s", self.mcafit.zz.shape) def __printps(self): self.__printmenu.exec_(self.cursor().pos()) @@ -1534,7 +1523,7 @@ class McaAdvancedFit(qt.QWidget): selection=self.info['legend'], fitresult=self.dict, concentrations=self._concentrationsDict, - plotdict={'logy':self.graph.isYAxisLogarithmic()}) + plotdict={'logy': self.graph.isYAxisLogarithmic()}) if 0: #this forces to open and read the file self.__lastreport = report.writeReport() @@ -1709,8 +1698,8 @@ class McaAdvancedFit(qt.QWidget): return h # pyflakes http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=666503 - def __print(self,text): - print("__print not working yet") + def __print(self, text): + _logger.info("__print not working yet") return printer = qt.QPrinter() printDialog = qt.QPrintDialog(printer, self) @@ -1729,9 +1718,8 @@ class McaAdvancedFit(qt.QWidget): document.print_(printer) def setdata(self, *var, **kw): - if DEBUG: - print("McaAdvancedFit.setdata deprecated, use setData instead.") - return self.setData( *var, **kw) + _logger.debug("McaAdvancedFit.setdata deprecated, use setData instead.") + return self.setData(*var, **kw) def setData(self,*var,**kw): """ @@ -1841,8 +1829,7 @@ class McaAdvancedFit(qt.QWidget): self.plot() def setheader(self, *var, **kw): - if DEBUG: - print("McaAdvancedFit.setheader deprecated, use setHeader instead.") + _logger.debug("McaAdvancedFit.setheader deprecated, use setHeader instead.") return self.setHeader( *var, **kw) def setHeader(self,*var,**kw): @@ -1886,18 +1873,14 @@ class McaAdvancedFit(qt.QWidget): msg.setText("No peaks defined.\nPlease configure peaks") msg.exec_() return - if DEBUG: - if DEBUG: - print("calling estimate") + if _logger.getEffectiveLevel() == logging.DEBUG: + _logger.debug("calling estimate") self.mcafit.estimate() - if DEBUG: - print("calling startfit") - fitresult,result = self.mcafit.startfit(digest=1) - if DEBUG: - print("filling table") + _logger.debug("calling startfit") + fitresult, result = self.mcafit.startfit(digest=1) + _logger.debug("filling table") self.mcatable.fillfrommca(result) - if DEBUG: - print("finished") + _logger.debug("finished") elif 1: try: self.mcafit.estimate() @@ -2001,17 +1984,16 @@ class McaAdvancedFit(qt.QWidget): if (str(self.mainTab.tabText(self.mainTab.currentIndex())).upper() == 'CONCENTRATIONS') or \ (self.concentrationsWidget.parent() is None): if not self.concentrationsWidget.isHidden(): - if DEBUG: + try: self.concentrations() - else: - try: - self.concentrations() - except: - msg = qt.QMessageBox(self) - msg.setIcon(qt.QMessageBox.Critical) - msg.setText("Concentrations Error: %s" % (sys.exc_info()[1])) - msg.exec_() - return + except: + if _logger.getEffectiveLevel() == logging.DEBUG: + raise + msg = qt.QMessageBox(self) + msg.setIcon(qt.QMessageBox.Critical) + msg.setText("Concentrations Error: %s" % (sys.exc_info()[1])) + msg.exec_() + return if str(self.mainTab.tabText(self.mainTab.currentIndex())).upper() == 'DIAGNOSTICS': try: self.diagnostics() @@ -2067,10 +2049,7 @@ class McaAdvancedFit(qt.QWidget): self.plot() def plot(self, ddict=None): - if self.graph.isYAxisLogarithmic(): - logfilter = 1 - else: - logfilter = 0 + self.graph.clearCurves() config = self.mcafit.configure() if ddict is None: if not self.__fitdone: @@ -2084,8 +2063,8 @@ class McaAdvancedFit(qt.QWidget): ydata = self.mcafit.ydata * 1.0 xdata.shape= [len(xdata),] ydata.shape= [len(ydata),] - self.graph.addCurve(xdata, ydata, legend="Data", replot=True, replace=True) - self.graph.updateLegends() + self.graph.addCurve(xdata, ydata, legend="Data", replace=True) + self.graph.setActiveCurve("Data") return else: ddict = self.dict @@ -2094,27 +2073,25 @@ class McaAdvancedFit(qt.QWidget): else: xdata = ddict['result']['xdata'][:] self.graph.addCurve(xdata, ddict['result']['ydata'], legend="Data", - replot=False) + replace=True, resetzoom=False) self.graph.addCurve(xdata, ddict['result']['yfit'], legend="Fit", - replot=False) + resetzoom=False) self.graph.addCurve(xdata, ddict['result']['continuum'], - legend="Continuum", - replot=False) + legend="Continuum", resetzoom=False) curveList = self.graph.getAllCurves(just_legend=True) if config['fit']['sumflag']: self.graph.addCurve(xdata, ddict['result']['pileup'] + \ ddict['result']['continuum'], - legend="Pile-up", replot=False) + legend="Pile-up", resetzoom=False) elif "Pile-up" in curveList: - self.graph.removeCurve("Pile-up", replot=False) + self.graph.removeCurve("Pile-up") if self.matrixSpectrumButton.isChecked(): if 'ymatrix' in ddict['result']: - self.graph.addCurve(xdata, - ddict['result']['ymatrix'], - legend="Matrix") + self.graph.addCurve(xdata, ddict['result']['ymatrix'], + legend="Matrix", resetzoom=False) else: self.graph.removeCurve("Matrix") else: @@ -2128,14 +2105,13 @@ class McaAdvancedFit(qt.QWidget): mcxdata = self._xrfmcMatrixSpectra[0] mcydata0 = self._xrfmcMatrixSpectra[2] mcydatan = self._xrfmcMatrixSpectra[-1] - self.graph.addCurve(mcxdata, - mcydata0, + self.graph.addCurve(mcxdata, mcydata0, legend='MC Matrix 1', - replot=False) + resetzoom=False) self.graph.addCurve(mcxdata, mcydatan, legend='MC Matrix %d' % (len(self._xrfmcMatrixSpectra) - 2), - replot=False) + resetzoom=False) if self.peaksSpectrumButton.isChecked(): keep = ['Data','Fit','Continuum','Matrix','Pile-up'] @@ -2154,19 +2130,16 @@ class McaAdvancedFit(qt.QWidget): self.graph.addCurve(xdata, ddict['result'][label], legend=label, - replot=False) + resetzoom=False) else: if group in curveList: - self.graph.removeCurve(label, replot=False) + self.graph.removeCurve(label) else: self.__clearPeaksSpectrum() - - self.graph.replot() - self.graph.updateLegends() + self.graph.setActiveCurve("Data") def _saveGraph(self, dict=None): - curves = self.graph.getAllCurves() - if not len(curves): + if not len(self.graph.getAllCurves(just_legend=True)): return if not self.__fitdone: if False: @@ -2205,7 +2178,7 @@ class McaAdvancedFit(qt.QWidget): fitresult['result']['xdata'][-1])) if MCLabels is not None: if MCSpectra[2].size != fitresult['result']['xdata'].size: - print("Monte Carlo Spectra not saved: Wrong spectrum length.") + _logger.warning("Monte Carlo Spectra not saved: Wrong spectrum length.") MCLabels = None MCSpectra = None @@ -2855,14 +2828,14 @@ class Line(qt.QFrame): def mouseDoubleClickEvent(self, event): - if DEBUG: - print("Double Click Event") + _logger.debug("Double Click Event") ddict={} ddict['event']="DoubleClick" ddict['data'] = event ddict['info'] = self.info self.sigLineDoubleClickEvent.emit(ddict) + class SimpleThread(qt.QThread): def __init__(self, function = None, kw = None): if kw is None: @@ -2882,65 +2855,113 @@ class SimpleThread(qt.QThread): except: self._result = ("Exception",) + sys.exc_info() -class McaGraphWindow(PlotWindow.PlotWindow): - def __init__(self, parent=None, backend=None, plugins=False, - newplot=False, position=True, control=True, **kw): + +class McaGraphWindow(PlotWindow): + def __init__(self, parent=None, backend=None, + position=True, control=True, **kw): super(McaGraphWindow, self).__init__(parent, backend=backend, - plugins=plugins, - newplot=newplot, - energy=True, - roi=True, - logx=False, - fit=True, - position=position, - control=control, - **kw) + position=position, control=control, + roi=True, aspectRatio=False, + print_=False, colormap=False, + yInverted=False, mask=False, + fit=False, save=False, + **kw) self.setDataMargins(0, 0, 0.025, 0.025) self.setPanWithArrowKeys(True) - self.printPreview = PyMcaPrintPreview.PyMcaPrintPreview(modal = 0) + + # No context menu by default, execute zoomBack on right click + plotArea = self.getWidgetHandle() + plotArea.setContextMenuPolicy(qt.Qt.CustomContextMenu) + plotArea.customContextMenuRequested.connect(self._zoomBack) + + # toolbar + # hide unused actions and separators + self.getInteractiveModeToolBar().setVisible(False) + self.getXAxisLogarithmicAction().setVisible(False) + for action in self.toolBar().actions(): + if action.isSeparator(): + action.setVisible(False) + + self.printPreviewTB = SingletonPrintPreviewToolButton( + parent=self.toolBar(), plot=self) + self.printPreviewTB.setIcon( + qt.QIcon(qt.QPixmap(IconDict["fileprint"]))) + + # self.fitIcon = qt.QIcon(qt.QPixmap(IconDict["fit"])) + self.fitButton = qt.QToolButton(self.toolBar()) + self.fitButton.setIcon(qt.QIcon(qt.QPixmap(IconDict["fit"]))) + self.fitButton.setToolTip('Fit of Active Curve') + self.fitButton.clicked.connect(self._fitIconSignal) + + # self.energyIcon = qt.QIcon(qt.QPixmap(IconDict["energy"])) + self.energyButton = qt.QToolButton(self.toolBar()) + self.energyButton.setCheckable(True) + self.energyButton.setIcon(qt.QIcon(qt.QPixmap(IconDict["energy"]))) + self.energyButton.setToolTip('Toggle Energy Axis (On/Off)') + self.energyButton.clicked.connect(self._energyIconSignal) + + self.saveButton = qt.QToolButton(self.toolBar()) + self.saveButton.setIcon(qt.QIcon(qt.QPixmap(IconDict["filesave"]))) + self.saveButton.setToolTip('Save plot snapshot or curves data') + self.saveButton.clicked.connect(self._saveIconSignal) + + self.fitAction = self.toolBar().insertWidget(self.getCopyAction(), + self.fitButton) + self.energyAction = self.toolBar().insertWidget(self.getRoiAction(), + self.energyButton) + + self.saveAction = self.getOutputToolBar().addWidget(self.saveButton) + self.getOutputToolBar().addWidget(qt.HorizontalSpacer(self.toolBar())) + self.printAction = self.getOutputToolBar().addWidget(self.printPreviewTB) + self.setGraphYLabel("Counts") if self.energyButton.isChecked(): self.setGraphXLabel("Energy") else: self.setGraphXLabel("Channel") - def printGraph(self): - pixmap = qt.QPixmap.grabWidget(self.getWidgetHandle()) - self.printPreview.addPixmap(pixmap) - if self.printPreview.isHidden(): - self.printPreview.show() - self.printPreview.raise_() + PyMca_Icons.change_icons(self) def _energyIconSignal(self): - legend = self.getActiveCurve(just_legend=True) - ddict={} - ddict['event'] = 'EnergyClicked' - ddict['active'] = legend - self.sigPlotSignal.emit(ddict) + self.sigPlotSignal.emit( + {'event': 'EnergyClicked', + 'active': self.getActiveCurve(just_legend=True)}) def _fitIconSignal(self): - legend = self.getActiveCurve(just_legend=True) - ddict={} - ddict['event'] = 'FitClicked' - ddict['active'] = legend - self.sigPlotSignal.emit(ddict) + self.sigPlotSignal.emit( + {'event': 'FitClicked', + 'active': self.getActiveCurve(just_legend=True)}) def _saveIconSignal(self): - legend = self.getActiveCurve(just_legend=True) - ddict={} - ddict['event'] = 'SaveClicked' - ddict['active'] = legend - self.sigPlotSignal.emit(ddict) - - def setActiveCurve(self, legend, replot=True): - super(McaGraphWindow, self).setActiveCurve(legend, replot=False) + self.sigPlotSignal.emit( + {'event': 'SaveClicked', + 'active': self.getActiveCurve(just_legend=True)}) + + def setActiveCurve(self, legend, replot=None): + if legend is not None: + # see vasole/pymca#314 + super(McaGraphWindow, self).setActiveCurve(legend, replot) self.setGraphYLabel("Counts") if self.energyButton.isChecked(): self.setGraphXLabel("Energy") else: self.setGraphXLabel("Channel") - if replot: - self.replot() + + def _zoomBack(self, pos): + self.getLimitsHistory().pop() + + if silx.version_info < (0, 9): + # overloaded to force dock widgets area to right + def addTabbedDockWidget(self, dock_widget): + if dock_widget not in self._dockWidgets: + self._dockWidgets.append(dock_widget) + if len(self._dockWidgets) == 1: + self.addDockWidget(qt.Qt.RightDockWidgetArea, dock_widget) + else: + # Other dock widgets are added as tabs to the same widget area + self.tabifyDockWidget(self._dockWidgets[0], + dock_widget) + def test(ffile='03novs060sum.mca', cfg=None): from PyMca5.PyMcaIO import specfilewrapper as specfile @@ -2977,6 +2998,7 @@ def main(): if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) if len(sys.argv) >1: ffile = sys.argv[1] else: diff --git a/PyMca5/PyMcaGui/physics/xrf/McaAdvancedTable.py b/PyMca5/PyMcaGui/physics/xrf/McaAdvancedTable.py index 67c9f38..dcfb0e8 100644 --- a/PyMca5/PyMcaGui/physics/xrf/McaAdvancedTable.py +++ b/PyMca5/PyMcaGui/physics/xrf/McaAdvancedTable.py @@ -30,13 +30,14 @@ __author__ = "V. Armando Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import logging from PyMca5.PyMcaGui import PyMcaQt as qt if hasattr(qt, "QString"): QString = qt.QString else: QString = str QTVERSION = qt.qVersion() -DEBUG=0 +_logger = logging.getLogger(__name__) QTable = qt.QTableWidget @@ -64,10 +65,11 @@ class McaTable(QTable): self.regionlist=[] self.regiondict={} - if QTVERSION < '5.0.0': - self.verticalHeader().setClickable(True) - else: + verticalHeader = self.verticalHeader() + if hasattr(verticalHeader, "setSectionsClickable"): self.verticalHeader().setSectionsClickable(True) + else: + self.verticalHeader().setClickable(True) self.verticalHeader().sectionClicked.connect(self.__myslot) self.itemSelectionChanged.connect(self.__myslot) @@ -244,8 +246,7 @@ class McaTable(QTable): hb = self.horizontalHeader().paletteBackgroundColor() hcolor = ("#%x%x%x" % (hb.red(),hb.green(),hb.blue())).upper() else: - if DEBUG: - print("color background to implement") + _logger.debug("color background to implement") hcolor = ("#%x%x%x" % (230,240,249)).upper() text = "" text += ("<nobr>") diff --git a/PyMca5/PyMcaGui/physics/xrf/McaCalWidget.py b/PyMca5/PyMcaGui/physics/xrf/McaCalWidget.py index 9d28fd1..7477eb5 100644 --- a/PyMca5/PyMcaGui/physics/xrf/McaCalWidget.py +++ b/PyMca5/PyMcaGui/physics/xrf/McaCalWidget.py @@ -35,9 +35,11 @@ import sys import numpy from numpy.linalg import inv as inverse import copy +import logging from PyMca5.PyMcaGui import PyMcaQt as qt -from PyMca5.PyMcaGui import PlotWidget +from silx.gui.plot import PlotWidget +from silx.gui.plot.tools.toolbars import OutputToolBar if hasattr(qt, "QString"): QString = qt.QString @@ -52,7 +54,7 @@ IconDict = PyMca_Icons.IconDict from . import PeakTableWidget if 0: from PyMca5 import XRDPeakTableWidget -DEBUG = 0 +_logger = logging.getLogger(__name__) LOW_HEIGHT_THRESHOLD = 660 @@ -136,8 +138,8 @@ class McaCalWidget(qt.QDialog): self.layout.addWidget(self.container) #The graph - self.graph= PlotWidget.PlotWidget(self.container, - backend=None) + self.graph = PlotWidget(self.container, + backend=None) self.graph.setGraphXLabel('Channel') self.graph.setGraphYLabel('Counts') self.graph.setDataMargins(0.0, 0.0, 0.0, 0.0) @@ -220,6 +222,13 @@ class McaCalWidget(qt.QDialog): self.manualsearch, 'Add a peak to the graph', toggle=True) + # copy to clipboard + output_tb = OutputToolBar(parent=self.toolbar, + plot=self.graph) + output_tb.getPrintAction().setVisible(False) + output_tb.getSaveAction().setVisible(False) + self.toolbar.layout.addWidget(output_tb) + self.toolbar.layout.addWidget(qt.HorizontalSpacer(toolbar)) label=qt.QLabel(toolbar) label.setText('<b>Channel:</b>') @@ -249,7 +258,6 @@ class McaCalWidget(qt.QDialog): self.epos.setFixedWidth(self.epos.fontMetrics().width('#########')) self.toolbar.layout.addWidget(self.epos) - #rest toolbar2 = qt.QWidget(self) self.layout.addWidget(toolbar2) @@ -289,8 +297,7 @@ class McaCalWidget(qt.QDialog): return tb def _toggleLogY(self): - if DEBUG: - print("_toggleLogY") + _logger.debug("_toggleLogY") if self.graph.isYAxisLogarithmic(): self.setYAxisLogarithmic(False) else: @@ -311,19 +318,19 @@ class McaCalWidget(qt.QDialog): self.okButton.clicked.connect(self.accept) self.cancelButton.clicked.connect(self.reject) - def plot(self,x,y,legend): + def plot(self, x, y, legend): #clear graph self.graph.clear() - self.graph.addCurve(x, y , legend=legend, replot=True) + self.graph.addCurve(x, y, legend=legend) self.dict['x'] = x self.dict['y'] = y self.dict['legend'] = legend + self.graph.setActiveCurve(legend) #reset the zoom self._resetZoom() def peakSearch(self): - if DEBUG: - print("Peak search called") + _logger.debug("Peak search called") if self.__manualsearch: self.__manualsearch = 0 if QTVERSION < '4.0.0': @@ -362,21 +369,18 @@ class McaCalWidget(qt.QDialog): for idx in peaksidx: self.foundPeaks.append(self.specfit.xdata[int(idx)]) #self.graph.insertx1marker(self.specfit.xdata[int(idx)],self.specfit.ydata[int(idx)]) - self.graph.insertXMarker(self.specfit.xdata[int(idx)], - legend="%d" % i, - text=None, - selectable=True, - draggable=False, - replot=False) + self.graph.addXMarker(self.specfit.xdata[int(idx)], + legend="%d" % i, + text=None, + selectable=True, + draggable=False) i += 1 - self.graph.replot() #make sure marker mode is on self.markermode = 0 self.__peakmarkermode() - def clearpeaks(self): - print("DEPRECATED: Use clearPeaks") + _logger.info("DEPRECATED: Use clearPeaks") return self.clearPeaks() def clearPeaks(self): @@ -384,7 +388,6 @@ class McaCalWidget(qt.QDialog): self.graph.clearMarkers() self.__destroylinewidgets() self.peakTable.clearPeaks() - self.graph.replot() def manualsearch(self): #disable peak selection @@ -402,17 +405,16 @@ class McaCalWidget(qt.QDialog): if self.markermode: self.graph.setCursor(qt.QCursor(qt.Qt.CrossCursor)) self.markermode = 0 - self.graph.setZoomModeEnabled(False) + self.graph.setInteractiveMode('select') else: self.markermode = 1 self.nomarkercursor = self.graph.cursor().shape() self.graph.setCursor(qt.QCursor(qt.Qt.PointingHandCursor)) - self.graph.setZoomModeEnabled(True) + self.graph.setInteractiveMode('zoom') #self.markerButton.setOn(self.markermode == 1) def __calparsignal(self,dict): - if DEBUG: - print("__calparsignal called dict = ",dict) + _logger.debug("__calparsignal called dict = %s", dict) if dict['event'] == 'coeff': current = dict['calname' ] self.current = current @@ -445,10 +447,9 @@ class McaCalWidget(qt.QDialog): calenergy = deltat * (i + 1) self.foundPeaks.append(channel) name = "%d" % i - marker = self.graph.insertXMarker(channel, - legend=name, - color="red", - replot=False) + marker = self.graph.addXMarker(channel, + legend=name, + color="red") if name in self.peakTable.peaks.keys(): self.peakTable.configure(number=name, channel=channel, @@ -466,7 +467,6 @@ class McaCalWidget(qt.QDialog): #make sure we cannot select the peaks again self.markermode = 1 self.__peakmarkermode() - self.graph.replot() else: self.caldict[current]['A'] = dict['caldict'][current]['A'] self.caldict[current]['B'] = dict['caldict'][current]['B'] @@ -493,25 +493,20 @@ class McaCalWidget(qt.QDialog): elif dict['boxname'] == 'Calibration': pass else: - if DEBUG: - print("Unknown combobox", dict['boxname']) + _logger.debug("Unknown combobox %s", dict['boxname']) else: - print("Unknown signal ", dict) + _logger.warning("Unknown signal %s", dict) def __graphsignal(self, ddict): - if DEBUG: - print("__graphsignal called with dict = ", ddict) + _logger.debug("__graphsignal called with dict = %s", ddict) if ddict['event'] in ['markerClicked', 'markerSelected']: - if DEBUG: - print("Setting marker color") + _logger.debug("Setting marker color") marker = int(ddict['label']) #The marker corresponds to the peak number channel = self.foundPeaks[marker] - self.graph.insertXMarker(channel, - legend=ddict['label'], - color='red', - replot=False) - self.graph.replot() + self.graph.addXMarker(channel, + legend=ddict['label'], + color='red') current = self.current calenergy = self.caldict[current]['A']+ \ self.caldict[current]['B'] * channel+ \ @@ -542,8 +537,7 @@ class McaCalWidget(qt.QDialog): ret = linewidget.exec_() if ret == qt.QDialog.Accepted: ddict=linewidget.getDict() - if DEBUG: - print("dict got from dialog = ",ddict) + _logger.debug("dict got from dialog = %s", ddict) if ddict != {}: if name in self.peakTable.peaks.keys(): self.peakTable.configure(*ddict) @@ -574,13 +568,10 @@ class McaCalWidget(qt.QDialog): self.caldict[current]['C'] = newcal[2] self.__peakTableSignal({'event':'use'}, calculate=False) else: - if DEBUG: - print("Dialog cancelled or closed ") - self.graph.insertXMarker(channel, - legend=ddict['label'], - color='black', - replot=False) - self.graph.replot() + _logger.debug("Dialog cancelled or closed ") + self.graph.addXMarker(channel, + legend=ddict['label'], + color='black') del linewidget elif ddict['event'] in ["mouseMoved", 'MouseAt']: self.xpos.setText('%.1f' % ddict['x']) @@ -602,19 +593,16 @@ class McaCalWidget(qt.QDialog): self.foundPeaks.append(x) legend = "%d" % (len(self.foundPeaks)-1) #self.graph.insertx1marker(self.specfit.xdata[int(idx)],self.specfit.ydata[int(idx)]) - self.graph.insertXMarker(x, legend=legend, - selectable=True, replot=False) - self.graph.replot() + self.graph.addXMarker(x, legend=legend, + selectable=True) self.markermode = 0 self.__peakmarkermode() self.__msb.setChecked(0) else: - if DEBUG: - print("Unhandled event ", ddict['event']) + _logger.debug("Unhandled event %s", ddict['event']) def __peakTableSignal(self, ddict, calculate=True): - if DEBUG: - print("__peaktablesignal called dict = ",ddict) + _logger.debug("__peaktablesignal called dict = %s", ddict) if (ddict['event'] == 'use') or (ddict['event'] == 'setenergy'): #get table dictionary peakdict = self.peakTable.getDict() @@ -821,10 +809,9 @@ class McaCalWidget(qt.QDialog): return self.calculateTOF(usedpeaks) if len(usedpeaks) == 1: if (usedpeaks[0][0] - 0.0) > 1.0E-20: - return [0.0,usedpeaks[0][1]/usedpeaks[0][0],0.0] + return [0.0, usedpeaks[0][1]/usedpeaks[0][0], 0.0] else: - if DEBUG: - print("Division by zero") + _logger.debug("Division by zero") current = self.current return [self.caldict[current]['A'], self.caldict[current]['B'], @@ -852,7 +839,7 @@ class McaCalWidget(qt.QDialog): return result def getdict(self): - print("DEPRECATED. Use getDict") + _logger.info("DEPRECATED. Use getDict") return self.getDict() def getDict(self): @@ -1079,6 +1066,7 @@ class CalibrationParameters(qt.QWidget): self.CFixed.clicked.connect(self._CFixSlot) self.orderbox.activated[str].connect(self.__orderbox) + self.savebox.lineEdit().editingFinished[()].connect(self.__savebox) self.savebox.activated[str].connect(self.__savebox) def setParameters(self, pars): @@ -1107,7 +1095,7 @@ class CalibrationParameters(qt.QWidget): return self.current def getdict(self): - print("DEPRECATED. Use getDict") + _logger.info("DEPRECATED. Use getDict") return self.getDict() def getDict(self): @@ -1146,8 +1134,10 @@ class CalibrationParameters(qt.QWidget): self.CFixed.hide() self.myslot(event='order') - def __savebox(self,qstring): - key = str(qstring) + def __savebox(self, qstring=None): + if qstring is None: + qstring = self.savebox.currentText() + key = qt.safe_str(qstring) if key not in self.caldict.keys(): self.caldict[key] = {} if QTVERSION < '4.0.0': @@ -1210,10 +1200,9 @@ class CalibrationParameters(qt.QWidget): msg.exec_() self.CText.setFocus() - def myslot(self,*var,**kw): - if DEBUG: - print("Cal Parameters Slot ",var,kw) - print(self.caldict[self.currentcal]) + def myslot(self, *var, **kw): + _logger.debug("Cal Parameters Slot %s %s", var, kw) + _logger.debug("%s", self.caldict[self.currentcal]) if 'event' in kw: ddict={} if (kw['event'] == 'order'): @@ -1361,8 +1350,9 @@ class InputLine(qt.QDialog): setenergy=setenergy, use=use, calenergy=calenergy) + def getdict(self): - print("DEPRECATED. Use getDict") + _logger.info("DEPRECATED. Use getDict") return self.getDict() def getDict(self): @@ -1620,7 +1610,7 @@ class McaCalCopy(qt.QDialog): self.CText.setText("%.7g" % self.caldict[text]['C']) def getdict(self): - print("DEPRECATED. Use getDict") + _logger.info("DEPRECATED. Use getDict") return self.getDict() def getDict(self): diff --git a/PyMca5/PyMcaGui/physics/xrf/PeakIdentifier.py b/PyMca5/PyMcaGui/physics/xrf/PeakIdentifier.py index d4b2d9c..fed357e 100644 --- a/PyMca5/PyMcaGui/physics/xrf/PeakIdentifier.py +++ b/PyMca5/PyMcaGui/physics/xrf/PeakIdentifier.py @@ -31,12 +31,14 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaPhysics import Elements from PyMca5.PyMcaGui import PyMca_Icons IconDict = PyMca_Icons.IconDict QTVERSION = qt.qVersion() -DEBUG = 0 + +_logger = logging.getLogger(__name__) class PeakIdentifier(qt.QWidget): @@ -158,7 +160,7 @@ class PeakIdentifier(qt.QWidget): return def myslot(self): - print("PeakIdentifier.py myslot deprecated, use mySlot") + _logger.info("PeakIdentifier.py myslot deprecated, use mySlot") return self.mySlot() def _thresholdSlot(self, value): @@ -281,6 +283,7 @@ class MyQLineEdit(qt.QLineEdit): qt.QLineEdit.focusOutEvent(self, event) def main(): + logging.basicConfig(level=logging.INFO) app = qt.QApplication(sys.argv) winpalette = qt.QPalette(qt.QColor(230,240,249),qt.QColor(238,234,238)) app.setPalette(winpalette) diff --git a/PyMca5/PyMcaGui/physics/xrf/PeakTableWidget.py b/PyMca5/PyMcaGui/physics/xrf/PeakTableWidget.py index 4003c1e..78f815a 100644 --- a/PyMca5/PyMcaGui/physics/xrf/PeakTableWidget.py +++ b/PyMca5/PyMcaGui/physics/xrf/PeakTableWidget.py @@ -31,6 +31,7 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys +import logging from PyMca5.PyMcaGui import PyMcaQt as qt if hasattr(qt, "QStringList"): QStringList = qt.QStringList @@ -43,7 +44,8 @@ else: QString = str from PyMca5.PyMcaPhysics import Elements -DEBUG=0 +_logger = logging.getLogger(__name__) + QTable = qt.QTableWidget class QComboTableItem(qt.QComboBox): @@ -55,8 +57,7 @@ class QComboTableItem(qt.QComboBox): self.activated[int].connect(self._cellChanged) def _cellChanged(self, idx): - if DEBUG: - print("cell changed",idx) + _logger.debug("cell changed %s", idx) self.sigCellChanged.emit(self._row, self._col) class QCheckBoxItem(qt.QCheckBox): @@ -178,14 +179,13 @@ class PeakTableWidget(QTable): self.peaks[peak]['use_item'].setChecked(self.peaks[peak]['use']) def myslot(self, row, col): - if DEBUG: - print("Passing by myslot", - self.peaks[self.peaklist[row]]['fields'][col]) - peak=self.peaklist[row] - field=self.peaks[peak]['fields'][col] + _logger.debug("Passing by myslot %s", + self.peaks[self.peaklist[row]]['fields'][col]) + peak = self.peaklist[row] + field = self.peaks[peak]['fields'][col] if (field == "element") or (field == "elementline"): - key = field+"_item" - newvalue=self.peaks[peak][key].currentText() + key = field + "_item" + newvalue = self.peaks[peak][key].currentText() elif field == "use": pass else: @@ -239,7 +239,7 @@ class PeakTableWidget(QTable): energy = "%.5f " % (Elements.Element[ele][transition]['energy']) break if energy == "0.0": - print("Something is wrong") + _logger.warning("Something is wrong") else: self.configure(name=peak,setenergy=energy) self.setReadOnly(peak,'setenergy') @@ -249,7 +249,7 @@ class PeakTableWidget(QTable): try: value = float(str(newvalue)) except: - print(field, " newvalue = ", newvalue, "taking old value", oldvalue) + _logger.warning("%s newvalue = %s taking old value %s", field, newvalue, oldvalue) item = self.item(row, col) item.setText("%s" % oldvalue) value = float(str(oldvalue)) @@ -263,7 +263,7 @@ class PeakTableWidget(QTable): try: value = float(str(newvalue)) except: - print(field, " newvalue = ", newvalue, "taking old value", oldvalue) + _logger.warning("%s newvalue = %s taking old value%s", field, newvalue, oldvalue) item = self.item(row, col) item.setText("%s" % oldvalue) value = float(str(oldvalue)) @@ -281,22 +281,18 @@ class PeakTableWidget(QTable): ddict['event'] = 'use' self.sigPeakTableWidgetSignal.emit(ddict) - def setReadOnly(self,parameter,fields): - if DEBUG: - print("peak ",parameter,"fields = ",fields,"asked to be read only") + def setReadOnly(self, parameter, fields): + _logger.debug("peak %s fields = %s asked to be read only", parameter, fields) self.setfield(parameter, fields, - qt.Qt.ItemIsSelectable|qt.Qt.ItemIsEnabled) - + qt.Qt.ItemIsSelectable | qt.Qt.ItemIsEnabled) - def setReadWrite(self,parameter,fields): - if DEBUG: - print("peak ",parameter,"fields = ",fields,"asked to be read write") + def setReadWrite(self, parameter, fields): + _logger.debug("peak %s fields = %s asked to be read write", parameter, fields) self.setfield(parameter, fields, - qt.Qt.ItemIsEditable|qt.Qt.ItemIsSelectable|qt.Qt.ItemIsEnabled) + qt.Qt.ItemIsEditable | qt.Qt.ItemIsSelectable | qt.Qt.ItemIsEnabled) def setfield(self,peak,fields,EditType): - if DEBUG: - print("setfield. peak =",peak,"fields = ",fields) + _logger.debug("setfield. peak = %s fields = %s",peak, fields) if type(peak) == type (()) or \ type(peak) == type ([]): peaklist=peak @@ -331,10 +327,9 @@ class PeakTableWidget(QTable): def configure(self,*vars,**kw): - if DEBUG: - print("configure called with **kw = ",kw) - print("configure called with *vars = ",vars) - name=None + _logger.debug("configure called with **kw = %s", kw) + _logger.debug("configure called with *vars = %s", vars) + name = None error=0 if 'name' in kw: name=kw['name'] @@ -371,13 +366,13 @@ class PeakTableWidget(QTable): try: self.myslot(row,col) except: - print("Error setting element") + _logger.warning("Error setting element") elif key is 'elementline': try: iv = self.peaks[name][key+"_item"].findText(QString(kw[key])) self.peaks[name][key+"_item"].setCurrentIndex(iv) except: - print("Error setting elementline") + _logger.warning("Error setting elementline") elif key is 'use': if kw[key]: self.peaks[name][key] = 1 @@ -400,11 +395,10 @@ class PeakTableWidget(QTable): else: item.setText(text) elif key == 'channel': - if DEBUG: - print("setting channel in configure") + _logger.debug("setting channel in configure") if len(str(kw[key])): - newvalue=float(str(kw[key])) - newvalue= QString("%.3f" % newvalue) + newvalue = float(str(kw[key])) + newvalue = QString("%.3f" % newvalue) self.peaks[name][key]=newvalue else: self.peaks[name][key]=oldvalue @@ -454,7 +448,7 @@ class PeakTableWidget(QTable): return 1 def getdict(self, *var): - print("PeakTableWidget.getdict deprecated. Use getDict") + _logger.warning("PeakTableWidget.getdict deprecated. Use getDict") return self.getDict(*var) def getDict(self,*var): diff --git a/PyMca5/PyMcaGui/physics/xrf/QXTube.py b/PyMca5/PyMcaGui/physics/xrf/QXTube.py index cb9551d..a04e3b6 100644 --- a/PyMca5/PyMcaGui/physics/xrf/QXTube.py +++ b/PyMca5/PyMcaGui/physics/xrf/QXTube.py @@ -30,14 +30,16 @@ __author__ = "V. Armando Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import logging from PyMca5.PyMcaPhysics import Elements from PyMca5.PyMcaPhysics import XRayTubeEbel import numpy -from PyMca5.PyMcaGui import PlotWindow from PyMca5.PyMcaGui import PyMcaQt as qt +from PyMca5.PyMcaGui.PluginsToolButton import PluginsToolButton +from silx.gui.plot import PlotWindow +_logger = logging.getLogger(__name__) -DEBUG = 0 if qt.qVersion() > '4.0.0': class QGridLayout(qt.QGridLayout): @@ -86,8 +88,14 @@ class QXTube(qt.QWidget): self.l.addWidget(label) self.l.addWidget(hbox) - self.graph = PlotWindow.PlotWindow(self, - backend=None) + self.graph = PlotWindow(self, colormap=False, yInverted=False, + aspectRatio=False, control=False, + position=False, roi=False, mask=False, + fit=False) + self.pluginsToolButton = PluginsToolButton(plot=self.graph) + self.graph.toolBar().addWidget(self.pluginsToolButton) + self.graph.getInteractiveModeToolBar().getZoomModeAction().setVisible(False) + self.graph.getInteractiveModeToolBar().getPanModeAction().setVisible(False) self.l.addWidget(self.graph) self.graph.setGraphXLabel("Energy (keV)") self.graph.setGraphYLabel("photons/sr/mA/keV/s") @@ -133,11 +141,8 @@ class QXTube(qt.QWidget): targetthickness=anodethickness, filterlist=filterlist) - - - - self.graph.addCurve(e, continuumR, "continuumR", replot=False) - self.graph.addCurve(e, continuumT, "continuumT", replot=False) + self.graph.addCurve(e, continuumR, "continuumR") + self.graph.addCurve(e, continuumT, "continuumT") else: continuum = XRayTubeEbel.continuumEbel([anode, anodedensity, anodethickness], voltage, e, @@ -146,10 +151,10 @@ class QXTube(qt.QWidget): transmission=transmission, targetthickness=anodethickness, filterlist=filterlist) - self.graph.addCurve(e, continuum, "continuum", replot=False) + self.graph.addCurve(e, continuum, "continuum") + self.graph.setActiveCurve("continuum") self.graph.resetZoom() - self.graph.replot() def _export(self): d = self.tubeWidget.getParameters() @@ -192,19 +197,18 @@ class QXTube(qt.QWidget): filterlist=filterlist) d["characteristic"] = fllines - if DEBUG: - fsum = 0.0 - for l in fllines: - print("%s %.4f %.3e" % (l[2],l[0],l[1])) - fsum += l[1] - print(fsum) - - energy, energyweight, energyscatter = XRayTubeEbel.generateLists([anode, anodedensity, - anodethickness], + fsum = 0.0 + for l in fllines: + _logger.debug("%s %.4f %.3e", l[2], l[0], l[1]) + fsum += l[1] + _logger.debug("%s", fsum) + + energy, energyweight, energyscatter = XRayTubeEbel.generateLists( + [anode, anodedensity, anodethickness], voltage, - window = [wele, wdensity, wthickness], - alphae = alphae, alphax = alphax, - transmission = transmission, + window=[wele, wdensity, wthickness], + alphae=alphae, alphax=alphax, + transmission=transmission, targetthickness=anodethickness, filterlist=filterlist) @@ -412,23 +416,19 @@ class TubeWidget(qt.QWidget): return d def _anodeSlot(self, ddict): - if DEBUG: - print("_anodeSlot", ddict) + _logger.debug("_anodeSlot %s", ddict) self.anodeDensity.setText("%f" % Elements.Element[ddict["element"]]["density"]) def _windowSlot(self, ddict): - if DEBUG: - print("_windowSlot", ddict) + _logger.debug("_windowSlot %s", ddict) self.windowDensity.setText("%f" % Elements.Element[ddict["element"]]["density"]) def _filter1Slot(self, ddict): - if DEBUG: - print("_filter1Slot", ddict) + _logger.debug("_filter1Slot %s", ddict) self.filter1Density.setText("%f" % Elements.Element[ddict["element"]]["density"]) def _transmissionSlot(self): - if DEBUG: - print("_transmissionSlot") + _logger.debug("_transmissionSlot") if self.transmissionCheckBox.isChecked(): self.anodeThickness.setEnabled(1) else: @@ -460,8 +460,7 @@ class MyQComboBox(qt.QComboBox): return self.currentIndex(),str(self.currentText()) def _mySignal(self, qstring0): - if DEBUG: - print("_mySignal ", qstring0) + _logger.debug("_mySignal %s", qstring0) text = str(qstring0) d = {} d['event'] = 'activated' @@ -470,6 +469,7 @@ class MyQComboBox(qt.QComboBox): self.sigMyQComboBoxSignal.emit(d) if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) app = qt.QApplication([]) w = QXTube() w.show() diff --git a/PyMca5/PyMcaGui/physics/xrf/StrategyHandler.py b/PyMca5/PyMcaGui/physics/xrf/StrategyHandler.py index 710371f..07dfd6e 100644 --- a/PyMca5/PyMcaGui/physics/xrf/StrategyHandler.py +++ b/PyMca5/PyMcaGui/physics/xrf/StrategyHandler.py @@ -32,6 +32,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import copy +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import PyMcaFileDialogs from PyMca5.PyMcaPhysics import Elements @@ -41,7 +42,8 @@ from .MaterialEditor import MaterialComboBox IconDict = PyMca_Icons.IconDict QTVERSION = qt.qVersion() -DEBUG = 0 +_logger = logging.getLogger(__name__) + def _getPeakList(fitConfiguration): elementsList = [] @@ -365,10 +367,9 @@ class IterationTable(qt.QTableWidget): item.setEditText(material) def mySlot(self,row,col): - if DEBUG: - print("Value changed row = %d col = %d" % (row, col)) - if col != 0: - print("Text = %s" % self.cellWidget(row, col).currentText()) + _logger.debug("Value changed row = %d col = %d", row, col) + if col != 0: + _logger.debug("Text = %s", self.cellWidget(row, col).currentText()) def _checkBoxSlot(self, ddict): # check we do not have duplicates @@ -461,8 +462,7 @@ class IterationTable(qt.QTableWidget): materialItem.setCurrentIndex(0) def _peakFamilySlot(self, ddict): - if DEBUG: - print("_peakFamilySlot", ddict) + _logger.debug("_peakFamilySlot %s", ddict) # check we do not have duplicates target = ddict["text"].split()[0] row = ddict['row'] @@ -486,8 +486,7 @@ class IterationTable(qt.QTableWidget): self.sigValueChanged.emit(row, col) def _comboSlot(self, ddict): - if DEBUG: - print("_comboSlot", ddict) + _logger.debug("_comboSlot %s", ddict) row = ddict['row'] col = ddict['col'] text = ddict['text'] diff --git a/PyMca5/PyMcaGui/plotting/ColormapDialog.py b/PyMca5/PyMcaGui/plotting/ColormapDialog.py index b42bac2..e0851ce 100644 --- a/PyMca5/PyMcaGui/plotting/ColormapDialog.py +++ b/PyMca5/PyMcaGui/plotting/ColormapDialog.py @@ -28,12 +28,14 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys +import logging from PyMca5.PyMcaGui import PyMcaQt as qt -from . import PlotWidget +from silx.gui.plot import PlotWidget QTVERSION = qt.qVersion() -DEBUG = 0 +_logger = logging.getLogger(__name__) + class MyQLineEdit(qt.QLineEdit): def __init__(self,parent=None,name=""): @@ -47,8 +49,7 @@ class MyQLineEdit(qt.QLineEdit): self.returnPressed[()].emit() def setPaletteBackgroundColor(self, color): - if DEBUG: - print("setPalettebackgroundColor not implemented yet") + _logger.debug("setPalettebackgroundColor not implemented yet") pass """ @@ -213,9 +214,9 @@ class ColormapDialog(qt.QDialog): # Graph widget for color curve... - self.c = PlotWidget.PlotWidget(self, backend=None) + self.c = PlotWidget(self, backend=None) self.c.setGraphXLabel("Data Values") - self.c.setZoomModeEnabled(False) + self.c.setInteractiveMode('select') self.marge = (abs(self.dataMax) + abs(self.dataMin)) / 6.0 self.minmd = self.dataMin - self.marge @@ -244,11 +245,11 @@ class ColormapDialog(qt.QDialog): color = "black" #TODO symbol legend = "%d" % i - self.c.insertXMarker(x[i], - legend=legend, - text=labelList[i], - draggable=draggable, - color=color) + self.c.addXMarker(x[i], + legend=legend, + text=labelList[i], + draggable=draggable, + color=color) self.markers.append((legend, "")) self.c.setMinimumSize(qt.QSize(250,200)) @@ -267,16 +268,13 @@ class ColormapDialog(qt.QDialog): bins, counts = self.histogramData self.c.addCurve(bins, counts, "Histogram", - color='pink', # TODO: Change fill color - symbol='s', - linestyle='-', # Line style - #fill=True, - yaxis='right') - # TODO: Do not use info! + color='darkYellow', + histogram='center', + yaxis='right', + fill=True) def _update(self): - if DEBUG: - print("colormap _update called") + _logger.debug("colormap _update called") self.marge = (abs(self.dataMax) + abs(self.dataMin)) / 6.0 self.minmd = self.dataMin - self.marge self.maxpd = self.dataMax + self.marge @@ -300,17 +298,15 @@ class ColormapDialog(qt.QDialog): color = "black" key = self.markers[i][0] label = self.markers[i][1] - self.c.insertXMarker(self.__x[i], - legend=key, - text=label, - draggable=draggable, - color=color) - self.c.replot() + self.c.addXMarker(self.__x[i], + legend=key, + text=label, + draggable=draggable, + color=color) self.sendColormap() def buttonGroupChange(self, val): - if DEBUG: - print("buttonGroup asking to update colormap") + _logger.debug("buttonGroup asking to update colormap") self.setColormapType(val, update=True) self._update() @@ -327,8 +323,7 @@ class ColormapDialog(qt.QDialog): self._update() def chval(self, ddict): - if DEBUG: - print("Received ", ddict) + _logger.debug("Received %s", ddict) if ddict['event'] == 'markerMoving': diam = int(ddict['label']) x = ddict['x'] @@ -368,8 +363,7 @@ class ColormapDialog(qt.QDialog): self.sendColormap() def setAutoscale(self, val): - if DEBUG: - print("setAutoscale called", val) + _logger.debug("setAutoscale called %s", val) if val: self.autoScaleButton.setChecked(True) self.autoScale90Button.setChecked(False) @@ -378,14 +372,14 @@ class ColormapDialog(qt.QDialog): self.setMaxValue(self.dataMax) self.maxText.setEnabled(0) self.minText.setEnabled(0) - self.c.setEnabled(0) + self.c.setEnabled(False) #self.c.disablemarkermode() else: self.autoScaleButton.setChecked(False) self.autoScale90Button.setChecked(False) self.minText.setEnabled(1) self.maxText.setEnabled(1) - self.c.setEnabled(1) + self.c.setEnabled(True) #self.c.enablemarkermode() """ @@ -403,12 +397,12 @@ class ColormapDialog(qt.QDialog): self.setMaxValue(self.dataMax - abs(self.dataMax/10)) self.minText.setEnabled(0) self.maxText.setEnabled(0) - self.c.setEnabled(0) + self.c.setEnabled(False) else: self.autoScale90Button.setChecked(False) self.minText.setEnabled(1) self.maxText.setEnabled(1) - self.c.setEnabled(1) + self.c.setEnabled(True) self.c.setFocus() @@ -424,7 +418,7 @@ class ColormapDialog(qt.QDialog): self.__x[1] = v key = self.markers[1][0] label = self.markers[1][1] - self.c.insertXMarker(v, legend=key, text=label, color="blue", draggable=True) + self.c.addXMarker(v, legend=key, text=label, color="blue", draggable=True) self.c.addCurve(self.__x, self.__y, legend="ConstrainedCurve", @@ -455,7 +449,7 @@ class ColormapDialog(qt.QDialog): self.__x[1] = val key = self.markers[1][0] label = self.markers[1][1] - self.c.insertXMarker(val, legend=key, text=label, color="blue", draggable=True) + self.c.addXMarker(val, legend=key, text=label, color="blue", draggable=True) self.c.addCurve(self.__x, self.__y, legend="ConstrainedCurve", color='black', @@ -472,7 +466,7 @@ class ColormapDialog(qt.QDialog): self.__x[2] = v key = self.markers[2][0] label = self.markers[2][1] - self.c.insertXMarker(v, legend=key, text=label, color="blue", draggable=True) + self.c.addXMarker(v, legend=key, text=label, color="blue", draggable=True) self.c.addCurve(self.__x, self.__y, legend="ConstrainedCurve", color='black', @@ -501,7 +495,7 @@ class ColormapDialog(qt.QDialog): self.__x[2] = val key = self.markers[2][0] label = self.markers[2][1] - self.c.insertXMarker(val, legend=key, text=label, color="blue", draggable=True) + self.c.addXMarker(val, legend=key, text=label, color="blue", draggable=True) self.c.addCurve(self.__x, self.__y, legend="ConstrainedCurve", color='black', @@ -542,8 +536,7 @@ class ColormapDialog(qt.QDialog): send 'ColormapChanged' signal """ def sendColormap(self): - if DEBUG: - print("sending colormap") + _logger.debug("sending colormap") try: cmap = self.getColormap() self.sigColormapChanged.emit(cmap) diff --git a/PyMca5/PyMcaGui/plotting/ImageView.py b/PyMca5/PyMcaGui/plotting/ImageView.py index 2f8bd11..3371533 100644 --- a/PyMca5/PyMcaGui/plotting/ImageView.py +++ b/PyMca5/PyMcaGui/plotting/ImageView.py @@ -31,25 +31,11 @@ __contact__ = "thomas.vincent@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" __doc__ = """ -QWidget displaying a 2D image with histograms on its sides. +The classes in this module are deprecated. Use +:class:`silx.gui.plot.ImageView` and +:class:`silx.gui.plot.ImageViewMainWindow` instead. -The :class:`ImageView` implements this widget, and -:class:`ImageViewMainWindow` provides a main window with additional toolbar -and status bar. - -Basic usage of :class:`ImageView` is through the following methods: - -- :meth:`ImageView.getColormap`, :meth:`ImageView.setColormap` to update the - default colormap to use and update the currently displayed image. -- :meth:`ImageView.setImage` to update the displayed image. - -The :class:`ImageView` uses :class:`PlotWindow` and also -exposes :class:`PyMca5.PyMcaGraph.Plot` API for further control -(plot title, axes labels, adding other images, ...). - -For an example of use, see the implementation of :class:`ImageViewMainWindow`. - -The ImageView module can also be used to open an EDF or TIFF file +This module can be used to open an EDF or TIFF file from the shell command line. To view an image file: ``python -m PyMca5.PyMcaGui.plotting.ImageView <file to open>`` @@ -59,872 +45,50 @@ To get help: # import ###################################################################### - -import numpy as np - +import logging +import traceback try: from .. import PyMcaQt as qt except ImportError: from PyMca5.PyMcaGui import PyMcaQt as qt -from .PlotWindow import PlotWindow -from .Toolbars import ProfileToolBar, LimitsToolBar - -from PyMca5.PyMcaGraph import Plot - - -# utils ####################################################################### - -_COLORMAP_CURSOR_COLORS = { - 'gray': 'pink', - 'reversed gray': 'pink', - 'temperature': 'black', - 'red': 'gray', - 'green': 'gray', - 'blue': 'gray'} - - -def _cursorColorForColormap(colormapName): - """Get a color suitable for overlay over a colormap. - - :param str colormapName: The name of the colormap. - :return: Name of the color. - :rtype: str - """ - return _COLORMAP_CURSOR_COLORS.get(colormapName, 'black') - - -# RadarView ################################################################### - -class RadarView(qt.QGraphicsView): - """Widget presenting a synthetic view of a 2D area and - the current visible area. - - Coordinates are as in QGraphicsView: - x goes from left to right and y goes from top to bottom. - This widget preserves the aspect ratio of the areas. - - The 2D area and the visible area can be set with :meth:`setDataRect` - and :meth:`setVisibleRect`. - When the visible area has been dragged by the user, its new position - is signaled by the *visibleRectDragged* signal. - - It is possible to invert the direction of the axes by using the - :meth:`scale` method of QGraphicsView. - """ - - visibleRectDragged = qt.pyqtSignal(float, float, float, float) - """Signals that the visible rectangle has been dragged. - - It provides: left, top, width, height in data coordinates. - """ - - _DATA_PEN = qt.QPen(qt.QColor('white')) - _DATA_BRUSH = qt.QBrush(qt.QColor('light gray')) - _VISIBLE_PEN = qt.QPen(qt.QColor('red')) - _VISIBLE_BRUSH = qt.QBrush(qt.QColor(0, 0, 0, 0)) - _TOOLTIP = 'Radar View:\nVisible area (in red)\nof the image (in gray).' - - _PIXMAP_SIZE = 256 - - class _DraggableRectItem(qt.QGraphicsRectItem): - """RectItem which signals its change through visibleRectDragged.""" - def __init__(self, *args, **kwargs): - super(RadarView._DraggableRectItem, self).__init__(*args, **kwargs) - self.setFlag(qt.QGraphicsItem.ItemIsMovable) - self.setFlag(qt.QGraphicsItem.ItemSendsGeometryChanges) - self._ignoreChange = False - self._constraint = 0, 0, 0, 0 - - def setConstraintRect(self, left, top, width, height): - """Set the constraint rectangle for dragging. - - The coordinates are in the _DraggableRectItem coordinate system. - - This constraint only applies to modification through interaction - (i.e., this constraint is not applied to change through API). - - If the _DraggableRectItem is smaller than the constraint rectangle, - the _DraggableRectItem remains within the constraint rectangle. - If the _DraggableRectItem is wider than the constraint rectangle, - the constraint rectangle remains within the _DraggableRectItem. - """ - self._constraint = left, left + width, top, top + height - - def setPos(self, *args, **kwargs): - """Overridden to ignore changes from API in itemChange.""" - self._ignoreChange = True - super(RadarView._DraggableRectItem, self).setPos(*args, **kwargs) - self._ignoreChange = False - - def moveBy(self, *args, **kwargs): - """Overridden to ignore changes from API in itemChange.""" - self._ignoreChange = True - super(RadarView._DraggableRectItem, self).moveBy(*args, **kwargs) - self._ignoreChange = False - - def itemChange(self, change, value): - """Callback called before applying changes to the item.""" - if (change == qt.QGraphicsItem.ItemPositionChange and - not self._ignoreChange): - # Makes sure that the visible area is in the data - # or that data is in the visible area if area is too wide - x, y = value.x(), value.y() - xMin, xMax, yMin, yMax = self._constraint - - if self.rect().width() <= (xMax - xMin): - if x < xMin: - value.setX(xMin) - elif x > xMax - self.rect().width(): - value.setX(xMax - self.rect().width()) - else: - if x > xMin: - value.setX(xMin) - elif x < xMax - self.rect().width(): - value.setX(xMax - self.rect().width()) - - if self.rect().height() <= (yMax - yMin): - if y < yMin: - value.setY(yMin) - elif y > yMax - self.rect().height(): - value.setY(yMax - self.rect().height()) - else: - if y > yMin: - value.setY(yMin) - elif y < yMax - self.rect().height(): - value.setY(yMax - self.rect().height()) - - if self.pos() != value: - # Notify change through signal - views = self.scene().views() - assert len(views) == 1 - views[0].visibleRectDragged.emit( - value.x() + self.rect().left(), - value.y() + self.rect().top(), - self.rect().width(), - self.rect().height()) - - return value - - return super(RadarView._DraggableRectItem, self).itemChange( - change, value) - - def __init__(self, parent=None): - self._scene = qt.QGraphicsScene() - self._dataRect = self._scene.addRect(0, 0, 1, 1, - self._DATA_PEN, - self._DATA_BRUSH) - self._visibleRect = self._DraggableRectItem(0, 0, 1, 1) - self._visibleRect.setPen(self._VISIBLE_PEN) - self._visibleRect.setBrush(self._VISIBLE_BRUSH) - self._scene.addItem(self._visibleRect) - - super(RadarView, self).__init__(self._scene, parent) - self.setHorizontalScrollBarPolicy(qt.Qt.ScrollBarAlwaysOff) - self.setVerticalScrollBarPolicy(qt.Qt.ScrollBarAlwaysOff) - self.setFocusPolicy(qt.Qt.NoFocus) - self.setStyleSheet('border: 0px') - self.setToolTip(self._TOOLTIP) - - def sizeHint(self): - # """Overridden to avoid sizeHint to depend on content size.""" - return self.minimumSizeHint() - - def wheelEvent(self, event): - # """Overridden to disable vertical scrolling with wheel.""" - event.ignore() - - def resizeEvent(self, event): - # """Overridden to fit current content to new size.""" - self.fitInView(self._scene.itemsBoundingRect(), qt.Qt.KeepAspectRatio) - super(RadarView, self).resizeEvent(event) - - def setDataRect(self, left, top, width, height): - """Set the bounds of the data rectangular area. - - This sets the coordinate system. - """ - self._dataRect.setRect(left, top, width, height) - self._visibleRect.setConstraintRect(left, top, width, height) - self.fitInView(self._scene.itemsBoundingRect(), qt.Qt.KeepAspectRatio) - - def setVisibleRect(self, left, top, width, height): - """Set the visible rectangular area. - - The coordinates are relative to the data rect. - """ - self._visibleRect.setRect(0, 0, width, height) - self._visibleRect.setPos(left, top) - self.fitInView(self._scene.itemsBoundingRect(), qt.Qt.KeepAspectRatio) - - -# ImageView ################################################################### - -class ImageView(qt.QWidget): - """Display a single image with horizontal and vertical histograms. - - Use :meth:`setImage` to control the displayed image. - This class also provides the :class:`PyMca5.PyMcaGraph.Plot` API. - """ - - HISTOGRAMS_COLOR = 'blue' - """Color to use for the side histograms.""" - - HISTOGRAMS_HEIGHT = 200 - """Height in pixels of the side histograms.""" - - IMAGE_MIN_SIZE = 200 - """Minimum size in pixels of the image area.""" - - # Qt signals - valueChanged = qt.pyqtSignal(float, float, float) - """Signals that the data value under the cursor has changed. - - It provides: row, column, data value. - - When the cursor is over an histogram, either row or column is Nan - and the provided data value is the histogram value - (i.e., the sum along the corresponding row/column). - Row and columns are either Nan or integer values. - """ - - def __init__(self, parent=None, windowFlags=qt.Qt.Widget, backend=None): - self._imageLegend = '__ImageView__image' + str(id(self)) - self._cache = None # Store currently visible data information - self._updatingLimits = False - - super(ImageView, self).__init__(parent, windowFlags) - self.setStyleSheet('background-color: white;') - self._initWidgets(backend) - - # Sync PlotBackend and ImageView - self._updateYAxisInverted() - - # Set-up focus proxy to handle arrow key event - self.setFocusProxy(self._imagePlot) - - def _initWidgets(self, backend): - """Set-up layout and plots.""" - # Monkey-patch for histogram size - # alternative: create a layout that does not use widget size hints - def sizeHint(): - return qt.QSize(self.HISTOGRAMS_HEIGHT, self.HISTOGRAMS_HEIGHT) - - self._histoHPlot = Plot.Plot(backend=backend) - self._histoHPlot.setZoomModeEnabled(True) - self._histoHPlot.setCallback(self._histoHPlotCB) - self._histoHPlot.getWidgetHandle().sizeHint = sizeHint - self._histoHPlot.getWidgetHandle().minimumSizeHint = sizeHint - - self._imagePlot = PlotWindow(backend=backend, plugins=False, - colormap=True, flip=True, - grid=False, togglePoints=False, - logx=False, logy=False, - aspect=True) - self._imagePlot.usePlotBackendColormap = True - self._imagePlot.setPanWithArrowKeys(True) - - self._imagePlot.setZoomModeEnabled(True) # Color is set in setColormap - self._imagePlot.sigPlotSignal.connect(self._imagePlotCB) - self._imagePlot.hFlipToolButton.clicked.connect( - self._updateYAxisInverted) - self._imagePlot.sigColormapChangedSignal.connect(self.setColormap) - - self._histoVPlot = Plot.Plot(backend=backend) - self._histoVPlot.setZoomModeEnabled(True) - self._histoVPlot.setCallback(self._histoVPlotCB) - self._histoVPlot.getWidgetHandle().sizeHint = sizeHint - self._histoVPlot.getWidgetHandle().minimumSizeHint = sizeHint - - self._radarView = RadarView() - self._radarView.visibleRectDragged.connect(self._radarViewCB) - - self._layout = qt.QGridLayout() - self._layout.addWidget(self._imagePlot, 0, 0) - self._layout.addWidget(self._histoVPlot.getWidgetHandle(), 0, 1) - self._layout.addWidget(self._histoHPlot.getWidgetHandle(), 1, 0) - self._layout.addWidget(self._radarView, 1, 1) - - self._layout.setColumnMinimumWidth(0, self.IMAGE_MIN_SIZE) - self._layout.setColumnStretch(0, 1) - self._layout.setColumnMinimumWidth(1, self.HISTOGRAMS_HEIGHT) - self._layout.setColumnStretch(1, 0) - - self._layout.setRowMinimumHeight(0, self.IMAGE_MIN_SIZE) - self._layout.setRowStretch(0, 1) - self._layout.setRowMinimumHeight(1, self.HISTOGRAMS_HEIGHT) - self._layout.setRowStretch(1, 0) - - self._layout.setSpacing(0) - self._layout.setContentsMargins(0, 0, 0, 0) - - self.setLayout(self._layout) - - def _dirtyCache(self): - self._cache = None - - def _updateHistograms(self): - """Update histograms content using current active image.""" - activeImage = self._imagePlot.getActiveImage() - if activeImage is not None: - wasUpdatingLimits = self._updatingLimits - self._updatingLimits = True - - data, legend, info, pixmap = activeImage - xScale, yScale = info['plot_xScale'], info['plot_yScale'] - height, width = data.shape - - xMin, xMax = self._imagePlot.getGraphXLimits() - yMin, yMax = self._imagePlot.getGraphYLimits() - - # Convert plot area limits to image coordinates - # and work in image coordinates (i.e., in pixels) - xMin = int((xMin - xScale[0]) / xScale[1]) - xMax = int((xMax - xScale[0]) / xScale[1]) - yMin = int((yMin - yScale[0]) / yScale[1]) - yMax = int((yMax - yScale[0]) / yScale[1]) - - if (xMin < width and xMax >= 0 and - yMin < height and yMax >= 0): - # The image is at least partly in the plot area - # Get the visible bounds in image coords (i.e., in pixels) - subsetXMin = 0 if xMin < 0 else xMin - subsetXMax = (width if xMax >= width else xMax) + 1 - subsetYMin = 0 if yMin < 0 else yMin - subsetYMax = (height if yMax >= height else yMax) + 1 - - if (self._cache is None or - subsetXMin != self._cache['dataXMin'] or - subsetXMax != self._cache['dataXMax'] or - subsetYMin != self._cache['dataYMin'] or - subsetYMax != self._cache['dataYMax']): - # The visible area of data has changed, update histograms - - # Rebuild histograms for visible area - visibleData = data[subsetYMin:subsetYMax, - subsetXMin:subsetXMax] - histoHVisibleData = np.sum(visibleData, axis=0) - histoVVisibleData = np.sum(visibleData, axis=1) - - self._cache = { - 'dataXMin': subsetXMin, - 'dataXMax': subsetXMax, - 'dataYMin': subsetYMin, - 'dataYMax': subsetYMax, - - 'histoH': histoHVisibleData, - 'histoHMin': np.min(histoHVisibleData), - 'histoHMax': np.max(histoHVisibleData), - - 'histoV': histoVVisibleData, - 'histoVMin': np.min(histoVVisibleData), - 'histoVMax': np.max(histoVVisibleData) - } - - # Convert to histogram curve and update plots - # Taking into account origin and scale - coords = np.arange(2 * histoHVisibleData.size) - xCoords = (coords + 1) // 2 + subsetXMin - xCoords = xScale[0] + xScale[1] * xCoords - xData = np.take(histoHVisibleData, coords // 2) - self._histoHPlot.addCurve(xCoords, xData, - xlabel='', ylabel='', - replace=False, replot=False, - color=self.HISTOGRAMS_COLOR, - linestyle='-', - selectable=False) - vMin = self._cache['histoHMin'] - vMax = self._cache['histoHMax'] - vOffset = 0.1 * (vMax - vMin) - if vOffset == 0.: - vOffset = 1. - self._histoHPlot.setGraphYLimits(vMin - vOffset, - vMax + vOffset) - - coords = np.arange(2 * histoVVisibleData.size) - yCoords = (coords + 1) // 2 + subsetYMin - yCoords = yScale[0] + yScale[1] * yCoords - yData = np.take(histoVVisibleData, coords // 2) - self._histoVPlot.addCurve(yData, yCoords, - xlabel='', ylabel='', - replace=False, replot=False, - color=self.HISTOGRAMS_COLOR, - linestyle='-', - selectable=False) - vMin = self._cache['histoVMin'] - vMax = self._cache['histoVMax'] - vOffset = 0.1 * (vMax - vMin) - if vOffset == 0.: - vOffset = 1. - self._histoVPlot.setGraphXLimits(vMin - vOffset, - vMax + vOffset) - else: - self._dirtyCache() - self._histoHPlot.clearCurves() - self._histoVPlot.clearCurves() +from silx.gui.plot.ImageView import ImageView as SilxImageView +from silx.gui.plot.ImageView import ImageViewMainWindow as SilxImageViewMainWindow - self._updatingLimits = wasUpdatingLimits - def _updateRadarView(self): - """Update radar view visible area. +_logger = logging.getLogger(__name__) +_logger.warning("%s is deprecated, you are advised to use " + "silx.gui.plot.ImageView instead", + __name__) +for line in traceback.format_stack(limit=3): + _logger.warning(line.rstrip()) - Takes care of y coordinate conversion. - """ - xMin, xMax = self._imagePlot.getGraphXLimits() - yMin, yMax = self._imagePlot.getGraphYLimits() - self._radarView.setVisibleRect(xMin, yMin, xMax - xMin, yMax - yMin) - - # Plots event listeners - - def _imagePlotCB(self, eventDict): - """Callback for imageView plot events.""" - if eventDict['event'] == 'mouseMoved': - activeImage = self._imagePlot.getActiveImage() - if activeImage is not None: - data = activeImage[0] - height, width = data.shape - x, y = int(eventDict['x']), int(eventDict['y']) - if x >= 0 and x < width and y >= 0 and y < height: - self.valueChanged.emit(float(x), float(y), - data[y][x]) - elif eventDict['event'] == 'limitsChanged': - # Do not handle histograms limitsChanged while - # updating their limits from here. - self._updatingLimits = True - - # Refresh histograms - self._updateHistograms() - - # could use eventDict['xdata'], eventDict['ydata'] instead - xMin, xMax = self._imagePlot.getGraphXLimits() - yMin, yMax = self._imagePlot.getGraphYLimits() - - # Set horizontal histo limits - self._histoHPlot.setGraphXLimits(xMin, xMax) - self._histoHPlot.replot() - - # Set vertical histo limits - self._histoVPlot.setGraphYLimits(yMin, yMax) - self._histoVPlot.replot() - - self._updateRadarView() - - self._updatingLimits = False - - # Replot in case limitsChanged due to set*Limits - # called from console. - # This results in an extra replot call in other cases. - self._imagePlot.replot() - - def _histoHPlotCB(self, eventDict): - """Callback for horizontal histogram plot events.""" - if eventDict['event'] == 'mouseMoved': - if self._cache is not None: - activeImage = self._imagePlot.getActiveImage() - if activeImage is not None: - xOrigin, xScaleFactor = activeImage[2]['plot_xScale'] - - minValue = xOrigin + xScaleFactor * self._cache['dataXMin'] - data = self._cache['histoH'] - width = data.shape[0] - x = int(eventDict['x']) - if x >= minValue and x < minValue + width: - self.valueChanged.emit(float('nan'), float(x), - data[x - minValue]) - elif eventDict['event'] == 'limitsChanged': - if (not self._updatingLimits and - eventDict['xdata'] != self._imagePlot.getGraphXLimits()): - xMin, xMax = eventDict['xdata'] - self._imagePlot.setGraphXLimits(xMin, xMax) - self._imagePlot.replot() - - def _histoVPlotCB(self, eventDict): - """Callback for vertical histogram plot events.""" - if eventDict['event'] == 'mouseMoved': - if self._cache is not None: - activeImage = self._imagePlot.getActiveImage() - if activeImage is not None: - yOrigin, yScaleFactor = activeImage[2]['plot_yScale'] - - minValue = yOrigin + yScaleFactor * self._cache['dataYMin'] - data = self._cache['histoV'] - height = data.shape[0] - y = int(eventDict['y']) - if y >= minValue and y < minValue + height: - self.valueChanged.emit(float(y), float('nan'), - data[y - minValue]) - elif eventDict['event'] == 'limitsChanged': - if (not self._updatingLimits and - eventDict['ydata'] != self._imagePlot.getGraphYLimits()): - yMin, yMax = eventDict['ydata'] - self._imagePlot.setGraphYLimits(yMin, yMax) - self._imagePlot.replot() - - def _radarViewCB(self, left, top, width, height): - """Slot for radar view visible rectangle changes.""" - if not self._updatingLimits: - # Takes care of Y axis conversion - self._imagePlot.setLimits(left, left + width, top, top + height) - self._imagePlot.replot() - - def _updateYAxisInverted(self): - """Sync image, vertical histogram and radar view axis orientation.""" - inverted = self._imagePlot.isYAxisInverted() - - self._imagePlot.invertYAxis(inverted) - self._histoVPlot.invertYAxis(inverted) - - # Use scale to invert radarView - # RadarView default Y direction is from top to bottom - # As opposed to Plot. So invert RadarView when Plot is NOT inverted. - self._radarView.resetTransform() - if not inverted: - self._radarView.scale(1., -1.) - self._updateRadarView() - - self._imagePlot.replot() - self._histoVPlot.replot() - self._radarView.update() - - def getHistogram(self, axis): - """Return the histogram and corresponding row or column extent. - - The returned value when an histogram is available is a dict with keys: - - - 'data': numpy array of the histogram values. - - 'extent': (start, end) row or column index. - end index is not included in the histogram. - :param str axis: 'x' for horizontal, 'y' for vertical - :return: The histogram and its extent as a dict or None. - :rtype: dict +class ImageView(SilxImageView): + def __init__(self, parent=None, windowFlags=None, backend=None): """ - assert axis in ('x', 'y') - if self._cache is None: - return None - else: - if axis == 'x': - return dict( - data=np.array(self._cache['histoH'], copy=True), - extent=(self._cache['dataXMin'], self._cache['dataXMax'])) - else: - return dict( - data=np.array(self._cache['histoV'], copy=True), - extent=(self._cache['dataYMin'], self._cache['dataYMax'])) - def radarView(self): - """Get the lower right radarView widget.""" - return self._radarView - - def setRadarView(self, radarView): - """Change the lower right radarView widget. - - :param RadarView radarView: Widget subclassing RadarView to replace - the lower right corner widget. - """ - self._radarView.visibleRectDragged.disconnect(self._radarViewCB) - self._radarView = radarView - self._radarView.visibleRectDragged.connect(self._radarViewCB) - self._layout.addWidget(self._radarView, 1, 1) - - self._updateYAxisInverted() - - # PlotWindow toolbar - - def toolBar(self): - """Returns the tool bar associated with the image plot. - - This is the toolBar provided by :class:`PlotWindow`. - - :return: The toolBar associated to the image plot. - :rtype: QToolBar - """ - return self._imagePlot.toolBar - - # High-level API - - def getColormap(self): - """Get the default colormap description. - - :return: A description of the current colormap. - See :meth:`setColormap` for details. - :rtype: dict + :param parent: + :param windowFlags: windowFlags (e.g. qt.Qt.Widget, qt.Qt.Window...) + If None, the silx default behavior is used: behave as a widget if + parent is not None, else behave as a Window. + :param backend: """ - return self._imagePlot.getDefaultColormap() - - def setColormap(self, colormap=None, normalization=None, - autoscale=None, vmin=None, vmax=None, colors=256): - """Set the default colormap and update active image. - - Parameters that are not provided are taken from the current colormap. - - The colormap parameter can also be a dict with the following keys: - - - *name*: string. The colormap to use: - 'gray', 'reversed gray', 'temperature', 'red', 'green', 'blue'. - - *normalization*: string. The mapping to use for the colormap: - either 'linear' or 'log'. - - *autoscale*: bool. Whether to use autoscale (True) - or range provided by keys 'vmin' and 'vmax' (False). - - *vmin*: float. The minimum value of the range to use if 'autoscale' - is False. - - *vmax*: float. The maximum value of the range to use if 'autoscale' - is False. - - :param colormap: Name of the colormap in - 'gray', 'reversed gray', 'temperature', 'red', 'green', 'blue'. - Or the description of the colormap as a dict. - :type colormap: dict or str. - :param str normalization: Colormap mapping: 'linear' or 'log'. - :param bool autoscale: Whether to use autoscale (True) - or [vmin, vmax] range (False). - :param float vmin: The minimum value of the range to use if - 'autoscale' is False. - :param float vmax: The maximum value of the range to use if - 'autoscale' is False. - """ - cmapDict = self._imagePlot.getDefaultColormap() - - if isinstance(colormap, dict): - # Support colormap parameter as a dict - assert normalization is None - assert autoscale is None - assert vmin is None - assert vmax is None - assert colors == 256 - for key, value in colormap.items(): - cmapDict[key] = value - - else: - if colormap is not None: - cmapDict['name'] = colormap - if normalization is not None: - cmapDict['normalization'] = normalization - if autoscale is not None: - cmapDict['autoscale'] = autoscale - if vmin is not None: - cmapDict['vmin'] = vmin - if vmax is not None: - cmapDict['vmax'] = vmax - - if 'colors' not in cmapDict: - cmapDict['colors'] = 256 - - cursorColor = _cursorColorForColormap(cmapDict['name']) - self._imagePlot.setZoomModeEnabled(True, color=cursorColor) + super(ImageView, self).__init__(parent=parent, backend=backend) - self._imagePlot.setDefaultColormap(cmapDict) + # SilxImageView does not have a windowFlags parameter. + # A silx PlotWidget behaves as a Widget if parent is not None, + # else it behaves as a QMainWindow. + if windowFlags is not None: + self.setWindowFlags(windowFlags) - activeImage = self._imagePlot.getActiveImage() - if activeImage is not None: # Refresh image with new colormap - data, legend, info, pixmap = activeImage - - self._imagePlot.addImage(data, legend=legend, info=info, - colormap=self.getColormap(), - replace=False, replot=False) - self._imagePlot.replot() - - def setImage(self, image, origin=(0, 0), scale=(1., 1.), - copy=True, reset=True): - """Set the image to display. - - :param image: A 2D array representing the image or None to empty plot. - :type image: numpy.ndarray-like with 2 dimensions or None. - :param origin: The (x, y) position of the origin of the image. - Default: (0, 0). - The origin is the lower left corner of the image when - the Y axis is not inverted. - :type origin: Tuple of 2 floats: (origin x, origin y). - :param scale: The scale factor to apply to the image on X and Y axes. - Default: (1, 1). - It is the size of a pixel in the coordinates of the axes. - Scales must be positive numbers. - :type scale: Tuple of 2 floats: (scale x, scale y). - :param bool copy: Whether to copy image data (default) or not. - :param bool reset: Whether to reset zoom and ROI (default) or not. - """ - self._dirtyCache() - - assert len(origin) == 2 - assert len(scale) == 2 - assert scale[0] > 0 - assert scale[1] > 0 - - if image is None: - self._imagePlot.removeImage(self._imageLegend, replot=False) - return - - data = np.array(image, order='C', copy=copy) - assert data.size != 0 - assert len(data.shape) == 2 - height, width = data.shape - - self._imagePlot.addImage(data, - legend=self._imageLegend, - xScale=(origin[0], scale[0]), - yScale=(origin[1], scale[1]), - colormap=self.getColormap(), - replace=False, - replot=False) - self._imagePlot.setActiveImage(self._imageLegend) - self._updateHistograms() - - self._radarView.setDataRect(origin[0], - origin[1], - width * scale[0], - height * scale[1]) - - if reset: - self.resetZoom() - else: - self._histoHPlot.replot() - self._histoVPlot.replot() - self._imagePlot.replot() - - #################### - # Plot API proxies # - #################### - - # Rebuild side histograms if active image gets changed through the Plot API - - def addImage(self, data, legend=None, info=None, - replace=True, replot=True, - xScale=None, yScale=None, z=0, - selectable=False, draggable=False, - colormap=None, **kw): - if legend == self._imagePlot.getActiveImage(just_legend=True): - # Updating active image, resets side histograms cache - self._dirtyCache() - - result = self._imagePlot.addImage(data, legend, info, replace, replot, - xScale, yScale, z, - selectable, draggable, - colormap, **kw) - self._updateHistograms() - - if replot: - self._histoHPlot.replot() - self._histoVPlot.replot() - - return result - - def clear(self): - self._dirtyCache() - return self._imagePlot.clear() - - def clearImages(self): - self._dirtyCache() - return self._imagePlot.clearImages() - - def removeImage(self, legend, replot=True): - if legend == self._imagePlot.getActiveImage(just_legend=True): - # Removing active image, resets side histograms cache - self._dirtyCache() - - result = self._imageView.removeImage(legend, replot) - self._updateHistograms() - - if replot: - self._histoHPlot.replot() - self._histoVPlot.replot() - - return result - - def setActiveImage(self, legend, replot=True): - # Active image changes, resets side histogram cache - self._dirtyCache() - - result = self._imagePlot.setActiveImage(legend, replot) - self._updateHistograms() - - if replot: - self._histoHPlot.replot() - self._histoVPlot.replot() - return result - - # Invert axes - - def invertYAxis(self, flag=True): - result = self._imagePlot.invertYAxis(flag) - self._updateYAxisInverted() # To sync vert. histo and radar view - return result - - # Ugly yet simple proxy for the Plot API - - def __getattr__(self, name): - """Proxy to expose image plot API.""" - return getattr(self._imagePlot, name) - - -# ImageViewMainWindow ######################################################### - -class ImageViewMainWindow(qt.QMainWindow): - """QMainWindow embedding an ImageView. - - Surrounds the ImageView with an associated toolbar and status bar. - """ +class ImageViewMainWindow(SilxImageViewMainWindow): def __init__(self, parent=None, windowFlags=qt.Qt.Widget, backend=None): - self._dataInfo = None - super(ImageViewMainWindow, self).__init__(parent, windowFlags) - - # Create the ImageView widget and add it to the QMainWindow - self.imageView = ImageView(backend=backend) - self.imageView.setGraphXLabel('X') - self.imageView.setGraphYLabel('Y') - self.imageView.setGraphTitle('Image') - self.imageView._imagePlot.sigColormapChangedSignal.connect( - self._colormapUpdated) - self.setCentralWidget(self.imageView) - - # Using PlotWindow's toolbar - self.addToolBar(self.imageView.toolBar()) - self.profileToolBar = ProfileToolBar(self.imageView._imagePlot) - self.addToolBar(self.profileToolBar) - self.addToolBar(qt.Qt.BottomToolBarArea, LimitsToolBar(self.imageView)) - - self.statusBar() - - # Connect to ImageView's signal - self.imageView.valueChanged.connect(self._statusBarSlot) - - def _colormapUpdated(self, colormap): - """Sync ROI color with current colormap""" - self.profileToolBar.overlayColor = _cursorColorForColormap( - colormap['name']) - - def _statusBarSlot(self, row, column, value): - """Update status bar with coordinates/value from plots.""" - if np.isnan(row): - msg = 'Column: %d, Sum: %g' % (int(column), value) - elif np.isnan(column): - msg = 'Row: %d, Sum: %g' % (int(row), value) - else: - msg = 'Position: (%d, %d), Value: %g' % (int(row), int(column), - value) - if self._dataInfo is not None: - msg = self._dataInfo + ', ' + msg - - self.statusBar().showMessage(msg) - - def setImage(self, image, *args, **kwargs): - """Set the displayed image. - - See :meth:`ImageView.setImage` for details. - """ - if hasattr(image, 'dtype') and hasattr(image, 'shape'): - assert len(image.shape) == 2 - height, width = image.shape - self._dataInfo = 'Data: %dx%d (%s)' % (width, height, - str(image.dtype)) - self.statusBar().showMessage(self._dataInfo) - else: - self._dataInfo = None + super(ImageViewMainWindow, self).__init__(parent, backend) + if windowFlags is not None: + self.setWindowFlags(windowFlags) - # Set the new image in ImageView widget - self.imageView.setImage(image, *args, **kwargs) - self.profileToolBar.updateProfile() - self.setStatusBar(None) # main ######################################################################## diff --git a/PyMca5/PyMcaGui/plotting/MaskImageTools.py b/PyMca5/PyMcaGui/plotting/MaskImageTools.py index 61466f7..5c6394f 100644 --- a/PyMca5/PyMcaGui/plotting/MaskImageTools.py +++ b/PyMca5/PyMcaGui/plotting/MaskImageTools.py @@ -30,8 +30,6 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" __doc__ = """ Common tools to deal with common graphics operations on images. """ -import sys -import os import numpy from PyMca5 import spslut @@ -253,9 +251,9 @@ def applyMaskToImage(pixmap, mask=None, colors=None, copy=True): if __name__ == "__main__": from PyMca5.PyMcaGui import PyMcaQt as qt - from PyMca5.PyMcaGui import PlotWidget + from silx.gui.plot import PlotWidget app = qt.QApplication([]) - w = PlotWidget.PlotWidget() + w = PlotWidget() data = numpy.arange(10000.).reshape(100, 100) mask = numpy.zeros(data.shape, dtype=numpy.uint8) mask[25:75, 25:75] = 1 diff --git a/PyMca5/PyMcaGui/plotting/MaskImageWidget.py b/PyMca5/PyMcaGui/plotting/MaskImageWidget.py index 5116c8b..5be6bae 100644 --- a/PyMca5/PyMcaGui/plotting/MaskImageWidget.py +++ b/PyMca5/PyMcaGui/plotting/MaskImageWidget.py @@ -30,6 +30,7 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import numpy +import logging from PyMca5.PyMcaGraph.ctools import pnpoly from . import RGBCorrelatorGraph from . import ColormapDialog @@ -61,7 +62,8 @@ OVERLAY_DRAW = True DEFAULT_COLORMAP_INDEX = 2 DEFAULT_COLORMAP_LOG_FLAG = False -DEBUG = 0 +_logger = logging.getLogger(__name__) + USE_PICKER = False @@ -85,7 +87,6 @@ class MaskImageWidget(qt.QWidget): self.setMaximumWidth(int(screenWidth)-5) self.setMinimumWidth(min(int(0.5*screenWidth),800)) - self._y1AxisInverted = False self.__selectionMask = None self._selectionColors = None self.__imageData = None @@ -185,13 +186,12 @@ class MaskImageWidget(qt.QWidget): self.buildStandaloneSaveMenu() self.graphWidget.zoomResetToolButton.clicked.connect(self._zoomResetSignal) - self.graphWidget.graph.setDrawModeEnabled(False) - self.graphWidget.graph.setZoomModeEnabled(True) + self.graphWidget.graph.setInteractiveMode('zoom') if self.__selectionFlag: if self.__imageIconsFlag: self.setSelectionMode(False) self._toggleSelectionMode() - self.graphWidget.graph.setDrawModeEnabled(True, + self.graphWidget.graph.setInteractiveMode('draw', shape="rectangle", label="mask") else: @@ -323,9 +323,8 @@ class MaskImageWidget(qt.QWidget): self._profileSignalSlot(ddict) def _profileSignalSlot(self, ddict): - if DEBUG: - print("_profileSignalSLot, event = %s" % ddict['event']) - print("Received ddict = ", ddict) + _logger.debug("_profileSignalSLot, event = %s", ddict['event']) + _logger.debug("Received ddict = %s", ddict) if ddict['event'] in [None, "NONE"]: #Nothing to be made @@ -333,10 +332,10 @@ class MaskImageWidget(qt.QWidget): if ddict['event'] == "profileWidthChanged": if self.__lastOverlayLegend is not None: - legend = self.__lastOverlayLegend - #TODO: Find a better way to deal with this - if legend in self.graphWidget.graph._itemDict: - info = self.graphWidget.graph._itemDict[legend]['info'] + shape = self.graphWidget.graph._getItem(kind='item', + legend=self.__lastOverlayLegend) + if shape is not None: + info = shape.getInfo(copy=False) if info['mode'] == ddict['mode']: newDict = {} newDict['event'] = "updateProfile" @@ -344,7 +343,6 @@ class MaskImageWidget(qt.QWidget): newDict['ydata'] = info['ydata'] * 1 newDict['mode'] = info['mode'] * 1 newDict['pixelwidth'] = ddict['pixelwidth'] * 1 - info = None #self._updateProfileCurve(newDict) self._profileSignalSlot(newDict) return @@ -355,13 +353,13 @@ class MaskImageWidget(qt.QWidget): self._profileSelectionWindow = ProfileScanWidget.ProfileScanWidget(actions=False) else: self._profileSelectionWindow = ProfileScanWidget.ProfileScanWidget(actions=True) - self._profileSelectionWindow.sigAddClicked.connect( \ + self._profileSelectionWindow.sigAddClicked.connect( self._profileSelectionSlot) - self._profileSelectionWindow.sigRemoveClicked.connect( \ + self._profileSelectionWindow.sigRemoveClicked.connect( self._profileSelectionSlot) self._profileSelectionWindow.sigReplaceClicked.connect( self._profileSelectionSlot) - self._interpolate = SpecfitFuns.interpol + self._interpolate = SpecfitFuns.interpol #if I do not return here and the user interacts with the graph while #the profileSelectionWindow is not shown, I get crashes under Qt 4.5.3 and MacOS X #when calling _getProfileCurve @@ -376,16 +374,14 @@ class MaskImageWidget(qt.QWidget): if curve is None: return xdata, ydata, legend, info = curve - replot=True - replace=True idx = numpy.isfinite(ydata) xdata = xdata[idx] ydata = ydata[idx] self._profileSelectionWindow.addCurve(xdata, ydata, legend=legend, info=info, - replot=replot, - replace=replace) + resetzoom=True, + replace=True) def getGraphTitle(self): try: @@ -438,7 +434,7 @@ class MaskImageWidget(qt.QWidget): if ddict['event'] == 'profileModeChanged': if self.__lastOverlayLegend: - self.graphWidget.graph.removeItem(self.__lastOverlayLegend, replot=True) + self.graphWidget.graph.removeItem(self.__lastOverlayLegend) return #if I show the image here it does not crash, but it is not nice because @@ -476,13 +472,12 @@ class MaskImageWidget(qt.QWidget): ydata = imageData[row, :] legend = "Row = %d" % row if overlay: - #self.drawOverlayItem(x, y, legend=name, info=info, replot, replace) + #self.drawOverlayItem(x, y, legend=name, info=info, replace) self.drawOverlayItem([0.0, shape[1], shape[1], 0.0], [row, row, row+1, row+1], legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) else: row0 = int(int(ddict['row'][0]) - 0.5 * width) if row0 < 0: @@ -497,13 +492,12 @@ class MaskImageWidget(qt.QWidget): ydata = imageData[row0:row1+1, :].sum(axis=0) legend = "Row = %d to %d" % (row0, row1) if overlay: - #self.drawOverlayItem(x, y, legend=name, info=info, replot, replace) + #self.drawOverlayItem(x, y, legend=name, info=info, replace) self.drawOverlayItem([0.0, 0.0, shape[1], shape[1]], [row0, row1+1, row1+1, row0], legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) xdata = numpy.arange(shape[1]).astype(numpy.float) if self._xScale is not None: xdata = self._xScale[0] + xdata * self._xScale[1] @@ -519,13 +513,12 @@ class MaskImageWidget(qt.QWidget): ydata = imageData[:, column] legend = "Column = %d" % column if overlay: - #self.drawOverlayItem(x, y, legend=name, info=info, replot, replace) + #self.drawOverlayItem(x, y, legend=name, info=info, replace) self.drawOverlayItem([column, column, column+1, column+1], [0.0, shape[0], shape[0], 0.0], legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) else: col0 = int(int(ddict['column'][0]) - 0.5 * width) if col0 < 0: @@ -540,13 +533,12 @@ class MaskImageWidget(qt.QWidget): ydata = imageData[:, col0:col1+1].sum(axis=1) legend = "Col = %d to %d" % (col0, col1) if overlay: - #self.drawOverlayItem(x, y, legend=name, info=info, replot, replace) + #self.drawOverlayItem(x, y, legend=name, info=info, replace) self.drawOverlayItem([col0, col0, col1+1, col1+1], [0, shape[0], shape[0], 0.], legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) xdata = numpy.arange(shape[0]).astype(numpy.float) if self._yScale is not None: xdata = self._yScale[0] + xdata * self._yScale[1] @@ -577,8 +569,7 @@ class MaskImageWidget(qt.QWidget): if npoints == 1: #all points are the same - if DEBUG: - print("START AND END POINT ARE THE SAME!!") + _logger.debug("START AND END POINT ARE THE SAME!!") return if width < 0: # width = pixelwidth - 1 @@ -591,13 +582,12 @@ class MaskImageWidget(qt.QWidget): xdata = numpy.arange(float(npoints)) if overlay: - #self.drawOverlayItem(x, y, legend=name, info=info, replot, replace) + #self.drawOverlayItem(x, y, legend=name, info=info, replace) self.drawOverlayItem([col0, col1], [row0, row1], legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) elif deltaCol == 0: #vertical line col0 = int(int(ddict['column'][0]) - 0.5 * width) @@ -624,13 +614,12 @@ class MaskImageWidget(qt.QWidget): npoints = max(ydata.shape) xdata = numpy.arange(float(npoints)) if overlay: - #self.drawOverlayItem(x, y, legend=name, info=info, replot, replace) + #self.drawOverlayItem(x, y, legend=name, info=info, replace) self.drawOverlayItem([col0, col0, col1+1, col1+1], [row0, row1+1, row1+1, row0], legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) elif deltaRow == 0: #horizontal line row0 = int(int(ddict['row'][0]) - 0.5 * width) @@ -657,13 +646,12 @@ class MaskImageWidget(qt.QWidget): npoints = max(ydata.shape) xdata = numpy.arange(float(npoints)) if overlay: - #self.drawOverlayItem(x, y, legend=name, info=info, replot, replace) + #self.drawOverlayItem(x, y, legend=name, info=info, replace) self.drawOverlayItem([col0, col0, col1+1, col1+1], [row0, row1+1, row1+1, row0], legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) else: #restore original value of width width = ddict['pixelwidth'] @@ -687,9 +675,8 @@ class MaskImageWidget(qt.QWidget): newRow0 = 0.0 newRow1 = -(col1-col0) * sinalpha + (row1-row0) * cosalpha - if DEBUG: - print("new X0 Y0 = %f, %f " % (newCol0, newRow0)) - print("new X1 Y1 = %f, %f " % (newCol1, newRow1)) + _logger.debug("new X0 Y0 = %f, %f ", newCol0, newRow0) + _logger.debug("new X1 Y1 = %f, %f ", newCol1, newRow1) tmpX = numpy.linspace(newCol0, newCol1, npoints).astype(numpy.float) rotMatrix = numpy.zeros((2,2), numpy.float) @@ -697,19 +684,19 @@ class MaskImageWidget(qt.QWidget): rotMatrix[0,1] = - sinalpha rotMatrix[1,0] = sinalpha rotMatrix[1,1] = cosalpha - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: #test if I recover the original points - testX = numpy.zeros((2, 1) , numpy.float) + testX = numpy.zeros((2, 1), numpy.float) colRow = numpy.dot(rotMatrix, testX) - print("Recovered X0 = %f" % (colRow[0,0] + col0)) - print("Recovered Y0 = %f" % (colRow[1,0] + row0)) - print("It should be = %f, %f" % (col0, row0)) - testX[0,0] = newCol1 - testX[1,0] = newRow1 + _logger.debug("Recovered X0 = %f", colRow[0, 0] + col0) + _logger.debug("Recovered Y0 = %f", colRow[1, 0] + row0) + _logger.debug("It should be = %f, %f", col0, row0) + testX[0, 0] = newCol1 + testX[1, 0] = newRow1 colRow = numpy.dot(rotMatrix, testX) - print("Recovered X1 = %f" % (colRow[0,0] + col0)) - print("Recovered Y1 = %f" % (colRow[1,0] + row0)) - print("It should be = %f, %f" % (col1, row1)) + _logger.debug("Recovered X1 = %f", colRow[0, 0] + col0) + _logger.debug("Recovered Y1 = %f", colRow[1, 0] + row0) + _logger.debug("It should be = %f, %f", col1, row1) #find the drawing limits testX = numpy.zeros((2, 4) , numpy.float) @@ -727,21 +714,18 @@ class MaskImageWidget(qt.QWidget): for a in rowLimits0: if (a >= shape[0]) or (a < 0): - if DEBUG: - print("outside row limits",a) + _logger.debug("outside row limits %s", a) return for a in colLimits0: if (a >= shape[1]) or (a < 0): - if DEBUG: - print("outside column limits",a) + _logger.debug("outside column limits %s", a) return r0 = rowLimits0[0] r1 = rowLimits0[1] if r0 > r1: - if DEBUG: - print("r0 > r1", r0, r1) + _logger.debug("r0 > r1 %s %s", r0, r1) raise ValueError("r0 > r1") x = numpy.zeros((2, npoints) , numpy.float) @@ -809,8 +793,7 @@ class MaskImageWidget(qt.QWidget): rowLimits0, legend=ddict['mode'], info=ddict, - replace=True, - replot=True) + replace=True) if self.__lineProjectionMode == 'X': xLabel = self.getXLabel() xdata += col0 @@ -831,8 +814,7 @@ class MaskImageWidget(qt.QWidget): float(deltaRow) * deltaRow)/(npoints-1.0) xdata *= deltaDistance else: - if DEBUG: - print("Mode %s not supported yet" % ddict['mode']) + _logger.debug("Mode %s not supported yet", ddict['mode']) return self.__lastOverlayWidth = ddict['pixelwidth'] @@ -842,8 +824,7 @@ class MaskImageWidget(qt.QWidget): return xdata, ydata, legend, info def _profileSelectionSlot(self, ddict): - if DEBUG: - print(ddict) + _logger.debug("%s", ddict) # the curves as [[x0, y0, legend0, info0], ...] curveList = ddict['curves'] label = ddict['label'] @@ -851,25 +832,17 @@ class MaskImageWidget(qt.QWidget): if ddict['event'] == 'ADD': for i in range(n): x, y, legend, info = curveList[i] - info['profilelabel'] = label - if i == (n-1): - replot = True + resetzoom = (i == (n-1)) self._profileScanWindow.addCurve(x, y, legend=legend, info=info, - replot=replot, replace=False) + resetzoom=resetzoom, replace=False) elif ddict['event'] == 'REPLACE': for i in range(n): x, y, legend, info = curveList[i] info['profilelabel'] = label - if i in [0, n-1]: - replace = True - else: - replace = False - if i == (n-1): - replot = True - else: - replot = False + replace = (i in [0, n-1]) + resetzoom = (i == (n-1)) self._profileScanWindow.addCurve(x, y, legend=legend, info=info, - replot=replot, replace=replace) + resetzoom=resetzoom, replace=replace) elif ddict['event'] == 'REMOVE': curveList = self._profileScanWindow.getAllCurves() if curveList in [None, []]: @@ -885,13 +858,10 @@ class MaskImageWidget(qt.QWidget): n = len(toDelete) for i in range(n): legend = toDelete[i] - if i == (n-1): - replot = True - else: - replot = False - self._profileScanWindow.removeCurve(legend, replot=replot) + resetzoom = (i == (n-1)) + self._profileScanWindow.removeCurve(legend, resetzoom=resetzoom) - def drawOverlayItem(self, x, y, legend=None, info=None, replace=False, replot=True): + def drawOverlayItem(self, x, y, legend=None, info=None, replace=False): #same call as the plot1D addCurve command if legend is None: legend="UnnamedOverlayItem" @@ -911,31 +881,22 @@ class MaskImageWidget(qt.QWidget): for i in y: yList.append(self._yScale[0] + i * self._yScale[1]) self.graphWidget.graph.addItem(xList, yList, legend=legend, info=info, - replace=replace, replot=replot, - shape="polygon", fill=True) + replace=replace, shape="polygon", fill=True) self.__lastOverlayLegend = legend def _hFlipIconSignal(self): - self._y1AxisInverted = self.graphWidget.graph.isYAxisInverted() - if self._y1AxisInverted: - self._y1AxisInverted = False - else: - self._y1AxisInverted = True - #self.graphWidget.graph.zoomReset() - self.graphWidget.graph.invertYAxis(self._y1AxisInverted) - self._y1AxisInverted = self.graphWidget.graph.isYAxisInverted() - self.graphWidget.graph.replot() + yaxis = self.graphWidget.graph.getYAxis() + yaxis.setInverted(not yaxis.isInverted()) #inform the other widgets ddict = {} ddict['event'] = "hFlipSignal" - ddict['current'] = self._y1AxisInverted * 1 + ddict['current'] = yaxis.isInverted() * 1 ddict['id'] = id(self) self.emitMaskImageSignal(ddict) def setY1AxisInverted(self, value): - self._y1AxisInverted = value - self.graphWidget.graph.invertYAxis(self._y1AxisInverted) + self.graphWidget.graph.getYAxis().setInverted(value) def setXLabel(self, label="Column"): return self.graphWidget.setXLabel(label) @@ -988,38 +949,34 @@ class MaskImageWidget(qt.QWidget): self._replaceImageClicked) def _setEraseSelectionMode(self): - if DEBUG: - print("_setEraseSelectionMode") + _logger.debug("_setEraseSelectionMode") self.__eraseMode = True self.__brushMode = True - self.graphWidget.graph.setDrawModeEnabled(False) + self.graphWidget.graph.setInteractiveMode('select') def _setRectSelectionMode(self): - if DEBUG: - print("_setRectSelectionMode") + _logger.debug("_setRectSelectionMode") self.__eraseMode = False self.__brushMode = False - self.graphWidget.graph.setDrawModeEnabled(True, + self.graphWidget.graph.setInteractiveMode("draw", shape="rectangle", label="mask") def _setPolygonSelectionMode(self): self.__eraseMode = False self.__brushMode = False - self.graphWidget.graph.setDrawModeEnabled(True, + self.graphWidget.graph.setInteractiveMode('draw', shape="polygon", label="mask") def _setBrushSelectionMode(self): - if DEBUG: - print("_setBrushSelectionMode") + _logger.debug("_setBrushSelectionMode") self.__eraseMode = False self.__brushMode = True - self.graphWidget.graph.setDrawModeEnabled(False) + self.graphWidget.graph.setInteractiveMode('select') def _setBrush(self): - if DEBUG: - print("_setBrush") + _logger.debug("_setBrush") if self.__brushMenu is None: self.__brushMenu = qt.QMenu() self.__brushMenu.addAction(QString(" 1 Image Pixel Width"), @@ -1055,25 +1012,25 @@ class MaskImageWidget(qt.QWidget): self.__brushWidth = 20 def _toggleSelectionMode(self): - drawMode = self.graphWidget.graph.getDrawMode() - if drawMode is None: + mode = self.graphWidget.graph.getInteractiveMode() + if mode['mode'] != 'draw': # we are not drawing anything - if self.graphWidget.graph.isZoomModeEnabled(): + if self.graphWidget.graph.getInteractiveMode()['mode'] == 'zoom': # we have to pass to mask mode self.setSelectionMode(True) else: # we set zoom mode and show the line icons self.setSelectionMode(False) - elif drawMode['label'] is not None: - if drawMode['label'].startswith('mask'): - #we set the zoom mode and show the line icons + elif mode['label'] is not None: + if mode['label'].startswith('mask'): + # we set the zoom mode and show the line icons self.setSelectionMode(False) else: # we disable zoom and drawing and set mask mode self.setSelectionMode(True) - elif drawMode['label'] in [None]: + elif mode['label'] in [None]: # we are not drawing anything - if self.graphWidget.graph.isZoomModeEnabled(): + if self.graphWidget.graph.getInteractiveMode()['mode'] == 'zoom': # we have to pass to mask mode self.setSelectionMode(True) else: @@ -1085,17 +1042,17 @@ class MaskImageWidget(qt.QWidget): #if not self.__imageIconsFlag: # mode = False if mode: - self.graphWidget.graph.setDrawModeEnabled(True, - 'rectangle', + self.graphWidget.graph.setInteractiveMode('draw', + shape='rectangle', label='mask') - self.__brushMode = False + self.__brushMode = False self.graphWidget.hideProfileSelectionIcons() self.graphWidget.selectionToolButton.setChecked(True) self.graphWidget.selectionToolButton.setDown(True) self.graphWidget.showImageIcons() else: self.graphWidget.showProfileSelectionIcons() - self.graphWidget.graph.setZoomModeEnabled(True) + self.graphWidget.graph.setInteractiveMode('zoom') self.graphWidget.selectionToolButton.setChecked(False) self.graphWidget.selectionToolButton.setDown(False) self.graphWidget.hideImageIcons() @@ -1169,8 +1126,7 @@ class MaskImageWidget(qt.QWidget): self._resetSelection(True) def _resetSelection(self, owncall=True): - if DEBUG: - print("_resetSelection") + _logger.debug("_resetSelection") self.__selectionMask = None if self.__imageData is None: return @@ -1210,8 +1166,8 @@ class MaskImageWidget(qt.QWidget): if data is None: self.__imageData = data self.__selectionMask = None - self.plotImage(update = True) - self.graphWidget._zoomReset(replot=True) + self.plotImage(update=True) + self.graphWidget.graph.resetZoom() return else: self.__imageData = data @@ -1226,8 +1182,8 @@ class MaskImageWidget(qt.QWidget): self.colormapDialog.setDisplayedMaxValue(maxData) self.colormapDialog.setDataMinMax(minData, maxData, update=True) else: - self.plotImage(update = True) - self.graphWidget._zoomReset(replot=True) + self.plotImage(update=True) + self.graphWidget.graph.resetZoom() def getImageData(self): return self.__imageData @@ -1282,8 +1238,8 @@ class MaskImageWidget(qt.QWidget): self.__pixmap0 = pixmap if clearmask: self.__selectionMask = None - self.plotImage(update = True) - self.graphWidget._zoomReset(replot=True) + self.plotImage(update=True) + self.graphWidget.graph.resetZoom() def plotImage(self, update=True): if self.__imageData is None: @@ -1295,13 +1251,14 @@ class MaskImageWidget(qt.QWidget): self.__pixmap0 = self.__pixmap.copy() self.__applyMaskToImage() - # replot=False as it triggers a zoom reset in Plot.py + origin, scale = None, None + if self._xScale is not None: + origin = (self._xScale[0], self._yScale[0]) + scale = (self._xScale[1], self._yScale[1]) self.graphWidget.graph.addImage(self.__pixmap, - "image", - xScale=self._xScale, - yScale=self._yScale, - replot=False) - self.graphWidget.graph.replot() + "image", resetzoom=False, + origin=origin, + scale=scale) self.updateProfileSelectionWindow() def getPixmapFromData(self): @@ -1535,18 +1492,15 @@ class MaskImageWidget(qt.QWidget): alteration = (1 - (0.2 * (self.__selectionMask > 0))) - \ 0.1 * (self.__selectionMask == self._roiTags[self._nRoi - 1]) if self.colormap is None: - if DEBUG: - print("Colormap is None") + _logger.debug("Colormap is None") if self.__image is not None: if self.__image.format() == qt.QImage.Format_ARGB32: - if DEBUG: - print("__applyMaskToImage CASE 1") + _logger.debug("__applyMaskToImage CASE 1") for i in range(4): self.__pixmap[:,:,i] = (self.__pixmap0[:,:,i] *\ alteration).astype(numpy.uint8) else: - if DEBUG: - print("__applyMaskToImage CASE 2") + _logger.debug("__applyMaskToImage CASE 2") self.__pixmap = self.__pixmap0.copy() tmp = self.__selectionMask > 0 self.__pixmap[tmp, 0] = 0x40 @@ -1559,8 +1513,7 @@ class MaskImageWidget(qt.QWidget): self.__pixmap[roiTag, 3] = 2*0x40 else: if self.__defaultColormap > 1: - if DEBUG: - print("__applyMaskToImage CASE 3") + _logger.debug("__applyMaskToImage CASE 3") self.__pixmap = self.__pixmap0.copy() for i in range(3): self.__pixmap[:,:,i] = (self.__pixmap0[:,:,i] *\ @@ -1573,8 +1526,7 @@ class MaskImageWidget(qt.QWidget): for i in range(3): self.__pixmap[:,:,i] *= tmpMask else: - if DEBUG: - print("__applyMaskToImage CASE 4") + _logger.debug("__applyMaskToImage CASE 4") self.__pixmap = self.__pixmap0.copy() self.__pixmap[self.__selectionMask>0,0] = 0x40 self.__pixmap[self.__selectionMask>0,2] = 0x70 @@ -1594,8 +1546,7 @@ class MaskImageWidget(qt.QWidget): self.__pixmap[tmpMask,2] = 0xff self.__pixmap[tmpMask,3] = 0xff elif int(str(self.colormap[0])) > 1: #color - if DEBUG: - print("__applyMaskToImage CASE 5") + _logger.debug("__applyMaskToImage CASE 5") for i in range(3): self.__pixmap[:,:,i] = (self.__pixmap0[:,:,i] * alteration) if 0: @@ -1606,8 +1557,7 @@ class MaskImageWidget(qt.QWidget): for i in range(3): self.__pixmap[:,:,i] *= tmpMask elif self._maxNRois > 1: - if DEBUG: - print("__applyMaskToImage CASE 6") + _logger.debug("__applyMaskToImage CASE 6") tmp = 1 - (self.__selectionMask>0) tmp2 = (self.__selectionMask == self._roiTags[self._nRoi - 1]) self.__pixmap[:, :, 2] = (0x70 * (self.__selectionMask>0) + \ @@ -1616,8 +1566,7 @@ class MaskImageWidget(qt.QWidget): self.__pixmap[:,:, 3] = (0x40 * (self.__selectionMask>0) + 0x40 * tmp2) +\ tmp * self.__pixmap0[:,:,3] else: - if DEBUG: - print("__applyMaskToImage CASE 7") + _logger.debug("__applyMaskToImage CASE 7") self.__pixmap = self.__pixmap0.copy() tmp = 1 - self.__selectionMask self.__pixmap[:, :, 2] = (0x70 * self.__selectionMask) +\ @@ -1802,7 +1751,7 @@ class MaskImageWidget(qt.QWidget): elif self.__pixmap0 is not None: imageShape = self.__pixmap0.shape[0:2] else: - print("Cannot handle polygon mask") + _logger.warning("Cannot handle polygon mask") return x = self._xScale[0] + self._xScale[1] * numpy.arange(imageShape[1]) y = self._yScale[0] + self._yScale[1] * numpy.arange(imageShape[0]) @@ -1888,8 +1837,7 @@ class MaskImageWidget(qt.QWidget): if ownsignal: pass if None in [ddict['x'], ddict['y']]: - if DEBUG: - print("Signal from outside region", ddict) + _logger.debug("Signal from outside region %s", ddict) return if self.graphWidget.infoWidget.isHidden() or self.__brushMode: @@ -1948,7 +1896,7 @@ class MaskImageWidget(qt.QWidget): self.setMouseText("%g, %g, %g" % (x, y, self.__imageData[row, column])) if self.__brushMode: - if self.graphWidget.graph.isZoomModeEnabled(): + if self.graphWidget.graph.getInteractiveMode()['mode'] == 'zoom': return if ddict['button'] != "left": return @@ -1982,9 +1930,8 @@ class MaskImageWidget(qt.QWidget): self.sigMaskImageWidgetSignal.emit(ddict) def _zoomResetSignal(self): - if DEBUG: - print("_zoomResetSignal") - self.graphWidget._zoomReset(replot=False) + _logger.debug("_zoomResetSignal") + self.graphWidget.graph.resetZoom() self.plotImage(True) def getOutputFileName(self): @@ -1996,7 +1943,8 @@ class MaskImageWidget(qt.QWidget): filedialog.setFileMode(filedialog.AnyFile) filedialog.setAcceptMode(qt.QFileDialog.AcceptSave) filedialog.setWindowIcon(qt.QIcon(qt.QPixmap(IconDict["gioconda16"]))) - formatlist = ["ASCII Files *.dat", + formatlist = ["TIFF Files *.tif", + "ASCII Files *.dat", "EDF Files *.edf", 'CSV(, separated) Files *.csv', 'CSV(; separated) Files *.csv', @@ -2008,7 +1956,7 @@ class MaskImageWidget(qt.QWidget): for f in formatlist: strlist.append(f) if self._saveFilter is None: - self._saveFilter =formatlist[0] + self._saveFilter = formatlist[0] if hasattr(filedialog, "setFilters"): filedialog.setFilters(strlist) filedialog.selectFilter(self._saveFilter) @@ -2027,12 +1975,12 @@ class MaskImageWidget(qt.QWidget): self._saveFilter = qt.safe_str(filedialog.selectedFilter()) else: self._saveFilter = qt.safe_str(filedialog.selectedNameFilter()) - filterused = "."+self._saveFilter[-3:] + filterused = "." + self._saveFilter[-3:] PyMcaDirs.outputDir = os.path.dirname(filename) if len(filename) < 4: - filename = filename+ filterused + filename = filename + filterused elif filename[-4:] != filterused : - filename = filename+ filterused + filename = filename + filterused else: filename = "" return filename @@ -2063,10 +2011,15 @@ class MaskImageWidget(qt.QWidget): return if filename is None: filename = self.getOutputFileName() - if not len(filename):return + if not len(filename): + return if filename.lower().endswith(".edf"): ArraySave.save2DArrayListAsEDF(imageList, filename, labels) + elif filename.lower().endswith(".tif"): + ArraySave.save2DArrayListAsMonochromaticTiff(imageList, + filename, + labels) elif filename.lower().endswith(".csv"): if "," in self._saveFilter: csvseparator = "," diff --git a/PyMca5/PyMcaGui/plotting/MaskScatterWidget.py b/PyMca5/PyMcaGui/plotting/MaskScatterWidget.py index d9ece40..09fff5c 100644 --- a/PyMca5/PyMcaGui/plotting/MaskScatterWidget.py +++ b/PyMca5/PyMcaGui/plotting/MaskScatterWidget.py @@ -37,70 +37,109 @@ ___doc__ = """ - Final layer containing the selected points with the selected colors. """ -import sys -import os import numpy +import logging from PyMca5.PyMcaGraph.ctools import pnpoly -DEBUG = 0 +_logger = logging.getLogger(__name__) -from . import PlotWindow from . import MaskImageWidget from . import MaskImageTools -qt = PlotWindow.qt +from PyMca5.PyMcaGui import PyMcaQt as qt +from .MaskToolBar import MaskToolBar +from . import ColormapDialog +from .PyMca_Icons import IconDict + +from silx.gui.plot import PlotWindow + if hasattr(qt, "QString"): QString = qt.QString else: QString = qt.safe_str -IconDict = PlotWindow.IconDict -class MaskScatterWidget(PlotWindow.PlotWindow): + +class MaskScatterWidget(PlotWindow): sigMaskScatterWidgetSignal = qt.pyqtSignal(object) DEFAULT_COLORMAP_INDEX = 2 DEFAULT_COLORMAP_LOG_FLAG = True - def __init__(self, parent=None, backend=None, plugins=False, newplot=False, - control=False, position=False, maxNRois=1, grid=False, - logx=False, logy=False, togglePoints=False, normal=True, - polygon=True, colormap=True, aspect=True, - imageIcons=True, bins=None, **kw): + def __init__(self, parent=None, backend=None, control=False, + position=False, maxNRois=1, grid=False, logScale=False, + curveStyle=False, resetzoom=True, + aspectRatio=True, imageIcons=True, polygon=True, bins=None): super(MaskScatterWidget, self).__init__(parent=parent, backend=backend, - plugins=plugins, - newplot=newplot, control=control, position=position, grid=grid, - logx=logx, - logy=logy, - togglePoints=togglePoints, - normal=normal, - aspect=aspect, - colormap=colormap, - imageIcons=imageIcons, - polygon=polygon, - **kw) - self._buildAdditionalSelectionMenuDict() + logScale=logScale, + curveStyle=curveStyle, + resetzoom=resetzoom, + aspectRatio=aspectRatio, + colormap=False, + mask=False, + yInverted=False, + roi=False, + copy=True, + print_=False) + if parent is None: + self.setWindowTitle("MaskScatterWidget") + self.setActiveCurveHandling(False) + + # No context menu by default, execute zoomBack on right click + plotArea = self.getWidgetHandle() + plotArea.setContextMenuPolicy(qt.Qt.CustomContextMenu) + plotArea.customContextMenuRequested.connect(self._zoomBack) + + self.colormapIcon = qt.QIcon(qt.QPixmap(IconDict["colormap"])) + self.colormapToolButton = qt.QToolButton(self.toolBar()) + self.colormapToolButton.setIcon(self.colormapIcon) + self.colormapToolButton.setToolTip('Change Colormap') + self.colormapToolButton.clicked.connect(self._colormapIconSignal) + self.colormapAction = self.toolBar().insertWidget(self.getSaveAction(), + self.colormapToolButton) + + self.maskToolBar = None + if polygon or imageIcons: + self.maskToolBar = MaskToolBar(parent=self, + plot=self, + imageIcons=imageIcons, + polygon=polygon) + self.addToolBar(self.maskToolBar) + self._selectionCurve = None self._selectionMask = None - self._selectionColors = numpy.zeros((len(self.colorList), 4), numpy.uint8) self._alphaLevel = None - for i in range(len(self.colorList)): - self._selectionColors[i, 0] = eval("0x" + self.colorList[i][-2:]) - self._selectionColors[i, 1] = eval("0x" + self.colorList[i][3:-2]) - self._selectionColors[i, 2] = eval("0x" + self.colorList[i][1:3]) - self._selectionColors[i, 3] = 0xff + self._xScale = None + self._yScale = None + self._maxNRois = maxNRois self._nRoi = 1 self._zoomMode = True self._eraseMode = False self._brushMode = False self._brushWidth = 5 - self._brushMenu = None self._bins = bins self._densityPlotWidget = None self._pixmap = None + self._imageData = None + self.colormapDialog = None + self.colormap = None self.setPlotViewMode("scatter", bins=bins) - self.setDrawModeEnabled(False) + + def _colormapIconSignal(self): + image = self.getActiveImage() + if image is None: + return + + if hasattr(image, "getColormap"): + if self.colormapDialog is None: + self._initColormapDialog(image.getData(), + image.getColormap()._toDict()) + self.colormapDialog.show() + else: + # RGBA image + _logger.info("No colormap to be handled") + return def setPlotViewMode(self, mode="scatter", bins=None): if mode.lower() != "density": @@ -110,41 +149,24 @@ class MaskScatterWidget(PlotWindow.PlotWindow): def _activateScatterPlotView(self): self._plotViewMode = "scatter" - for key in ["colormap", "brushSelection", "brush"]: - self.setToolBarActionVisible(key, False) - if hasattr(self, "eraseSelectionToolButton"): - self.eraseSelectionToolButton.setToolTip("Set erase mode if checked") - self.eraseSelectionToolButton.setCheckable(True) - if self._eraseMode: - self.eraseSelectionToolButton.setChecked(True) - else: - self.eraseSelectionToolButton.setChecked(False) - if hasattr(self, "polygonSelectionToolButton"): - self.polygonSelectionToolButton.setCheckable(True) - if hasattr(self, "rectSelectionToolButton"): - self.rectSelectionToolButton.setCheckable(True) - if hasattr(self, "brushSelectionToolButton"): - if self.brushSelectionToolButton.isChecked(): - self.brushSelectionToolButton.setChecked(False) - self._brushMode = False - self.setZoomModeEnabled(True) + self.colormapAction.setVisible(False) + self._brushMode = False + self.setInteractiveMode("select") + + if hasattr(self, "maskToolBar"): + self.maskToolBar.activateScatterPlotView() + self.clearImages() self._updatePlot() def _activateDensityPlotView(self, bins=None): self._plotViewMode = "density" - for key in ["colormap", "brushSelection", "brush", "rectangle"]: - self.setToolBarActionVisible(key, True) - if hasattr(self, "eraseSelectionToolButton"): - self.eraseSelectionToolButton.setCheckable(True) - if hasattr(self, "brushSelectionToolButton"): - self.brushSelectionToolButton.setCheckable(True) - if hasattr(self, "polygonSelectionToolButton"): - self.polygonSelectionToolButton.setCheckable(True) - if hasattr(self, "rectSelectionToolButton"): - self.rectSelectionToolButton.setCheckable(True) - - if DEBUG: + self.colormapAction.setVisible(True) + + if hasattr(self, "maskToolBar"): + self.maskToolBar.activateDensityPlotView() + + if _logger.getEffectiveLevel() == logging.DEBUG: if self._densityPlotWidget is None: self._densityPlotWidget = MaskImageWidget.MaskImageWidget( imageicons=True, @@ -166,7 +188,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): curve = self.getCurve(self._selectionCurve) if curve is None: return - x, y, legend, info = curve[0:4] + x, y, = curve[0:2] if bins is not None: if type(bins) == type(1): bins = (bins, bins) @@ -175,7 +197,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): else: bins = bins[0:2] elif self._bins is None: - bins = [int(x.size/ 10), int(y.size/10)] + bins = [int(x.size / 10), int(y.size/10)] if bins[0] > 100: bins[0] = 100 elif bins[0] < 2: @@ -198,15 +220,14 @@ class MaskScatterWidget(PlotWindow.PlotWindow): #print("shape", image[0].shape, "image max min ", image[0].max(), image[0].min()) #print("deltaxmin and max", (self._binsX[1:] - self._binsX[:-1]).min(), # (self._binsX[1:] - self._binsX[:-1]).max()) - deltaX = (self._binsX[1:]- self._binsX[:-1]).mean() - deltaY = (self._binsY[1:]- self._binsY[:-1]).mean() + deltaX = (self._binsX[1:] - self._binsX[:-1]).mean() + deltaY = (self._binsY[1:] - self._binsY[:-1]).mean() self._xScale = (x0, deltaX) self._yScale = (y0, deltaY) return image[0] def _updateDensityPlot(self, bins=None): - if DEBUG: - print("_updateDensityPlot called") + _logger.debug("_updateDensityPlot called") if self._densityPlotWidget is None: return curve = self.getCurve(self._selectionCurve) @@ -234,8 +255,8 @@ class MaskScatterWidget(PlotWindow.PlotWindow): bins = self._bins x0 = x.min() y0 = y.min() - deltaX = (x.max() - x0)/float(bins[0] - 1) - deltaY = (y.max() - y0)/float(bins[1] - 1) + deltaX = (x.max() - x0) / float(bins[0] - 1) + deltaY = (y.max() - y0) / float(bins[1] - 1) self.xScale = (x0, deltaX) self.yScale = (y0, deltaY) binsX = numpy.arange(bins[0]) * deltaX @@ -244,27 +265,27 @@ class MaskScatterWidget(PlotWindow.PlotWindow): self._binsX = image[2] self._binsY = image[1] self._bins = bins - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: # this does not work properly # update mask levels if self._selectionMask is not None: weights = self._selectionMask[:] weights.shape = x.shape if self._maxNRois > 1: - print("BAD PATH") + _logger.debug("BAD PATH") # this does not work properly yet weightsSum = weights.sum(dtype=numpy.float64) volume = (binsY[1] - binsY[0]) * (binsX[1] - binsX[0]) - mask = numpy.round(numpy.histogram2d(y, x, - bins=(binsY, binsX), - weights=weights, - normed=True)[0] * weightsSum * volume).astype(numpy.uint8) + mask = numpy.round(numpy.histogram2d(y, x, + bins=(binsY, binsX), + weights=weights, + normed=True)[0] * weightsSum * volume).astype(numpy.uint8) else: #print("GOOD PATH") - mask = numpy.histogram2d(y, x, - bins=(binsY, binsX), - weights=weights, - normed=False)[0] + mask = numpy.histogram2d(y, x, + bins=(binsY, binsX), + weights=weights, + normed=False)[0] mask[mask > 0] = 1 #print(mask.min(), mask.max()) self._densityPlotWidget.setSelectionMask(mask, plot=False) @@ -275,7 +296,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): xScale=self.xScale, yScale=self.yScale) - # do not ovelay plot (yet) + # do not overlay plot (yet) pixmap = self._densityPlotWidget.getPixmap() * 1 #pixmap[:, :, 3] = 128 #self.addImage(pixmap, @@ -285,10 +306,84 @@ class MaskScatterWidget(PlotWindow.PlotWindow): self._imageData = image[0] #raise NotImplemented("Density plot view not implemented yet") + def _initColormapDialog(self, imageData, colormap=None): + """Set-up the colormap dialog default values. + + :param numpy.ndarray imageData: data used to init dialog. + :param dict colormap: Description of the colormap as a dict. + See :class:`PlotBackend` for details. + If None, use default values. + """ + goodData = imageData[numpy.isfinite(imageData)] + if goodData.size > 0: + maxData = goodData.max() + minData = goodData.min() + else: + qt.QMessageBox.critical(self, "No Data", + "Image data does not contain any real value") + return + + self.colormapDialog = ColormapDialog.ColormapDialog(self) + + if colormap is None: + colormapIndex = self.DEFAULT_COLORMAP_INDEX + if colormapIndex == 6: + colormapIndex = 1 + self.colormapDialog.setColormap(colormapIndex) + self.colormapDialog.setDataMinMax(minData, maxData) + self.colormapDialog.setAutoscale(1) + self.colormapDialog.setColormap(self.colormapDialog.colormapIndex) + # linear or logarithmic + self.colormapDialog.setColormapType(self.DEFAULT_COLORMAP_LOG_FLAG, + update=False) + else: + # Set-up colormap dialog from provided colormap dict + cmapList = ColormapDialog.colormapDictToList(colormap) + index, autoscale, vMin, vMax, dataMin, dataMax, cmapType = cmapList + self.colormapDialog.setColormap(index) + self.colormapDialog.setAutoscale(autoscale) + self.colormapDialog.setMinValue(vMin) + self.colormapDialog.setMaxValue(vMax) + self.colormapDialog.setDataMinMax(minData, maxData) + self.colormapDialog.setColormapType(cmapType, update=False) + + self.colormap = self.colormapDialog.getColormap() # Is it used? + self.colormapDialog.setWindowTitle("Colormap Dialog") + self.colormapDialog.sigColormapChanged.connect( + self.updateActiveImageColormap) + self.colormapDialog._update() + + def updateActiveImageColormap(self, colormap): + if len(colormap) == 1: + colormap = colormap[0] + # TODO: Once everything is ready to work with dict instead of + # list, we can remove this translation + plotBackendColormap = ColormapDialog.colormapListToDict(colormap) + self.setDefaultColormap(plotBackendColormap) + + image = self.getActiveImage() + if image is None: + if self.colormapDialog is not None: + self.colormapDialog.hide() + return + + if not hasattr(image, "getColormap"): + if self.colormapDialog is not None: + self.colormapDialog.hide() + return + pixmap = MaskImageTools.getPixmapFromData(image.getData(), colormap) + self.addImage(image.getData(), legend=image.getLegend(), + info=image.getInfo(), + pixmap=pixmap) + def setSelectionCurveData(self, x, y, legend=None, info=None, - replot=True, replace=True, linestyle=" ", color=None, - symbol=None, selectable=None, **kw): - self.enableActiveCurveHandling(False) + replace=True, linestyle=" ", resetzoom=True, + color=None, symbol=None, selectable=None, + **kw): + if "replot" in kw: + _logger.warning("MaskScatterWidget.setSelectionCurveData: deprecated replot parameter") + resetzoom = kw["replot"] and resetzoom + self.setActiveCurveHandling(False) if legend is None: legend = "MaskScatterWidget" if symbol is None: @@ -309,9 +404,9 @@ class MaskScatterWidget(PlotWindow.PlotWindow): # the basic curve is drawn self.addCurve(x=x, y=y, legend=legend, info=info, - replace=replace, replot=False, linestyle=linestyle, - color=color, symbol=symbol, selectable=selectable,z=0, - **kw) + replace=replace, resetzoom=False, linestyle=linestyle, + color=color, symbol=symbol, selectable=selectable, + z=0, **kw) self._selectionCurve = legend # if view mode, draw the image @@ -322,14 +417,18 @@ class MaskScatterWidget(PlotWindow.PlotWindow): if self.colormapDialog is None: self._initColormapDialog(imageData) cmap = self.colormapDialog.getColormap() - pixmap=MaskImageTools.getPixmapFromData(imageData, - colormap=cmap) + pixmap = MaskImageTools.getPixmapFromData(imageData, + colormap=cmap) + origin, scale = (0., 0.), (1., 1.) + if self._xScale is not None and self._yScale is not None: + origin = self._xScale[0], self._yScale[0] + scale = self._xScale[1], self._yScale[1] + self.addImage(imageData, legend=legend + "density", - xScale=self._xScale, - yScale=self._yScale, + origin=origin, scale=scale, z=0, pixmap=pixmap, - replot=False) + resetzoom=False) self._imageData = imageData self._pixmap = pixmap @@ -339,26 +438,26 @@ class MaskScatterWidget(PlotWindow.PlotWindow): if self._selectionMask.max(): hasMaskedData = True - if hasMaskedData or (replace==False): - self._updatePlot(replot=False) + if hasMaskedData or not replace: + self._updatePlot(resetzoom=False) - # update the plot if it was requested - if replot: - self.replot() + # update the limits if it was requested + if resetzoom: + self.resetZoom() if 0 :#or self._plotViewMode == "density": # get the binned data imageData = self.getDensityData() # get the associated pixmap - pixmap=MaskImageTools.getPixmapFromData(imageData) + pixmap = MaskImageTools.getPixmapFromData(imageData) if 0: self.addImage(imageData, legend=legend + "density", - xScale=self._xScale, - yScale=self._yScale, - z=0, - pixmap=pixmap, - replot=True) - if DEBUG: + xScale=self._xScale, + yScale=self._yScale, + z=0, + pixmap=pixmap, + resetzoom=True) + if _logger.getEffectiveLevel() == logging.DEBUG: if self._densityPlotWidget is None: self._densityPlotWidget = MaskImageWidget.MaskImageWidget( imageicons=True, @@ -367,8 +466,8 @@ class MaskScatterWidget(PlotWindow.PlotWindow): aspect=True, polygon=True) self._updateDensityPlot() - print("CLOSE = ", numpy.allclose(imageData, self._imageData)) - print("CLOSE PIXMAP = ", numpy.allclose(pixmap, self._pixmap)) + _logger.debug("CLOSE = %s", numpy.allclose(imageData, self._imageData)) + _logger.debug("CLOSE PIXMAP = %s", numpy.allclose(pixmap, self._pixmap)) self._imageData = imageData self._pixmap = pixmap #self._updatePlot() @@ -403,14 +502,14 @@ class MaskScatterWidget(PlotWindow.PlotWindow): def getSelectionMask(self): if self._selectionMask is None: if self._selectionCurve is not None: - x, y, legend, info = self.getCurve(self._selectionCurve) + x, y = self.getCurve(self._selectionCurve)[0:2] self._selectionMask = numpy.zeros(x.shape, numpy.uint8) return self._selectionMask - def _updatePlot(self, replot=True, replace=True): + def _updatePlot(self, resetzoom=False, replace=True): if self._selectionCurve is None: return - x0, y0, legend, info = self.getCurve(self._selectionCurve) + x0, y0, legend, info = self.getCurve(self._selectionCurve)[0:4] # make sure we work with views x = x0[:] y = y0[:] @@ -423,16 +522,16 @@ class MaskScatterWidget(PlotWindow.PlotWindow): tmpMask = self._selectionMask[:] tmpMask.shape = -1 for i in range(0, self._maxNRois + 1): - colors[tmpMask == i, :] = self._selectionColors[i] + colors[tmpMask == i, :] = self.maskToolBar._selectionColors[i] self.setSelectionCurveData(x, y, legend=legend, info=info, #color=colors, color="k", linestyle=" ", - replot=replot, replace=replace) + resetzoom=resetzoom, replace=replace) else: if self._selectionMask is None: for i in range(1, self._maxNRois + 1): - self.removeCurve(legend=legend + " %02d" % i, replot=False) + self.removeCurve(legend=legend + " %02d" % i) else: tmpMask = self._selectionMask[:] tmpMask.shape = -1 @@ -446,77 +545,38 @@ class MaskScatterWidget(PlotWindow.PlotWindow): xMask = x[tmpMask == i] yMask = y[tmpMask == i] if xMask.size < 1: - self.removeCurve(legend=legend + " %02d" % i, - replot=False) + self.removeCurve(legend=legend + " %02d" % i) continue - color = self._selectionColors[i].copy() + color = self.maskToolBar._selectionColors[i].copy() if useAlpha: if len(color) == 4: if type(color[3]) in [numpy.uint8, numpy.int]: color[3] = self._alphaLevel # a copy of the input info is needed in order not # to set the main curve to that color + self.addCurve(xMask, yMask, legend=legend + " %02d" % i, - info=info.copy(), color=color, linestyle=" ", + info=info.copy(), color=color, + ylabel=legend + " %02d" % i, + linestyle=" ", symbol="o", selectable=False, z=1, - replot=False, replace=False) - if replot: - self.replot() - #self.resetZoom() + resetzoom=False, replace=False) + if resetzoom: + self.resetZoom() def setActiveRoiNumber(self, intValue): if (intValue < 0) or (intValue > self._maxNRois): raise ValueError("Value %d outside the interval [0, %d]" % (intValue, self._maxNRois)) self._nRoi = intValue - - def _eraseSelectionIconSignal(self): - if self.eraseSelectionToolButton.isChecked(): - self._eraseMode = True - else: - self._eraseMode = False - - def _polygonIconSignal(self): - if self.polygonSelectionToolButton.isChecked(): - self.setPolygonSelectionMode() - else: - self.setZoomModeEnabled(True) - - def _rectSelectionIconSignal(self): - if DEBUG: - print("_rectSelectionIconSignal") - if self.rectSelectionToolButton.isChecked(): - self.setRectangularSelectionMode() - else: - self.setZoomModeEnabled(True) - - def setZoomModeEnabled(self, flag, color=None): - if color is None: - if hasattr(self, "colormapDialog"): - if self.colormapDialog is None: - color = "#00FFFF" - else: - cmap = self.colormapDialog.getColormap() - if cmap[0] < 2: - color = "#00FFFF" - else: - color = "black" - super(MaskScatterWidget, self).setZoomModeEnabled(flag, color=color) - if flag: - if hasattr(self,"polygonSelectionToolButton"): - self.polygonSelectionToolButton.setChecked(False) - if hasattr(self,"brushSelectionToolButton"): - self.brushSelectionToolButton.setChecked(False) - def _handlePolygonMask(self, points): - if DEBUG: - print("_handlePolygonMask called") + _logger.debug("_handlePolygonMask called") if self._eraseMode: value = 0 else: value = self._nRoi - x, y, legend, info = self.getCurve(self._selectionCurve) + x, y = self.getCurve(self._selectionCurve)[0:2] x.shape = -1 y.shape = -1 currentMask = self.getSelectionMask() @@ -533,12 +593,21 @@ class MaskScatterWidget(PlotWindow.PlotWindow): self.setSelectionMask(currentMask, plot=True) self._emitMaskChangedSignal() + def setMouseText(self, text=""): + try: + if text: + qt.QToolTip.showText(self.cursor().pos(), + text, self, qt.QRect()) + else: + qt.QToolTip.hideText() + except: + _logger.warning("Error trying to show mouse text <%s>" % text) + def graphCallback(self, ddict): - if DEBUG: - print("MaskScatterWidget graphCallback", ddict) + _logger.debug("MaskScatterWidget graphCallback %s", ddict) if ddict["event"] == "drawingFinished": if ddict["parameters"]["shape"].lower() == "rectangle": - points = numpy.zeros((5,2), dtype=ddict["points"].dtype) + points = numpy.zeros((5, 2), dtype=ddict["points"].dtype) points[0] = ddict["points"][0] points[1, 0] = ddict["points"][0, 0] points[1, 1] = ddict["points"][1, 1] @@ -553,7 +622,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): if (self._plotViewMode == "density") and \ (self._imageData is not None): shape = self._imageData.shape - row, column = MaskImageTools.convertToRowAndColumn( \ + row, column = MaskImageTools.convertToRowAndColumn( ddict['x'], ddict['y'], shape, @@ -601,7 +670,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): #self.setMouseText("%g, %g, %g" % (row, column, self.__imageData[rowMin, columnMin])) #To show mouse coordinates: #self.setMouseText("%g, %g, %g" % (ddict['x'], ddict['y'], self.__imageData[rowMin, columnMin])) - if self._xScale is not None: + if self._xScale is not None and self._yScale is not None: x = self._xScale[0] + column * self._xScale[1] y = self._yScale[0] + row * self._yScale[1] else: @@ -610,7 +679,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): self.setMouseText("%g, %g, %g" % (x, y, self._imageData[row, column])) if self._brushMode: - if self.isZoomModeEnabled(): + if self.getInteractiveMode()['mode'] == 'zoom': return if ddict['button'] != "left": return @@ -632,103 +701,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): # the base implementation handles ROIs, mouse position and activeCurve super(MaskScatterWidget, self).graphCallback(ddict) - def _brushIconSignal(self): - if DEBUG: - print("brushIconSignal") - if self._brushMenu is None: - self._brushMenu = qt.QMenu() - self._brushMenu.addAction(QString(" 1 Image Pixel Width"), - self._setBrush1) - self._brushMenu.addAction(QString(" 2 Image Pixel Width"), - self._setBrush2) - self._brushMenu.addAction(QString(" 3 Image Pixel Width"), - self._setBrush3) - self._brushMenu.addAction(QString(" 5 Image Pixel Width"), - self._setBrush4) - self._brushMenu.addAction(QString("10 Image Pixel Width"), - self._setBrush5) - self._brushMenu.addAction(QString("20 Image Pixel Width"), - self._setBrush6) - self._brushMenu.exec_(self.cursor().pos()) - - def _brushSelectionIconSignal(self): - if DEBUG: - print("_setBrushSelectionMode") - if hasattr(self, "polygonSelectionToolButton"): - self.polygonSelectionToolButton.setChecked(False) - self.setDrawModeEnabled(False) - if self.brushSelectionToolButton.isChecked(): - self._brushMode = True - self.setZoomModeEnabled(False) - else: - self._brushMode = False - self.setZoomModeEnabled(True) - - def _setBrush1(self): - self._brushWidth = 1 - - def _setBrush2(self): - self._brushWidth = 2 - - def _setBrush3(self): - self._brushWidth = 3 - - def _setBrush4(self): - self._brushWidth = 5 - - def _setBrush5(self): - self._brushWidth = 10 - - def _setBrush6(self): - self._brushWidth = 20 - - def setRectangularSelectionMode(self): - """ - Resets zoom mode and enters selection mode with the current active ROI index - """ - self._zoomMode = False - self._brushMode = False - color = self._selectionColors[self._nRoi] - # make sure the selection is made with a non transparent color - if len(color) == 4: - if type(color[-1]) in [numpy.uint8, numpy.int8]: - color = color.copy() - color[-1] = 255 - self.setDrawModeEnabled(True, - shape="rectangle", - label="mask", - color=color) - self.setZoomModeEnabled(False) - if hasattr(self, "brushSelectionToolButton"): - self.brushSelectionToolButton.setChecked(False) - if hasattr(self,"polygonSelectionToolButton"): - self.polygonSelectionToolButton.setChecked(False) - if hasattr(self,"rectSelectionToolButton"): - self.rectSelectionToolButton.setChecked(True) - - def setPolygonSelectionMode(self): - """ - Resets zoom mode and enters selection mode with the current active ROI index - """ - self._zoomMode = False - self._brushMode = False - color = self._selectionColors[self._nRoi] - # make sure the selection is made with a non transparent color - if len(color) == 4: - if type(color[-1]) in [numpy.uint8, numpy.int8]: - color = color.copy() - color[-1] = 255 - self.setDrawModeEnabled(True, shape="polygon", label="mask", - color=color) - self.setZoomModeEnabled(False) - if hasattr(self, "brushSelectionToolButton"): - self.brushSelectionToolButton.setChecked(False) - if hasattr(self,"rectSelectionToolButton"): - self.rectSelectionToolButton.setChecked(False) - if hasattr(self,"polygonSelectionToolButton"): - self.polygonSelectionToolButton.setChecked(True) - - def setEraseSelectionMode(self, erase=True): + def setEraseSelectionMode(self, erase=True): # TODO: unused? if erase: self._eraseMode = True else: @@ -751,54 +724,11 @@ class MaskScatterWidget(PlotWindow.PlotWindow): def emitMaskScatterWidgetSignal(self, ddict): self.sigMaskScatterWidgetSignal.emit(ddict) - def _imageIconSignal(self): - self.__resetSelection() - - def _buildAdditionalSelectionMenuDict(self): - self._additionalSelectionMenu = {} - #scatter view menu - menu = qt.QMenu() - menu.addAction(QString("Density plot view"), self.__setDensityPlotView) - menu.addAction(QString("Reset Selection"), self.__resetSelection) - menu.addAction(QString("Invert Selection"), self._invertSelection) - self._additionalSelectionMenu["scatter"] = menu - - # density view menu - menu = qt.QMenu() - menu.addAction(QString("Scatter plot view"), self.__setScatterPlotView) - menu.addAction(QString("Reset Selection"), self.__resetSelection) - menu.addAction(QString("Invert Selection"), self._invertSelection) - menu.addAction(QString("I >= Colormap Max"), self._selectMax) - menu.addAction(QString("Colormap Min < I < Colormap Max"), - self._selectMiddle) - menu.addAction(QString("I <= Colormap Min"), self._selectMin) - menu.addAction(QString("Increase mask alpha"), self._increaseMaskAlpha) - menu.addAction(QString("Decrease mask alpha"), self._decreaseMaskAlpha) - self._additionalSelectionMenu["density"] = menu - - def __setScatterPlotView(self): - self.setPlotViewMode(mode="scatter") - - def __setDensityPlotView(self): - self.setPlotViewMode(mode="density") - - def _additionalIconSignal(self): - if self._plotViewMode == "density": # and imageData is not none ... - self._additionalSelectionMenu["density"].exec_(self.cursor().pos()) - else: - self._additionalSelectionMenu["scatter"].exec_(self.cursor().pos()) - - def __resetSelection(self): - # Needed because receiving directly in _resetSelection it was passing - # False as argument - self._resetSelection(True) - def _resetSelection(self, owncall=True): - if DEBUG: - print("_resetSelection") + _logger.debug("_resetSelection") if self._selectionMask is None: - print("Selection mask is None, doing nothing") + _logger.info("Selection mask is None, doing nothing") return else: self._selectionMask[:] = 0 @@ -870,19 +800,18 @@ class MaskScatterWidget(PlotWindow.PlotWindow): self._emitMaskChangedSignal() def _setSelectionMaskFromDensityMask(self, densityPlotMask, update=None): - if DEBUG: - print("_setSelectionMaskFromDensityMask called") + _logger.debug("_setSelectionMaskFromDensityMask called") curve = self.getCurve(self._selectionCurve) if curve is None: return - x, y, legend, info = curve[0:4] + x, y = curve[0:2] bins = self._bins x0 = x.min() y0 = y.min() deltaX = (x.max() - x0)/float(bins[0]) deltaY = (y.max() - y0)/float(bins[1]) columns = numpy.digitize(x, self._binsX, right=True) - columns[columns>=densityPlotMask.shape[1]] = \ + columns[columns >= densityPlotMask.shape[1]] = \ densityPlotMask.shape[1] - 1 rows = numpy.digitize(y, self._binsY, right=True) rows[rows>=densityPlotMask.shape[0]] = densityPlotMask.shape[0] - 1 @@ -907,8 +836,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): self.setSelectionMask(view, plot=True) def _densityPlotSlot(self, ddict): - if DEBUG: - print("_densityPlotSlot called") + _logger.debug("_densityPlotSlot called") if ddict["event"] == "resetSelection": self.__resetSelection() return @@ -918,13 +846,13 @@ class MaskScatterWidget(PlotWindow.PlotWindow): curve = self.getCurve(self._selectionCurve) if curve is None: return - x, y, legend, info = curve[0:4] + x, y = curve[0:2] bins = self._bins x0 = x.min() y0 = y.min() deltaX = (x.max() - x0)/float(bins[0]) deltaY = (y.max() - y0)/float(bins[1]) - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: if self._selectionMask is None: view = numpy.zeros(x.size, dtype=numpy.uint8) else: @@ -960,7 +888,7 @@ class MaskScatterWidget(PlotWindow.PlotWindow): view2[:] = values[:] if self._selectionMask is not None: view2.shape = self._selectionMask.shape - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: if not numpy.allclose(view, view2): a = view[:] b = view2[:] @@ -969,15 +897,15 @@ class MaskScatterWidget(PlotWindow.PlotWindow): c = 0 for i in range(a.size): if a[i] != b[i]: - print(i, "a = ", a[i], "b = ", b[i], "(x, y) = ", x[i], y[i]) + _logger.debug("%d a = %s, b = %s, (x, y) = (%s, %s)", + i, a[i], b[i], x[i], y[i]) c += 1 if c > 10: break else: - print("OK!!!") + _logger.debug("OK!!!") self.setSelectionMask(view2) - def _initializeAlpha(self): self._alphaLevel = 128 @@ -998,6 +926,15 @@ class MaskScatterWidget(PlotWindow.PlotWindow): self._alphaLevel = 2 self._updatePlot() + def setPolygonSelectionMode(self): + """ + Resets zoom mode and enters selection mode with the current active ROI index + """ + self.maskToolBar.setPolygonSelectionMode() + + def _zoomBack(self, pos): + self.getLimitsHistory().pop() + if __name__ == "__main__": backend = "matplotlib" #backend = "opengl" @@ -1006,7 +943,8 @@ if __name__ == "__main__": print("Received: ", ddict) x = numpy.arange(100.) y = x * 1 - w = MaskScatterWidget(maxNRois=10, bins=(100,100), backend=backend) + w = MaskScatterWidget(maxNRois=10, bins=(100, 100), backend=backend, + control=True) w.setSelectionCurveData(x, y, color="k", selectable=False) import numpy.random w.setSelectionMask(numpy.random.permutation(100) % 10) diff --git a/PyMca5/PyMcaGui/plotting/MaskToolBar.py b/PyMca5/PyMcaGui/plotting/MaskToolBar.py new file mode 100644 index 0000000..b53e81a --- /dev/null +++ b/PyMca5/PyMcaGui/plotting/MaskToolBar.py @@ -0,0 +1,331 @@ +#/*########################################################################## +# Copyright (C) 2004-2017 V.A. Sole, European Synchrotron Radiation Facility +# +# This file is part of the PyMca X-ray Fluorescence Toolkit developed at +# the ESRF by the Software group. +# +# 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. +# +#############################################################################*/ +"""This module implements a plot toolbar with buttons to draw and erase masks. +""" + +__author__ = "P. Knobel" +__license__ = "MIT" +__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" + +import numpy + +from PyMca5.PyMcaGui import PyMcaQt as qt +from .PyMca_Icons import IconDict + +from silx.gui import colors + +if hasattr(qt, "QString"): + QString = qt.QString +else: + QString = qt.safe_str + + +_COLORDICT = colors.COLORDICT +# these are color RGBA strings '#0000ff' +_COLORLIST = [_COLORDICT['black'], + _COLORDICT['blue'], + _COLORDICT['red'], + _COLORDICT['green'], + _COLORDICT['pink'], + _COLORDICT['yellow'], + _COLORDICT['brown'], + _COLORDICT['cyan'], + _COLORDICT['magenta'], + _COLORDICT['orange'], + _COLORDICT['violet'], + #_COLORDICT['bluegreen'], + _COLORDICT['grey'], + _COLORDICT['darkBlue'], + _COLORDICT['darkRed'], + _COLORDICT['darkGreen'], + _COLORDICT['darkCyan'], + _COLORDICT['darkMagenta'], + _COLORDICT['darkYellow'], + _COLORDICT['darkBrown']] + + +class MaskToolBar(qt.QToolBar): + """Toolbar with buttons controlling the mask drawing and erasing + interactions on a :class:`MaskScatterWidget`, to select or deselect + data.""" + # sigIconSignal = qt.pyqtSignal(object) + colorList = _COLORLIST + + def __init__(self, parent=None, plot=None, title="Mask tools", + imageIcons=True, polygon=True): + super(MaskToolBar, self).__init__(title, parent) + assert plot is not None + assert imageIcons or polygon,\ + "It makes no sense to build an empty mask toolbar" + self.plot = plot + self._brushMenu = None + + self.polygonIcon = qt.QIcon(qt.QPixmap(IconDict["polygon"])) + self.imageIcon = qt.QIcon(qt.QPixmap(IconDict["image"])) + self.eraseSelectionIcon = qt.QIcon(qt.QPixmap(IconDict["eraseselect"])) + self.rectSelectionIcon = qt.QIcon(qt.QPixmap(IconDict["boxselect"])) + self.brushSelectionIcon = qt.QIcon(qt.QPixmap(IconDict["brushselect"])) + self.brushIcon = qt.QIcon(qt.QPixmap(IconDict["brush"])) + self.additionalIcon = qt.QIcon(qt.QPixmap(IconDict["additionalselect"])) + + self.polygonSelectionToolButton = qt.QToolButton(self) + self.imageToolButton = qt.QToolButton(self) + self.eraseSelectionToolButton = qt.QToolButton(self) + self.rectSelectionToolButton = qt.QToolButton(self) + self.brushSelectionToolButton = qt.QToolButton(self) + self.brushToolButton = qt.QToolButton(self) + self.additionalSelectionToolButton = qt.QToolButton(self) + + self.polygonSelectionToolButton.setIcon(self.polygonIcon) + self.imageToolButton.setIcon(self.imageIcon) + self.eraseSelectionToolButton.setIcon(self.eraseSelectionIcon) + self.rectSelectionToolButton.setIcon(self.rectSelectionIcon) + self.brushSelectionToolButton.setIcon(self.brushSelectionIcon) + self.brushToolButton.setIcon(self.brushIcon) + self.additionalSelectionToolButton.setIcon(self.additionalIcon) + + self.polygonSelectionToolButton.setToolTip('Polygon selection\n' + 'Click first point to finish') + self.imageToolButton.setToolTip('Reset') + self.eraseSelectionToolButton.setToolTip('Erase Selection') + self.rectSelectionToolButton.setToolTip('Rectangular Selection') + self.brushSelectionToolButton.setToolTip('Brush Selection') + self.brushToolButton.setToolTip('Brush Size') + self.additionalSelectionToolButton.setToolTip('Additional Selections Menu') + + self.eraseSelectionToolButton.setCheckable(True) + self.polygonSelectionToolButton.setCheckable(True) + self.rectSelectionToolButton.setCheckable(True) + self.brushSelectionToolButton.setCheckable(True) + + self.imageAction = self.addWidget(self.imageToolButton) + self.eraseSelectionAction = self.addWidget(self.eraseSelectionToolButton) + self.rectSelectionAction = self.addWidget(self.rectSelectionToolButton) + self.brushSelectionAction = self.addWidget(self.brushSelectionToolButton) + self.brushAction = self.addWidget(self.brushToolButton) + self.polygonSelectionAction = self.addWidget(self.polygonSelectionToolButton) + self.additionalSelectionAction = self.addWidget(self.additionalSelectionToolButton) + + self.imageToolButton.clicked.connect(self._imageIconSignal) + self.eraseSelectionToolButton.clicked.connect(self._eraseSelectionIconSignal) + self.rectSelectionToolButton.clicked.connect(self._rectSelectionIconSignal) + self.brushSelectionToolButton.clicked.connect(self._brushSelectionIconSignal) + self.brushToolButton.clicked.connect(self._brushIconSignal) + self.polygonSelectionToolButton.clicked.connect(self._polygonIconSignal) + self.additionalSelectionToolButton.clicked.connect(self._additionalIconSignal) + + if not imageIcons: + self.imageAction.setVisible(False) + self.eraseSelectionAction.setVisible(False) + self.rectSelectionAction.setVisible(False) + self.brushSelectionAction.setVisible(False) + self.brushAction.setVisible(False) + self.polygonSelectionAction.setVisible(False) + self.additionalSelectionAction.setVisible(False) + + if not polygon: + self.polygonSelectionAction.setVisible(False) + + self._buildAdditionalSelectionMenuDict() + + # selection colors as a RBGA (uint8) array + self._selectionColors = numpy.zeros((len(self.colorList), 4), numpy.uint8) + for i in range(len(self.colorList)): + self._selectionColors[i, 0] = eval("0x" + self.colorList[i][-2:]) + self._selectionColors[i, 1] = eval("0x" + self.colorList[i][3:-2]) + self._selectionColors[i, 2] = eval("0x" + self.colorList[i][1:3]) + self._selectionColors[i, 3] = 0xff + + self.plot.sigInteractiveModeChanged.connect(self._interactiveModeChanged) + + def activateScatterPlotView(self): + self.brushSelectionAction.setVisible(False) + self.brushAction.setVisible(False) + self.eraseSelectionAction.setToolTip("Set erase mode if checked") + + self.eraseSelectionToolButton.setChecked(self.plot._eraseMode) + self.brushSelectionToolButton.setChecked(False) + + def activateDensityPlotView(self): + self.brushSelectionAction.setVisible(True) + self.brushAction.setVisible(True) + self.rectSelectionAction.setVisible(True) + + def _imageIconSignal(self, checked=False): + self.plot._resetSelection(owncall=True) + + def _eraseSelectionIconSignal(self, checked=False): + self.plot._eraseMode = checked + + def _getSelectionColor(self): + """Return a selection color as hex "#RRGGBBAA" string""" + rgba_color_array = self._selectionColors[self.plot._nRoi] + # make sure the selection is made with a non transparent color + if len(rgba_color_array) == 4: + rgba_color_array = rgba_color_array.copy() + rgba_color_array[-1] = 255 + + # convert to string + s = "#" + for channel_uint8_value in rgba_color_array: + s += "{:02x}".format(channel_uint8_value) + return s + + def _polygonIconSignal(self, checked=False): + if checked: + self.plot.setInteractiveMode("draw", shape="polygon", + label="mask", + color=self._getSelectionColor()) + self.plot._zoomMode = False + self.plot._brushMode = False + + self.brushSelectionToolButton.setChecked(False) + self.rectSelectionToolButton.setChecked(False) + self.polygonSelectionToolButton.setChecked(True) + else: + self.plot.setInteractiveMode("select") + self._uncheckAllSelectionButtons() + + def setPolygonSelectionMode(self): + """ + Resets zoom mode and enters selection mode with the current active ROI index + """ + self.polygonSelectionToolButton.setChecked(True) + self.polygonSelectionAction.trigger() # calls _polygonIconSignal + + def _rectSelectionIconSignal(self, checked=False): + if checked: + self.plot._zoomMode = False + self.plot._brushMode = False + self.brushSelectionToolButton.setChecked(False) + self.polygonSelectionToolButton.setChecked(False) + self.rectSelectionToolButton.setChecked(True) + + self.plot.setInteractiveMode("draw", + shape="rectangle", + label="mask", + color=self._getSelectionColor()) + else: + self.plot.setInteractiveMode("select") + self._uncheckAllSelectionButtons() + + def _brushSelectionIconSignal(self, checked=False): + self.polygonSelectionToolButton.setChecked(False) + self.rectSelectionToolButton.setChecked(False) + if checked: + self.plot._brushMode = True + self.plot.setInteractiveMode('select') + else: + self._brushMode = False + + def _brushIconSignal(self, checked=False): + if self._brushMenu is None: + self._brushMenu = qt.QMenu() + self._brushMenu.addAction(QString(" 1 Image Pixel Width"), + self._setBrush1) + self._brushMenu.addAction(QString(" 2 Image Pixel Width"), + self._setBrush2) + self._brushMenu.addAction(QString(" 3 Image Pixel Width"), + self._setBrush3) + self._brushMenu.addAction(QString(" 5 Image Pixel Width"), + self._setBrush4) + self._brushMenu.addAction(QString("10 Image Pixel Width"), + self._setBrush5) + self._brushMenu.addAction(QString("20 Image Pixel Width"), + self._setBrush6) + self._brushMenu.exec_(self.cursor().pos()) + + def _setBrush1(self): + self.plot._brushWidth = 1 + + def _setBrush2(self): + self.plot._brushWidth = 2 + + def _setBrush3(self): + self.plot._brushWidth = 3 + + def _setBrush4(self): + self.plot._brushWidth = 5 + + def _setBrush5(self): + self.plot._brushWidth = 10 + + def _setBrush6(self): + self.plot._brushWidth = 20 + + def _buildAdditionalSelectionMenuDict(self): + self._additionalSelectionMenu = {} + #scatter view menu + menu = qt.QMenu() + menu.addAction(QString("Density plot view"), self.__setDensityPlotView) + menu.addAction(QString("Reset Selection"), self.__resetSelection) + menu.addAction(QString("Invert Selection"), self.plot._invertSelection) + self._additionalSelectionMenu["scatter"] = menu + + # density view menu + menu = qt.QMenu() + menu.addAction(QString("Scatter plot view"), self.__setScatterPlotView) + menu.addAction(QString("Reset Selection"), self.__resetSelection) + menu.addAction(QString("Invert Selection"), self.plot._invertSelection) + menu.addAction(QString("I >= Colormap Max"), self.plot._selectMax) + menu.addAction(QString("Colormap Min < I < Colormap Max"), + self.plot._selectMiddle) + menu.addAction(QString("I <= Colormap Min"), self.plot._selectMin) + menu.addAction(QString("Increase mask alpha"), self.plot._increaseMaskAlpha) + menu.addAction(QString("Decrease mask alpha"), self.plot._decreaseMaskAlpha) + + self._additionalSelectionMenu["density"] = menu + + def __setScatterPlotView(self): + self.plot.setPlotViewMode(mode="scatter") + + def __setDensityPlotView(self): + self.plot.setPlotViewMode(mode="density") + + def __resetSelection(self): + self.plot._resetSelection(owncall=True) + + def _additionalIconSignal(self, checked=False): + if self.plot._plotViewMode == "density": # and imageData is not none ... + self._additionalSelectionMenu["density"].exec_(self.cursor().pos()) + else: + self._additionalSelectionMenu["scatter"].exec_(self.cursor().pos()) + + def _uncheckAllSelectionButtons(self): + self.brushSelectionToolButton.setChecked(False) + self.polygonSelectionToolButton.setChecked(False) + self.brushSelectionToolButton.setChecked(False) + + def _interactiveModeChanged(self, source): + if self.plot.getInteractiveMode()['mode'] != "draw": + self._uncheckAllSelectionButtons() + + # def emitIconSignal(self, key, event="iconClicked"): + # ddict = {"key": key, + # "event": event} + # self.sigIconSignal.emit(ddict) + + diff --git a/PyMca5/PyMcaGui/plotting/McaROIWidget.py b/PyMca5/PyMcaGui/plotting/McaROIWidget.py index e49948e..5a2c663 100644 --- a/PyMca5/PyMcaGui/plotting/McaROIWidget.py +++ b/PyMca5/PyMcaGui/plotting/McaROIWidget.py @@ -29,6 +29,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os +import logging from PyMca5.PyMcaGui import PyMcaQt as qt if hasattr(qt, "QString"): @@ -41,7 +42,9 @@ QTVERSION = qt.qVersion() from PyMca5.PyMcaCore import PyMcaDirs from PyMca5.PyMcaIO import ConfigDict -DEBUG = 0 +_logger = logging.getLogger(__name__) + + class McaROIWidget(qt.QWidget): sigMcaROIWidgetSignal = qt.pyqtSignal(object) @@ -109,8 +112,7 @@ class McaROIWidget(qt.QWidget): self.mcaROITable.sigMcaROITableSignal.connect(self._forward) def _add(self): - if DEBUG: - print("McaROIWidget._add") + _logger.debug("McaROIWidget._add") ddict={} ddict['event'] = "AddROI" roilist, roidict = self.mcaROITable.getROIListAndDict() @@ -409,8 +411,7 @@ class McaROITable(qt.QTableWidget): else: if currentroi in self.roidict.keys(): self.selectRow(self.roidict[currentroi]['line']) - if DEBUG: - print("Qt4 ensureCellVisible to be implemented") + _logger.debug("Qt4 ensureCellVisible to be implemented") self.building = False def addROI(self, roi, key=None): @@ -472,8 +473,7 @@ class McaROITable(qt.QTableWidget): ddict['row' ] = row ddict['col' ] = col if row >= len(self.roilist): - if DEBUG: - print("deleting???") + _logger.debug("deleting???") return row = 0 item = self.item(row, 0) @@ -492,8 +492,7 @@ class McaROITable(qt.QTableWidget): self._emitSelectionChangedSignal(row, 0) def _cellChangedSlot(self, row, col): - if DEBUG: - print("_cellChangedSlot(%d, %d)" % (row, col)) + _logger.debug("_cellChangedSlot(%d, %d)", row, col) if self.building: return if col == 0: @@ -513,8 +512,7 @@ class McaROITable(qt.QTableWidget): except: return if row >= len(self.roilist): - if DEBUG: - print("deleting???") + _logger.debug("deleting???") return if QTVERSION < '4.0.0': text = str(self.text(row, 0)) @@ -535,8 +533,7 @@ class McaROITable(qt.QTableWidget): def nameSlot(self, row, col): if col != 0: return if row >= len(self.roilist): - if DEBUG: - print("deleting???") + _logger.debug("deleting???") return item = self.item(row, col) if item is None: @@ -572,8 +569,7 @@ class McaROITable(qt.QTableWidget): col = var[1] if col == 0: if row >= len(self.roilist): - if DEBUG: - print("deleting???") + _logger.debug("deleting???") return row = 0 item = self.item(row, col) diff --git a/PyMca5/PyMcaGui/plotting/PlotWidget.py b/PyMca5/PyMcaGui/plotting/PlotWidget.py index d6282a8..4619feb 100644 --- a/PyMca5/PyMcaGui/plotting/PlotWidget.py +++ b/PyMca5/PyMcaGui/plotting/PlotWidget.py @@ -28,7 +28,9 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys -import os +import logging +import traceback + from PyMca5.PyMcaGraph import Plot SVG = True @@ -82,10 +84,21 @@ else: if not hasattr(QtCore, "Signal"): QtCore.Signal = QtCore.pyqtSignal + +_logger = logging.getLogger(__name__) + DEBUG = 0 if DEBUG: + _logger.setLevel(logging.DEBUG) Plot.DEBUG = DEBUG +_logger.warning("%s is deprecated, you are advised to use " + "silx.gui.plot.PlotWidget instead", + __name__) +for line in traceback.format_stack(limit=3): + _logger.warning(line.rstrip()) + + class PlotWidget(QtGui.QMainWindow, Plot.Plot): sigPlotSignal = QtCore.Signal(object) diff --git a/PyMca5/PyMcaGui/plotting/PlotWindow.py b/PyMca5/PyMcaGui/plotting/PlotWindow.py index 8dc46f7..b100f3e 100644 --- a/PyMca5/PyMcaGui/plotting/PlotWindow.py +++ b/PyMca5/PyMcaGui/plotting/PlotWindow.py @@ -1474,7 +1474,10 @@ class PlotWindow(PlotWidget.PlotWidget): os.remove(filename) if filterused[0].upper() == "WIDGET": fformat = filename[-3:].upper() - pixmap = qt.QPixmap.grabWidget(self) + if hasattr(qt.QPixmap,"grabWidget"): + pixmap = qt.QPixmap.grabWidget(self) + else: + pixmap = self.grab() if not pixmap.save(filename, fformat): msg = qt.QMessageBox(self) msg.setIcon(qt.QMessageBox.Critical) diff --git a/PyMca5/PyMcaGui/plotting/ProfileScanWidget.py b/PyMca5/PyMcaGui/plotting/ProfileScanWidget.py index 93cc866..6b3d95a 100644 --- a/PyMca5/PyMcaGui/plotting/ProfileScanWidget.py +++ b/PyMca5/PyMcaGui/plotting/ProfileScanWidget.py @@ -34,7 +34,7 @@ if 1: # if not, we miss profile fitting ... from PyMca5.PyMcaGui.pymca.ScanWindow import ScanWindow as Window else: - from .PlotWindow import PlotWindow as Window + from silx.gui.plot import PlotWindow as Window DEBUG = 0 class ProfileScanWidget(Window): @@ -118,7 +118,7 @@ class ProfileScanWidget(Window): elif action == 'REMOVE': self.sigRemoveClicked.emit(ddict) else: - self.replaceAddClicked.emit(ddict) + self.sigReplaceClicked.emit(ddict) def test(): app = qt.QApplication([]) diff --git a/PyMca5/PyMcaGui/plotting/PyMca_Icons.py b/PyMca5/PyMcaGui/plotting/PyMca_Icons.py index 56fdc97..ebb3f37 100644 --- a/PyMca5/PyMcaGui/plotting/PyMca_Icons.py +++ b/PyMca5/PyMcaGui/plotting/PyMca_Icons.py @@ -4177,9 +4177,22 @@ class _PatchedIconDict(MutableMapping): # we also need to remove the key from internal translation table del self._translation_table[key] + IconDict = _PatchedIconDict(IconDict0) +def change_icons(plot): + """Replace some of the silx icons with PyMca icons. + + :param plot: Silx plot window, or ScanWindow, or McaWindow + :return: + """ + from PyMca5.PyMcaGui import PyMcaQt as qt + plot.getRoiAction().setIcon(qt.QIcon(qt.QPixmap(IconDict["roi"]))) + if hasattr(plot, "printPreview"): + plot.printPreview.setIcon(qt.QIcon(qt.QPixmap(IconDict["fileprint"]))) + + def showIcons(): w = qt.QWidget() g = qt.QGridLayout(w) diff --git a/PyMca5/PyMcaGui/plotting/Q4PyMcaPrintPreview.py b/PyMca5/PyMcaGui/plotting/Q4PyMcaPrintPreview.py index 1a4608b..3734a52 100644 --- a/PyMca5/PyMcaGui/plotting/Q4PyMcaPrintPreview.py +++ b/PyMca5/PyMcaGui/plotting/Q4PyMcaPrintPreview.py @@ -28,7 +28,8 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys -import os +import logging +import traceback from PyMca5.PyMcaGui import PyMcaQt as qt DEBUG = 0 __revision__="$Revision: 1.7 $" @@ -39,6 +40,14 @@ __revision__="$Revision: 1.7 $" QTVERSION = qt.qVersion() +_logger = logging.getLogger(__name__) +_logger.warning("%s is deprecated, you are advised to use " + "silx.gui.widgets.PrintPreview instead", + __name__) + +for line in traceback.format_stack(limit=3): + _logger.warning(line.rstrip()) + ################################################################################ ################## PyMcaPrintPreview ################### diff --git a/PyMca5/PyMcaGui/plotting/RGBCorrelatorGraph.py b/PyMca5/PyMcaGui/plotting/RGBCorrelatorGraph.py index bcaf7ae..24d874c 100644 --- a/PyMca5/PyMcaGui/plotting/RGBCorrelatorGraph.py +++ b/PyMca5/PyMcaGui/plotting/RGBCorrelatorGraph.py @@ -30,14 +30,24 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import numpy -from . import PlotWidget +import logging from PyMca5.PyMcaGui import PyMcaQt as qt +from silx.gui.plot import PlotWidget +from silx.gui.plot.PrintPreviewToolButton import SingletonPrintPreviewToolButton from .PyMca_Icons import IconDict -from . import PyMcaPrintPreview from PyMca5.PyMcaCore import PyMcaDirs +from silx.gui import icons as silx_icons + +if sys.version_info[0] == 3: + from io import BytesIO +else: + import cStringIO as _StringIO + BytesIO = _StringIO.StringIO QTVERSION = qt.qVersion() -DEBUG = 0 +_logger = logging.getLogger(__name__) + + def convertToRowAndColumn(x, y, shape, xScale=None, yScale=None, safe=True): if xScale is None: @@ -59,6 +69,7 @@ def convertToRowAndColumn(x, y, shape, xScale=None, yScale=None, safe=True): r = int(r) return r, c + class RGBCorrelatorGraph(qt.QWidget): sigProfileSignal = qt.pyqtSignal(object) @@ -71,17 +82,22 @@ class RGBCorrelatorGraph(qt.QWidget): self.mainLayout.setContentsMargins(0, 0, 0, 0) self.mainLayout.setSpacing(0) self._keepDataAspectRatioFlag = False + self.graph = PlotWidget(parent=self, backend=backend) + self.graph.setGraphXLabel("Column") + self.graph.setGraphYLabel("Row") + self.graph.setYAxisAutoScale(True) + self.graph.setXAxisAutoScale(True) + plotArea = self.graph.getWidgetHandle() + plotArea.setContextMenuPolicy(qt.Qt.CustomContextMenu) + plotArea.customContextMenuRequested.connect(self._zoomBack) + self._buildToolBar(selection, colormap, imageicons, standalonesave, standalonezoom=standalonezoom, profileselection=profileselection, aspect=aspect, polygon=polygon) - self.graph = PlotWidget.PlotWidget(self, backend=backend, aspect=aspect) - self.graph.setGraphXLabel("Column") - self.graph.setGraphYLabel("Row") - self.graph.setYAxisAutoScale(True) - self.graph.setXAxisAutoScale(True) + if profileselection: if len(self._pickerSelectionButtons): self.graph.sigPlotSignal.connect(\ @@ -91,9 +107,6 @@ class RGBCorrelatorGraph(qt.QWidget): self.saveDirectory = os.getcwd() self.mainLayout.addWidget(self.graph) - self.printPreview = PyMcaPrintPreview.PyMcaPrintPreview(modal = 0) - if DEBUG: - print("printPreview id = %d" % id(self.printPreview)) def sizeHint(self): return qt.QSize(1.5 * qt.QWidget.sizeHint(self).width(), @@ -123,6 +136,7 @@ class RGBCorrelatorGraph(qt.QWidget): self.hLineIcon = qt.QIcon(qt.QPixmap(IconDict["horizontal"])) self.vLineIcon = qt.QIcon(qt.QPixmap(IconDict["vertical"])) self.lineIcon = qt.QIcon(qt.QPixmap(IconDict["diagonal"])) + self.copyIcon = silx_icons.getQIcon("edit-copy") self.toolBar = qt.QWidget(self) self.toolBarLayout = qt.QHBoxLayout(self.toolBar) @@ -160,16 +174,16 @@ class RGBCorrelatorGraph(qt.QWidget): #Aspect ratio if aspect: self.aspectButton = self._addToolButton(self.solidCircleIcon, - self._aspectButtonSignal, - 'Keep data aspect ratio', - toggle = False) + self._aspectButtonSignal, + 'Keep data aspect ratio', + toggle=False) self.aspectButton.setChecked(False) #colormap if colormap: tb = self._addToolButton(self.colormapIcon, None, - 'Change Colormap') + 'Change Colormap') self.colormapToolButton = tb #flip @@ -190,6 +204,10 @@ class RGBCorrelatorGraph(qt.QWidget): 'Save') self.saveToolButton = tb + self.copyToolButton = self._addToolButton(self.copyIcon, + self._copyIconSignal, + "Copy graph to clipboard") + #Selection if selection: tb = self._addToolButton(self.selectionIcon, @@ -221,7 +239,6 @@ class RGBCorrelatorGraph(qt.QWidget): 'Brush Selection') self.brushSelectionToolButton = tb - tb = self._addToolButton(self.brushIcon, None, 'Select Brush') @@ -292,7 +309,7 @@ class RGBCorrelatorGraph(qt.QWidget): #self.lineWidthProfileButton = tb #self._pickerSelectionButtons.append(tb) if self._polygonSelection: - print("Polygon selection not implemented yet") + _logger.info("Polygon selection not implemented yet") #hide profile selection buttons if imageicons: for button in self._pickerSelectionButtons: @@ -311,13 +328,13 @@ class RGBCorrelatorGraph(qt.QWidget): self.toolBarLayout.addWidget(qt.HorizontalSpacer(self.toolBar)) # ---print - tb = self._addToolButton(self.printIcon, - self.printGraph, - 'Prints the Graph') + self.printPreview = SingletonPrintPreviewToolButton(parent=self, + plot=self.graph) + self.printPreview.setIcon(self.printIcon) + self.toolBarLayout.addWidget(self.printPreview) def _aspectButtonSignal(self): - if DEBUG: - print("_aspectButtonSignal") + _logger.debug("_aspectButtonSignal") if self._keepDataAspectRatioFlag: self.keepDataAspectRatio(False) else: @@ -332,7 +349,7 @@ class RGBCorrelatorGraph(qt.QWidget): self._keepDataAspectRatioFlag = False self.aspectButton.setIcon(self.solidCircleIcon) self.aspectButton.setToolTip("Keep data aspect ratio") - self.graph.keepDataAspectRatio(self._keepDataAspectRatioFlag) + self.graph.setKeepDataAspectRatio(self._keepDataAspectRatioFlag) def showInfo(self): self.infoWidget.show() @@ -351,7 +368,7 @@ class RGBCorrelatorGraph(qt.QWidget): else: qt.QToolTip.hideText() except: - print("Error trying to show mouse text <%s>" % text) + _logger.warning("Error trying to show mouse text <%s>" % text) def focusOutEvent(self, ev): qt.QToolTip.hideText() @@ -447,8 +464,8 @@ class RGBCorrelatorGraph(qt.QWidget): button.hide() self._pickerSelectionWidthLabel.hide() self._pickerSelectionWidthValue.hide() - #self.graph.setPickerSelectionModeOff() - self.graph.setDrawModeEnabled(False) + if self.graph.getInteractiveMode()['mode'] == 'draw': + self.graph.setInteractiveMode('select') def showProfileSelectionIcons(self): if not len(self._pickerSelectionButtons): @@ -471,8 +488,7 @@ class RGBCorrelatorGraph(qt.QWidget): def _setPickerSelectionMode(self, mode=None): if mode is None: - self.graph.setDrawModeEnabled(False) - self.graph.setZoomModeEnabled(True) + self.graph.setInteractiveMode('zoom') else: if mode == "HORIZONTAL": shape = "hline" @@ -480,8 +496,7 @@ class RGBCorrelatorGraph(qt.QWidget): shape = "vline" else: shape = "line" - self.graph.setZoomModeEnabled(False) - self.graph.setDrawModeEnabled(True, + self.graph.setInteractiveMode('draw', shape=shape, label=mode) ddict = {} @@ -492,15 +507,14 @@ class RGBCorrelatorGraph(qt.QWidget): self.sigProfileSignal.emit(ddict) def _graphPolygonSignalReceived(self, ddict): - if DEBUG: - print("PolygonSignal Received") - for key in ddict.keys(): - print(key, ddict[key]) + _logger.debug("PolygonSignal Received") + for key in ddict.keys(): + _logger.debug("%s: %s", key, ddict[key]) if ddict['event'] not in ['drawingProgress', 'drawingFinished']: return label = ddict['parameters']['label'] - if label not in ['HORIZONTAL', 'VERTICAL', 'LINE']: + if label not in ['HORIZONTAL', 'VERTICAL', 'LINE']: return ddict['mode'] = label ddict['pixelwidth'] = self._pickerSelectionWidthValue.value() @@ -531,32 +545,33 @@ class RGBCorrelatorGraph(qt.QWidget): self._zoomReset() def _zoomReset(self, replot=None): - if DEBUG: - print("_zoomReset") - if replot is None: - replot = True + _logger.debug("_zoomReset") if self.graph is not None: self.graph.resetZoom() - if replot: - self.graph.replot() def _yAutoScaleToggle(self): if self.graph is not None: - if self.graph.isYAxisAutoScale(): - self.graph.setYAxisAutoScale(False) - self.yAutoScaleToolButton.setDown(False) - else: - self.graph.setYAxisAutoScale(True) - self.yAutoScaleToolButton.setDown(True) + self.yAutoScaleToolButton.setDown( + not self.graph.isYAxisAutoScale()) + self.graph.setYAxisAutoScale( + not self.graph.isYAxisAutoScale()) def _xAutoScaleToggle(self): if self.graph is not None: - if self.graph.isXAxisAutoScale(): - self.graph.setXAxisAutoScale(False) - self.xAutoScaleToolButton.setDown(False) - else: - self.graph.setXAxisAutoScale(True) - self.xAutoScaleToolButton.setDown(True) + self.xAutoScaleToolButton.setDown( + not self.graph.isXAxisAutoScale()) + self.graph.setXAxisAutoScale( + not self.graph.isXAxisAutoScale()) + + def _copyIconSignal(self): + pngFile = BytesIO() + self.graph.saveGraph(pngFile, fileFormat='png') + pngFile.flush() + pngFile.seek(0) + pngData = pngFile.read() + pngFile.close() + image = qt.QImage.fromData(pngData, 'png') + qt.QApplication.clipboard().setImage(image) def _saveIconSignal(self): self.saveDirectory = PyMcaDirs.outputDir @@ -620,25 +635,26 @@ class RGBCorrelatorGraph(qt.QWidget): return if filetype.upper() == "IMAGE": - self.saveGraphImage(outputFile, original = True) + self.saveGraphImage(outputFile, original=True) elif filetype.upper() == "ZOOMEDIMAGE": - self.saveGraphImage(outputFile, original = False) + self.saveGraphImage(outputFile, original=False) else: self.saveGraphWidget(outputFile) - def saveGraphImage(self, filename, original = False): + def saveGraphImage(self, filename, original=False): format_ = filename[-3:].upper() - #This is the whole image, not the zoomed one ... - rgbData, legend, info, pixmap = self.graph.getActiveImage() + activeImage = self.graph.getActiveImage() + rgbdata = activeImage.getRgbaImageData() + # silx to pymca scale convention (a + b x) + xScale = activeImage.getOrigin()[0], activeImage.getScale()[0] + yScale = activeImage.getOrigin()[1], activeImage.getScale()[1] if original: # save whole image - bgrData = numpy.array(rgbData, copy=True) - bgrData[:,:,0] = rgbData[:, :, 2] - bgrData[:,:,2] = rgbData[:, :, 0] + bgradata = numpy.array(rgbdata, copy=True) + bgradata[:, :, 0] = rgbdata[:, :, 2] + bgradata[:, :, 2] = rgbdata[:, :, 0] else: - xScale = info.get("plot_xScale", None) - yScale = info.get("plot_yScale", None) - shape = rgbData.shape[:2] + shape = rgbdata.shape[:2] xmin, xmax = self.graph.getGraphXLimits() ymin, ymax = self.graph.getGraphYLimits() # save zoomed image, for that we have to get the limits @@ -652,16 +668,16 @@ class RGBCorrelatorGraph(qt.QWidget): row1 += 1 if col1 < shape[1]: col1 += 1 - tmpArray = rgbData[row0:row1, col0:col1, :] - bgrData = numpy.array(tmpArray, copy=True, dtype=rgbData.dtype) - bgrData[:,:,0] = tmpArray[:, :, 2] - bgrData[:,:,2] = tmpArray[:, :, 0] + tmpArray = rgbdata[row0:row1, col0:col1, :] + bgradata = numpy.array(tmpArray, copy=True, dtype=rgbdata.dtype) + bgradata[:, :, 0] = tmpArray[:, :, 2] + bgradata[:, :, 2] = tmpArray[:, :, 0] if self.graph.isYAxisInverted(): - qImage = qt.QImage(bgrData, bgrData.shape[1], bgrData.shape[0], - qt.QImage.Format_RGB32) + qImage = qt.QImage(bgradata, bgradata.shape[1], bgradata.shape[0], + qt.QImage.Format_ARGB32) else: - qImage = qt.QImage(bgrData, bgrData.shape[1], bgrData.shape[0], - qt.QImage.Format_RGB32).mirrored(False, True) + qImage = qt.QImage(bgradata, bgradata.shape[1], bgradata.shape[0], + qt.QImage.Format_ARGB32).mirrored(False, True) pixmap = qt.QPixmap.fromImage(qImage) if pixmap.save(filename, format_): return @@ -669,10 +685,10 @@ class RGBCorrelatorGraph(qt.QWidget): qt.QMessageBox.critical(self, "Save Error", "%s" % sys.exc_info()[1]) return - + def saveGraphWidget(self, filename): format_ = filename[-3:].upper() - if hasattr(qt.QPixmap, "graphWidget"): + if hasattr(qt.QPixmap, "grabWidget"): # Qt4 pixmap = qt.QPixmap.grabWidget(self.graph.getWidgetHandle()) else: @@ -691,20 +707,12 @@ class RGBCorrelatorGraph(qt.QWidget): else: return False - def printGraph(self): - if hasattr(qt.QPixmap, "grabWidget"): - pixmap = qt.QPixmap.grabWidget(self.graph.getWidgetHandle()) - else: - pixmap = self.graph.getWidgetHandle().grab() - self.printPreview.addPixmap(pixmap) - if self.printPreview.isReady(): - if self.printPreview.isHidden(): - self.printPreview.show() - self.printPreview.raise_() - def selectColormap(self): qt.QMessageBox.information(self, "Open", "Not implemented (yet)") + def _zoomBack(self, pos): + self.graph.getLimitsHistory().pop() + class MyQLabel(qt.QLabel): def __init__(self,parent=None,name=None,fl=0,bold=True, color= qt.Qt.red): qt.QLabel.__init__(self,parent) diff --git a/PyMca5/PyMcaGui/plotting/ScatterPlotCorrelatorWidget.py b/PyMca5/PyMcaGui/plotting/ScatterPlotCorrelatorWidget.py index e55aff9..8d95a0d 100644 --- a/PyMca5/PyMcaGui/plotting/ScatterPlotCorrelatorWidget.py +++ b/PyMca5/PyMcaGui/plotting/ScatterPlotCorrelatorWidget.py @@ -141,12 +141,12 @@ class ScatterPlotCorrelatorWidget(MaskScatterWidget.MaskScatterWidget): self.setSelectionCurveData(x, y, legend=None, color="k", symbol=".", - replot=False, + resetzoom=False, replace=True, xlabel=xLabel, ylabel=yLabel, selectable=False) - self._updatePlot(replot=False, replace=True) + self._updatePlot(resetzoom=False, replace=True) #matplotlib needs a zoom reset to update the scales # that problem does not seem to be present with OpenGL self.resetZoom() diff --git a/PyMca5/PyMcaGui/plotting/SilxMaskImageWidget.py b/PyMca5/PyMcaGui/plotting/SilxMaskImageWidget.py index c8345ea..6df787a 100644 --- a/PyMca5/PyMcaGui/plotting/SilxMaskImageWidget.py +++ b/PyMca5/PyMcaGui/plotting/SilxMaskImageWidget.py @@ -1,5 +1,5 @@ # /*######################################################################### -# Copyright (C) 2004-2017 V.A. Sole, European Synchrotron Radiation Facility +# Copyright (C) 2004-2018 European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -112,7 +112,8 @@ def convertToRowAndColumn(x, y, shape, class MyMaskToolsWidget(MaskToolsWidget): """Backport of the setSelectionMask behavior implemented in silx 0.6.0, - to synchronize mask parameters with the active image.""" + to synchronize mask parameters with the active image. + This widget must not be used with silx >= 0.6""" def setSelectionMask(self, mask, copy=True): """Set the mask to a new array. :param numpy.ndarray mask: The array to use for the mask. @@ -157,7 +158,7 @@ class MyMaskToolsDockWidget(MaskToolsDockWidget): """ def __init__(self, parent=None, plot=None, name='Mask'): super(MyMaskToolsDockWidget, self).__init__(parent, plot, name) - if silx.version < "0.6": + if silx.version_info < (0, 6): self.setWidget(MyMaskToolsWidget(plot=plot)) self.widget().sigMaskChanged.connect(self._emitSigMaskChanged) @@ -206,6 +207,10 @@ class SaveImageListAction(qt.QAction): if filename.lower().endswith(".edf"): ArraySave.save2DArrayListAsEDF(imageList, filename, labels) + elif filename.lower().endswith(".tif"): + ArraySave.save2DArrayListAsMonochromaticTiff(imageList, + filename, + labels) elif filename.lower().endswith(".csv"): assert csvseparator is not None ArraySave.save2DArrayListAsASCII(imageList, filename, labels, @@ -267,7 +272,8 @@ class SaveImageListAction(qt.QAction): filedialog.setFileMode(filedialog.AnyFile) filedialog.setAcceptMode(qt.QFileDialog.AcceptSave) filedialog.setWindowIcon(qt.QIcon(qt.QPixmap(IconDict["gioconda16"]))) - formatlist = ["ASCII Files *.dat", + formatlist = ["TIFF Files *.tif", + "ASCII Files *.dat", "EDF Files *.edf", 'CSV(, separated) Files *.csv', 'CSV(; separated) Files *.csv', @@ -307,8 +313,7 @@ class SaveImageListAction(qt.QAction): class SaveMatplotlib(qt.QAction): - """Save current image and mask (if any) in a :class:`MaskImageWidget` - to EDF or CSV""" + """Save current image ho high quality graphics using matplotlib""" def __init__(self, title, maskImageWidget): super(SaveMatplotlib, self).__init__(QString(title), maskImageWidget) @@ -756,15 +761,16 @@ class SilxMaskImageWidget(qt.QMainWindow): The mask can be cropped or padded to fit active image, the returned shape is that of the active image. """ - if mask is None: - mask = numpy.zeros_like(self._getMaskToolsDockWidget().getSelectionMask()) - if not len(mask): - return # disconnect temporarily to avoid infinite loop self._getMaskToolsDockWidget().sigMaskChanged.disconnect( self._emitMaskImageWidgetSignal) - ret = self._getMaskToolsDockWidget().setSelectionMask(mask, - copy=copy) + if mask is None and silx.version_info <= (0, 7, 0): + self._getMaskToolsDockWidget().resetSelectionMask() + ret = None + else: + # from silx 0.8 onwards, setSelectionMask(None) is supported + ret = self._getMaskToolsDockWidget().setSelectionMask(mask, + copy=copy) self._getMaskToolsDockWidget().sigMaskChanged.connect( self._emitMaskImageWidgetSignal) return ret @@ -775,7 +781,7 @@ class SilxMaskImageWidget(qt.QMainWindow): :param bool copy: True (default) to get a copy of the mask. If False, the returned array MUST not be modified. :return: The array of the mask with dimension of the 'active' image. - If there is no active image, an empty array is returned. + If there is no active image, None is returned. :rtype: 2D numpy.ndarray of uint8 """ return self._getMaskToolsDockWidget().getSelectionMask(copy=copy) diff --git a/PyMca5/PyMcaGui/plotting/_ImageProfile.py b/PyMca5/PyMcaGui/plotting/_ImageProfile.py index e57986c..0943317 100644 --- a/PyMca5/PyMcaGui/plotting/_ImageProfile.py +++ b/PyMca5/PyMcaGui/plotting/_ImageProfile.py @@ -37,9 +37,10 @@ Functions to extract a profile curve of a region of interest in an image. # import ###################################################################### import numpy +import logging from PyMca5.PyMcaMath.fitting import SpecfitFuns -DEBUG = 0 +_logger = logging.getLogger(__name__) # utils ####################################################################### @@ -209,8 +210,7 @@ def _getROILineProfileCurve(image, roiStart, roiEnd, roiWidth, coordsRange = row0, row1 if nPoints == 1: # all points are the same - if DEBUG: - print("START AND END POINT ARE THE SAME!!") + _logger.debug("START AND END POINT ARE THE SAME!!") return None # the coordinates of the reference points @@ -249,9 +249,8 @@ def _getROILineProfileCurve(image, roiStart, roiEnd, roiWidth, newRow0 = 0.0 newRow1 = - (col1 - col0) * sinalpha + (row1 - row0) * cosalpha - if DEBUG: - print("new X0 Y0 = %f, %f " % (newCol0, newRow0)) - print("new X1 Y1 = %f, %f " % (newCol1, newRow1)) + _logger.debug("new X0 Y0 = %f, %f ", newCol0, newRow0) + _logger.debug("new X1 Y1 = %f, %f ", newCol1, newRow1) tmpX = numpy.linspace(newCol0, newCol1, nPoints).astype(numpy.float) @@ -260,19 +259,19 @@ def _getROILineProfileCurve(image, roiStart, roiEnd, roiWidth, rotMatrix[0, 1] = - sinalpha rotMatrix[1, 0] = sinalpha rotMatrix[1, 1] = cosalpha - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: # test if I recover the original points testX = numpy.zeros((2, 1), numpy.float) colRow = numpy.dot(rotMatrix, testX) - print("Recovered X0 = %f" % (colRow[0, 0] + col0)) - print("Recovered Y0 = %f" % (colRow[1, 0] + row0)) - print("It should be = %f, %f" % (col0, row0)) + _logger.debug("Recovered X0 = %f", colRow[0, 0] + col0) + _logger.debug("Recovered Y0 = %f", colRow[1, 0] + row0) + _logger.debug("It should be = %f, %f", col0, row0) testX[0, 0] = newCol1 testX[1, 0] = newRow1 colRow = numpy.dot(rotMatrix, testX) - print("Recovered X1 = %f" % (colRow[0, 0] + col0)) - print("Recovered Y1 = %f" % (colRow[1, 0] + row0)) - print("It should be = %f, %f" % (col1, row1)) + _logger.debug("Recovered X1 = %f", colRow[0, 0] + col0) + _logger.debug("Recovered Y1 = %f", colRow[1, 0] + row0) + _logger.debug("It should be = %f, %f", col1, row1) # find the drawing limits testX = numpy.zeros((2, 4), numpy.float) @@ -290,21 +289,18 @@ def _getROILineProfileCurve(image, roiStart, roiEnd, roiWidth, for a in rowLimits0: if (a >= image.shape[0]) or (a < 0): - if DEBUG: - print("outside row limits", a) + _logger.debug("outside row limits %s", a) return None for a in colLimits0: if (a >= image.shape[1]) or (a < 0): - if DEBUG: - print("outside column limits", a) + _logger.debug("outside column limits %s", a) return None r0 = rowLimits0[0] r1 = rowLimits0[1] if r0 > r1: - if DEBUG: - print("r0 > r1", r0, r1) + _logger.debug("r0 > r1 %s %s", r0, r1) raise ValueError("r0 > r1") x = numpy.zeros((2, nPoints), numpy.float) diff --git a/PyMca5/PyMcaGui/pymca/EdfFileSimpleViewer.py b/PyMca5/PyMcaGui/pymca/EdfFileSimpleViewer.py index c03127c..3a6538e 100644 --- a/PyMca5/PyMcaGui/pymca/EdfFileSimpleViewer.py +++ b/PyMca5/PyMcaGui/pymca/EdfFileSimpleViewer.py @@ -28,10 +28,11 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import logging from PyMca5.PyMcaGui import PyMcaQt as qt QTVERSION = qt.qVersion() -DEBUG = 0 +_logger = logging.getLogger(__name__) from PyMca5.PyMcaGui import QSourceSelector from PyMca5.PyMcaGui.pymca import QDataSource @@ -59,9 +60,8 @@ class EdfFileSimpleViewer(qt.QWidget): self._sourceSelectorSlot) def _sourceSelectorSlot(self, ddict): - if DEBUG: - print("_sourceSelectorSlot(self, ddict)") - print("ddict = ",ddict) + _logger.debug("_sourceSelectorSlot(self, ddict)") + _logger.debug("ddict = %s", ddict) if ddict["event"] == "NewSourceSelected": source = QDataSource.QDataSource(ddict["sourcelist"]) self.sourceList.append(source) @@ -74,8 +74,7 @@ class EdfFileSimpleViewer(qt.QWidget): found = 1 break if not found: - if DEBUG: - print("WARNING: source not found") + _logger.debug("WARNING: source not found") return sourceType = source.sourceType self.selectorWidget[sourceType].setDataSource(source) @@ -86,8 +85,7 @@ class EdfFileSimpleViewer(qt.QWidget): found = 1 break if not found: - if DEBUG: - print("WARNING: source not found") + _logger.debug("WARNING: source not found") return sourceType = source.sourceType del self.sourceList[self.sourceList.index(source)] @@ -111,18 +109,19 @@ class EdfFileSimpleViewer(qt.QWidget): def main(): import sys import getopt + from PyMca5.PyMcaCore.LoggingLevel import getLoggingLevel app=qt.QApplication(sys.argv) winpalette = qt.QPalette(qt.QColor(230,240,249),qt.QColor(238,234,238)) app.setPalette(winpalette) options='' - longoptions=[] + longoptions=['logging=', 'debug='] opts, args = getopt.getopt( sys.argv[1:], options, longoptions) - for opt,arg in opts: - pass - filelist=args + + logging.basicConfig(level=getLoggingLevel(opts)) + filelist = args app.lastWindowClosed.connect(app.quit) w=EdfFileSimpleViewer() if len(filelist): @@ -130,5 +129,6 @@ def main(): w.show() app.exec_() + if __name__ == "__main__": main() diff --git a/PyMca5/PyMcaGui/pymca/ExternalImagesWindow.py b/PyMca5/PyMcaGui/pymca/ExternalImagesWindow.py index ab104d7..4f2035c 100644 --- a/PyMca5/PyMcaGui/pymca/ExternalImagesWindow.py +++ b/PyMca5/PyMcaGui/pymca/ExternalImagesWindow.py @@ -378,16 +378,13 @@ class ExternalImagesWindow(MaskImageWidget.MaskImageWidget): if i == 0: overlay = MaskImageWidget.OVERLAY_DRAW replace = True - if len(self.imageNames) == 1: - replot = True - else: - replot = False - elif i == (nImages -1): + replot = len(self.imageNames) == 1 + elif i == (nImages - 1): replot = True - replace=False + replace = False else: replot = False - replace= False + replace = False curve = self._getProfileCurve(ddict, image=image, overlay=overlay) if curve is None: return @@ -396,7 +393,7 @@ class ExternalImagesWindow(MaskImageWidget.MaskImageWidget): self._profileSelectionWindow.addCurve(xdata, ydata, legend=newLegend, info=info, - replot=replot, + resetzoom=replot, replace=replace) def getCurrentIndex(self): diff --git a/PyMca5/PyMcaGui/pymca/LegacyScanWindow.py b/PyMca5/PyMcaGui/pymca/LegacyScanWindow.py new file mode 100644 index 0000000..db1053c --- /dev/null +++ b/PyMca5/PyMcaGui/pymca/LegacyScanWindow.py @@ -0,0 +1,1591 @@ +#/*########################################################################## +# Copyright (C) 2004-2018 V.A. Sole, European Synchrotron Radiation Facility +# +# This file is part of the PyMca X-ray Fluorescence Toolkit developed at +# the ESRF by the Software group. +# +# 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. +# +#############################################################################*/ +"""Legacy PyMca ScanWindow, to be replaced by a ScanWindow based on a +silx PlotWidget. +This module should not be used anywhere, it will no longer be maintained.""" +__author__ = "V.A. Sole - ESRF Data Analysis" +__contact__ = "sole@esrf.fr" +__license__ = "MIT" +__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import sys +import os +import numpy +#from numpy import argsort, nonzero, take +import time +import traceback +from PyMca5.PyMcaGui import PyMcaQt as qt +if hasattr(qt, 'QString'): + QString = qt.QString +else: + QString = qt.safe_str +if __name__ == "__main__": + app = qt.QApplication([]) + +from PyMca5.PyMcaGui.io import PyMcaFileDialogs +from PyMca5.PyMcaGui.plotting import PlotWindow +from . import ScanFit +from PyMca5.PyMcaMath import SimpleMath +from PyMca5.PyMcaCore import DataObject +import copy +from PyMca5.PyMcaGui.plotting import PyMcaPrintPreview +from PyMca5.PyMcaCore import PyMcaDirs +from . import ScanWindowInfoWidget +#implement the plugins interface +from PyMca5.PyMcaGui import QPyMcaMatplotlibSave1D +MATPLOTLIB = True +#force understanding of utf-8 encoding +#otherways it cannot generate svg output +try: + import encodings.utf_8 +except: + #not a big problem + pass + +PLUGINS_DIR = None +try: + import PyMca5 + if os.path.exists(os.path.join(os.path.dirname(PyMca5.__file__), "PyMcaPlugins")): + from PyMca5 import PyMcaPlugins + PLUGINS_DIR = os.path.dirname(PyMcaPlugins.__file__) + else: + directory = os.path.dirname(__file__) + while True: + if os.path.exists(os.path.join(directory, "PyMcaPlugins")): + PLUGINS_DIR = os.path.join(directory, "PyMcaPlugins") + break + directory = os.path.dirname(directory) + if len(directory) < 5: + break + userPluginsDirectory = PyMca5.getDefaultUserPluginsDirectory() + if userPluginsDirectory is not None: + if PLUGINS_DIR is None: + PLUGINS_DIR = userPluginsDirectory + else: + PLUGINS_DIR = [PLUGINS_DIR, userPluginsDirectory] +except: + pass + +DEBUG = 0 + +class ScanWindow(PlotWindow.PlotWindow): + def __init__(self, parent=None, name="Scan Window", specfit=None, backend=None, + plugins=True, newplot=True, roi=True, fit=True, + control=True, position=True, info=False, **kw): + + super(ScanWindow, self).__init__(parent, + newplot=newplot, + plugins=plugins, + backend=backend, + roi=roi, + fit=fit, + control=control, + position=position, + **kw) + self.setDataMargins(0, 0, 0.025, 0.025) + #self._togglePointsSignal() + self.setPanWithArrowKeys(True) + self.setWindowType("SCAN") + # this two objects are the same + self.dataObjectsList = self._curveList + # but this is tricky + self.dataObjectsDict = {} + + self.setWindowTitle(name) + self.matplotlibDialog = None + + if PLUGINS_DIR is not None: + if type(PLUGINS_DIR) == type([]): + pluginDir = PLUGINS_DIR + else: + pluginDir = [PLUGINS_DIR] + self.getPlugins(method="getPlugin1DInstance", + directoryList=pluginDir) + + if info: + self.scanWindowInfoWidget = ScanWindowInfoWidget.\ + ScanWindowInfoWidget() + self.infoDockWidget = qt.QDockWidget(self) + self.infoDockWidget.layout().setContentsMargins(0, 0, 0, 0) + self.infoDockWidget.setWidget(self.scanWindowInfoWidget) + self.infoDockWidget.setWindowTitle(self.windowTitle()+(" Info")) + self.addDockWidget(qt.Qt.BottomDockWidgetArea, + self.infoDockWidget) + controlMenu = qt.QMenu() + controlMenu.addAction(QString("Show/Hide Legends"), + self.toggleLegendWidget) + controlMenu.addAction(QString("Show/Hide Info"), + self._toggleInfoWidget) + controlMenu.addAction(QString("Toggle Crosshair"), + self.toggleCrosshairCursor) + controlMenu.addAction(QString("Toggle Arrow Keys Panning"), + self.toggleArrowKeysPanning) + self.setControlMenu(controlMenu) + else: + self.scanWindowInfoWidget = None + #self.fig = None + if fit: + self.scanFit = ScanFit.ScanFit(specfit=specfit) + self.printPreview = PyMcaPrintPreview.PyMcaPrintPreview(modal = 0) + self.simpleMath = SimpleMath.SimpleMath() + self.outputDir = None + self.outputFilter = None + + #signals + # this one was made in the base class + #self.setCallback(self.graphCallback) + if fit: + from PyMca5.PyMcaGui.math.fitting import SimpleFitGui + self.customFit = SimpleFitGui.SimpleFitGui() + self.scanFit.sigScanFitSignal.connect(self._scanFitSignalReceived) + self.customFit.sigSimpleFitSignal.connect( \ + self._customFitSignalReceived) + + self.fitButtonMenu = qt.QMenu() + self.fitButtonMenu.addAction(QString("Simple Fit"), + self._simpleFitSignal) + self.fitButtonMenu.addAction(QString("Customized Fit") , + self._customFitSignal) + + def _toggleInfoWidget(self): + if self.infoDockWidget.isHidden(): + self.infoDockWidget.show() + legend = self.getActiveCurve(just_legend=True) + if legend is not None: + ddict ={} + ddict['event'] = "curveClicked" + ddict['label'] = legend + ddict['legend'] = legend + self.graphCallback(ddict) + else: + self.infoDockWidget.hide() + + def _buildLegendWidget(self): + if self.legendWidget is None: + super(ScanWindow, self)._buildLegendWidget() + if hasattr(self, "infoDockWidget") and \ + hasattr(self, "roiDockWidget"): + self.tabifyDockWidget(self.infoDockWidget, + self.roiDockWidget, + self.legendDockWidget) + elif hasattr(self, "infoDockWidget"): + self.tabifyDockWidget(self.infoDockWidget, + self.legendDockWidget) + + def _toggleROI(self, position=None): + super(ScanWindow, self)._toggleROI(position=position) + if hasattr(self, "infoDockWidget"): + self.tabifyDockWidget(self.infoDockWidget, + self.roiDockWidget) + + def setDispatcher(self, w): + w.sigAddSelection.connect(self._addSelection) + w.sigRemoveSelection.connect(self._removeSelection) + w.sigReplaceSelection.connect(self._replaceSelection) + + def _addSelection(self, selectionlist, replot=True): + if DEBUG: + print("_addSelection(self, selectionlist)",selectionlist) + if type(selectionlist) == type([]): + sellist = selectionlist + else: + sellist = [selectionlist] + + if len(self._curveList): + activeCurve = self.getActiveCurve(just_legend=True) + else: + activeCurve = None + nSelection = len(sellist) + for selectionIndex in range(nSelection): + sel = sellist[selectionIndex] + if selectionIndex == (nSelection - 1): + actualReplot = replot + else: + actualReplot = False + source = sel['SourceName'] + key = sel['Key'] + legend = sel['legend'] #expected form sourcename + scan key + if not ("scanselection" in sel): continue + if sel['scanselection'] == "MCA": + continue + if not sel["scanselection"]:continue + if len(key.split(".")) > 2: continue + dataObject = sel['dataobject'] + #only one-dimensional selections considered + if dataObject.info["selectiontype"] != "1D": continue + + #there must be something to plot + if not hasattr(dataObject, 'y'): + continue + if not hasattr(dataObject, 'x'): + ylen = len(dataObject.y[0]) + if ylen: + xdata = numpy.arange(ylen).astype(numpy.float) + else: + #nothing to be plot + continue + if dataObject.x is None: + ylen = len(dataObject.y[0]) + if ylen: + xdata = numpy.arange(ylen).astype(numpy.float) + else: + #nothing to be plot + continue + elif len(dataObject.x) > 1: + if DEBUG: + print("Mesh plots") + continue + else: + xdata = dataObject.x[0] + sps_source = False + if 'SourceType' in sel: + if sel['SourceType'] == 'SPS': + sps_source = True + + if sps_source: + ycounter = -1 + if 'selection' not in dataObject.info: + dataObject.info['selection'] = copy.deepcopy(sel['selection']) + for ydata in dataObject.y: + xlabel = None + ylabel = None + ycounter += 1 + if dataObject.m is None: + mdata = [numpy.ones(len(ydata)).astype(numpy.float)] + elif len(dataObject.m[0]) > 0: + if len(dataObject.m[0]) == len(ydata): + index = numpy.nonzero(dataObject.m[0])[0] + if not len(index): + continue + xdata = numpy.take(xdata, index) + ydata = numpy.take(ydata, index) + mdata = numpy.take(dataObject.m[0], index) + #A priori the graph only knows about plots + ydata = ydata/mdata + else: + raise ValueError("Monitor data length different than counter data") + else: + mdata = [numpy.ones(len(ydata)).astype(numpy.float)] + ylegend = 'y%d' % ycounter + if dataObject.info['selection'] is not None: + if type(dataObject.info['selection']) == type({}): + if 'x' in dataObject.info['selection']: + #proper scan selection + ilabel = dataObject.info['selection']['y'][ycounter] + ylegend = dataObject.info['LabelNames'][ilabel] + ylabel = ylegend + if sel['selection']['x'] is not None: + if len(dataObject.info['selection']['x']): + xlabel = dataObject.info['LabelNames'] \ + [dataObject.info['selection']['x'][0]] + dataObject.info["xlabel"] = xlabel + dataObject.info["ylabel"] = ylabel + newLegend = legend + " " + ylegend + self.dataObjectsDict[newLegend] = dataObject + self.addCurve(xdata, ydata, legend=newLegend, info=dataObject.info, + xlabel=xlabel, ylabel=ylabel, replot=False) + # replot=actualReplot) + if self.scanWindowInfoWidget is not None: + if not self.infoDockWidget.isHidden(): + activeLegend = self.getActiveCurve(just_legend=True) + if activeLegend is not None: + if activeLegend == newLegend: + self.scanWindowInfoWidget.updateFromDataObject\ + (dataObject) + else: + dummyDataObject = DataObject.DataObject() + dummyDataObject.y=[numpy.array([])] + dummyDataObject.x=[numpy.array([])] + self.scanWindowInfoWidget.updateFromDataObject(dummyDataObject) + else: + #we have to loop for all y values + ycounter = -1 + for ydata in dataObject.y: + ylen = len(ydata) + if ylen == 1: + if len(xdata) > 1: + ydata = ydata[0] * numpy.ones(len(xdata)).astype(numpy.float) + elif len(xdata) == 1: + xdata = xdata[0] * numpy.ones(ylen).astype(numpy.float) + ycounter += 1 + newDataObject = DataObject.DataObject() + newDataObject.info = copy.deepcopy(dataObject.info) + if dataObject.m is None: + mdata = numpy.ones(len(ydata)).astype(numpy.float) + elif len(dataObject.m[0]) > 0: + if len(dataObject.m[0]) == len(ydata): + index = numpy.nonzero(dataObject.m[0])[0] + if not len(index): + continue + xdata = numpy.take(xdata, index) + ydata = numpy.take(ydata, index) + mdata = numpy.take(dataObject.m[0], index) + #A priori the graph only knows about plots + ydata = ydata/mdata + elif len(dataObject.m[0]) == 1: + mdata = numpy.ones(len(ydata)).astype(numpy.float) + mdata *= dataObject.m[0][0] + index = numpy.nonzero(dataObject.m[0])[0] + if not len(index): + continue + xdata = numpy.take(xdata, index) + ydata = numpy.take(ydata, index) + mdata = numpy.take(dataObject.m[0], index) + #A priori the graph only knows about plots + ydata = ydata/mdata + else: + raise ValueError("Monitor data length different than counter data") + else: + mdata = numpy.ones(len(ydata)).astype(numpy.float) + newDataObject.x = [xdata] + newDataObject.y = [ydata] + newDataObject.m = [mdata] + newDataObject.info['selection'] = copy.deepcopy(sel['selection']) + ylegend = 'y%d' % ycounter + xlabel = None + ylabel = None + if sel['selection'] is not None: + if type(sel['selection']) == type({}): + if 'x' in sel['selection']: + #proper scan selection + newDataObject.info['selection']['x'] = sel['selection']['x'] + newDataObject.info['selection']['y'] = [sel['selection']['y'][ycounter]] + newDataObject.info['selection']['m'] = sel['selection']['m'] + ilabel = newDataObject.info['selection']['y'][0] + ylegend = newDataObject.info['LabelNames'][ilabel] + ylabel = ylegend + if len(newDataObject.info['selection']['x']): + ilabel = newDataObject.info['selection']['x'][0] + xlabel = newDataObject.info['LabelNames'][ilabel] + else: + xlabel = "Point number" + if ('operations' in dataObject.info) and len(dataObject.y) == 1: + newDataObject.info['legend'] = legend + symbol = 'x' + else: + symbol=None + newDataObject.info['legend'] = legend + " " + ylegend + newDataObject.info['selectionlegend'] = legend + yaxis = None + if "plot_yaxis" in dataObject.info: + yaxis = dataObject.info["plot_yaxis"] + elif 'operations' in dataObject.info: + if dataObject.info['operations'][-1] == 'derivate': + yaxis = 'right' + #print("sending legend = ", newDataObject.info['legend'], "replot = ", False) + self.dataObjectsDict[newDataObject.info['legend']] = newDataObject + self.addCurve(xdata, ydata, legend=newDataObject.info['legend'], + info=newDataObject.info, + symbol=symbol, + yaxis=yaxis, + xlabel=xlabel, + ylabel=ylabel, + replot=False) + self.dataObjectsList = self._curveList + try: + if activeCurve is None: + if len(self._curveList) > 0: + activeCurve = self._curveList[0] + ddict = {} + ddict['event'] = "curveClicked" + ddict['label'] = activeCurve + self.graphCallback(ddict) + finally: + if replot: + #self.replot() + self.resetZoom() + self.updateLegends() + + def _removeSelection(self, selectionlist): + if DEBUG: + print("_removeSelection(self, selectionlist)",selectionlist) + if type(selectionlist) == type([]): + sellist = selectionlist + else: + sellist = [selectionlist] + + removelist = [] + for sel in sellist: + source = sel['SourceName'] + key = sel['Key'] + if not ("scanselection" in sel): continue + if sel['scanselection'] == "MCA": + continue + if not sel["scanselection"]:continue + if len(key.split(".")) > 2: continue + + legend = sel['legend'] #expected form sourcename + scan key + if type(sel['selection']) == type({}): + if 'y' in sel['selection']: + for lName in ['cntlist', 'LabelNames']: + if lName in sel['selection']: + for index in sel['selection']['y']: + removelist.append(legend +" "+\ + sel['selection'][lName][index]) + + if len(removelist): + self.removeCurves(removelist) + + def removeCurves(self, removeList, replot=True): + for legend in removeList: + if legend == removeList[-1]: + self.removeCurve(legend, replot=replot) + else: + self.removeCurve(legend, replot=False) + if legend in self.dataObjectsDict: + del self.dataObjectsDict[legend] + self.dataObjectsList = self._curveList + + def _replaceSelection(self, selectionlist): + if DEBUG: + print("_replaceSelection(self, selectionlist)",selectionlist) + if type(selectionlist) == type([]): + sellist = selectionlist + else: + sellist = [selectionlist] + + doit = 0 + for sel in sellist: + if not ("scanselection" in sel): continue + if sel['scanselection'] == "MCA": + continue + if not sel["scanselection"]:continue + if len(sel["Key"].split(".")) > 2: continue + dataObject = sel['dataobject'] + if dataObject.info["selectiontype"] == "1D": + if hasattr(dataObject, 'y'): + doit = 1 + break + if not doit: + return + self.clearCurves() + self.dataObjectsDict={} + self.dataObjectsList=self._curveList + self._addSelection(selectionlist, replot=True) + + def _handleMarkerEvent(self, ddict): + if ddict['event'] == 'markerMoved': + label = ddict['label'] + if label.startswith('ROI'): + return self._handleROIMarkerEvent(ddict) + else: + if DEBUG: + print("Unhandled marker %s" % label) + return + + def graphCallback(self, ddict): + if DEBUG: + print("graphCallback", ddict) + if ddict['event'] in ['markerMoved', 'markerSelected']: + self._handleMarkerEvent(ddict) + elif ddict['event'] in ["mouseMoved", "MouseAt"]: + if self._toggleCounter > 0: + activeCurve = self.getActiveCurve() + if activeCurve in [None, []]: + self._handleMouseMovedEvent(ddict) + else: + x, y, legend, info = activeCurve[0:4] + # calculate the maximum distance + xMin, xMax = self.getGraphXLimits() + maxXDistance = abs(xMax - xMin) + yMin, yMax = self.getGraphYLimits() + maxYDistance = abs(yMax - yMin) + if (maxXDistance > 0.0) and (maxYDistance > 0.0): + closestIndex = (pow((x - ddict['x'])/maxXDistance, 2) + \ + pow((y - ddict['y'])/maxYDistance, 2)) + else: + closestIndex = (pow(x - ddict['x'], 2) + \ + pow(y - ddict['y'], 2)) + xText = '----' + yText = '----' + if len(closestIndex): + closestIndex = closestIndex.argmin() + xCurve = x[closestIndex] + if abs(xCurve - ddict['x']) < (0.05 * maxXDistance): + yCurve = y[closestIndex] + if abs(yCurve - ddict['y']) < (0.05 * maxYDistance): + xText = '%.7g' % xCurve + yText = '%.7g' % yCurve + if xText == '----': + if self.getGraphCursor(): + self._xPos.setStyleSheet("color: rgb(255, 0, 0);") + self._yPos.setStyleSheet("color: rgb(255, 0, 0);") + xText = '%.7g' % ddict['x'] + yText = '%.7g' % ddict['y'] + else: + self._xPos.setStyleSheet("color: rgb(0, 0, 0);") + self._yPos.setStyleSheet("color: rgb(0, 0, 0);") + else: + self._xPos.setStyleSheet("color: rgb(0, 0, 0);") + self._yPos.setStyleSheet("color: rgb(0, 0, 0);") + self._xPos.setText(xText) + self._yPos.setText(yText) + else: + self._xPos.setStyleSheet("color: rgb(0, 0, 0);") + self._yPos.setStyleSheet("color: rgb(0, 0, 0);") + self._handleMouseMovedEvent(ddict) + elif ddict['event'] in ["curveClicked", "legendClicked"]: + legend = ddict["label"] + if legend is None: + if len(self.dataObjectsList): + legend = self.dataObjectsList[0] + else: + return + if legend not in self.dataObjectsList: + if DEBUG: + print("unknown legend %s" % legend) + return + + #force the current x label to the appropriate value + dataObject = self.dataObjectsDict[legend] + if 'selection' in dataObject.info: + ilabel = dataObject.info['selection']['y'][0] + ylabel = dataObject.info['LabelNames'][ilabel] + if len(dataObject.info['selection']['x']): + ilabel = dataObject.info['selection']['x'][0] + xlabel = dataObject.info['LabelNames'][ilabel] + else: + xlabel = "Point Number" + if len(dataObject.info['selection']['m']): + ilabel = dataObject.info['selection']['m'][0] + ylabel += "/" + dataObject.info['LabelNames'][ilabel] + else: + xlabel = dataObject.info.get('xlabel', None) + ylabel = dataObject.info.get('ylabel', None) + if xlabel is not None: + self.setGraphXLabel(xlabel) + if ylabel is not None: + self.setGraphYLabel(ylabel) + self.setGraphTitle(legend) + self.setActiveCurve(legend) + #self.setGraphTitle(legend) + if self.scanWindowInfoWidget is not None: + if not self.infoDockWidget.isHidden(): + self.scanWindowInfoWidget.updateFromDataObject\ + (dataObject) + elif ddict['event'] == "removeCurveEvent": + legend = ddict['legend'] + self.removeCurves([legend]) + elif ddict['event'] == "renameCurveEvent": + legend = ddict['legend'] + newlegend = ddict['newlegend'] + if legend in self.dataObjectsDict: + self.dataObjectsDict[newlegend]= copy.deepcopy(self.dataObjectsDict[legend]) + self.dataObjectsDict[newlegend].info['legend'] = newlegend + self.dataObjectsList.append(newlegend) + self.removeCurves([legend], replot=False) + self.newCurve(self.dataObjectsDict[newlegend].x[0], + self.dataObjectsDict[newlegend].y[0], + legend=self.dataObjectsDict[newlegend].info['legend']) + + #make sure the plot signal is forwarded because we have overwritten + #its handling + self.sigPlotSignal.emit(ddict) + + + def _customFitSignalReceived(self, ddict): + if ddict['event'] == "FitFinished": + newDataObject = self.__customFitDataObject + + xplot = ddict['x'] + yplot = ddict['yfit'] + newDataObject.x = [xplot] + newDataObject.y = [yplot] + newDataObject.m = [numpy.ones(len(yplot)).astype(numpy.float)] + + #here I should check the log or linear status + self.dataObjectsDict[newDataObject.info['legend']] = newDataObject + self.addCurve(xplot, + yplot, + legend=newDataObject.info['legend']) + + def _scanFitSignalReceived(self, ddict): + if DEBUG: + print("_scanFitSignalReceived", ddict) + if ddict['event'] == "EstimateFinished": + return + if ddict['event'] == "FitFinished": + newDataObject = self.__fitDataObject + + xplot = self.scanFit.specfit.xdata * 1.0 + yplot = self.scanFit.specfit.gendata(parameters=ddict['data']) + newDataObject.x = [xplot] + newDataObject.y = [yplot] + newDataObject.m = [numpy.ones(len(yplot)).astype(numpy.float)] + + self.dataObjectsDict[newDataObject.info['legend']] = newDataObject + self.addCurve(x=xplot, y=yplot, legend=newDataObject.info['legend']) + + def _fitIconSignal(self): + if DEBUG: + print("_fitIconSignal") + self.fitButtonMenu.exec_(self.cursor().pos()) + + def _simpleFitSignal(self): + if DEBUG: + print("_simpleFitSignal") + self._QSimpleOperation("fit") + + def _customFitSignal(self): + if DEBUG: + print("_customFitSignal") + self._QSimpleOperation("custom_fit") + + def _saveIconSignal(self): + if DEBUG: + print("_saveIconSignal") + self._QSimpleOperation("save") + + def _averageIconSignal(self): + if DEBUG: + print("_averageIconSignal") + self._QSimpleOperation("average") + + def _smoothIconSignal(self): + if DEBUG: + print("_smoothIconSignal") + self._QSimpleOperation("smooth") + + def _getOutputFileName(self): + #get outputfile + self.outputDir = PyMcaDirs.outputDir + if self.outputDir is None: + self.outputDir = os.getcwd() + wdir = os.getcwd() + elif os.path.exists(self.outputDir): + wdir = self.outputDir + else: + self.outputDir = os.getcwd() + wdir = self.outputDir + + filterlist = ['Specfile MCA *.mca', + 'Specfile Scan *.dat', + 'Specfile MultiScan *.dat', + 'Raw ASCII *.txt', + '","-separated CSV *.csv', + '";"-separated CSV *.csv', + '"tab"-separated CSV *.csv', + 'OMNIC CSV *.csv', + 'Widget PNG *.png', + 'Widget JPG *.jpg', + 'Graphics PNG *.png', + 'Graphics EPS *.eps', + 'Graphics SVG *.svg'] + fileList, fileFilter = PyMcaFileDialogs.getFileList(self, + filetypelist=filterlist, + message="Output File Selection", + currentdir=wdir, + single=True, + mode="SAVE", + getfilter=True, + currentfilter=self.outputFilter) + if not len(fileList): + return + filterused = fileFilter.split() + filetype = filterused[1] + extension = filterused[2] + outdir = qt.safe_str(fileList[0]) + try: + self.outputDir = os.path.dirname(outdir) + PyMcaDirs.outputDir = os.path.dirname(outdir) + except: + print("setting output directory to default") + self.outputDir = os.getcwd() + try: + outputFile = os.path.basename(outdir) + except: + outputFile = outdir + if len(outputFile) < 5: + outputFile = outputFile + extension[-4:] + elif outputFile[-4:] != extension[-4:]: + outputFile = outputFile + extension[-4:] + return os.path.join(self.outputDir, outputFile), filetype, filterused + + def _QSimpleOperation(self, operation): + try: + self._simpleOperation(operation) + except: + msg = qt.QMessageBox(self) + msg.setIcon(qt.QMessageBox.Critical) + msg.setInformativeText(str(sys.exc_info()[1])) + msg.setDetailedText(traceback.format_exc()) + msg.exec_() + + def _saveOperation(self, fileName, fileType, fileFilter): + filterused = fileFilter + filetype = fileType + filename = fileName + if os.path.exists(filename): + os.remove(filename) + if filterused[0].upper() == "WIDGET": + fformat = filename[-3:].upper() + if hasattr(qt.QPixmap,"grabWidget"): + pixmap = qt.QPixmap.grabWidget(self) + else: + pixmap = self.grab() + if not pixmap.save(filename, fformat): + qt.QMessageBox.critical(self, + "Save Error", + "%s" % sys.exc_info()[1]) + return + try: + if filename[-3:].upper() in ['EPS', 'PNG', 'SVG']: + self.graphicsSave(filename) + return + except: + msg = qt.QMessageBox(self) + msg.setIcon(qt.QMessageBox.Critical) + msg.setText("Graphics Saving Error: %s" % (sys.exc_info()[1])) + msg.exec_() + return + systemline = os.linesep + os.linesep = '\n' + try: + if sys.version < "3.0": + ffile=open(filename, "wb") + else: + ffile=open(filename, "w", newline='') + except IOError: + msg = qt.QMessageBox(self) + msg.setIcon(qt.QMessageBox.Critical) + msg.setText("Input Output Error: %s" % (sys.exc_info()[1])) + msg.exec_() + return + x, y, legend, info = self.getActiveCurve() + xlabel = info.get("xlabel", "X") + ylabel = info.get("ylabel", "Y") + if 0: + if "selection" in info: + if type(info['selection']) == type({}): + if 'x' in info['selection']: + #proper scan selection + ilabel = info['selection']['y'][0] + ylegend = info['LabelNames'][ilabel] + ylabel = ylegend + if info['selection']['x'] is not None: + if len(info['selection']['x']): + xlabel = info['LabelNames'] [info['selection']['x'][0]] + else: + xlabel = "Point number" + try: + if filetype in ['Scan', 'MultiScan']: + ffile.write("#F %s\n" % filename) + savingDate = "#D %s\n"%(time.ctime(time.time())) + ffile.write(savingDate) + ffile.write("\n") + ffile.write("#S 1 %s\n" % legend) + ffile.write(savingDate) + ffile.write("#N 2\n") + ffile.write("#L %s %s\n" % (xlabel, ylabel) ) + for i in range(len(y)): + ffile.write("%.7g %.7g\n" % (x[i], y[i])) + ffile.write("\n") + if filetype == 'MultiScan': + scan_n = 1 + curveList = self.getAllCurves() + for x, y, key, info in curveList: + if key == legend: + continue + xlabel = info.get("xlabel", "X") + ylabel = info.get("ylabel", "Y") + if 0: + if "selection" in info: + if type(info['selection']) == type({}): + if 'x' in info['selection']: + #proper scan selection + ilabel = info['selection']['y'][0] + ylegend = info['LabelNames'][ilabel] + ylabel = ylegend + if info['selection']['x'] is not None: + if len(info['selection']['x']): + xlabel = info['LabelNames'] [info['selection']['x'][0]] + else: + xlabel = "Point number" + scan_n += 1 + ffile.write("#S %d %s\n" % (scan_n, key)) + ffile.write(savingDate) + ffile.write("#N 2\n") + ffile.write("#L %s %s\n" % (xlabel, ylabel) ) + for i in range(len(y)): + ffile.write("%.7g %.7g\n" % (x[i], y[i])) + ffile.write("\n") + elif filetype == 'ASCII': + for i in range(len(y)): + ffile.write("%.7g %.7g\n" % (x[i], y[i])) + elif filetype == 'CSV': + if "," in filterused[0]: + csvseparator = "," + elif ";" in filterused[0]: + csvseparator = ";" + elif "OMNIC" in filterused[0]: + csvseparator = "," + else: + csvseparator = "\t" + if "OMNIC" not in filterused[0]: + ffile.write('"%s"%s"%s"\n' % (xlabel,csvseparator,ylabel)) + for i in range(len(y)): + ffile.write("%.7E%s%.7E\n" % (x[i], csvseparator,y[i])) + else: + ffile.write("#F %s\n" % filename) + ffile.write("#D %s\n"%(time.ctime(time.time()))) + ffile.write("\n") + ffile.write("#S 1 %s\n" % legend) + ffile.write("#D %s\n"%(time.ctime(time.time()))) + ffile.write("#@MCA %16C\n") + ffile.write("#@CHANN %d %d %d 1\n" % (len(y), x[0], x[-1])) + ffile.write("#@CALIB %.7g %.7g %.7g\n" % (0, 1, 0)) + ffile.write(self.array2SpecMca(y)) + ffile.write("\n") + ffile.close() + os.linesep = systemline + except: + os.linesep = systemline + raise + return + + + def _simpleOperation(self, operation): + if operation == 'subtract': + self._subtractOperation() + return + if operation == "save": + #getOutputFileName + filename = self._getOutputFileName() + if filename is None: + return + self._saveOperation(filename[0], filename[1], filename[2]) + return + if operation != "average": + #get active curve + legend = self.getActiveCurveLegend() + if legend is None:return + + found = False + for key in self.dataObjectsList: + if key == legend: + found = True + break + + if found: + dataObject = self.dataObjectsDict[legend] + else: + print("I should not be here") + print("active curve =",legend) + print("but legend list = ",self.dataObjectsList) + return + y = dataObject.y[0] + if dataObject.x is not None: + x = dataObject.x[0] + else: + x = numpy.arange(len(y)).astype(numpy.float) + ilabel = dataObject.info['selection']['y'][0] + ylabel = dataObject.info['LabelNames'][ilabel] + if len(dataObject.info['selection']['x']): + ilabel = dataObject.info['selection']['x'][0] + xlabel = dataObject.info['LabelNames'][ilabel] + else: + xlabel = "Point Number" + else: + x = [] + y = [] + legend = "" + i = 0 + ndata = 0 + for key in self._curveList: + if DEBUG: + print("key -> ", key) + if key in self.dataObjectsDict: + x.append(self.dataObjectsDict[key].x[0]) #only the first X + if len(self.dataObjectsDict[key].y) == 1: + y.append(self.dataObjectsDict[key].y[0]) + else: + sel_legend = self.dataObjectsDict[key].info['legend'] + ilabel = 0 + #I have to get the proper y associated to the legend + if sel_legend in key: + if key.index(sel_legend) == 0: + label = key[len(sel_legend):] + while (label.startswith(' ')): + label = label[1:] + if not len(label): + break + if label in self.dataObjectsDict[key].info['LabelNames']: + ilabel = self.dataObjectsDict[key].info['LabelNames'].index(label) + if DEBUG: + print("LABEL = ", label) + print("ilabel = ", ilabel) + y.append(self.dataObjectsDict[key].y[ilabel]) + if i == 0: + legend = key + firstcurve = key + i += 1 + else: + legend += " + " + key + lastcurve = key + ndata += 1 + if ndata == 0: return #nothing to average + dataObject = self.dataObjectsDict[firstcurve] + + #create the output data object + newDataObject = DataObject.DataObject() + newDataObject.data = None + newDataObject.info = copy.deepcopy(dataObject.info) + if 'selectionlegend' in newDataObject.info: + del newDataObject.info['selectionlegend'] + if not ('operations' in newDataObject.info): + newDataObject.info['operations'] = [] + newDataObject.info['operations'].append(operation) + + sel = {} + sel['SourceType'] = "Operation" + #get new x and new y + if operation == "derivate": + #xmin and xmax + xlimits=self.getGraphXLimits() + xplot, yplot = self.simpleMath.derivate(x, y, xlimits=xlimits) + ilabel = dataObject.info['selection']['y'][0] + ylabel = dataObject.info['LabelNames'][ilabel] + newDataObject.info['LabelNames'][ilabel] = ylabel+"'" + newDataObject.info['plot_yaxis'] = "right" + sel['SourceName'] = legend + sel['Key'] = "'" + sel['legend'] = legend + sel['Key'] + outputlegend = legend + sel['Key'] + elif operation == "average": + xplot, yplot = self.simpleMath.average(x, y) + if len(legend) < 80: + sel['SourceName'] = legend + sel['Key'] = "" + sel['legend'] = "(%s)/%d" % (legend, ndata) + outputlegend = "(%s)/%d" % (legend, ndata) + else: + sel['SourceName'] = legend + legend = "Average of %d from %s to %s" % (ndata, firstcurve, lastcurve) + sel['Key'] = "" + sel['legend'] = legend + outputlegend = legend + elif operation == "swapsign": + xplot = x * 1 + yplot = -y + sel['SourceName'] = legend + sel['Key'] = "" + sel['legend'] = "-(%s)" % legend + outputlegend = "-(%s)" % legend + elif operation == "smooth": + xplot = x * 1 + yplot = self.simpleMath.smooth(y) + sel['SourceName'] = legend + sel['Key'] = "" + sel['legend'] = "%s Smooth" % legend + outputlegend = "%s Smooth" % legend + if 'operations' in dataObject.info: + if len(dataObject.info['operations']): + if dataObject.info['operations'][-1] == "smooth": + sel['legend'] = legend + outputlegend = legend + elif operation == "forceymintozero": + xplot = x * 1 + yplot = y - min(y) + sel['SourceName'] = legend + sel['Key'] = "" + sel['legend'] = "(%s) - ymin" % legend + outputlegend = "(%s) - ymin" % legend + elif operation == "fit": + #remove a existing fit if present + xmin,xmax=self.getGraphXLimits() + outputlegend = legend + " Fit" + for key in self._curveList: + if key == outputlegend: + self.removeCurves([outputlegend], replot=False) + break + self.scanFit.setData(x = x, + y = y, + xmin = xmin, + xmax = xmax, + legend = legend) + if self.scanFit.isHidden(): + self.scanFit.show() + self.scanFit.raise_() + elif operation == "custom_fit": + #remove a existing fit if present + xmin, xmax=self.getGraphXLimits() + outputlegend = legend + "Custom Fit" + keyList = list(self._curveList) + for key in keyList: + if key == outputlegend: + self.removeCurves([outputlegend], replot=False) + break + self.customFit.setData(x = x, + y = y, + xmin = xmin, + xmax = xmax, + legend = legend) + if self.customFit.isHidden(): + self.customFit.show() + self.customFit.raise_() + else: + raise ValueError("Unknown operation %s" % operation) + if operation not in ["fit", "custom_fit"]: + newDataObject.x = [xplot] + newDataObject.y = [yplot] + newDataObject.m = [numpy.ones(len(yplot)).astype(numpy.float)] + + #and add it to the plot + if True and (operation not in ['fit', 'custom_fit']): + sel['dataobject'] = newDataObject + sel['scanselection'] = True + sel['selection'] = copy.deepcopy(dataObject.info['selection']) + sel['selectiontype'] = "1D" + if operation == 'average': + self._replaceSelection([sel]) + elif operation != 'fit': + self._addSelection([sel]) + else: + self.__fitDataObject = newDataObject + return + else: + newDataObject.info['legend'] = outputlegend + if operation == 'fit': + self.__fitDataObject = newDataObject + return + if operation == 'custom_fit': + self.__customFitDataObject = newDataObject + return + + self.dataObjectsDict[newDataObject.info['legend']] = newDataObject + #here I should check the log or linear status + self.addCurve(x=xplot, y=yplot, legend=newDataObject.info['legend'], replot=False) + self.replot() + + def graphicsSave(self, filename): + #use the plugin interface + x, y, legend, info = self.getActiveCurve()[:4] + curveList = self.getAllCurves() + size = (6, 3) #in inches + bw = False + if len(curveList) > 1: + legends = True + else: + legends = False + if self.matplotlibDialog is None: + self.matplotlibDialog = QPyMcaMatplotlibSave1D.\ + QPyMcaMatplotlibSaveDialog(size=size, + logx=self._logX, + logy=self._logY, + legends=legends, + bw = bw) + mtplt = self.matplotlibDialog.plot + mtplt.setParameters({'logy':self._logY, + 'logx':self._logX, + 'legends':legends, + 'bw':bw}) + xmin, xmax = self.getGraphXLimits() + ymin, ymax = self.getGraphYLimits() + mtplt.setLimits(xmin, xmax, ymin, ymax) + + legend0 = legend + xdata = x + ydata = y + dataCounter = 1 + alias = "%c" % (96+dataCounter) + mtplt.addDataToPlot( xdata, ydata, legend=legend0, alias=alias ) + for curve in curveList: + xdata, ydata, legend, info = curve[0:4] + if legend == legend0: + continue + dataCounter += 1 + alias = "%c" % (96+dataCounter) + mtplt.addDataToPlot( xdata, ydata, legend=legend, alias=alias ) + + if sys.version < '3.0': + self.matplotlibDialog.setXLabel(qt.safe_str(self.getGraphXLabel())) + self.matplotlibDialog.setYLabel(qt.safe_str(self.getGraphYLabel())) + else: + self.matplotlibDialog.setXLabel(self.getGraphXLabel()) + self.matplotlibDialog.setYLabel(self.getGraphYLabel()) + + if legends: + mtplt.plotLegends() + ret = self.matplotlibDialog.exec_() + if ret == qt.QDialog.Accepted: + mtplt.saveFile(filename) + return + + def getActiveCurveLegend(self): + return super(ScanWindow,self).getActiveCurve(just_legend=True) + + def _deriveIconSignal(self): + if DEBUG: + print("_deriveIconSignal") + self._QSimpleOperation('derivate') + + def _swapSignIconSignal(self): + if DEBUG: + print("_swapSignIconSignal") + self._QSimpleOperation('swapsign') + + def _yMinToZeroIconSignal(self): + if DEBUG: + print("_yMinToZeroIconSignal") + self._QSimpleOperation('forceymintozero') + + def _subtractIconSignal(self): + if DEBUG: + print("_subtractIconSignal") + self._QSimpleOperation('subtract') + + def _subtractOperation(self): + #identical to twice the average with the negative active curve + #get active curve + legend = self.getActiveCurveLegend() + if legend is None: + return + + found = False + for key in self.dataObjectsList: + if key == legend: + found = True + break + + if found: + dataObject = self.dataObjectsDict[legend] + else: + print("I should not be here") + print("active curve =",legend) + print("but legend list = ",self.dataObjectsList) + return + x = dataObject.x[0] + y = dataObject.y[0] + ilabel = dataObject.info['selection']['y'][0] + ylabel = dataObject.info['LabelNames'][ilabel] + if len(dataObject.info['selection']['x']): + ilabel = dataObject.info['selection']['x'][0] + xlabel = dataObject.info['LabelNames'][ilabel] + else: + xlabel = "Point Number" + + xActive = x + yActive = y + yActiveLegend = legend + yActiveLabel = ylabel + xActiveLabel = xlabel + + operation = "subtract" + sel_list = [] + i = 0 + ndata = 0 + keyList = list(self._curveList) + for key in keyList: + legend = "" + x = [xActive] + y = [-yActive] + if DEBUG: + print("key -> ", key) + if key in self.dataObjectsDict: + x.append(self.dataObjectsDict[key].x[0]) #only the first X + if len(self.dataObjectsDict[key].y) == 1: + y.append(self.dataObjectsDict[key].y[0]) + ilabel = self.dataObjectsDict[key].info['selection']['y'][0] + else: + sel_legend = self.dataObjectsDict[key].info['legend'] + ilabel = self.dataObjectsDict[key].info['selection']['y'][0] + #I have to get the proper y associated to the legend + if sel_legend in key: + if key.index(sel_legend) == 0: + label = key[len(sel_legend):] + while (label.startswith(' ')): + label = label[1:] + if not len(label): + break + if label in self.dataObjectsDict[key].info['LabelNames']: + ilabel = self.dataObjectsDict[key].info['LabelNames'].index(label) + if DEBUG: + print("LABEL = ", label) + print("ilabel = ", ilabel) + y.append(self.dataObjectsDict[key].y[ilabel]) + outputlegend = "(%s - %s)" % (key, yActiveLegend) + ndata += 1 + xplot, yplot = self.simpleMath.average(x, y) + yplot *= 2 + #create the output data object + newDataObject = DataObject.DataObject() + newDataObject.data = None + newDataObject.info.update(self.dataObjectsDict[key].info) + if not ('operations' in newDataObject.info): + newDataObject.info['operations'] = [] + newDataObject.info['operations'].append(operation) + newDataObject.info['LabelNames'][ilabel] = "(%s - %s)" % \ + (newDataObject.info['LabelNames'][ilabel], yActiveLabel) + newDataObject.x = [xplot] + newDataObject.y = [yplot] + newDataObject.m = None + sel = {} + sel['SourceType'] = "Operation" + sel['SourceName'] = key + sel['Key'] = "" + sel['legend'] = outputlegend + sel['dataobject'] = newDataObject + sel['scanselection'] = True + sel['selection'] = copy.deepcopy(dataObject.info['selection']) + #sel['selection']['y'] = [ilabel] + sel['selectiontype'] = "1D" + sel_list.append(sel) + if True: + #The legend menu was not working with the next line + #but if works if I add the list + self._replaceSelection(sel_list) + else: + oldlist = list(self.dataObjectsDict) + self._addSelection(sel_list) + self.removeCurves(oldlist) + + #The plugins interface + def getGraphYLimits(self): + #if the active curve is mapped to second axis + #I should give the second axis limits + return super(ScanWindow, self).getGraphYLimits() + + #end of plugins interface + def addCurve(self, x, y, legend=None, info=None, replace=False, replot=True, + color=None, symbol=None, linestyle=None, + xlabel=None, ylabel=None, yaxis=None, + xerror=None, yerror=None, **kw): + if legend in self._curveList: + if info is None: + info = {} + oldStuff = self.getCurve(legend) + if len(oldStuff): + oldX, oldY, oldLegend, oldInfo = oldStuff + else: + oldInfo = {} + if color is None: + color = info.get("plot_color", oldInfo.get("plot_color", None)) + if symbol is None: + symbol = info.get("plot_symbol",oldInfo.get("plot_symbol", None)) + if linestyle is None: + linestyle = info.get("plot_linestyle",oldInfo.get("plot_linestyle", None)) + if yaxis is None: + yaxis = info.get("plot_yaxis",oldInfo.get("plot_yaxis", None)) + else: + if info is None: + info = {} + if color is None: + color = info.get("plot_color", None) + if symbol is None: + symbol = info.get("plot_symbol", None) + if linestyle is None: + linestyle = info.get("plot_linestyle", None) + if yaxis is None: + yaxis = info.get("plot_yaxis", None) + if legend in self.dataObjectsDict: + # the info is changing + super(ScanWindow, self).addCurve(x, y, legend=legend, info=info, + replace=replace, replot=replot, color=color, symbol=symbol, + linestyle=linestyle, xlabel=xlabel, ylabel=ylabel, yaxis=yaxis, + xerror=xerror, yerror=yerror, **kw) + else: + # create the data object + self.newCurve(x, y, legend=legend, info=info, + replace=replace, replot=replot, color=color, symbol=symbol, + linestyle=linestyle, xlabel=xlabel, ylabel=ylabel, yaxis=yaxis, + xerror=xerror, yerror=yerror, **kw) + + def newCurve(self, x, y, legend=None, info=None, replace=False, replot=True, + color=None, symbol=None, linestyle=None, + xlabel=None, ylabel=None, yaxis=None, + xerror=None, yerror=None, **kw): + if legend is None: + legend = "Unnamed curve 1.1" + if xlabel is None: + xlabel = "X" + if ylabel is None: + ylabel = "Y" + if info is None: + info = {} + # this is awfull but I have no other way to pass the plot information ... + if color is not None: + info["plot_color"] = color + if symbol is not None: + info["plot_symbol"] = symbol + if linestyle is not None: + info["plot_linestyle"] = linestyle + if yaxis is not None: + info["plot_yaxis"] = yaxis + + newDataObject = DataObject.DataObject() + newDataObject.x = [x] + newDataObject.y = [y] + newDataObject.m = None + newDataObject.info = copy.deepcopy(info) + newDataObject.info['legend'] = legend + newDataObject.info['SourceName'] = legend + newDataObject.info['Key'] = "" + newDataObject.info['selectiontype'] = "1D" + newDataObject.info['LabelNames'] = [xlabel, ylabel] + newDataObject.info['selection'] = {'x':[0], 'y':[1]} + sel_list = [] + sel = {} + sel['SourceType'] = "Operation" + sel['SourceName'] = legend + sel['Key'] = "" + sel['legend'] = legend + sel['dataobject'] = newDataObject + sel['scanselection'] = True + sel['selection'] = {'x':[0], 'y':[1], 'm':[], 'cntlist':[xlabel, ylabel]} + #sel['selection']['y'] = [ilabel] + sel['selectiontype'] = "1D" + sel_list.append(sel) + if replace: + self._replaceSelection(sel_list) + else: + self._addSelection(sel_list, replot=replot) + + def printGraph(self): + if self.printPreview.printer is None: + # setup needed + self.printPreview.setup() + self._printer = self.printPreview.printer + if self._printer is None: + # printer was not selected + return + #self._printer = None + if PlotWindow.PlotWidget.SVG: + svg = True + self._svgRenderer = self.getSvgRenderer() + else: + svg = False + if hasattr(self, "getWidgetHandle"): + widget = self.getWidgetHandle() + else: + widget = self.centralWidget() + if hasattr(widget, "grab"): + pixmap = widget.grab() + else: + pixmap = qt.QPixmap.grabWidget(widget) + + title = None + comment = None + if self.scanWindowInfoWidget is not None: + if not self.infoDockWidget.isHidden(): + info = self.scanWindowInfoWidget.getInfo() + title = info['scan'].get('source', None) + comment = info['scan'].get('scan', None)+"\n" + h, k, l = info['scan'].get('hkl') + if h != "----": + comment += "H = %s K = %s L = %s\n" % (h, k, l) + peak = info['graph']['peak'] + peakAt = info['graph']['peakat'] + fwhm = info['graph']['fwhm'] + fwhmAt = info['graph']['fwhmat'] + com = info['graph']['com'] + mean = info['graph']['mean'] + std = info['graph']['std'] + minimum = info['graph']['min'] + maximum = info['graph']['max'] + delta = info['graph']['delta'] + xLabel = self.getGraphXLabel() + comment += "Peak %s at %s = %s\n" % (peak, xLabel, peakAt) + comment += "FWHM %s at %s = %s\n" % (fwhm, xLabel, fwhmAt) + comment += "COM = %s Mean = %s STD = %s\n" % (com, mean, std) + comment += "Min = %s Max = %s Delta = %s\n" % (minimum, + maximum, + delta) + + if hasattr(self, "scanFit"): + if not self.scanFit.isHidden(): + if comment is None: + comment = "" + comment += "\n" + comment += self.scanFit.getText() + + if svg: + self.printPreview.addSvgItem(self._svgRenderer, + title=None, + comment=comment, + commentPosition="LEFT") + else: + self.printPreview.addPixmap(pixmap, + title=None, + comment=comment, + commentPosition="LEFT") + if self.printPreview.isHidden(): + self.printPreview.show() + self.printPreview.raise_() + + def getSvgRenderer(self, printer=None): + if printer is None: + if self.printPreview.printer is None: + # setup needed + self.printPreview.setup() + self._printer = self.printPreview.printer + printer = self._printer + if printer is None: + # printer was not selected + # return a renderer without adjusting the viewbox + if sys.version < '3.0': + import cStringIO as StringIO + imgData = StringIO.StringIO() + else: + from io import StringIO + imgData = StringIO() + self.saveGraph(imgData, fileFormat='svg') + imgData.flush() + imgData.seek(0) + svgData = imgData.read() + imgData = None + svgRenderer = qt.QSvgRenderer() + svgRenderer._svgRawData = svgData + svgRenderer._svgRendererData = qt.QXmlStreamReader(svgData) + if not svgRenderer.load(svgRenderer._svgRendererData): + raise RuntimeError("Cannot interpret svg data") + return svgRenderer + + # we have what is to be printed + if sys.version < '3.0': + import cStringIO as StringIO + imgData = StringIO.StringIO() + else: + from io import StringIO + imgData = StringIO() + self.saveGraph(imgData, fileFormat='svg') + imgData.flush() + imgData.seek(0) + svgData = imgData.read() + imgData = None + svgRenderer = qt.QSvgRenderer() + + #svgRenderer = PlotWindow.PlotWindow.getSvgRenderer(self) + + # we have to specify the bounding box + config = self.getPrintConfiguration() + width = config['width'] + height = config['height'] + xOffset = config['xOffset'] + yOffset = config['yOffset'] + units = config['units'] + keepAspectRatio = config['keepAspectRatio'] + + + dpix = printer.logicalDpiX() + dpiy = printer.logicalDpiY() + + # get the available space + availableWidth = printer.width() + availableHeight = printer.height() + + # convert the offsets to dpi + if units.lower() in ['inch', 'inches']: + xOffset = xOffset * dpix + yOffset = yOffset * dpiy + if width is not None: + width = width * dpix + if height is not None: + height = height * dpiy + elif units.lower() in ['cm', 'centimeters']: + xOffset = (xOffset/2.54) * dpix + yOffset = (yOffset/2.54) * dpiy + if width is not None: + width = (width/2.54) * dpix + if height is not None: + height = (height/2.54) * dpiy + else: + # page units + xOffset = availableWidth * xOffset + yOffset = availableHeight * yOffset + if width is not None: + width = availableWidth * width + if height is not None: + height = availableHeight * height + + availableWidth -= xOffset + availableHeight -= yOffset + + if width is not None: + if (availableWidth + 0.1) < width: + txt = "Available width %f is less than requested width %f" % \ + (availableWidth, width) + raise ValueError(txt) + availableWidth = width + if height is not None: + if (availableHeight + 0.1) < height: + txt = "Available height %f is less than requested height %f" % \ + (availableHeight, height) + raise ValueError(txt) + availableHeight = height + + if keepAspectRatio: + #get the aspect ratio + widget = self.getWidgetHandle() + if widget is None: + # does this make sense? + graphWidth = availableWidth + graphHeight = availableHeight + else: + graphWidth = float(widget.width()) + graphHeight = float(widget.height()) + + graphRatio = graphHeight / graphWidth + # that ratio has to be respected + + bodyWidth = availableWidth + bodyHeight = availableWidth * graphRatio + + if bodyHeight > availableHeight: + bodyHeight = availableHeight + bodyWidth = bodyHeight / graphRatio + else: + bodyWidth = availableWidth + bodyHeight = availableHeight + + body = qt.QRectF(xOffset, + yOffset, + bodyWidth, + bodyHeight) + # this does not work if I set the svgData before + svgRenderer.setViewBox(body) + svgRenderer._viewBox = body + if not sys.version.startswith("2"): + svgData = svgData.encode(encoding="utf-8", + errors="replace") + svgRenderer._svgRawData = svgData + svgRenderer._svgRendererData = qt.QXmlStreamReader(svgData) + + if not svgRenderer.load(svgRenderer._svgRendererData): + raise RuntimeError("Cannot interpret svg data") + return svgRenderer + +def test(): + w = ScanWindow() + x = numpy.arange(1000.) + y = 10 * x + 10000. * numpy.exp(-0.5*(x-500)*(x-500)/400) + w.addCurve(x, y, legend="dummy", replot=True, replace=True) + w.resetZoom() + app.lastWindowClosed.connect(app.quit) + w.show() + app.exec_() + + +if __name__ == "__main__": + test() diff --git a/PyMca5/PyMcaGui/pymca/Mca2Edf.py b/PyMca5/PyMcaGui/pymca/Mca2Edf.py index d9428a5..1b30a9d 100644 --- a/PyMca5/PyMcaGui/pymca/Mca2Edf.py +++ b/PyMca5/PyMcaGui/pymca/Mca2Edf.py @@ -533,9 +533,12 @@ class Mca2EdfWindow(qt.QWidget): pass def main(): + import logging + from PyMca5.PyMcaCore.LoggingLevel import getLoggingLevel import getopt options = 'f' - longoptions = ['outdir=', 'listfile=', 'mcastep='] + longoptions = ['outdir=', 'listfile=', 'mcastep=', + 'logging=', 'debug='] filelist = None outdir = None listfile = None @@ -544,13 +547,15 @@ def main(): sys.argv[1:], options, longoptions) - for opt,arg in opts: + for opt, arg in opts: if opt in ('--outdir'): outdir = arg elif opt in ('--listfile'): listfile = arg elif opt in ('--mcastep'): mcastep = int(arg) + + logging.basicConfig(level=getLoggingLevel(opts)) if listfile is None: filelist=[] for item in args: diff --git a/PyMca5/PyMcaGui/pymca/McaCalibrationControlGUI.py b/PyMca5/PyMcaGui/pymca/McaCalibrationControlGUI.py index 881e1c4..8df05ee 100644 --- a/PyMca5/PyMcaGui/pymca/McaCalibrationControlGUI.py +++ b/PyMca5/PyMcaGui/pymca/McaCalibrationControlGUI.py @@ -29,6 +29,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os +import logging from PyMca5.PyMcaGui import PyMcaQt as qt QTVERSION = qt.qVersion() @@ -39,7 +40,8 @@ else: from PyMca5 import PyMcaDirs -DEBUG = 0 +_logger = logging.getLogger(__name__) + class McaCalibrationControlGUI(qt.QWidget): sigMcaCalibrationControlGUISignal = qt.pyqtSignal(object) @@ -82,29 +84,26 @@ class McaCalibrationControlGUI(qt.QWidget): self.calbut.clicked.connect(self._calbuttonclicked) def _calboxactivated(self, item=None): - if DEBUG: - item = qt.safe_str(item) - print("Calibration box activated %s" % item) + _logger.debug("Calibration box activated %s", qt.safe_str(item)) comboitem, combotext = self.calbox.getCurrent() - self._emitpysignal(box=[comboitem,combotext], + self._emitpysignal(box=[comboitem, combotext], boxname='Calibration', event='activated') def _calbuttonclicked(self): - if DEBUG: - print("Calibration button clicked") + _logger.debug("Calibration button clicked") self.calmenu.exec_(self.cursor().pos()) def _copysignal(self): - comboitem,combotext = self.calbox.getCurrent() + comboitem, combotext = self.calbox.getCurrent() self._emitpysignal(button="CalibrationCopy", - box=[comboitem,combotext], + box=[comboitem, combotext], event='clicked') def _computesignal(self): - comboitem,combotext = self.calbox.getCurrent() + comboitem, combotext = self.calbox.getCurrent() self._emitpysignal(button="Calibration", - box=[comboitem,combotext], + box=[comboitem, combotext], event='clicked') def _loadsignal(self): @@ -118,7 +117,7 @@ class McaCalibrationControlGUI(qt.QWidget): windir = self.lastInputDir if windir is None: windir = os.getcwd() - filename= qt.safe_str(qt.QFileDialog.getOpenFileName(self, + filename = qt.safe_str(qt.QFileDialog.getOpenFileName(self, "Load existing calibration file", windir, self.lastInputFilter)) @@ -225,8 +224,7 @@ class McaCalibrationControlGUI(qt.QWidget): checkbox=None, line_edit=None, event=None): - if DEBUG: - print("_emitpysignal called ",button,box) + _logger.debug("_emitpysignal called %s %s", button, box) data={} data['button'] = button data['box'] = box diff --git a/PyMca5/PyMcaGui/pymca/McaLegendselector.py b/PyMca5/PyMcaGui/pymca/McaLegendselector.py new file mode 100644 index 0000000..332e072 --- /dev/null +++ b/PyMca5/PyMcaGui/pymca/McaLegendselector.py @@ -0,0 +1,103 @@ +#/*########################################################################## +# Copyright (C) 2004-2017 V.A. Sole, European Synchrotron Radiation Facility +# +# This file is part of the PyMca X-ray Fluorescence Toolkit developed at +# the ESRF by the Software group. +# +# 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__ = "P. Knobel - ESRF Data Analysis" +__contact__ = "sole@esrf.fr" +__license__ = "MIT" +__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" + +import copy +import numpy +from silx.gui.plot import LegendSelector + + +class McaLegendsDockWidget(LegendSelector.LegendsDockWidget): + """Subclassing of the silx LegendsDockWidget to handle + curve renaming for McaWindow. + """ + def renameCurve(self, oldLegend, newLegend): + """Change the name of a curve using remove and addCurve. + The name must also be changed in dataObjetsDict and calDict. + + :param str oldLegend: The legend of the curve to be changed + :param str newLegend: The new legend of the curve + """ + is_active = self.plot.getActiveCurve(just_legend=True) == oldLegend + xChannels, yOrig, infoOrig = self.plot.getDataAndInfoFromLegend(oldLegend) + curve = self.plot.getCurve(oldLegend) + x = curve.getXData() + y = curve.getYData() + info = curve.getInfo() + calib = info.get('McaCalib', [0.0, 1.0, 0.0]) + calibrationOrder = info.get('McaCalibOrder', 2) + if calibrationOrder == 'TOF': + xFromChannels = calib[2] + calib[0] / pow(xChannels-calib[1], 2) + else: + xFromChannels = calib[0] + \ + calib[1] * xChannels + calib[2] * xChannels * xChannels + if numpy.allclose(xFromChannels, x): + x = xChannels + newInfo = copy.deepcopy(info) + newInfo['legend'] = newLegend + newInfo['SourceName'] = newLegend + newInfo['Key'] = "" + newInfo['selectiontype'] = "1D" + self.plot.removeCurve(oldLegend) + self.plot.addCurve(x, + y, + legend=newLegend, + info=newInfo, + color=curve.getColor(), + symbol=curve.getSymbol(), + linewidth=curve.getLineWidth(), + linestyle=curve.getLineStyle(), + xlabel=curve.getXLabel(), + ylabel=curve.getYLabel(), + xerror=curve.getXErrorData(copy=False), + yerror=curve.getYErrorData(copy=False), + z=curve.getZValue(), + selectable=curve.isSelectable(), + fill=curve.isFill(), + resetzoom=False) + if is_active: + self.plot.setActiveCurve(newLegend) + + # make sure the dicts are also renamed + self._renameInDataObjectsDict(oldLegend, newLegend) + self._renameInCalDict(oldLegend, newLegend) + + def _renameInDataObjectsDict(self, oldLegend, newLegend): + # This seems to be useless but I don't know why. + # dataObjectDict is already properly renamed. + if oldLegend in self.plot.dataObjectsDict: + self.plot.dataObjectsDict[newLegend] = copy.deepcopy( + self.plot.dataObjectsDict[oldLegend]) + self.plot.dataObjectsDict[newLegend].info['legend'] = newLegend + del self.plot.dataObjectsDict[oldLegend] + + def _renameInCalDict(self, oldLegend, newLegend): + if oldLegend in self.plot.caldict: + self.plot.caldict[newLegend] = copy.deepcopy(self.plot.caldict[oldLegend]) + del self.plot.caldict[oldLegend] diff --git a/PyMca5/PyMcaGui/pymca/McaWindow.py b/PyMca5/PyMcaGui/pymca/McaWindow.py index f965c13..7cc5814 100644 --- a/PyMca5/PyMcaGui/pymca/McaWindow.py +++ b/PyMca5/PyMcaGui/pymca/McaWindow.py @@ -27,12 +27,14 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" + import sys import os import numpy -#from numpy import argsort, nonzero, take import time import traceback +import logging + from PyMca5.PyMcaGui import PyMcaQt as qt if hasattr(qt, 'QString'): QString = qt.QString @@ -44,113 +46,159 @@ if __name__ == "__main__": import copy from PyMca5.PyMcaGui.io import PyMcaFileDialogs -from PyMca5.PyMcaGui import IconDict from . import ScanWindow from . import McaCalibrationControlGUI from PyMca5.PyMcaIO import ConfigDict -from PyMca5.PyMcaGui import McaAdvancedFit -from PyMca5.PyMcaCore import DataObject +from PyMca5.PyMcaGui.physics.xrf import McaAdvancedFit from PyMca5.PyMcaGui.physics.xrf import McaCalWidget +from PyMca5.PyMcaCore import DataObject from . import McaSimpleFit from PyMca5.PyMcaMath.fitting import Specfit -from PyMca5.PyMcaMath.fitting import SpecfitFuns -from PyMca5.PyMcaGui import PyMcaPrintPreview from PyMca5 import PyMcaDirs +from PyMca5.PyMcaGui.pymca.McaLegendselector import McaLegendsDockWidget + +import silx.gui.icons + -from PyMca5.PyMcaGui import QPyMcaMatplotlibSave1D MATPLOTLIB = True -#force understanding of utf-8 encoding -#otherways it cannot generate svg output +# force understanding of utf-8 encoding +# otherwise it cannot generate svg output try: import encodings.utf_8 except: - #not a big problem + # not a big problem pass -DEBUG = 0 + +_logger = logging.getLogger(__name__) +# _logger.setLevel(logging.DEBUG) + + class McaWindow(ScanWindow.ScanWindow): - def __init__(self, parent=None, name="Mca Window", specfit=None, backend=None, - plugins=True, newplot=False, roi=True, fit=True, **kw): + sigROISignal = qt.pyqtSignal(object) + + def __init__(self, parent=None, name="Mca Window", fit=True, backend=None, + plugins=True, control=True, position=True, roi=True, + specfit=None, info=False): ScanWindow.ScanWindow.__init__(self, parent, - name=name, - newplot=newplot, - plugins=plugins, - backend=backend, - roi=roi, - fit=fit, - **kw) - self.setWindowType("MCA") - # these two objects are the same - self.dataObjectsList = self._curveList - # but this is tricky + name=name, + plugins=plugins, + backend=backend, + control=control, + position=position, + roi=roi, + fit=False, # we redefine this + save=False, # we redefine this + info=info) + self._plotType = "MCA" # needed by legacy plugins + + # this is tricky self.dataObjectsDict = {} - #self.setWindowTitle(name) self.outputDir = None self.outputFilter = None - self.matplotlibDialog = None - self.calibration = 'None' - self.calboxoptions = ['None','Original (from Source)','Internal (from Source OR PyMca)'] - self.caldict={} - self.calwidget = None - self.currentROI = None - self.peakmarker = None - if specfit is None: - self.specfit = Specfit.Specfit() - else: - self.specfit = specfit - self.simplefit = McaSimpleFit.McaSimpleFit(specfit=self.specfit) + self.calboxoptions = ['None', 'Original (from Source)', + 'Internal (from Source OR PyMca)'] + self.caldict = {} + self.calwidget = None + self.peakmarker = None + + self.specfit = specfit if specfit is not None else Specfit.Specfit() + self.simplefit = McaSimpleFit.McaSimpleFit(specfit=self.specfit) self.specfit.fitconfig['McaMode'] = 1 self.advancedfit = McaAdvancedFit.McaAdvancedFit() - self.printPreview = PyMcaPrintPreview.PyMcaPrintPreview(modal = 0) - if DEBUG: - print("printPreview id = %d" % id(self.printPreview)) - self._buildCalibrationControlWidget() - self._toggleCounter = 2 - self._togglePointsSignal() + + self.setDefaultPlotLines(True) + self.setDefaultPlotPoints(False) + self._ownSignal = None - self.changeGridLevel() + self.setGraphGrid('major') self.connections() self.setGraphYLabel('Counts') - if 1: - self.fitButtonMenu = qt.QMenu() - self.fitButtonMenu.addAction(QString("Simple"), - self.mcaSimpleFitSignal) - self.fitButtonMenu.addAction(QString("Advanced") , - self.mcaAdvancedFitSignal) - #self.fitButtonMenu.addAction(QString("Simple Fit"), - # self._simpleFitSignal) - #self.fitButtonMenu.addAction(QString("Customized Fit") , - # self._customFitSignal) + # custom save + self.mcaSaveButton = qt.QToolButton(self) + self.mcaSaveButton.setIcon(silx.gui.icons.getQIcon('document-save')) + self.mcaSaveButton.setToolTip('Save as') + self.mcaSaveButton.clicked.connect(self._saveIconSignal) + self.getOutputToolBar().addWidget(self.mcaSaveButton) + + # Fit icon + self.fitToolButton = qt.QToolButton(self) + self.fitToolButton.setIcon(silx.gui.icons.getQIcon('math-fit')) + self.fitToolButton.setToolTip("Fit of Active Curve") + self.fitToolButton.clicked.connect(self._fitButtonClicked) + + self.fitButtonMenu = qt.QMenu() + self.fitButtonMenu.addAction(QString("Simple"), + self.mcaSimpleFitSignal) + self.fitButtonMenu.addAction(QString("Advanced"), + self.mcaAdvancedFitSignal) + + if fit: + self.toolBar().insertWidget(self.getMaskAction(), + self.fitToolButton) + + # hide a bunch of PlotWindow and ScanWindow actions + self.getOutputToolBar().getSaveAction().setVisible(False) + self.getOutputToolBar().getPrintAction().setVisible(False) + self.avgAction.setVisible(False) + self.derivativeAction.setVisible(False) + self.smoothAction.setVisible(False) + self.swapSignAction.setVisible(False) + self.yMinToZero.setVisible(False) + self.subtractAction.setVisible(False) + + def getLegendsDockWidget(self): + # customize the legendsdockwidget to handle curve renaming + if self._legendsDockWidget is None: + self._legendsDockWidget = McaLegendsDockWidget(plot=self) + self._legendsDockWidget.hide() + self.addTabbedDockWidget(self._legendsDockWidget) + return self._legendsDockWidget + + def getCurvesRoiDockWidget(self): + """Reimplemented to add the dock widget to the right of the plot. + """ + if self._curvesROIDockWidget is None: + self._curvesROIDockWidget =\ + ScanWindow.ScanWindow.getCurvesRoiDockWidget(self) + self.addTabbedDockWidget(self._curvesROIDockWidget) + self.addDockWidget(qt.Qt.RightDockWidgetArea, + self._curvesROIDockWidget) + return self._curvesROIDockWidget + + def _fitButtonClicked(self): + self.fitButtonMenu.exec_(self.cursor().pos()) def _buildCalibrationControlWidget(self): widget = self.centralWidget() - self.controlWidget = McaCalibrationControlGUI.McaCalibrationControlGUI(\ + self.controlWidget = McaCalibrationControlGUI.McaCalibrationControlGUI( widget) widget.layout().addWidget(self.controlWidget) - self.controlWidget.sigMcaCalibrationControlGUISignal.connect(\ + self.controlWidget.sigMcaCalibrationControlGUISignal.connect( self.__anasignal) def connections(self): self.simplefit.sigMcaSimpleFitSignal.connect(self.__anasignal) self.advancedfit.sigMcaAdvancedFitSignal.connect(self.__anasignal) + self.getCurvesRoiDockWidget().sigROISignal.connect(self.emitCurrentROISignal) def mcaSimpleFitSignal(self): legend = self.getActiveCurve(just_legend=True) if legend is None: - msg = qt.QMessageBox(self) - msg.setIcon(qt.QMessageBox.Critical) - msg.setText("Please Select an active curve") - msg.setWindowTitle('MCA Window Simple Fit') - msg.exec_() - return + msg = qt.QMessageBox(self) + msg.setIcon(qt.QMessageBox.Critical) + msg.setText("Please Select an active curve") + msg.setWindowTitle('MCA Window Simple Fit') + msg.exec_() + return x, y, info = self.getDataAndInfoFromLegend(legend) self.advancedfit.hide() self.simplefit.show() @@ -161,87 +209,42 @@ class McaWindow(ScanWindow.ScanWindow): self.__simplefitcalmode = self.calibration curveinfo = info if self.calibration == 'None': - calib = [0.0,1.0,0.0] + calib = [0.0, 1.0, 0.0] else: - if 'McaCalib' in curveinfo: - calib = curveinfo['McaCalib'] - else: - calib = [0.0, 1.0, 0.0] + calib = curveinfo.get('McaCalib', [0.0, 1.0, 0.0]) self.__simplefitcalibration = calib - calibrationOrder = curveinfo.get('McaCalibOrder',2) + calibrationOrder = curveinfo.get('McaCalibOrder', 2) if calibrationOrder == 'TOF': - x = calib[2] + calib[0] / pow(x-calib[1],2) + x = calib[2] + calib[0] / pow(x - calib[1], 2) else: x = calib[0] + calib[1] * x + calib[2] * x * x - self.simplefit.setdata(x=x,y=y, - xmin=xmin, - xmax=xmax, - legend=legend) - """ - if self.specfit.fitconfig['McaMode']: - self.specfitGUI.guiparameters.fillfromfit(self.specfit.paramlist, - current='Region 1') - self.specfitGUI.guiparameters.removeallviews(keep='Region 1') - else: - self.specfitGUI.guiparameters.fillfromfit(self.specfit.paramlist, - current='Fit') - self.specfitGUI.guiparameters.removeallviews(keep='Fit') - """ + self.simplefit.setdata(x=x, y=y, xmin=xmin, xmax=xmax, + legend=legend) + if self.specfit.fitconfig['McaMode']: self.simplefit.fit() else: - msg = qt.QMessageBox(self) - msg.setIcon(qt.QMessageBox.Critical) - msg.setText("Error. Trying to fit fitted data?") - msg.setWindowTitle('MCA Window Simple Fit') - msg.exec_() + msg = qt.QMessageBox(self) + msg.setIcon(qt.QMessageBox.Critical) + msg.setText("Error. Trying to fit fitted data?") + msg.setWindowTitle('MCA Window Simple Fit') + msg.exec_() def getActiveCurve(self, just_legend=False): - if DEBUG: - print("Local MCA window getActiveCurve called!!!!") - legend = super(McaWindow, self).getActiveCurve(just_legend) + _logger.debug("Local MCA window getActiveCurve called!!!!") + activeCurve = super(McaWindow, self).getActiveCurve(just_legend) if just_legend: - return legend - activeCurve = legend + return activeCurve if activeCurve in [None, []]: return None - x = activeCurve[0] - y = activeCurve[1] - legend = activeCurve[2] - curveinfo = activeCurve[3] + curveinfo = activeCurve.getInfo() xlabel = self.getGraphXLabel() ylabel = self.getGraphYLabel() - """ - if legend in self.dataObjectsDict.keys(): - info = self.dataObjectsDict[legend].info - if str(xlabel.upper()) != "CHANNEL": - x = self.dataObjectsDict[legend].x[0] - else: - info = None - else: - info = None - - if info is not None: - if self.calibration == 'None': - calib = [0.0,1.0,0.0] - else: - if 'McaCalib' in curveinfo: - calib = curveinfo['McaCalib'] - else: - calib = [0.0, 1.0, 0.0] - calibrationOrder = curveinfo.get('McaCalibOrder',2) - if calibrationOrder == 'TOF': - x = calib[2] + calib[0] / pow(x - calib[1],2) - else: - x = calib[0] + calib[1] * x + calib[2] * x * x - else: - info = curveinfo - """ - info = curveinfo - info['xlabel'] = xlabel - info['ylabel'] = ylabel - return x, y, legend, info + curveinfo['xlabel'] = xlabel + curveinfo['ylabel'] = ylabel + activeCurve.setInfo(curveinfo) + return activeCurve def getDataAndInfoFromLegend(self, legend): """ @@ -254,7 +257,7 @@ class McaWindow(ScanWindow.ScanWindow): if legend in self.dataObjectsDict: # The data as displayed - x, y, legend, curveinfo = self.getCurve(legend) + x, y, legend, curveinfo = self.getCurve(legend)[:4] # the data as first entered info = self.dataObjectsDict[legend].info if self.calibration == 'None': @@ -301,15 +304,15 @@ class McaWindow(ScanWindow.ScanWindow): return x, y, info = self.getDataAndInfoFromLegend(legend) - curveinfo = self.getCurve(legend)[3] - xmin,xmax = self.getGraphXLimits() + curveinfo = self.getCurve(legend).getInfo() + xmin, xmax = self.getGraphXLimits() if self.calibration == 'None': if 'McaCalibSource' in curveinfo: calib = curveinfo['McaCalibSource'] elif 'McaCalibSource' in info: calib = info['McaCalibSource'] else: - calib = [0.0,1.0,0.0] + calib = [0.0, 1.0, 0.0] else: calib = curveinfo['McaCalib'] energy = calib[0] + calib[1] * x + calib[2] * x * x @@ -325,7 +328,7 @@ class McaWindow(ScanWindow.ScanWindow): self.advancedfit.raise_() if info is not None: xlabel = 'Channel' - self.advancedfit.setData(x=x,y=y, + self.advancedfit.setData(x=x, y=y, xmin=xmin, xmax=xmax, legend=legend, @@ -341,17 +344,15 @@ class McaWindow(ScanWindow.ScanWindow): msg.exec_() return - def __anasignal(self,dict): - if DEBUG: - print("__anasignal called dict = ",dict) + def __anasignal(self, dict): + _logger.debug("__anasignal called dict = %s", dict) if dict['event'] == 'clicked': - # A button has been cicked - if dict['button'] == 'Source': + # A button has been clicked + if dict['button'] == 'Source': pass elif dict['button'] == 'Calibration': - #legend,x,y = self.graph.getactivecurve() - legend = self.getActiveCurve(just_legend=1) + legend = self.getActiveCurve(just_legend=True) if legend is None: msg = qt.QMessageBox(self) msg.setIcon(qt.QMessageBox.Critical) @@ -362,15 +363,15 @@ class McaWindow(ScanWindow.ScanWindow): x, y, info = self.getDataAndInfoFromLegend(legend) if info is None: return - ndict = {} - ndict[legend] = {'order':1, - 'A':0.0, - 'B':1.0, - 'C':0.0} + ndict = {legend: {'order': 1, + 'A': 0.0, + 'B': 1.0, + 'C': 0.0}} + if legend in self.caldict: ndict[legend].update(self.caldict[legend]) if abs(ndict[legend]['C']) > 0.0: - ndict[legend]['order'] = self.caldict[legend].get('order', 2) + ndict[legend]['order'] = self.caldict[legend].get('order', 2) elif 'McaCalib' in info: if type(info['McaCalib'][0]) == type([]): calib = info['McaCalib'][0] @@ -380,16 +381,14 @@ class McaWindow(ScanWindow.ScanWindow): if len(calib) > 1: ndict[legend]['A'] = calib[0] ndict[legend]['B'] = calib[1] - if len(calib) >2: - ndict[legend]['order'] = calibrationOrder - ndict[legend]['C'] = calib[2] + if len(calib) > 2: + ndict[legend]['order'] = calibrationOrder + ndict[legend]['C'] = calib[2] caldialog = McaCalWidget.McaCalWidget(legend=legend, - x=x, - y=y, - modal=1, - caldict=ndict) - #info,x,y = self.getinfodatafromlegend(legend) - #caldialog.graph.newCurve("fromlegend",x=x,y=y) + x=x, + y=y, + modal=1, + caldict=ndict) ret = caldialog.exec_() if ret == qt.QDialog.Accepted: @@ -409,8 +408,7 @@ class McaWindow(ScanWindow.ScanWindow): self.refresh() del caldialog elif dict['button'] == 'CalibrationCopy': - #legend,x,y = self.graph.getactivecurve() - legend = self.getActiveCurve(just_legend=1) + legend = self.getActiveCurve(just_legend=True) if legend is None: msg = qt.QMessageBox(self) msg.setIcon(qt.QMessageBox.Critical) @@ -421,52 +419,52 @@ class McaWindow(ScanWindow.ScanWindow): x, y, info = self.getDataAndInfoFromLegend(legend) if info is None: return - ndict=copy.deepcopy(self.caldict) + ndict = copy.deepcopy(self.caldict) if 'McaCalib' in info: if type(info['McaCalib'][0]) == type([]): sourcecal = info['McaCalib'][0] else: sourcecal = info['McaCalib'] else: - sourcecal = [0.0,1.0,0.0] + sourcecal = [0.0, 1.0, 0.0] for curve in self.getAllCurves(just_legend=True): curveinfo = self.getCurve(curve)[3] if 'McaCalibSource' in curveinfo: key = "%s (Source)" % curve if key not in ndict: - if curveinfo['McaCalibSource'] != [0.0,1.0,0.0]: - ndict[key] = {'A':curveinfo['McaCalibSource'][0], - 'B':curveinfo['McaCalibSource'][1], - 'C':curveinfo['McaCalibSource'][2]} + if curveinfo['McaCalibSource'] != [0.0, 1.0, 0.0]: + ndict[key] = {'A': curveinfo['McaCalibSource'][0], + 'B': curveinfo['McaCalibSource'][1], + 'C': curveinfo['McaCalibSource'][2]} if curveinfo['McaCalibSource'][2] != 0.0: ndict[key]['order'] = 2 else: ndict[key]['order'] = 1 if curve not in self.caldict.keys(): - if curveinfo['McaCalib'] != [0.0,1.0,0.0]: + if curveinfo['McaCalib'] != [0.0, 1.0, 0.0]: if curveinfo['McaCalib'] != curveinfo['McaCalibSource']: key = "%s (PyMca)" % curve - ndict[key] = {'A':curveinfo['McaCalib'][0], - 'B':curveinfo['McaCalib'][1], - 'C':curveinfo['McaCalib'][2]} + ndict[key] = {'A': curveinfo['McaCalib'][0], + 'B': curveinfo['McaCalib'][1], + 'C': curveinfo['McaCalib'][2]} if curveinfo['McaCalib'][2] != 0.0: ndict[key]['order'] = 2 else: ndict[key]['order'] = 1 else: if curve not in self.caldict.keys(): - if curveinfo['McaCalib'] != [0.0,1.0,0.0]: - key = "%s (PyMca)" % curve - ndict[key] = {'A':curveinfo['McaCalib'][0], - 'B':curveinfo['McaCalib'][1], - 'C':curveinfo['McaCalib'][2]} - if curveinfo['McaCalib'][2] != 0.0: - ndict[key]['order'] = 2 - else: - ndict[key]['order'] = 1 + if curveinfo['McaCalib'] != [0.0, 1.0, 0.0]: + key = "%s (PyMca)" % curve + ndict[key] = {'A': curveinfo['McaCalib'][0], + 'B': curveinfo['McaCalib'][1], + 'C': curveinfo['McaCalib'][2]} + if curveinfo['McaCalib'][2] != 0.0: + ndict[key]['order'] = 2 + else: + ndict[key]['order'] = 1 if not (legend in self.caldict): - ndict[legend]={} + ndict[legend] = {} ndict[legend]['A'] = sourcecal[0] ndict[legend]['B'] = sourcecal[1] ndict[legend]['C'] = sourcecal[2] @@ -474,7 +472,7 @@ class McaWindow(ScanWindow.ScanWindow): ndict[legend]['order'] = 2 else: ndict[legend]['order'] = 1 - caldialog = McaCalWidget.McaCalCopy(legend=legend,modal=1, + caldialog = McaCalWidget.McaCalCopy(legend=legend, modal=1, caldict=ndict, sourcecal=sourcecal, fl=0) @@ -498,7 +496,7 @@ class McaWindow(ScanWindow.ScanWindow): self.refresh() del caldialog elif dict['button'] == 'CalibrationLoad': - item = dict['box'][0] + # item = dict['box'][0] itemtext = dict['box'][1] filename = dict['line_edit'] if not os.path.exists(filename): @@ -557,15 +555,15 @@ class McaWindow(ScanWindow.ScanWindow): elif dict['button'] == 'Search': pass elif dict['button'] == 'Fit': - if dict['box'][1] == 'Simple': + if dict['box'][1] == 'Simple': self.mcasimplefitsignal() - elif dict['box'][1] == 'Advanced': + elif dict['box'][1] == 'Advanced': self.mcaadvancedfitsignal() else: - print("Unknown Fit Event") + _logger.error("Unknown Fit Event") elif dict['event'] == 'activated': # A comboBox has been selected - if dict['boxname'] == 'Source': + if dict['boxname'] == 'Source': pass elif dict['boxname'] == 'Calibration': self.calibration = dict['box'][1] @@ -593,31 +591,30 @@ class McaWindow(ScanWindow.ScanWindow): """ pass else: - if DEBUG: - print("Unknown combobox",dict['boxname']) + _logger.debug("Unknown combobox %s", dict['boxname']) - elif (dict['event'] == 'EstimateFinished'): + elif dict['event'] == 'EstimateFinished': pass elif (dict['event'] == 'McaAdvancedFitFinished') or \ - (dict['event'] == 'McaAdvancedFitMatrixFinished') : - x = dict['result']['xdata'] - yb = dict['result']['continuum'] - legend0= dict['info']['legend'] + (dict['event'] == 'McaAdvancedFitMatrixFinished'): + x = dict['result']['xdata'] + yb = dict['result']['continuum'] + legend0 = dict['info']['legend'] fitcalibration = [dict['result']['fittedpar'][0], dict['result']['fittedpar'][1], 0.0] if dict['event'] == 'McaAdvancedFitMatrixFinished': legend = dict['info']['legend'] + " Fit" legend3 = dict['info']['legend'] + " Matrix" - ymatrix = dict['result']['ymatrix'] * 1.0 - #copy the original info from the curve + ymatrix = dict['result']['ymatrix'] * 1.0 + # copy the original info from the curve newDataObject = DataObject.DataObject() newDataObject.info = copy.deepcopy(self.dataObjectsDict[legend0].info) - newDataObject.info['SourceType']= 'AdvancedFit' + newDataObject.info['SourceType'] = 'AdvancedFit' newDataObject.info['SourceName'] = 1 * self.dataObjectsDict[legend0].info['SourceName'] - newDataObject.info['legend'] = legend3 - newDataObject.info['Key'] = legend3 - newDataObject.info['McaCalib'] = fitcalibration * 1 + newDataObject.info['legend'] = legend3 + newDataObject.info['Key'] = legend3 + newDataObject.info['McaCalib'] = fitcalibration * 1 newDataObject.x = [x] newDataObject.y = [ymatrix] newDataObject.m = None @@ -625,16 +622,16 @@ class McaWindow(ScanWindow.ScanWindow): #self.graph.newCurve(legend3,x=x,y=ymatrix,logfilter=1) else: legend = dict['info']['legend'] + " Fit" - yfit = dict['result']['yfit'] * 1.0 + yfit = dict['result']['yfit'] * 1.0 - #copy the original info from the curve + # copy the original info from the curve newDataObject = DataObject.DataObject() newDataObject.info = copy.deepcopy(self.dataObjectsDict[legend0].info) - newDataObject.info['SourceType']= 'AdvancedFit' + newDataObject.info['SourceType'] = 'AdvancedFit' newDataObject.info['SourceName'] = 1 * self.dataObjectsDict[legend0].info['SourceName'] newDataObject.info['legend'] = legend - newDataObject.info['Key'] = legend - newDataObject.info['McaCalib'] = fitcalibration * 1 + newDataObject.info['Key'] = legend + newDataObject.info['McaCalib'] = fitcalibration * 1 newDataObject.data = numpy.reshape(numpy.concatenate((x,yfit,yb),0),(3,len(x))) newDataObject.x = [x] newDataObject.y = [yfit] @@ -643,15 +640,15 @@ class McaWindow(ScanWindow.ScanWindow): self.dataObjectsDict[legend] = newDataObject #self.graph.newCurve(legend,x=x,y=yfit,logfilter=1) - #the same for the background + # the same for the background legend2 = dict['info']['legend'] + " Bkg" newDataObject2 = DataObject.DataObject() newDataObject2.info = copy.deepcopy(self.dataObjectsDict[legend0].info) - newDataObject2.info['SourceType']= 'AdvancedFit' + newDataObject2.info['SourceType'] = 'AdvancedFit' newDataObject2.info['SourceName'] = 1 * self.dataObjectsDict[legend0].info['SourceName'] newDataObject2.info['legend'] = legend2 - newDataObject2.info['Key'] = legend2 - newDataObject2.info['McaCalib'] = fitcalibration * 1 + newDataObject2.info['Key'] = legend2 + newDataObject2.info['McaCalib'] = fitcalibration * 1 newDataObject2.data = None newDataObject2.x = [x] newDataObject2.y = [yb] @@ -659,12 +656,12 @@ class McaWindow(ScanWindow.ScanWindow): self.dataObjectsDict[legend2] = newDataObject2 #self.graph.newCurve(legend2,x=x,y=yb,logfilter=1) - if not (legend in self.caldict): + if legend not in self.caldict: self.caldict[legend] = {} - self.caldict[legend] ['order'] = 1 - self.caldict[legend] ['A'] = dict['result']['fittedpar'][0] - self.caldict[legend] ['B'] = dict['result']['fittedpar'][1] - self.caldict[legend] ['C'] = 0.0 + self.caldict[legend]['order'] = 1 + self.caldict[legend]['A'] = dict['result']['fittedpar'][0] + self.caldict[legend]['B'] = dict['result']['fittedpar'][1] + self.caldict[legend]['C'] = 0.0 options = [] for option in self.calboxoptions: options.append(option) @@ -673,59 +670,57 @@ class McaWindow(ScanWindow.ScanWindow): options.append(key) try: self.controlWidget.calbox.setOptions(options) - #I only reset the graph scale after a fit, not on a matrix spectrum + # I only reset the graph scale after a fit, not on a matrix spectrum if dict['event'] == 'McaAdvancedFitFinished': - #get current limits + # get current limits if self.calibration == 'None': - xmin, xmax =self.getGraphXLimits() - emin = dict['result']['fittedpar'][0] + \ - dict['result']['fittedpar'][1] * xmin - emax = dict['result']['fittedpar'][0] + \ - dict['result']['fittedpar'][1] * xmax + xmin, xmax = self.getGraphXLimits() + emin = dict['result']['fittedpar'][0] + \ + dict['result']['fittedpar'][1] * xmin + emax = dict['result']['fittedpar'][0] + \ + dict['result']['fittedpar'][1] * xmax else: - emin,emax = self.getGraphXLimits() - ymin, ymax =self.getGraphYLimits() + emin, emax = self.getGraphXLimits() + ymin, ymax = self.getGraphYLimits() self.controlWidget.calbox.setCurrentIndex(options.index(legend)) self.calibration = legend self.controlWidget._calboxactivated(legend) - self.setGraphYLimits(ymin, ymax, replot=False) + self.setGraphYLimits(ymin, ymax) if emin < emax: - self.setGraphXLimits(emin, emax, replot=True) + self.setGraphXLimits(emin, emax) else: - self.setGraphXLimits(emax, emin, replot=True) + self.setGraphXLimits(emax, emin) except: - if DEBUG: - print("Refreshing due to exception", sys.exc_info()[1]) + _logger.debug("Refreshing due to exception %s", sys.exc_info()[1]) self.refresh() - #self.graph.replot() elif dict['event'] == 'McaFitFinished': mcaresult = dict['data'] - legend = dict['info']['legend'] + " " i = 0 xfinal = [] yfinal = [] - ybfinal= [] + ybfinal = [] regions = [] - legend0= dict['info']['legend'] + legend0 = dict['info']['legend'] mcamode = True for result in mcaresult: i += 1 if result['chisq'] is not None: - mcamode = result['fitconfig']['McaMode'] - idx=numpy.nonzero((self.specfit.xdata0>=result['xbegin']) & \ - (self.specfit.xdata0<=result['xend']))[0] - x=numpy.take(self.specfit.xdata0,idx) - y=self.specfit.gendata(x=x,parameters=result['paramlist']) - nparb= len(self.specfit.bkgdict[self.specfit.fitconfig['fitbkg']][1]) - yb = self.specfit.gendata(x=x,parameters=result['paramlist'][0:nparb]) - xtoadd = numpy.take(self.dataObjectsDict[legend0].x[0],idx).tolist() - if not len(xtoadd): continue - xfinal = xfinal + xtoadd - regions.append([xtoadd[0],xtoadd[-1]]) - yfinal = yfinal + y.tolist() - ybfinal= ybfinal + yb.tolist() - #self.graph.newCurve(legend + 'Region %d' % i,x=x,y=yfit,logfilter=1) + mcamode = result['fitconfig']['McaMode'] + idx = numpy.nonzero((self.specfit.xdata0 >= result['xbegin']) & + (self.specfit.xdata0 <= result['xend']))[0] + x = numpy.take(self.specfit.xdata0, idx) + y = self.specfit.gendata(x=x, parameters=result['paramlist']) + nparb = len(self.specfit.bkgdict[self.specfit.fitconfig['fitbkg']][1]) + yb = self.specfit.gendata(x=x, parameters=result['paramlist'][0:nparb]) + xtoadd = numpy.take(self.dataObjectsDict[legend0].x[0], idx).tolist() + if not len(xtoadd): + continue + xfinal = xfinal + xtoadd + regions.append([xtoadd[0], xtoadd[-1]]) + yfinal = yfinal + y.tolist() + ybfinal = ybfinal + yb.tolist() + # self.graph.newCurve(legend + 'Region %d' % i,x=x,y=yfit,logfilter=1) legend = legend0 + " SFit" if legend in self.dataObjectsDict.keys(): if legend in self.getAllCurves(just_legend=True): @@ -735,44 +730,45 @@ class McaWindow(ScanWindow.ScanWindow): else: if 'baseline' in self.dataObjectsDict[legend].info: self.removeCurve(legend) - #copy the original info from the curve + # copy the original info from the curve newDataObject = DataObject.DataObject() newDataObject.info = copy.deepcopy(self.dataObjectsDict[legend0].info) - newDataObject.info['SourceType']= 'SimpleFit' + newDataObject.info['SourceType'] = 'SimpleFit' newDataObject.info['SourceName'] = 1 * self.dataObjectsDict[legend0].info['SourceName'] - newDataObject.info['legend'] = legend - newDataObject.info['Key'] = legend - newDataObject.info['CalMode'] = self.__simplefitcalmode - newDataObject.info['McaCalib'] = self.__simplefitcalibration - x = numpy.array(xfinal) + newDataObject.info['legend'] = legend + newDataObject.info['Key'] = legend + newDataObject.info['CalMode'] = self.__simplefitcalmode + newDataObject.info['McaCalib'] = self.__simplefitcalibration + x = numpy.array(xfinal) yfit = numpy.array(yfinal) yb = numpy.array(ybfinal) newDataObject.x = [x] newDataObject.y = [yfit] newDataObject.m = [numpy.ones(len(yfit)).astype(numpy.float)] if mcamode: - newDataObject.info['regions'] = regions + newDataObject.info['regions'] = regions newDataObject.info['baseline'] = yb self.dataObjectsDict[legend] = newDataObject self.refresh() return + elif dict['event'] == 'McaTableFilled': if self.peakmarker is not None: - self.graph.removeMarker(self.peakmarker) + self.removeMarker(self.peakmarker) self.peakmarker = None elif dict['event'] == 'McaTableRowHeaderClicked': - #I have to mark the peaks + # I have to mark the peaks if dict['row'] >= 0: pos = dict['Position'] label = 'PEAK %d' % (dict['row']+1) if self.peakmarker is not None: self.removeMarker(self.peakmarker) - self.insertXMarker(pos, - label, - text=label, - color='pink', - draggable=False) + self.addXMarker(pos, + label, + text=label, + color='pink', + draggable=False) self.peakmarker = label else: if self.peakmarker is not None: @@ -782,13 +778,13 @@ class McaWindow(ScanWindow.ScanWindow): if self.peakmarker is not None: self.removeMarker(self.peakmarker) self.peakmarker = None - elif (dict['event'] == 'McaAdvancedFitElementClicked') or \ - (dict['event'] == 'ElementClicked'): - #this has been moved to the fit window + + elif (dict['event'] == 'McaAdvancedFitElementClicked' or + dict['event'] == 'ElementClicked'): + # this has been moved to the fit window pass elif dict['event'] == 'McaAdvancedFitPrint': - #self.advancedfit.printps(doit=1) self.printHtml(dict['text']) elif dict['event'] == 'McaSimpleFitPrint': @@ -798,95 +794,105 @@ class McaWindow(ScanWindow.ScanWindow): if self.peakmarker is not None: self.removeMarker(self.peakmarker) self.peakmarker = None - self.replot() + elif dict['event'] == 'ScanFitPrint': self.printHtml(dict['text']) - elif dict['event'] == 'AddROI': - return super(McaWindow, self)._roiSignal(dict) - elif dict['event'] == 'DelROI': - return super(McaWindow, self)._roiSignal(dict) - - elif dict['event'] == 'ResetROI': - return super(McaWindow, self)._roiSignal(dict) - - elif dict['event'] == 'ActiveROI': - print("ActiveROI event") - pass elif dict['event'] == 'selectionChanged': - print("Selection changed event not implemented any more") + _logger.error("Selection changed event not implemented any more") else: - if DEBUG: - print("Unknown or ignored event",dict['event']) - - - def emitCurrentROISignal(self): - if self.currentROI is None: + _logger.debug("Unknown or ignored event %s", dict['event']) + + def emitCurrentROISignal(self, ddict=None): + """Emit a custom ROISignal with calibration info. + Ignore the incoming signal emitted by CurvesRoiDockWidget""" + currentRoi = self.getCurvesRoiDockWidget().currentROI + if currentRoi is None: + # could be a silx <= 0.7.0 bug + if hasattr(self.getCurvesRoiDockWidget().roiWidget, + "currentROI"): + currentRoi = self.getCurvesRoiDockWidget().roiWidget.currentROI + if currentRoi is None: + _logger.debug("No current ROI") return - #I have to get the current calibration + + # I have to get the current calibration if self.getGraphXLabel().upper() != "CHANNEL": - #I have to get the energy + # I have to get the energy A = self.controlWidget.calinfo.caldict['']['A'] B = self.controlWidget.calinfo.caldict['']['B'] C = self.controlWidget.calinfo.caldict['']['C'] order = self.controlWidget.calinfo.caldict['']['order'] else: A = 0.0 - try: - legend = self.getActiveCurve(just_legend=True) - if legend in self.dataObjectsDict.keys(): + legend = self.getActiveCurve(just_legend=True) + if legend in self.dataObjectsDict: + try: A = self.dataObjectsDict[legend].x[0][0] - except: - if DEBUG: - print("X axis offset not found") + except: + _logger.debug("X axis offset not found") B = 1.0 C = 0.0 order = 1 - key = self.currentROI - roiList, roiDict = self.roiWidget.getROIListAndDict() - fromdata = roiDict[key]['from' ] - todata = roiDict[key]['to'] - ddict = {} - ddict['event'] = "ROISignal" - ddict['name'] = key - ddict['from'] = fromdata - ddict['to'] = todata - ddict['type'] = roiDict[self.currentROI]["type"] - ddict['calibration']= [A, B, C, order] + + if hasattr(currentRoi, "getName"): + # TODO: double-check ROIWidget.currentROI API after merging silx#1714 + # silx.gui.plot.CurvesROIWidget.ROI object + name = currentRoi.getName() + else: + # assume it is a string (silx <= 0.7.0) + name = currentRoi + + roisDict = self.getCurvesRoiDockWidget().roiWidget.getRois() + assert name in roisDict, "roiWidget.currentRoi not found in roiDict!" + roi = roisDict[name] + if isinstance(roi, dict): + from_ = roi['from'] + to_ = roi['to'] + type_ = roi["type"] + else: + # silx >= 0.8.0 + from_ = roi.getFrom() + to_ = roi.getTo() + type_ = roi.getType() + + ddict = { + 'event': "ROISignal", + 'name': name, + 'from': from_, + 'to': to_, + 'type': type_, + 'calibration': [A, B, C, order]} self.sigROISignal.emit(ddict) - def setDispatcher(self, w): - w.sigAddSelection.connect(self._addSelection) - w.sigRemoveSelection.connect(self._removeSelection) - w.sigReplaceSelection.connect(self._replaceSelection) + def _addSelection(self, selection, resetzoom=True, replot=None): + _logger.debug("__add, selection = %s", selection) - def _addSelection(self, selection, replot=True): - if DEBUG: - print("__add, selection = ",selection) + if replot is not None: + _logger.warning( + 'deprecated replot argument, use resetzoom instead') + resetzoom = replot and resetzoom - if type(selection) == type([]): - sellist = selection - else: - sellist = [selection] + sellist = selection if isinstance(selection, list) else [selection] for sel in sellist: # force the selections to include their source for completeness? # source = sel['SourceName'] - key = sel['Key'] - if "scanselection" in sel: - if sel["scanselection"] not in [False, "MCA"]: - continue - mcakeys = [key] + key = sel['Key'] + if sel.get("scanselection", False) not in [False, "MCA"]: + continue + mcakeys = [key] for mca in mcakeys: legend = sel['legend'] dataObject = sel['dataobject'] info = dataObject.info - if "selectiontype" in dataObject.info: - if dataObject.info["selectiontype"] != "1D": continue + + if dataObject.info.get("selectiontype", "1D") != "1D": + continue if numpy.isscalar(dataObject.y[0]): dataObject.y[0] = numpy.array([dataObject.y[0]]) data = dataObject.y[0] - curveinfo=copy.deepcopy(info) + curveinfo = copy.deepcopy(info) curveinfo["ylabel"] = info.get("ylabel", "Counts") if dataObject.x is None: xhelp = None @@ -900,7 +906,7 @@ class McaWindow(ScanWindow.ScanWindow): if xhelp is None: if 'Channel0' not in info: info['Channel0'] = 0.0 - xhelp =info['Channel0'] + numpy.arange(len(data)).astype(numpy.float) + xhelp = info['Channel0'] + numpy.arange(len(data)).astype(numpy.float) dataObject.x = [xhelp] ylen = len(data) @@ -930,13 +936,13 @@ class McaWindow(ScanWindow.ScanWindow): xhelp = numpy.take(xhelp, index) data = numpy.take(data, index) mdata = numpy.take(mdata, index) - data = data/mdata + data = data / mdata dataObject.x = [xhelp * 1] dataObject.m = [numpy.ones(len(data)).astype(numpy.float)] elif (len(mdata) == 1) or (ylen == 1): if mdata[0] == 0.0: continue - data = data/mdata + data = data / mdata else: raise ValueError("Cannot normalize data") dataObject.y = [data] @@ -946,14 +952,14 @@ class McaWindow(ScanWindow.ScanWindow): else: simplefitplot = False try: - calib = [0.0,1.0,0.0] + calib = [0.0, 1.0, 0.0] for inputkey in ['baseline', 'regions', 'McaLiveTime']: if inputkey in info: curveinfo[inputkey] = info[inputkey] curveinfo['McaCalib'] = calib if 'McaCalib' in info: if type(info['McaCalib'][0]) == type([]): - calib0 = info['McaCalib'][info['McaDet']-1] + calib0 = info['McaCalib'][info['McaDet'] - 1] else: calib0 = info['McaCalib'] if 'McaCalibSource' in info: @@ -965,29 +971,28 @@ class McaWindow(ScanWindow.ScanWindow): calib = curveinfo['McaCalibSource'] elif 'McaCalib' in info: if type(info['McaCalib'][0]) == type([]): - calib = info['McaCalib'][info['McaDet']-1] + calib = info['McaCalib'][info['McaDet'] - 1] else: calib = info['McaCalib'] if len(calib) > 1: - xdata=calib[0]+ \ - calib[1]* xhelp + xdata = calib[0] + calib[1] * xhelp if len(calib) == 3: - xdata = xdata + calib[2]* xhelp * xhelp + xdata = xdata + calib[2] * xhelp * xhelp curveinfo['McaCalib'] = calib if simplefitplot: inforegions = [] for region in info['regions']: - inforegions.append([calib[0] + \ - calib[1] * region[0] +\ + inforegions.append([calib[0] + + calib[1] * region[0] + calib[2] * region[0] * region[0], - calib[0] + \ - calib[1] * region[1] +\ + calib[0] + + calib[1] * region[1] + calib[2] * region[1] * region[1]]) self.addCurve(xdata, data, legend=legend, - info=curveinfo, own=True) + info=curveinfo, own=True) else: self.addCurve(xdata, data, legend=legend, - info=curveinfo, own=True) + info=curveinfo, own=True) self.setGraphXLabel('Energy') elif self.calibration == self.calboxoptions[2]: calibrationOrder = None @@ -996,20 +1001,19 @@ class McaWindow(ScanWindow.ScanWindow): B = self.caldict[legend]['B'] C = self.caldict[legend]['C'] calibrationOrder = self.caldict[legend]['order'] - calib = [A,B,C] + calib = [A, B, C] elif 'McaCalib' in info: if type(info['McaCalib'][0]) == type([]): - calib = info['McaCalib'][info['McaDet']-1] + calib = info['McaCalib'][info['McaDet'] - 1] else: calib = info['McaCalib'] if len(calib) > 1: - xdata=calib[0]+ \ - calib[1]* xhelp + xdata = calib[0] + calib[1] * xhelp if len(calib) == 3: if calibrationOrder == 'TOF': - xdata = calib[2] + calib[0] / pow(xhelp-calib[1],2) + xdata = calib[2] + calib[0] / pow(xhelp - calib[1], 2) else: - xdata = xdata + calib[2]* xhelp * xhelp + xdata = xdata + calib[2] * xhelp * xhelp curveinfo['McaCalib'] = calib curveinfo['McaCalibOrder'] = calibrationOrder if simplefitplot: @@ -1019,61 +1023,60 @@ class McaWindow(ScanWindow.ScanWindow): inforegions.append([calib[2] + calib[0] / pow(region[0]-calib[1],2), calib[2] + calib[0] / pow(region[1]-calib[1],2)]) else: - inforegions.append([calib[0] + \ - calib[1] * region[0] +\ - calib[2] * region[0] * region[0], - calib[0] + \ - calib[1] * region[1] +\ - calib[2] * region[1] * region[1]]) + inforegions.append([calib[0] + + calib[1] * region[0] + + calib[2] * region[0] * region[0], + calib[0] + + calib[1] * region[1] + + calib[2] * region[1] * region[1]]) self.addCurve(xdata, data, - legend=legend, - info=curveinfo, - own=True) + legend=legend, + info=curveinfo, + own=True) else: self.addCurve(xdata, data, - legend=legend, - info=curveinfo, - own=True) + legend=legend, + info=curveinfo, + own=True) if calibrationOrder == 'ID18': self.setGraphXLabel('Time') else: self.setGraphXLabel('Energy') elif self.calibration == 'Fit': - print("Not yet implemented") + _logger.error("Not yet implemented") continue - elif self.calibration in self.caldict.keys(): + elif self.calibration in self.caldict.keys(): A = self.caldict[self.calibration]['A'] B = self.caldict[self.calibration]['B'] C = self.caldict[self.calibration]['C'] calibrationOrder = self.caldict[self.calibration]['order'] - calib = [A,B,C] + calib = [A, B, C] if calibrationOrder == 'TOF': - xdata = C + (A / ((xhelp - B) * (xhelp - B))) + xdata = C + (A / ((xhelp - B) * (xhelp - B))) else: - xdata=calib[0]+ \ - calib[1]* xhelp + \ - calib[2]* xhelp * xhelp + xdata = calib[0] + \ + calib[1] * xhelp + \ + calib[2] * xhelp * xhelp curveinfo['McaCalib'] = calib curveinfo['McaCalibOrder'] = calibrationOrder if simplefitplot: inforegions = [] for region in info['regions']: if calibrationOrder == 'TOF': - inforegions.append([calib[2] + calib[0] / pow(region[0]-calib[1],2), - calib[2] + calib[0] / pow(region[1]-calib[1],2)]) + inforegions.append( + [calib[2] + calib[0] / pow(region[0]-calib[1], 2), + calib[2] + calib[0] / pow(region[1]-calib[1], 2)]) else: - inforegions.append([calib[0] + \ - calib[1] * region[0] +\ - calib[2] * region[0] * region[0], - calib[0] + \ - calib[1] * region[1] +\ - calib[2] * region[1] * region[1]]) + inforegions.append([calib[0] + + calib[1] * region[0] + + calib[2] * region[0] * region[0], + calib[0] + + calib[1] * region[1] + + calib[2] * region[1] * region[1]]) self.addCurve(xdata, data, legend=legend, info=curveinfo, own=True) - #baseline = info['baseline'], - #regions = inforegions) else: self.addCurve(xdata, data, legend=legend, @@ -1089,8 +1092,6 @@ class McaWindow(ScanWindow.ScanWindow): legend=legend, info=curveinfo, own=True) - #baseline = info['baseline'], - #regions = info['regions']) else: self.addCurve(xhelp, data, legend=legend, @@ -1100,14 +1101,11 @@ class McaWindow(ScanWindow.ScanWindow): except: del self.dataObjectsDict[legend] raise - if replot: - #self.replot() + if resetzoom: self.resetZoom() - self.updateLegends() def _removeSelection(self, selectionlist): - if DEBUG: - print("_removeSelection(self, selectionlist)",selectionlist) + _logger.debug("_removeSelection(self, selectionlist) %d", selectionlist) if type(selectionlist) == type([]): sellist = selectionlist else: @@ -1115,35 +1113,29 @@ class McaWindow(ScanWindow.ScanWindow): legendlist = [] for sel in sellist: - key = sel['Key'] + key = sel['Key'] if "scanselection" in sel: if sel['scanselection'] not in [False, "MCA"]: continue - mcakeys = [key] + mcakeys = [key] for mca in mcakeys: legend = sel['legend'] legendlist.append(legend) - self.removeCurves(legendlist, replot=True) + self.removeCurves(legendlist) - def removeCurves(self, removelist, replot=True): + def removeCurves(self, removelist): for legend in removelist: - self.removeCurve(legend, replot=False) - if replot: - self.replot() + self.removeCurve(legend) - def removeCurve(self, legend, replot=True): - super(McaWindow, self).removeCurve(legend, replot=False) + def removeCurve(self, legend): + super(McaWindow, self).removeCurve(legend) if legend in self.dataObjectsDict.keys(): del self.dataObjectsDict[legend] - self.dataObjectsList = self._curveList - if replot: - self.replot() def _replaceSelection(self, selectionlist): - if DEBUG: - print("_replaceSelection(self, selectionlist)",selectionlist) + _logger.debug("_replaceSelection(self, selectionlist) %s", selectionlist) if type(selectionlist) == type([]): sellist = selectionlist else: @@ -1151,63 +1143,17 @@ class McaWindow(ScanWindow.ScanWindow): doit = False for sel in sellist: - if "scanselection" in sel: - if sel['scanselection'] not in [False, "MCA"]: - continue + if sel.get('scanselection', False) not in [False, "MCA"]: + continue doit = True break if not doit: return self.clearCurves() - self.dataObjectsDict={} - self.dataObjectsList=self._curveList + self.dataObjectsDict = {} self._addSelection(selectionlist) - def graphCallback(self, ddict): - if DEBUG: - print("McaWindow._graphCallback", ddict) - if ddict['event'] in ['markerMoved', 'markerSelected']: - return self._handleMarkerEvent(ddict) - elif ddict['event'] in ["mouseMoved", "MouseAt"]: - if self.calibration == self.calboxoptions[0]: - self._xPos.setText('%.2f' % ddict['x']) - self._yPos.setText('%.2f' % ddict['y']) - else: - self._xPos.setText('%.4f' % ddict['x']) - self._yPos.setText('%.2f' % ddict['y']) - elif ddict['event'] in ["curveClicked", "legendClicked"]: - legend = ddict.get('legend', None) - legend = ddict.get('label', legend) - if legend is None: - if len(self.dataObjectsList): - legend = self.dataObjectsList[0] - else: - return - self.setActiveCurve(legend) - elif ddict['event'] == "renameCurveEvent": - legend = ddict['legend'] - newlegend = ddict['newlegend'] - if legend in self.dataObjectsDict: - self.dataObjectsDict[newlegend]= copy.deepcopy(\ - self.dataObjectsDict[legend]) - self.dataObjectsDict[newlegend].info['legend'] = newlegend - self.removeCurve(legend) - self.addCurve(self.dataObjectsDict[newlegend].x[0], - self.dataObjectsDict[newlegend].y[0], - legend=newlegend, - info=self.dataObjectsDict[newlegend].info['legend'], - own=True, - replot=False) - if legend in self.caldict: - self.caldict[newlegend] = copy.deepcopy(self.caldict[legend]) - del self.dataObjectsDict[legend] - self.replot() - else: - super(McaWindow, self).graphCallback(ddict) - return - self.sigPlotSignal.emit(ddict) - - def setActiveCurve(self, legend=None): + def setActiveCurve(self, legend): if legend is None: legend = self.getActiveCurve(just_legend=True) if legend is None: @@ -1215,84 +1161,49 @@ class McaWindow(ScanWindow.ScanWindow): self.controlWidget.calinfo.BText.setText("?????") self.controlWidget.calinfo.CText.setText("?????") return - if legend in self.dataObjectsDict.keys(): - x0 = self.dataObjectsDict[legend].x[0] - y = self.dataObjectsDict[legend].y[0] - #those are the actual data - if str(self.getGraphXLabel()).upper() != "CHANNEL": - #I have to get the energy - A = self.controlWidget.calinfo.caldict['']['A'] - B = self.controlWidget.calinfo.caldict['']['B'] - C = self.controlWidget.calinfo.caldict['']['C'] - order = self.controlWidget.calinfo.caldict['']['order'] - else: - A = 0.0 - B = 1.0 - C = 0.0 - order = 1 - calib = [A,B,C] - if order == "TOF": - x = calib[2] + calib[0] / pow(x0-calib[1],2) - else: - x = calib[0]+ \ - calib[1]* x0 + \ - calib[2]* x0 * x0 - else: - print("Received legend = ", legend) - print("legends recognized = ", self.dataObjectsDict.keys()) - print("Should not be here") + # if legend in self.dataObjectsDict.keys(): # todo: unused block + # x0 = self.dataObjectsDict[legend].x[0] + # y = self.dataObjectsDict[legend].y[0] + # # those are the actual data + # if str(self.getGraphXLabel()).upper() != "CHANNEL": + # # I have to get the energy + # A = self.controlWidget.calinfo.caldict['']['A'] + # B = self.controlWidget.calinfo.caldict['']['B'] + # C = self.controlWidget.calinfo.caldict['']['C'] + # order = self.controlWidget.calinfo.caldict['']['order'] + # else: + # A = 0.0 + # B = 1.0 + # C = 0.0 + # order = 1 + # calib = [A, B, C] + # if order == "TOF": + # x = calib[2] + calib[0] / pow(x0-calib[1], 2) + # else: + # x = calib[0] + \ + # calib[1] * x0 + \ + # calib[2] * x0 * x0 + # else: + if legend not in self.dataObjectsDict.keys(): + _logger.error("Received legend = %s\nlegends recognized = %s\n" + "Should not be here", + legend, self.dataObjectsDict.keys()) return try: info = self.getCurve(legend)[3] calib = info['McaCalib'] - self.controlWidget.calinfo.setParameters({'A':calib[0], - 'B':calib[1], - 'C':calib[2]}) + self.controlWidget.calinfo.setParameters({'A': calib[0], + 'B': calib[1], + 'C': calib[2]}) except KeyError: self.controlWidget.calinfo.AText.setText("?????") self.controlWidget.calinfo.BText.setText("?????") self.controlWidget.calinfo.CText.setText("?????") xlabel = self.getGraphXLabel() ylabel = self.getGraphYLabel() - super(McaWindow, self).setActiveCurve(legend, replot=False) + super(McaWindow, self).setActiveCurve(legend) self.setGraphXLabel(xlabel) self.setGraphYLabel(ylabel) - self.replot() - - def _customFitSignalReceived(self, ddict): - if ddict['event'] == "FitFinished": - newDataObject = self.__customFitDataObject - - xplot = ddict['x'] - yplot = ddict['yfit'] - newDataObject.x = [xplot] - newDataObject.y = [yplot] - newDataObject.m = [numpy.ones(len(yplot)).astype(numpy.float)] - - #here I should check the log or linear status - self.dataObjectsDict[newDataObject.info['legend']] = newDataObject - self.addCurve(xplot, - yplot, - legend=newDataObject.info['legend'], - own=True) - - def _scanFitSignalReceived(self, ddict): - if DEBUG: - print("_graphSignalReceived", ddict) - if ddict['event'] == "EstimateFinished": - return - if ddict['event'] == "FitFinished": - newDataObject = self.__fitDataObject - - xplot = self.scanFit.specfit.xdata * 1.0 - yplot = self.scanFit.specfit.gendata(parameters=ddict['data']) - newDataObject.x = [xplot] - newDataObject.y = [yplot] - newDataObject.m = [numpy.ones(len(yplot)).astype(numpy.float)] - - self.dataObjectsDict[newDataObject.info['legend']] = newDataObject - self.addCurve(x=xplot, y=yplot, - legend=newDataObject.info['legend'], own=True) def _saveIconSignal(self): legend = self.getActiveCurve(just_legend=True) @@ -1303,7 +1214,7 @@ class McaWindow(ScanWindow.ScanWindow): msg.setWindowTitle('MCA window') msg.exec_() return - #get outputfile + # get outputfile self.outputDir = PyMcaDirs.outputDir if self.outputDir is None: self.outputDir = os.getcwd() @@ -1329,7 +1240,8 @@ class McaWindow(ScanWindow.ScanWindow): if self.outputFilter is None: self.outputFilter = format_list[0] - fileList, fileFilter = PyMcaFileDialogs.getFileList(self, + fileList, fileFilter = PyMcaFileDialogs.getFileList( + self, filetypelist=format_list, message="Output File Selection", currentdir=wdir, @@ -1341,34 +1253,33 @@ class McaWindow(ScanWindow.ScanWindow): return self.outputFilter = fileFilter filterused = self.outputFilter.split() - filetype = filterused[1] + filetype = filterused[1] extension = filterused[2] - outdir=qt.safe_str(fileList[0]) + outdir = qt.safe_str(fileList[0]) try: - self.outputDir = os.path.dirname(outdir) + self.outputDir = os.path.dirname(outdir) PyMcaDirs.outputDir = os.path.dirname(outdir) except: - self.outputDir = "." + self.outputDir = "." try: outputFile = os.path.basename(outdir) except: - outputFile = outdir + outputFile = outdir - #get active curve - x, y, legend, info = self.getActiveCurve() + # get active curve + x, y, legend, info, params = self.getActiveCurve() if info is None: return - ndict = {} - ndict[legend] = {'order':1,'A':0.0,'B':1.0,'C':0.0} + ndict = {legend: {'order': 1, 'A': 0.0, 'B': 1.0, 'C': 0.0}} if self.getGraphXLabel().upper() == "CHANNEL": if legend in self.caldict: calibrationOrder = self.caldict[legend].get('McaCalibOrder',2) ndict[legend].update(self.caldict[legend]) if abs(ndict[legend]['C']) > 0.0: - ndict[legend]['order'] = 2 + ndict[legend]['order'] = 2 elif 'McaCalib' in info: - calibrationOrder = info.get('McaCalibOrder',2) + calibrationOrder = info.get('McaCalibOrder', 2) if type(info['McaCalib'][0]) == type([]): calib = info['McaCalib'][0] else: @@ -1376,11 +1287,11 @@ class McaWindow(ScanWindow.ScanWindow): if len(calib) > 1: ndict[legend]['A'] = calib[0] ndict[legend]['B'] = calib[1] - if len(calib) >2: - ndict[legend]['order'] = 2 - ndict[legend]['C'] = calib[2] + if len(calib) > 2: + ndict[legend]['order'] = 2 + ndict[legend]['C'] = calib[2] elif legend in self.dataObjectsDict: - calibrationOrder = self.dataObjectsDict[legend].info.get('McaCalibOrder',2) + calibrationOrder = self.dataObjectsDict[legend].info.get('McaCalibOrder', 2) if 'McaCalib' in self.dataObjectsDict[legend].info: calib = self.dataObjectsDict[legend].info['McaCalib'] ndict[legend]['A'] = calib[0] @@ -1388,16 +1299,16 @@ class McaWindow(ScanWindow.ScanWindow): ndict[legend]['C'] = calib[2] calib = [ndict[legend]['A'], ndict[legend]['B'], ndict[legend]['C']] if calibrationOrder == 'TOF': - energy = calib[2] + calib[0] / pow(x - calib[1],2) + energy = calib[2] + calib[0] / pow(x - calib[1], 2) else: energy = calib[0] + calib[1] * x + calib[2] * x * x else: - #I have it in energy + # I have it in energy A = self.controlWidget.calinfo.caldict['']['A'] B = self.controlWidget.calinfo.caldict['']['B'] C = self.controlWidget.calinfo.caldict['']['C'] order = self.controlWidget.calinfo.caldict['']['order'] - ndict[legend] = {'order':order,'A':A,'B':B,'C':C} + ndict[legend] = {'order': order, 'A': A, 'B': B, 'C': C} calib = [A, B, C] energy = x * 1 if legend in self.dataObjectsDict.keys(): @@ -1409,9 +1320,9 @@ class McaWindow(ScanWindow.ScanWindow): if numpy.allclose(energy, x0): x = self.dataObjectsDict[legend].x[0] else: - ndict[legend] = {'order':1,'A': 0.0, 'B':1.0, 'C': 1.0} + ndict[legend] = {'order': 1, 'A': 0.0, 'B': 1.0, 'C': 1.0} - #always overwrite for the time being + # always overwrite for the time being if not outputFile.endswith(extension[1:]): outputFile += extension[1:] specFile = os.path.join(self.outputDir, outputFile) @@ -1428,9 +1339,13 @@ class McaWindow(ScanWindow.ScanWindow): os.linesep = '\n' if filterused[0].upper() == "WIDGET": fformat = specFile[-3:].upper() - pixmap = qt.QPixmap.grabWidget(self.getWidgetHandle()) + if hasattr(qt.QPixmap, "grabWidget"): + pixmap = qt.QPixmap.grabWidget(self.getWidgetHandle()) + else: + pixmap = self.getWidgetHandle().grab() if not pixmap.save(specFile, fformat): - qt.QMessageBox.critical(self, + qt.QMessageBox.critical( + self, "Save Error", "%s" % "I could not save the file\nwith the desired format") return @@ -1438,13 +1353,13 @@ class McaWindow(ScanWindow.ScanWindow): if MATPLOTLIB: try: if specFile[-3:].upper() in ['EPS', 'PNG', 'SVG']: - self.graphicsSave(specFile) + self._graphicsSave(plot=self, filename=specFile) return except: msg = qt.QMessageBox(self) msg.setIcon(qt.QMessageBox.Critical) msg.setWindowTitle("Save error") - msg.setInformativeText("Graphics Saving Error: %s" % \ + msg.setInformativeText("Graphics Saving Error: %s" % (sys.exc_info()[1])) msg.setDetailedText(traceback.format_exc()) msg.exec_() @@ -1461,18 +1376,16 @@ class McaWindow(ScanWindow.ScanWindow): msg.setText("Input Output Error: %s" % (sys.exc_info()[1])) msg.exec_() return - systemline = os.linesep - os.linesep = '\n' - #This was giving problems on legends with a leading b - #legend = legend.strip('<b>') - #legend = legend.strip('<\b>') + # This was giving problems on legends with a leading b + # legend = legend.strip('<b>') + # legend = legend.strip('<\b>') try: if filetype == 'Scan': ffile.write("#F %s\n" % specFile) - ffile.write("#D %s\n"%(time.ctime(time.time()))) + ffile.write("#D %s\n" % (time.ctime(time.time()))) ffile.write("\n") ffile.write("#S 1 %s\n" % legend) - ffile.write("#D %s\n"%(time.ctime(time.time()))) + ffile.write("#D %s\n" % (time.ctime(time.time()))) ffile.write("#N 3\n") ffile.write("#L channel counts energy\n") for i in range(len(y)): @@ -1480,7 +1393,7 @@ class McaWindow(ScanWindow.ScanWindow): ffile.write("\n") elif filetype == 'ASCII': for i in range(len(y)): - ffile.write("%.7g %.7g %.7g\n" % (x[i], y[i], energy[i])) + ffile.write("%.7g %.7g %.7g\n" % (x[i], y[i], energy[i])) elif filetype == 'CSV': if "," in filterused[0]: csv = "," @@ -1492,24 +1405,24 @@ class McaWindow(ScanWindow.ScanWindow): csv = "\t" if "OMNIC" in filterused[0]: for i in range(len(y)): - ffile.write("%.7E%s%.7E\n" % \ - (energy[i], csv, y[i])) + ffile.write("%.7E%s%.7E\n" % + (energy[i], csv, y[i])) else: ffile.write('"channel"%s"counts"%s"energy"\n' % (csv, csv)) for i in range(len(y)): - ffile.write("%.7E%s%.7E%s%.7E\n" % \ - (x[i], csv, y[i], csv, energy[i])) + ffile.write("%.7E%s%.7E%s%.7E\n" % + (x[i], csv, y[i], csv, energy[i])) else: ffile.write("#F %s\n" % specFile) - ffile.write("#D %s\n"%(time.ctime(time.time()))) + ffile.write("#D %s\n" % (time.ctime(time.time()))) ffile.write("\n") ffile.write("#S 1 %s\n" % legend) - ffile.write("#D %s\n"%(time.ctime(time.time()))) + ffile.write("#D %s\n" % (time.ctime(time.time()))) ffile.write("#@MCA %16C\n") - ffile.write("#@CHANN %d %d %d 1\n" % (len(y), x[0], x[-1])) + ffile.write("#@CHANN %d %d %d 1\n" % (len(y), x[0], x[-1])) ffile.write("#@CALIB %.7g %.7g %.7g\n" % (ndict[legend]['A'], - ndict[legend]['B'], - ndict[legend]['C'])) + ndict[legend]['B'], + ndict[legend]['C'])) ffile.write(self.array2SpecMca(y)) ffile.write("\n") ffile.close() @@ -1518,12 +1431,6 @@ class McaWindow(ScanWindow.ScanWindow): raise return - def _simpleOperation(self, operation): - if operation != "save": - return super(McaWindow, self)._simpleOperation(operation) - else: - return self._saveIconSignal() - def getCalibrations(self): return copy.deepcopy(self.caldict) @@ -1545,56 +1452,57 @@ class McaWindow(ScanWindow.ScanWindow): self.controlWidget.calbox.setCurrentIndex(item) self.refresh() - #The plugins interface + # The plugins interface def _toggleLogY(self): - if DEBUG: - print("McaWindow _toggleLogY") + _logger.debug("McaWindow _toggleLogY") + # ensure call to addCurve does not change dataObjectsDict self._ownSignal = True try: - super(McaWindow, self)._toggleLogY() + self.setYAxisLogarithmic(not self.isYAxisLogarithmic()) finally: self._ownSignal = None def _toggleLogX(self): - if DEBUG: - print("McaWindow _toggleLogX") + _logger.debug("McaWindow _toggleLogX") + # ensure call to addCurve does not change dataObjectsDict self._ownSignal = True try: - super(McaWindow, self)._toggleLogX() + self.setXAxisLogarithmic(not self.isXAxisLogarithmic()) finally: self._ownSignal = None def getGraphYLimits(self): - #if the active curve is mapped to second axis - #I should give the second axis limits + # if the active curve is mapped to second axis + # I should give the second axis limits return super(McaWindow, self).getGraphYLimits() - #end of plugins interface - def addCurve(self, x, y, legend=None, info=None, replace=False, replot=True, + # end of plugins interface + def addCurve(self, x, y, legend=None, info=None, replace=False, color=None, symbol=None, linestyle=None, xlabel=None, ylabel=None, yaxis=None, - xerror=None, yerror=None, own=None, **kw): - if legend in self._curveList: + xerror=None, yerror=None, own=None, + resetzoom=True, **kw): + if "replot" in kw: + _logger.warning("addCurve deprecated replot argument, " + "use resetzoom instead") + resetzoom = kw["replot"] and resetzoom + all_legends = self.getAllCurves(just_legend=True) + if legend in all_legends: if info is None: info = {} oldStuff = self.getCurve(legend) if oldStuff not in [[], None]: - oldX, oldY, oldLegend, oldInfo = oldStuff + oldX, oldY, oldLegend, oldInfo, oldParams = oldStuff else: oldInfo = {} if color is None: color = info.get("plot_color", oldInfo.get("plot_color", None)) if symbol is None: - symbol = info.get("plot_symbol",oldInfo.get("plot_symbol", None)) + symbol = info.get("plot_symbol", oldInfo.get("plot_symbol", None)) if linestyle is None: - if self._plotLines: - linestyle = info.get("plot_linestyle",oldInfo.get("plot_linestyle", None)) - if linestyle in [' ', None, '']: - linestyle = '-' - else: - linestyle = ' ' + linestyle = info.get("plot_linestyle", oldInfo.get("plot_linestyle", None)) if yaxis is None: - yaxis = info.get("plot_yaxis",oldInfo.get("plot_yaxis", None)) + yaxis = info.get("plot_yaxis", oldInfo.get("plot_yaxis", None)) if xlabel is None: xlabel = self.getGraphXLabel() if ylabel is None: @@ -1604,14 +1512,16 @@ class McaWindow(ScanWindow.ScanWindow): if own and (legend in self.dataObjectsDict): # The curve is already registered super(McaWindow, self).addCurve(x, y, legend=legend, info=info, - replace=replace, replot=replot, color=color, symbol=symbol, - linestyle=linestyle, xlabel=xlabel, ylabel=ylabel, yaxis=yaxis, - xerror=xerror, yerror=yerror, **kw) + replace=replace, resetzoom=resetzoom, + color=color, symbol=symbol, + linestyle=linestyle, xlabel=xlabel, + ylabel=ylabel, yaxis=yaxis, + xerror=xerror, yerror=yerror, **kw) else: if legend in self.dataObjectsDict: xChannels, yOrig, infoOrig = self.getDataAndInfoFromLegend(legend) calib = info.get('McaCalib', [0.0, 1.0, 0.0]) - calibrationOrder = info.get('McaCalibOrder',2) + calibrationOrder = info.get('McaCalibOrder', 2) if calibrationOrder == 'TOF': xFromChannels = calib[2] + calib[0] / pow(xChannels-calib[1], 2) else: @@ -1621,14 +1531,21 @@ class McaWindow(ScanWindow.ScanWindow): x = xChannels # create the data object (Is this necessary????) self.newCurve(x, y, legend=legend, info=info, - replace=replace, replot=replot, color=color, symbol=symbol, - linestyle=linestyle, xlabel=xlabel, ylabel=ylabel, yaxis=yaxis, - xerror=xerror, yerror=yerror, **kw) + replace=replace, color=color, symbol=symbol, resetzoom=resetzoom, + linestyle=linestyle, xlabel=xlabel, ylabel=ylabel, yaxis=yaxis, + xerror=xerror, yerror=yerror, **kw) + # activate first curve + if not all_legends: + self.setActiveCurve(legend) - def newCurve(self, x, y, legend=None, info=None, replace=False, replot=True, - color=None, symbol=None, linestyle=None, + def newCurve(self, x, y, legend=None, info=None, replace=False, + color=None, symbol=None, linestyle=None, resetzoom=True, xlabel=None, ylabel=None, yaxis=None, xerror=None, yerror=None, **kw): + if "replot" in kw: + _logger.warning("addCurve deprecated replot argument, " + "use resetzoom instead") + resetzoom = kw["replot"] and resetzoom if info is None: info = {} if legend is None: @@ -1655,23 +1572,22 @@ class McaWindow(ScanWindow.ScanWindow): newDataObject.info['Key'] = "" newDataObject.info['selectiontype'] = "1D" sel_list = [] - sel = {} - sel['SourceType'] = "Operation" - sel['SourceName'] = legend - sel['Key'] = legend - sel['legend'] = legend - sel['dataobject'] = newDataObject - sel['scanselection'] = False - sel['selectiontype'] = "1D" + sel = { + 'SourceType': "Operation", + 'SourceName': legend, + 'Key': legend, + 'legend': legend, + 'dataobject': newDataObject, + 'scanselection': False, + 'selectiontype': "1D"} sel_list.append(sel) if replace: self._replaceSelection(sel_list) else: - self._addSelection(sel_list, replot=replot) + self._addSelection(sel_list, resetzoom=resetzoom) def refresh(self): - if DEBUG: - print(" DANGEROUS REFRESH CALLED") + _logger.debug(" DANGEROUS REFRESH CALLED") activeCurve = self.getActiveCurve(just_legend=True) # try to keep the same curve order legendList = self.getAllCurves(just_legend=True) @@ -1679,52 +1595,29 @@ class McaWindow(ScanWindow.ScanWindow): sellist = [] for key in legendList: if key in dataObjectsKeyList: - sel ={} - sel['SourceName'] = self.dataObjectsDict[key].info['SourceName'] - sel['dataobject'] = self.dataObjectsDict[key] - sel['legend'] = key - sel['Key'] = self.dataObjectsDict[key].info['Key'] + sel = {'SourceName': self.dataObjectsDict[key].info['SourceName'], + 'dataobject': self.dataObjectsDict[key], + 'legend': key, + 'Key': self.dataObjectsDict[key].info['Key']} sellist.append(sel) for key in dataObjectsKeyList: if key not in legendList: - sel ={} - sel['SourceName'] = self.dataObjectsDict[key].info['SourceName'] - sel['dataobject'] = self.dataObjectsDict[key] - sel['legend'] = key - sel['Key'] = self.dataObjectsDict[key].info['Key'] + sel = {'SourceName': self.dataObjectsDict[key].info['SourceName'], + 'dataobject': self.dataObjectsDict[key], + 'legend': key, + 'Key': self.dataObjectsDict[key].info['Key']} sellist.append(sel) self.clearCurves() self._addSelection(sellist) if activeCurve is not None: self.setActiveCurve(activeCurve) - self.replot() - - def renameCurve(self, oldLegend, newLegend, replot=True): - xChannels, yOrig, infoOrig = self.getDataAndInfoFromLegend(oldLegend) - x, y, legend, info = self.getCurve(oldLegend)[:4] - calib = info.get('McaCalib', [0.0, 1.0, 0.0]) - calibrationOrder = info.get('McaCalibOrder',2) - if calibrationOrder == 'TOF': - xFromChannels = calib[2] + calib[0] / pow(xChannels-calib[1], 2) - else: - xFromChannels = calib[0] + \ - calib[1] * xChannels + calib[2] * xChannels * xChannels - if numpy.allclose(xFromChannels, x): - x = xChannels - newInfo = copy.deepcopy(info) - newInfo['legend'] = newLegend - newInfo['SourceName'] = newLegend - newInfo['Key'] = "" - newInfo['selectiontype'] = "1D" - # create the data object (Is this necessary????) - self.removeCurve(oldLegend, replot=False) - self.addCurve(x, y, legend=newLegend, info=newInfo, replot=replot) - self.updateLegends() + self.resetZoom() + def test(): w = McaWindow() x = numpy.arange(1000.) - y = 10 * x + 10000. * numpy.exp(-0.5*(x-500)*(x-500)/400) + y = 10 * x + 10000. * numpy.exp(-0.5*(x-500)*(x-500)/400) w.addCurve(x, y, legend="dummy", replot=True, replace=True) w.resetZoom() app.lastWindowClosed.connect(app.quit) diff --git a/PyMca5/PyMcaGui/pymca/Median2DBrowser.py b/PyMca5/PyMcaGui/pymca/Median2DBrowser.py index 021310a..583d053 100644 --- a/PyMca5/PyMcaGui/pymca/Median2DBrowser.py +++ b/PyMca5/PyMcaGui/pymca/Median2DBrowser.py @@ -28,15 +28,17 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import numpy -DEBUG = 0 +import logging +_logger = logging.getLogger(__name__) + try: from PyMca5.PyMcaGui.pymca import StackBrowser from PyMca5.PyMcaMath.PyMcaSciPy.signal import median except ImportError: - if DEBUG: - import traceback - print("Median2DBrowser problem!") - print(traceback.format_exc()) + _logger.warning("Median2DBrowser problem!") + import traceback + print(traceback.format_exc()) + medfilt2d = median.medfilt2d qt = StackBrowser.qt diff --git a/PyMca5/PyMcaGui/pymca/PyMcaBatch.py b/PyMca5/PyMcaGui/pymca/PyMcaBatch.py index 6df8d78..984d29b 100644 --- a/PyMca5/PyMcaGui/pymca/PyMcaBatch.py +++ b/PyMca5/PyMcaGui/pymca/PyMcaBatch.py @@ -32,6 +32,7 @@ import sys import os import time import subprocess +import logging from PyMca5.PyMcaGui import PyMcaQt as qt @@ -57,7 +58,8 @@ from PyMca5.PyMcaCore import PyMcaDirs from PyMca5.PyMcaCore import PyMcaBatchBuildOutput ROIWIDTH = 100. -DEBUG = 0 +_logger = logging.getLogger(__name__) + class McaBatchGUI(qt.QWidget): def __init__(self,parent=None,name="PyMca batch fitting",fl=None, @@ -192,8 +194,7 @@ class McaBatchGUI(qt.QWidget): if QTVERSION < '4.0.0': palette.setDisabled(palette.active()) else: - if DEBUG: - print("palette set disabled") + _logger.debug("palette set disabled") self.__imgBox.setChecked(True) self.__imgBox.setEnabled(False) vbox2.l.addWidget(self.__imgBox) @@ -786,9 +787,8 @@ class McaBatchGUI(qt.QWidget): allowSingleFileSplitProcesses = False if HDF5SUPPORT: if h5py.is_hdf5(self.fileList[0]): - if DEBUG: - print("Disallow single HDF5 process split") - print("due to problems with concurrent access") + _logger.debug("Disallow single HDF5 process split") + _logger.debug("due to problems with concurrent access") #allowSingleFileSplitProcesses = True allowSingleFileSplitProcesses = False if not allowSingleFileSplitProcesses: @@ -1007,8 +1007,7 @@ class McaBatchGUI(qt.QWidget): self.hide() qApp = qt.QApplication.instance() qApp.processEvents() - if DEBUG: - print("cmd = %s" % cmd) + _logger.debug("cmd = %s", cmd) if self.__splitBox.isChecked(): nbatches = int(qt.safe_str(self.__splitSpin.text())) if len(self.fileList) > 1: @@ -1028,8 +1027,7 @@ class McaBatchGUI(qt.QWidget): subprocess.Popen(cmd1.encode(sys.getfilesystemencoding()), cwd=os.getcwd())) - if DEBUG: - print("cmd = %s" % cmd1) + _logger.debug("cmd = %s", cmd1) else: #f = open("CMD", 'wb') processList = [] @@ -1037,8 +1035,7 @@ class McaBatchGUI(qt.QWidget): #the mcastep has been dealt with above cmd1 = cmd + " --mcaoffset=%d --chunk=%d" % (i, i) processList.append(subprocess.Popen(cmd1, cwd=os.getcwd())) - if DEBUG: - print("CMD = %s" % cmd1) + _logger.debug("CMD = %s", cmd1) #f.write(cmd1+"\n") #f.close() self._processList = processList @@ -1048,7 +1045,7 @@ class McaBatchGUI(qt.QWidget): if not self._timer.isActive(): self._timer.start(1000) else: - print("timer was already active") + _logger.info("timer was already active") return else: try: @@ -1135,8 +1132,7 @@ class McaBatchGUI(qt.QWidget): self.outputDir, overwrite, filestep, mcastep, html, htmlindex, listfile, concentrations, table, fitfiles, selectionFlag) - if DEBUG: - print("cmd = %s" % cmd) + _logger.debug("cmd = %s", cmd) if self.__splitBox.isChecked(): qApp = qt.QApplication.instance() qApp.processEvents() @@ -1168,11 +1164,11 @@ class McaBatchGUI(qt.QWidget): if not self._timer.isActive(): self._timer.start(1000) else: - print("timer was already active") + _logger.info("timer was already active") return else: os.system(cmd) - print(" COMMAND = ", cmd) + _logger.info(" COMMAND = %s", cmd) msg = qt.QMessageBox(self) msg.setIcon(qt.QMessageBox.Information) text = "Your batch has been started as an independent process." @@ -1184,7 +1180,7 @@ class McaBatchGUI(qt.QWidget): try: os.remove(listfile) except: - print("Cannot delete file %s" % listfile) + _logger.error("Cannot delete file %s", listfile) raise if config is None: lst = self.fileList @@ -1224,7 +1220,7 @@ class McaBatchGUI(qt.QWidget): self.raise_() work = PyMcaBatchBuildOutput.PyMcaBatchBuildOutput(os.path.join(self.outputDir, "IMAGES")) - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: a, b, c = work.buildOutput(delete=False) else: a, b, c = work.buildOutput(delete=True) @@ -1245,7 +1241,7 @@ class McaBatchGUI(qt.QWidget): else: os.system("%s %s &" % (rgb, b[0])) work = PyMcaBatchBuildOutput.PyMcaBatchBuildOutput(self.outputDir) - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: work.buildOutput(delete=False) else: work.buildOutput(delete=True) @@ -1301,8 +1297,7 @@ class McaBatch(McaAdvancedFitBatch.McaAdvancedFitBatch, qt.QThread): qt.QApplication.postEvent(self.parent, McaCustomEvent.McaCustomEvent(ddict)) def onMca(self, mca, nmca, filename=None, key=None, info=None): - if DEBUG: - print("onMca key = %s" % key) + _logger.debug("onMca key = %s", key) ddict = {'mca':mca, 'nmca':nmca, 'mcastep':self.mcaStep, @@ -1322,8 +1317,7 @@ class McaBatch(McaAdvancedFitBatch.McaAdvancedFitBatch, qt.QThread): if self.pleasePause:self.__pauseMethod() def onEnd(self): - if DEBUG: - print("onEnd") + _logger.debug("onEnd") ddict = {'event':'onEnd', 'filestep':self.fileStep, @@ -1454,12 +1448,11 @@ class McaBatchWindow(qt.QWidget): elif event.dict['event'] == 'reportWritten':self.onReportWritten() else: - print("Unhandled event %s" % event) + _logger.warning("Unhandled event %s", event) def onNewFile(self, file, filelist, filestep, filebeginoffset =0, fileendoffset = 0): - if DEBUG: - print("onNewFile: %s" % file) + _logger.debug("onNewFile: %s", file) indexlist = list(range(0,len(filelist),filestep)) index = indexlist.index(filelist.index(file)) - filebeginoffset #print index + filebeginoffset @@ -1474,7 +1467,7 @@ class McaBatchWindow(qt.QWidget): try: os.remove(self.htmlindex) except: - print("cannot delete file %s" % self.htmlindex) + _logger.warning("cannot delete file %s", self.htmlindex) nfiles = len(indexlist)-filebeginoffset-fileendoffset self.status.setText("Processing file %s" % file) e = time.time() @@ -1497,9 +1490,8 @@ class McaBatchWindow(qt.QWidget): qApp = qt.QApplication.instance() qApp.processEvents() - def onImage(self,key,keylist): - if DEBUG: - print("onImage %s" % key) + def onImage(self, key, keylist): + _logger.debug("onImage %s", key) i = keylist.index(key) + 1 n = len(keylist) if QTVERSION < '4.0.0': @@ -1516,8 +1508,7 @@ class McaBatchWindow(qt.QWidget): #def onMca(self, mca, nmca, mcastep): def onMca(self, ddict): - if DEBUG: - print("onMca ", ddict['mca']) + _logger.debug("onMca %s", ddict['mca']) mca = ddict['mca'] nmca = ddict['nmca'] mcastep = ddict['mcastep'] @@ -1537,12 +1528,12 @@ class McaBatchWindow(qt.QWidget): self.__htmlReport(filename, key, outputdir, useExistingFiles, info, firstmca = False) except: - print("ERROR on REPORT",sys.exc_info()) - print(sys.exc_info()[1]) - print("filename = %s key =%s " % (filename, key)) - print("If your batch is stopped, please report this") - print("error sending the above mentioned file and the") - print("associated fit configuration file.") + _logger.warning("ERROR on REPORT %s", sys.exc_info()) + _logger.warning("%s", sys.exc_info()[1]) + _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") + _logger.warning("associated fit configuration file.") if QTVERSION < '4.0.0': self.mcaBar.setTotalSteps(nmca) self.mcaBar.setProgress(mca) @@ -1566,20 +1557,20 @@ class McaBatchWindow(qt.QWidget): try: os.mkdir(fitdir) except: - print("I could not create directory %s" % fitdir) + _logger.warning("I could not create directory %s", fitdir) return - fitdir = os.path.join(fitdir,filename+"_HTMLDIR") + fitdir = os.path.join(fitdir, filename+"_HTMLDIR") if not os.path.exists(fitdir): try: os.mkdir(fitdir) except: - print("I could not create directory %s" % fitdir) + _logger.warning("I could not create directory %s", fitdir) return localindex = os.path.join(fitdir, "index.html") if not os.path.isdir(fitdir): - print("%s does not seem to be a valid directory" % fitdir) + _logger.warning("%s does not seem to be a valid directory", fitdir) else: - outfile = filename +"_"+key+".html" + outfile = filename + "_" + key + ".html" outfile = os.path.join(fitdir, outfile) useExistingResult = useExistingFiles if os.path.exists(outfile): @@ -1587,7 +1578,7 @@ class McaBatchWindow(qt.QWidget): try: os.remove(outfile) except: - print("cannot delete file %s" % outfile) + _logger.warning("cannot delete file %s", outfile) useExistingResult = 0 else: useExistingResult = 0 @@ -1596,7 +1587,7 @@ class McaBatchWindow(qt.QWidget): fitdir = os.path.join(fitdir,filename+"_FITDIR") fitfile= os.path.join(fitdir, filename +"_"+key+".fit") if not os.path.exists(fitfile): - print("fit file %s does not exists!" % fitfile) + _logger.warning("fit file %s does not exists!", fitfile) return if self.report is None: #first file @@ -1704,8 +1695,7 @@ class McaBatchWindow(qt.QWidget): if sys.executable in ["PyMcaMain", "PyMcaMain.exe", "PyMcaBatch", "PyMcaBatch.exe"]: frozen = True - if DEBUG: - print("final dirname = %s" % dirname) + _logger.debug("final dirname = %s", dirname) if frozen: # we are at level PyMca5\PyMcaGui\pymca dirname = os.path.dirname(dirname) @@ -1718,19 +1708,20 @@ class McaBatchWindow(qt.QWidget): else: myself = sys.executable+" "+os.path.join(dirname, "EdfFileSimpleViewer.py") cmd = "%s %s &" % (myself, filelist) - if DEBUG: - print("cmd = %s" % cmd) + _logger.debug("cmd = %s", cmd) os.system(cmd) def main(): sys.excepthook = qt.exceptionHandler import getopt + from PyMca5.PyMcaCore.LoggingLevel import getLoggingLevel options = 'f' longoptions = ['cfg=','outdir=','roifit=','roi=','roiwidth=', 'overwrite=', 'filestep=', 'mcastep=', 'html=','htmlindex=', 'listfile=','cfglistfile=', 'concentrations=', 'table=', 'fitfiles=', 'filebeginoffset=','fileendoffset=','mcaoffset=', 'chunk=', - 'nativefiledialogs=','selection=', 'exitonend='] + 'nativefiledialogs=','selection=', 'exitonend=', + 'logging=', 'debug='] filelist = None outdir = None cfg = None @@ -1807,6 +1798,8 @@ def main(): elif opt in ('--exitonend'): exitonend = int(arg) + logging.basicConfig(level=getLoggingLevel(opts)) + if listfile is None: filelist=[] for item in args: @@ -1859,8 +1852,8 @@ def main(): mcaoffset=mcaoffset, chunk=chunk, selection=selection) except: if exitonend: - print("Error: " % sys.exc_info()[1]) - print("Quitting as requested") + _logger.warning("Error: ", sys.exc_info()[1]) + _logger.warning("Quitting as requested") qt.QApplication.instance().quit() else: msg = qt.QMessageBox() diff --git a/PyMca5/PyMcaGui/pymca/PyMcaGLWindow.py b/PyMca5/PyMcaGui/pymca/PyMcaGLWindow.py index 100a221..76c8fd0 100644 --- a/PyMca5/PyMcaGui/pymca/PyMcaGLWindow.py +++ b/PyMca5/PyMcaGui/pymca/PyMcaGLWindow.py @@ -28,20 +28,20 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import numpy -DEBUG = 0 +import logging +_logger = logging.getLogger(__name__) + try: from PyMca5 import Object3D from PyMca5.Object3D import Object3DScene except ImportError: - if DEBUG: - print("PyMcaGLWindow imports Object3D direcly. Frozen version?") + _logger.debug("PyMcaGLWindow imports Object3D direcly. Frozen version?") import Object3D from Object3D import Object3DScene class SceneGLWindow(Object3D.Object3DScene.Object3DScene): def _addSelection(self, selectionlist, replot=True): - if DEBUG: - print("addSelection(self, selectionlist)",selectionlist) + _logger.debug("addSelection(self, selectionlist=%s)", selectionlist) if type(selectionlist) == type([]): sellist = selectionlist else: @@ -68,8 +68,7 @@ class SceneGLWindow(Object3D.Object3DScene.Object3DScene): else: numberOfXAxes = len(dataObject.x) if numberOfXAxes > 1: - if DEBUG: - print("Mesh plots") + _logger.debug("Mesh plots") else: xdata = dataObject.x[0] @@ -93,8 +92,7 @@ class SceneGLWindow(Object3D.Object3DScene.Object3DScene): def _removeSelection(self, selectionlist): - if DEBUG: - print("_removeSelection(self, selectionlist)",selectionlist) + _logger.debug("_removeSelection(self, selectionlist=%s)", selectionlist) if type(selectionlist) == type([]): sellist = selectionlist else: @@ -117,8 +115,7 @@ class SceneGLWindow(Object3D.Object3DScene.Object3DScene): def _replaceSelection(self, selectionlist): - if DEBUG: - print("_replaceSelection(self, selectionlist)",selectionlist) + _logger.debug("_replaceSelection(self, selectionlist=%s)", selectionlist) if type(selectionlist) == type([]): sellist = selectionlist else: @@ -165,8 +162,7 @@ class SceneGLWindow(Object3D.Object3DScene.Object3DScene): #or not if ndim == ndata: if len(data.shape) == 3: - if DEBUG: - print("CASE 1") + _logger.debug("CASE 1") if (xDimList[0] != data.shape[0]) or\ (xDimList[1] != data.shape[1]) or\ (xDimList[2] != data.shape[2]): @@ -185,8 +181,7 @@ class SceneGLWindow(Object3D.Object3DScene.Object3DScene): legend=legend, update_scene=update_scene) elif len(data.shape) == 2: - if DEBUG: - print("CASE 2") + _logger.debug("CASE 2") object3D = self.mesh(data, x=dataObject.x[0], y=dataObject.x[1], @@ -195,8 +190,7 @@ class SceneGLWindow(Object3D.Object3DScene.Object3DScene): legend=legend, update_scene=update_scene) elif len(data.shape) == 1: - if DEBUG: - print("CASE 3") + _logger.debug("CASE 3") object3D = self.mesh(data, x=dataObject.x[0], y=numpy.zeros((1,1), numpy.float32), @@ -205,9 +199,8 @@ class SceneGLWindow(Object3D.Object3DScene.Object3DScene): update_scene=update_scene) return object3D elif (len(data.shape) == 3) and (len(xDimList) == 2): - print("WARNING Assuming last dimension") - if DEBUG: - print("CASE 1.1") + _logger.warning("Assuming last dimension") + _logger.debug("CASE 1.1") if (xDimList[0] != data.shape[0]) or\ (xDimList[1] != data.shape[1]): text = "Wrong dimensions:" @@ -247,8 +240,7 @@ class SceneGLWindow(Object3D.Object3DScene.Object3DScene): #I force a surface plot. if ndata < 200000: cfg = object3D.setConfiguration({'common':{'mode':3}}) - if DEBUG: - print("DEFAULT CASE") + _logger.debug("DEFAULT CASE") object3D.setData(values, xyz=xyzData) self.addObject(object3D, legend, update_scene=update_scene) return object3D diff --git a/PyMca5/PyMcaGui/pymca/PyMcaHKLImageWindow.py b/PyMca5/PyMcaGui/pymca/PyMcaHKLImageWindow.py index 2ad4aac..651790c 100644 --- a/PyMca5/PyMcaGui/pymca/PyMcaHKLImageWindow.py +++ b/PyMca5/PyMcaGui/pymca/PyMcaHKLImageWindow.py @@ -29,12 +29,14 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import numpy +import logging from . import PyMcaImageWindow from PyMca5.PyMcaPhysics import SixCircle arctan = numpy.arctan -DEBUG = 0 +_logger = logging.getLogger(__name__) + class PyMcaHKLImageWindow(PyMcaImageWindow.PyMcaImageWindow): def __init__(self, *var, **kw): @@ -276,8 +278,7 @@ class PyMcaHKLImageWindow(PyMcaImageWindow.PyMcaImageWindow): ddict['lambda'] = float(info[key]) continue - if DEBUG: - for key in ddict.keys(): - print(key, ddict[key]) + for key in ddict.keys(): + _logger.debug("%s: %s", key, ddict[key]) return ddict diff --git a/PyMca5/PyMcaGui/pymca/PyMcaImageWindow.py b/PyMca5/PyMcaGui/pymca/PyMcaImageWindow.py index 67418c4..ee6535f 100644 --- a/PyMca5/PyMcaGui/pymca/PyMcaImageWindow.py +++ b/PyMca5/PyMcaGui/pymca/PyMcaImageWindow.py @@ -29,8 +29,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import numpy -import time -from PyMca5.PyMcaGui import IconDict +import logging from . import RGBImageCalculator qt = RGBImageCalculator.qt QTVERSION = qt.qVersion() @@ -38,7 +37,8 @@ from . import RGBCorrelator from PyMca5.PyMcaGui import FrameBrowser USE_BROWSER = True -DEBUG = 0 +_logger = logging.getLogger(__name__) + class PyMcaImageWindow(RGBImageCalculator.RGBImageCalculator): def __init__(self, parent = None, @@ -99,8 +99,7 @@ class PyMcaImageWindow(RGBImageCalculator.RGBImageCalculator): w.sigReplaceSelection.connect(self._replaceSelection) def _addSelection(self, selectionlist): - if DEBUG: - print("_addSelection(self, selectionlist)",selectionlist) + _logger.debug("_addSelection(self, selectionlist=%s)", selectionlist) if type(selectionlist) == type([]): sellist = selectionlist else: @@ -164,7 +163,7 @@ class PyMcaImageWindow(RGBImageCalculator.RGBImageCalculator): tmpData.shape = int(nRows), int(nColumns) dataObject.data[yIndex] = tmpData else: - print("Nothing to plot") + _logger.info("Nothing to plot") elif hasattr(dataObject, "x") and (dataObject.x is not None): shape = dataObject.data.shape if len(dataObject.x) == 2: @@ -176,7 +175,7 @@ class PyMcaImageWindow(RGBImageCalculator.RGBImageCalculator): nColumns = numpy.argmin(abs(x0-x0[0]) < 1.0e-6) nRows = x1.size / nColumns if nRows!= int(nRows): - print("%f != %d" % (nRows, int(nRows))) + _logger.warning("%f != %d", nRows, int(nRows)) raise ValueError("2D Selection not understood") transpose = False nColumns = int(nColumns) @@ -186,7 +185,7 @@ class PyMcaImageWindow(RGBImageCalculator.RGBImageCalculator): nRows = numpy.argmin(abs(x1-x1[0]) < 1.0e-6) nColumns = x0.size / nRows if nColumns != int(nColumns): - print("%f != %d" % (nColumns, int(nColumns))) + _logger.warning("%f != %d", nColumns, int(nColumns)) raise ValueError("2D Selection not understood") transpose = True nRows = int(nRows) @@ -299,8 +298,7 @@ class PyMcaImageWindow(RGBImageCalculator.RGBImageCalculator): pass def _removeSelection(self, selectionlist): - if DEBUG: - print("_removeSelection(self, selectionlist)",selectionlist) + _logger.debug("_removeSelection(self, selectionlist=%s)", selectionlist) if type(selectionlist) == type([]): sellist = selectionlist else: @@ -316,8 +314,8 @@ class PyMcaImageWindow(RGBImageCalculator.RGBImageCalculator): #self.plotImage(True) def _replaceSelection(self, selectionlist): - if DEBUG: - print("_replaceSelection(self, selectionlist)",selectionlist) + _logger.debug("_replaceSelection(self, selectionlist=%s)", + selectionlist) current = self.slider.value() self._addSelection(selectionlist) if current < self._nImages: @@ -362,7 +360,7 @@ class TimerLoop: self.__timer.start(period) def test(self): - print("Test function called") + _logger.info("Test function called") if __name__ == "__main__": from PyMca5 import DataObject @@ -378,8 +376,7 @@ if __name__ == "__main__": def buildSelection(dataObject, name = "image_data0"): key = dataObject.info['Key'] def dataObjectDestroyed(ref, dataObjectKey=key): - if DEBUG: - print("dataObject distroyed key = %s" % key) + _logger.debug("dataObject distroyed key = %s", key) dataObjectRef=weakref.proxy(dataObject, dataObjectDestroyed) selection = {} selection['SourceType'] = 'SPS' diff --git a/PyMca5/PyMcaGui/pymca/PyMcaMain.py b/PyMca5/PyMcaGui/pymca/PyMcaMain.py index 991a8b1..02fa20e 100644 --- a/PyMca5/PyMcaGui/pymca/PyMcaMain.py +++ b/PyMca5/PyMcaGui/pymca/PyMcaMain.py @@ -30,11 +30,12 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys, getopt import traceback +import logging if sys.platform == 'win32': import ctypes from ctypes.wintypes import MAX_PATH nativeFileDialogs = None -DEBUG = 0 +_logger = logging.getLogger(__name__) backend=None if __name__ == '__main__': options = '-f' @@ -45,14 +46,15 @@ if __name__ == '__main__': 'backend=', 'nativefiledialogs=', 'PySide=', - 'binding='] + 'binding=', + 'logging='] try: opts, args = getopt.getopt( sys.argv[1:], options, longoptions) except getopt.error: - print(sys.exc_info()[1]) + print("%s" % sys.exc_info()[1]) sys.exit(1) keywords={} @@ -65,8 +67,10 @@ if __name__ == '__main__': elif opt in ('--shm'): keywords['shm'] = arg elif opt in ('--debug'): - debugreport = 1 - DEBUG = 1 + if arg.lower() not in ['0', 'false']: + debugreport = 1 + _logger.setLevel(logging.DEBUG) + # --debug is also parsed later for the global logging level elif opt in ('-f'): keywords['fresh'] = 1 elif opt in ('--qt'): @@ -89,8 +93,13 @@ if __name__ == '__main__': if sys.version_info < (3,): try: import sip - sip.setapi("QString", 2) - sip.setapi("QVariant", 2) + sip.setapi('QString', 2) + sip.setapi('QVariant', 2) + sip.setapi('QDate', 2) + sip.setapi('QDateTime', 2) + sip.setapi('QTextStream', 2) + sip.setapi('QTime', 2) + sip.setapi('QUrl', 2) except: print("Cannot set sip API") import PyQt4.QtCore @@ -115,6 +124,9 @@ if __name__ == '__main__': elif qtversion == '5': import PyQt5.QtCore + from PyMca5.PyMcaCore.LoggingLevel import getLoggingLevel + logging.basicConfig(level=getLoggingLevel(opts)) + from PyMca5.PyMcaGui import PyMcaQt as qt QTVERSION = qt.qVersion() if sys.platform == 'darwin': @@ -212,7 +224,6 @@ if __name__ == "__main__": qApp = qt.QApplication.instance() qApp.processEvents() -from PyMca5.PyMcaGraph.Plot import Plot from PyMca5.PyMcaGui.pymca import ScanWindow from PyMca5.PyMcaGui.pymca import McaWindow @@ -294,7 +305,7 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): self.sourceWidget.sourceSelector._openFileSlot) self.openMenu.addAction("Load Training Data", self.loadTrainingData) - + self.trainingDataMenu = None self.__useTabWidget = True @@ -308,13 +319,14 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): self.mdi.addWindow(self.scanWindow) else: if backend is not None: - Plot.defaultBackend = backend + import silx + silx.config.DEFAULT_PLOT_BACKEND = backend self.mainTabWidget = qt.QTabWidget(self.mdi) self.mainTabWidget.setWindowTitle("Main Window") self.mcaWindow = McaWindow.McaWindow(backend=backend) self.scanWindow = ScanWindow.ScanWindow(info=True, backend=backend) - self.scanWindow._togglePointsSignal() + self.scanWindow.getCurveStyleAction().trigger() if OBJECT3D: self.glWindow = SceneGLWindow.SceneGLWindow() self.mainTabWidget.addTab(self.mcaWindow, "MCA") @@ -380,7 +392,7 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): self._startupSelection(source=kw['spec'], selection=None) - def connectDispatcher(self, viewer, dispatcher = None): + def connectDispatcher(self, viewer, dispatcher=None): #I could connect sourceWidget to myself and then #pass the selections to the active window!! #That will be made in a next iteration I guess @@ -447,22 +459,22 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): if self.mainTabWidget.isHidden(): #make sure it is visible in case of being closed self.mainTabWidget.show() - if DEBUG: + + try: self._dispatcherAddSelectionSlot(ddict) - else: - try: - self._dispatcherAddSelectionSlot(ddict) - except: - msg = qt.QMessageBox(self) - msg.setIcon(qt.QMessageBox.Critical) - msg.setText("Error: %s" % sys.exc_info()[1]) - msg.setInformativeText(str(sys.exc_info()[1])) - msg.setDetailedText(traceback.format_exc()) - msg.exec_() + except: + if _logger.getEffectiveLevel() == logging.DEBUG: + raise + msg = qt.QMessageBox(self) + msg.setIcon(qt.QMessageBox.Critical) + msg.setText("Error: %s" % sys.exc_info()[1]) + msg.setInformativeText(str(sys.exc_info()[1])) + msg.setDetailedText(traceback.format_exc()) + msg.exec_() def _dispatcherAddSelectionSlot(self, dictOrList): - if DEBUG: - print("self._dispatcherAddSelectionSlot(ddict), ddict = ", dictOrList) + _logger.debug("self._dispatcherAddSelectionSlot(ddict), ddict = %s", + dictOrList) if type(dictOrList) == type([]): ddict = dictOrList[0] else: @@ -470,8 +482,7 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): toadd = False if self._is2DSelection(ddict): - if DEBUG: - print("2D selection") + _logger.debug("2D selection") if self.imageWindowCorrelator is None: self.imageWindowCorrelator = RGBCorrelator.RGBCorrelator() #toadd = True @@ -493,13 +504,15 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): except: pass if hkl: - imageWindow = PyMcaHKLImageWindow.PyMcaHKLImageWindow(name = legend, - correlator = self.imageWindowCorrelator, - scanwindow=self.scanWindow) + imageWindow = PyMcaHKLImageWindow.PyMcaHKLImageWindow( + name=legend, + correlator=self.imageWindowCorrelator, + scanwindow=self.scanWindow) else: - imageWindow = PyMcaImageWindow.PyMcaImageWindow(name = legend, - correlator = self.imageWindowCorrelator, - scanwindow=self.scanWindow) + imageWindow = PyMcaImageWindow.PyMcaImageWindow( + name=legend, + correlator=self.imageWindowCorrelator, + scanwindow=self.scanWindow) self.imageWindowDict[legend] = imageWindow imageWindow.sigAddImageClicked.connect( \ @@ -527,8 +540,7 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): else: self.imageWindowDict[legend]._addSelection(ddict) elif self._isStackSelection(ddict): - if DEBUG: - print("Stack selection") + _logger.debug("Stack selection") legend = ddict['legend'] widget = QStackWidget.QStackWidget() widget.notifyCloseEventToWidget(self) @@ -539,18 +551,15 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): else: if OBJECT3D: if ddict['dataobject'].info['selectiontype'] == "1D": - if DEBUG: - print("1D selection") + _logger.debug("1D selection") self.mcaWindow._addSelection(dictOrList) self.scanWindow._addSelection(dictOrList) else: - if DEBUG: - print("3D selection") + _logger.debug("3D selection") self.mainTabWidget.setCurrentWidget(self.glWindow) self.glWindow._addSelection(dictOrList) else: - if DEBUG: - print("1D selection") + _logger.debug("1D selection") self.mcaWindow._addSelection(dictOrList) self.scanWindow._addSelection(dictOrList) @@ -565,8 +574,7 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): def _dispatcherRemoveSelectionSlot(self, dictOrList): - if DEBUG: - print("self.dispatcherRemoveSelectionSlot(ddict), ddict = ", dictOrList) + _logger.debug("self.dispatcherRemoveSelectionSlot(ddict), ddict = %s", dictOrList) if type(dictOrList) == type([]): ddict = dictOrList[0] else: @@ -597,8 +605,8 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): msg.exec_() def _dispatcherReplaceSelectionSlot(self, dictOrList): - if DEBUG: - print("self.dispatcherReplaceSelectionSlot(ddict), ddict = ", dictOrList) + _logger.debug("self.dispatcherReplaceSelectionSlot(ddict), ddict = %s", + dictOrList) if type(dictOrList) == type([]): ddict = dictOrList[0] else: @@ -632,8 +640,8 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): self.scanWindow._replaceSelection(dictOrList) def dispatcherOtherSignalsSlot(self, dictOrList): - if DEBUG: - print("self.dispatcherOtherSignalsSlot(ddict), ddict = ",dictOrList) + _logger.debug("self.dispatcherOtherSignalsSlot(ddict), ddict = %s", + dictOrList) if type(dictOrList) == type([]): ddict = dictOrList[0] else: @@ -652,8 +660,7 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): if ddict['event'] == "SourceTypeChanged": pass return - if DEBUG: - print("Unhandled dict") + _logger.debug("Unhandled dict") def setConfig(self, configDict): if 'PyMca' in configDict: @@ -729,12 +736,8 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): d["PyMca"]["McaWindow"]["calibrations"] = self.mcaWindow.getCalibrations() #ROIs - d['ROI']={} - if self.mcaWindow.roiWidget is None: - roilist = [] - roidict = {} - else: - roilist, roidict = self.mcaWindow.roiWidget.getROIListAndDict() + d['ROI'] = {} + roilist, roidict = self.mcaWindow.getCurvesRoiDockWidget().roiWidget.getROIListAndDict() d['ROI']['roilist'] = roilist d['ROI']['roidict'] = {} d['ROI']['roidict'].update(roidict) @@ -763,15 +766,14 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): #ScanFit related d['ScanSimpleFit'] = {} d['ScanSimpleFit']['Configuration'] = {} - if DEBUG: - d['ScanSimpleFit']['Configuration'].update(\ - self.scanWindow.scanFit.getConfiguration()) - else: - try: - d['ScanSimpleFit']['Configuration'].update(\ - self.scanWindow.scanFit.getConfiguration()) - except: - print("Error getting ScanFint configuration") + + try: + d['ScanSimpleFit']['Configuration'].update( + self.scanWindow.scanFit.getConfiguration()) + except: + if _logger.getEffectiveLevel() == logging.DEBUG: + raise + _logger.warning("Error getting ScanFit configuration") return d def saveConfig(self, config, filename = None): @@ -792,6 +794,7 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): key = 'Splitter' if key in ddict['Geometry'].keys(): self.splitter.setSizes(ddict['Geometry'][key]) + # TODO: Recover this functionality with silx if hasattr(self.mcaWindow, "graph"): # this was the way of working of 4.x.x versions key = 'McaWindow' @@ -884,10 +887,10 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): if type(roilist) != type([]): roilist=[roilist] roidict = ddict['roidict'] - if self.mcaWindow.roiWidget is None: - self.mcaWindow.showRoiWidget(qt.Qt.BottomDockWidgetArea) - self.mcaWindow.roiWidget.fillFromROIDict(roilist=roilist, - roidict=roidict) + # TODO: silx branch should show the ROI always with the McaWindow + roiWidget = self.mcaWindow.getCurvesRoiDockWidget().roiWidget + roiWidget.fillFromROIDict(roilist=roilist, + roidict=roidict) def __configureElements(self, ddict): if 'Material' in ddict: @@ -930,8 +933,8 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): (d['LastFit']['ydata0'] != 'None'): self.mcaWindow.advancedfit.setdata(x=d['LastFit']['xdata0'], y=d['LastFit']['ydata0'], - sigmay=d['LastFit']['sigmay0'], - **d['Information']) + sigmay=d['LastFit']['sigmay0'], + **d['Information']) if d['LastFit']['hidden'] == 'False': self.mcaWindow.advancedfit.show() self.mcaWindow.advancedfit.raiseW() @@ -941,7 +944,7 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): except: pass else: - print("hidden") + _logger.info("hidden") def __configureScanCustomFit(self, ddict): pass @@ -1115,8 +1118,7 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): """ def menuToolsAboutToShow(self): - if DEBUG: - print("menu ToolsAboutToShow") + _logger.debug("menu ToolsAboutToShow") self.menuTools.clear() if self.sourceFrame.isHidden(): self.menuTools.addAction("Show Source",self.toggleSource) @@ -1139,8 +1141,7 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): self._xrfmcPyMca) if SUMRULES_FLAG: self.menuTools.addAction("Sum Rules Tool", self._sumRules) - if DEBUG: - print("Fit to Specfile missing") + _logger.debug("Fit to Specfile missing") if TOMOGUI_FLAG: self.menuTools.addAction("Tomography reconstruction", self.__tomoRecons) @@ -1153,8 +1154,7 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): def toggleSource(self,**kw): - if DEBUG: - print("toggleSource called") + _logger.debug("toggleSource called") if self.sourceFrame.isHidden(): self.sourceFrame.show() self.sourceFrame.raise_() @@ -1395,8 +1395,7 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): if widget.parent() is None: widget.close() except: - if DEBUG: - print("Error closing widget") + _logger.debug("Error closing widget") return PyMcaMdi.PyMcaMdi.closeEvent(self, event) def __xiaCorrect(self): @@ -1430,8 +1429,10 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): self.saveMenu.addAction("Active Mca", self.mcaWindow._saveIconSignal) elif text.upper() == 'SCAN': - self.saveMenu.addAction("Active Scan", - self.scanWindow._saveIconSignal) + self.saveMenu.addAction( + "Active Scan", + self.scanWindow.getSaveAction().trigger) + elif text in self.imageWindowDict.keys(): self.saveMenu.addAction("Active Image", self.imageWindowDict[text].graphWidget._saveIconSignal) @@ -1505,10 +1506,27 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): self.saveConfig(self.getConfig(), filename) def loadTrainingData(self): + if self.trainingDataMenu is None: + self.trainingDataMenu = qt.QMenu() + self.trainingSources = {"XRF Analysis": os.path.join(PyMcaDataDir.PYMCA_DATA_DIR, 'XRFSpectrum.mca'), + "Tertiary Excitation": os.path.join(PyMcaDataDir.PYMCA_DATA_DIR, 'Steel.spe')} + self.trainingActions = [] + for key in ["XRF Analysis", "Tertiary Excitation"]: + action = qt.QAction(key, None) + self.trainingActions.append(action) + self.trainingDataMenu.addAction(action) try: - source = os.path.join(PyMcaDataDir.PYMCA_DATA_DIR, - 'XRFSpectrum.mca') + selectedAction = self.trainingDataMenu.exec_(qt.QCursor.pos()) + key = qt.safe_str(selectedAction.text()) + source = self.trainingSources[key] self.sourceWidget.sourceSelector.openSource(source) + # only in case of the steel sample we set the input dir to simplify accessing the supplied cfg file + if key in ["Tertiary Excitation"]: + # we do not change the input dir currently used by the source selector + #self.sourceWidget.sourceSelector.lastInputDir = os.path.dirname(source) + # but we change the input dir to allow easy loading of the config file from the + # fit configuration window + PyMcaDirs.inputDir = os.path.dirname(source) except: msg = qt.QMessageBox(self) msg.setIcon(qt.QMessageBox.Critical) @@ -1519,8 +1537,7 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): msg.exec_() def openSource(self,index=0): - if DEBUG: - print("index = %d " % index) + _logger.debug("index = %d ", index) if index <= 0: outfile = qt.QFileDialog(self) outfile.setWindowTitle("Select PyMca Configuration File") @@ -1635,18 +1652,19 @@ class PyMcaMain(PyMcaMdi.PyMcaMdi): self.changeLog.show() def onDebug(self): - print("Module name PyQt ",qt.PYQT_VERSION_STR) + _logger.debug("Module name PyQt %s", qt.PYQT_VERSION_STR) for module in sys.modules.values(): try: if 'Revision' in module.__revision__: if module.__name__ != "__main__": - print("Module name = ",module.__name__,module.__revision__.replace("$","")) + _logger.debug("Module name = %s, %s", + module.__name__, + module.__revision__.replace("$", "")) except: pass def onPrint(self): - if DEBUG: - print("onPrint called") + _logger.debug("onPrint called") if not self.scanWindow.isHidden(): self.scanWindow.printGraph() return @@ -1671,7 +1689,7 @@ if 0: #name= self.__getNewGraphName() if name == "MCA Graph": - graph= McaWindow.McaWindow(self.mdi, name=name) + graph = McaWindow.McaWindow(self.mdi, name=name) graph.windowClosed[()].connect(self.closeGraph) graph.show() @@ -1704,7 +1722,7 @@ if 0: """ Called after a graph is closed """ - print("closeGraph", name) + _logger.info("closeGraph", name) def __getGraphNames(self): return [ str(window.caption()) for window in self.mdi.windowList() ] @@ -1744,8 +1762,7 @@ class MyQTextBrowser(qt.QTextBrowser): class Line(qt.QFrame): sigLineDoubleClickEvent = qt.pyqtSignal(object) def mouseDoubleClickEvent(self,event): - if DEBUG: - print("Double Click Event") + _logger.debug("Double Click Event") ddict={} ddict['event']="DoubleClick" ddict['data'] = event @@ -1754,8 +1771,7 @@ class Line(qt.QFrame): class PixmapLabel(qt.QLabel): sigPixmapLabelMousePressEvent = qt.pyqtSignal(object) def mousePressEvent(self,event): - if DEBUG: - print("Mouse Press Event") + _logger.debug("Mouse Press Event") ddict={} ddict['event']="MousePress" ddict['data'] = event diff --git a/PyMca5/PyMcaGui/pymca/PyMcaMdi.py b/PyMca5/PyMcaGui/pymca/PyMcaMdi.py index c4d515a..4ec1955 100644 --- a/PyMca5/PyMcaGui/pymca/PyMcaMdi.py +++ b/PyMca5/PyMcaGui/pymca/PyMcaMdi.py @@ -28,6 +28,7 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys, getopt, string +import logging from PyMca5.PyMcaGui import PyMcaQt as qt if hasattr(qt, "QString"): QString = qt.QString @@ -40,7 +41,7 @@ from PyMca5.PyMcaGui import PyMca_Icons IconDict = PyMca_Icons.IconDict IconDict0 = PyMca_Icons.IconDict0 from .PyMca_help import HelpDict -DEBUG = 0 +_logger = logging.getLogger(__name__) __version__ = "1.5" @@ -174,8 +175,7 @@ class PyMcaMdi(qt.QMainWindow): self.winToolBar = self.addToolBar("wintoolbar") def onWinToolMenu(self, idx): - if DEBUG: - print("onWinToolMenu %d " % idx) + _logger.debug("onWinToolMenu %d ", idx) for midx in self.winToolMenuIndex: self.winToolMenu.setItemChecked(midx, midx==idx) act= self.winToolMenuIndex.index(idx) @@ -287,8 +287,7 @@ class PyMcaMdi(qt.QMainWindow): self.menuBar().addMenu(self.menuHelp) def menuWindowAboutToShow(self): - if DEBUG: - print("menuWindowAboutToShow") + _logger.debug("menuWindowAboutToShow") self.menuWindow.clear() if len(self.mdi.windowList())==0: return @@ -312,10 +311,10 @@ class PyMcaMdi(qt.QMainWindow): def _windowMapperMapSlot(self): return self.windowMapper.map() - def menuWindowActivated(self, idx = None): - if DEBUG: - print("menuWindowActivated idx = ",idx) - if idx is None:return + def menuWindowActivated(self, idx=None): + _logger.debug("menuWindowActivated idx = %s", idx) + if idx is None: + return if self.menuWindowMap[idx].isHidden(): self.menuWindowMap[idx].show() self.menuWindowMap[idx].raise_() @@ -336,7 +335,7 @@ class PyMcaMdi(qt.QMainWindow): self.followActiveWindow= follow def onWindowActivated(self, win): - print("Window activated") + _logger.info("Window activated") pass # @@ -368,8 +367,7 @@ class PyMcaMdi(qt.QMainWindow): pass def menuToolsAboutToShow(self): - if DEBUG: - print("menuToolsAboutToShow") + _logger.debug("menuToolsAboutToShow") self.menuTools.clear() self.menuToolsMap= {} """ @@ -384,8 +382,7 @@ class PyMcaMdi(qt.QMainWindow): self.menuTools.insertItem("Customize", self.customize) def menuToolsActivated(self, idx): - if DEBUG: - print("menuToolsActivated idx = ",idx) + _logger.debug("menuToolsActivated idx = %s", idx) if self.menuTools.isItemChecked(idx): self.menuToolsMap[idx].hide() else: @@ -445,7 +442,7 @@ def main(args): options, longoptions) except getopt.error: - print(sys.exc_info()[1]) + _logger.error(sys.exc_info()[1]) sys.exit(1) # --- waiting widget kw={} diff --git a/PyMca5/PyMcaGui/pymca/PyMcaNexusWidget.py b/PyMca5/PyMcaGui/pymca/PyMcaNexusWidget.py index 9462fdc..b41012d 100644 --- a/PyMca5/PyMcaGui/pymca/PyMcaNexusWidget.py +++ b/PyMca5/PyMcaGui/pymca/PyMcaNexusWidget.py @@ -30,6 +30,7 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import posixpath import h5py +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaCore import DataObject from PyMca5.PyMcaGui.io.hdf5 import QNexusWidget @@ -39,7 +40,8 @@ if hasattr(qt, 'QString'): QString = qt.QString else: QString = str -DEBUG=0 +_logger = logging.getLogger(__name__) + class PyMcaNexusWidget(QNexusWidget.QNexusWidget): def __init__(self, parent=None, mca=True): @@ -100,23 +102,19 @@ class PyMcaNexusWidget(QNexusWidget.QNexusWidget): return def _stack1DSignal(self): - if DEBUG: - print("_stack1DSignal") + _logger.debug("_stack1DSignal") self._stackSignal(index=-1, load=False) def _loadStack1DSignal(self): - if DEBUG: - print("_stack1DSignal") + _logger.debug("_stack1DSignal") self._stackSignal(index=-1, load=True) def _loadStack2DSignal(self): - if DEBUG: - print("_loadStack2DSignal") + _logger.debug("_loadStack2DSignal") self._stackSignal(index=0, load=True) def _stack2DSignal(self, load=False): - if DEBUG: - print("_stack2DSignal") + _logger.debug("_stack2DSignal") self._stackSignal(index=0, load=False) def _stackSignal(self, index=-1, load=False): @@ -171,7 +169,7 @@ class PyMcaNexusWidget(QNexusWidget.QNexusWidget): try: axes = axes.decode('utf-8') except: - print("WARNING: Cannot decode axes") + _logger.warning("Cannot decode axes") axes = axes.split(":") for axis in axes: if axis in group.keys(): @@ -181,7 +179,7 @@ class PyMcaNexusWidget(QNexusWidget.QNexusWidget): except: # I cannot afford this Nexus specific things # to break the generic HDF5 functionality - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise axesList = [] diff --git a/PyMca5/PyMcaGui/pymca/PyMcaPostBatch.py b/PyMca5/PyMcaGui/pymca/PyMcaPostBatch.py index f460d72..2a34338 100644 --- a/PyMca5/PyMcaGui/pymca/PyMcaPostBatch.py +++ b/PyMca5/PyMcaGui/pymca/PyMcaPostBatch.py @@ -116,13 +116,16 @@ class PyMcaPostBatch(RGBCorrelator.RGBCorrelator): return filelist def test(): + import logging + from PyMca5.PyMcaCore.LoggingLevel import getLoggingLevel sys.excepthook = qt.exceptionHandler app = qt.QApplication([]) app.lastWindowClosed.connect(app.quit) import getopt options='' - longoptions=["nativefiledialogs=","transpose=", "fileindex="] + longoptions=["nativefiledialogs=", "transpose=", "fileindex=", + "logging=", "debug="] opts, args = getopt.getopt( sys.argv[1:], options, @@ -140,6 +143,9 @@ def test(): elif opt in '--fileindex': if int(arg): transpose=True + + logging.basicConfig(level=getLoggingLevel(opts)) + filelist=args w = PyMcaPostBatch() w.layout().setContentsMargins(11, 11, 11, 11) diff --git a/PyMca5/PyMcaGui/pymca/QDispatcher.py b/PyMca5/PyMcaGui/pymca/QDispatcher.py index f84651c..b584742 100644 --- a/PyMca5/PyMcaGui/pymca/QDispatcher.py +++ b/PyMca5/PyMcaGui/pymca/QDispatcher.py @@ -28,15 +28,16 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys -import os import traceback +import logging from PyMca5.PyMcaGui import PyMcaQt as qt QTVERSION = qt.qVersion() from PyMca5.PyMcaGui.io import QSourceSelector from . import QDataSource #import weakref -DEBUG = 0 +_logger = logging.getLogger(__name__) + class QDispatcher(qt.QWidget): sigAddSelection = qt.pyqtSignal(object) @@ -93,9 +94,8 @@ class QDispatcher(qt.QWidget): self.tabWidget.currentChanged[int].connect(self._tabChanged) def _addSelectionSlot(self, sel_list, event=None): - if DEBUG: - print("QDispatcher._addSelectionSlot") - print("sel_list = ",sel_list) + _logger.debug("QDispatcher._addSelectionSlot") + _logger.debug("sel_list = %s", sel_list) if event is None: event = "addSelection" @@ -144,31 +144,29 @@ class QDispatcher(qt.QWidget): #I should create a weakref to it in order to be informed #about its deletion. if source.sourceType != "SPS": - if DEBUG: + try: dataObject = source.getDataObject(sel['Key'], selection=sel['selection']) - else: - try: - dataObject = source.getDataObject(sel['Key'], - selection=sel['selection']) - except: - error = sys.exc_info() - text = "Failed to read data source.\n" - text += "Source: %s\n" % source.sourceName - text += "Key: %s\n" % sel['Key'] - text += "Error: %s" % error[1] - if QTVERSION < '4.0.0': - qt.QMessageBox.critical(self, - "%s" % error[0], - text) - else: - msg = qt.QMessageBox(self) - msg.setWindowTitle('Source Error') - msg.setIcon(qt.QMessageBox.Critical) - msg.setInformativeText(text) - msg.setDetailedText(\ - traceback.format_exc()) - continue + except: + if _logger.getEffectiveLevel() == logging.DEBUG: + raise + error = sys.exc_info() + text = "Failed to read data source.\n" + text += "Source: %s\n" % source.sourceName + text += "Key: %s\n" % sel['Key'] + text += "Error: %s" % error[1] + if QTVERSION < '4.0.0': + qt.QMessageBox.critical(self, + "%s" % error[0], + text) + else: + msg = qt.QMessageBox(self) + msg.setWindowTitle('Source Error') + msg.setIcon(qt.QMessageBox.Critical) + msg.setInformativeText(text) + msg.setDetailedText(\ + traceback.format_exc()) + continue else: dataObject = source.getDataObject(sel['Key'], selection=sel['selection'], @@ -209,7 +207,7 @@ class QDispatcher(qt.QWidget): self.sigRemoveSelection.emit(selectionList) selectionList = [] else: - print("Unhandled dispatcher event = ", event) + _logger.warning("Unhandled dispatcher event = %s", event) del selectionList[-1] if len(selectionList): if event.lower() == "addselection": @@ -221,9 +219,8 @@ class QDispatcher(qt.QWidget): lastEvent = None def _removeSelectionSlot(self, sel_list): - if DEBUG: - print("_removeSelectionSlot") - print("sel_list = ",sel_list) + _logger.debug("_removeSelectionSlot") + _logger.debug("sel_list = %s", sel_list) for sel in sel_list: ddict = {} ddict.update(sel) @@ -231,23 +228,21 @@ class QDispatcher(qt.QWidget): self.sigRemoveSelection.emit(ddict) def _replaceSelectionSlot(self, sel_list): - if DEBUG: - print("_replaceSelectionSlot") - print("sel_list = ",sel_list) + _logger.debug("_replaceSelectionSlot") + _logger.debug("sel_list = %s", sel_list) if len(sel_list) == 1: - self._addSelectionSlot([sel_list[0]], event = "replaceSelection") + self._addSelectionSlot([sel_list[0]], event="replaceSelection") elif len(sel_list) > 1: - self._addSelectionSlot([sel_list[0]], event = "replaceSelection") - self._addSelectionSlot(sel_list[1:], event = "addSelection") + self._addSelectionSlot([sel_list[0]], event="replaceSelection") + self._addSelectionSlot(sel_list[1:], event="addSelection") def _otherSignalsSlot(self, ddict): self.sigOtherSignals.emit(ddict) def _sourceSelectorSlot(self, ddict): - if DEBUG: - print("_sourceSelectorSlot(self, ddict)") - print("ddict = ",ddict) + _logger.debug("_sourceSelectorSlot(self, ddict)") + _logger.debug("ddict = %s", ddict) if ddict["event"] == "NewSourceSelected": source = QDataSource.QDataSource(ddict["sourcelist"]) self.sourceList.append(source) @@ -265,8 +260,7 @@ class QDispatcher(qt.QWidget): found = 1 break if not found: - if DEBUG: - print("WARNING: source not found") + _logger.debug("WARNING: source not found") return sourceType = source.sourceType if ddict["event"] == "SourceReloaded": @@ -280,8 +274,7 @@ class QDispatcher(qt.QWidget): found = 1 break if not found: - if DEBUG: - print("WARNING: source not found") + _logger.debug("WARNING: source not found") return sourceType = source.sourceType del self.sourceList[self.sourceList.index(source)] @@ -299,13 +292,10 @@ class QDispatcher(qt.QWidget): self.selectorWidget[sourceType].setDataSource(None) self.tabWidget.setCurrentWidget(self.selectorWidget[sourceType]) elif ddict["event"] == "SourceClosed": - if DEBUG: - print("not implemented yet") - + _logger.debug("not implemented yet") def _selectionUpdatedSlot(self, ddict): - if DEBUG: - print("_selectionUpdatedSlot(self, dict)",ddict) + _logger.debug("_selectionUpdatedSlot(self, dict=%s)") if 'selectionlist' in ddict: sel_list = ddict['selectionlist'] else: @@ -332,8 +322,7 @@ class QDispatcher(qt.QWidget): self._addSelectionSlot(sel_list) def _tabChanged(self, value): - if DEBUG: - print("self._tabChanged(value), value = ",value) + _logger.debug("self._tabChanged(value), value = %s", value) text = str(self.tabWidget.tabText(value)) ddict = {} ddict['SourceType'] = text @@ -355,16 +344,17 @@ class QDispatcher(qt.QWidget): ddict['SourceName'] = self.selectorWidget[text].data.sourceName else: ddict['SourceName'] = None - print(ddict) - print("===========================") + _logger.info("%s", ddict) + _logger.info("===========================") for source in self.sourceList: - print(source) - print(source.sourceType) + _logger.info(source) + _logger.info(source.sourceType) sourceType = source.sourceType - print(self.selectorWidget[sourceType].currentSelectionList()) + _logger.info(self.selectorWidget[sourceType].currentSelectionList()) - if self.pluginsCallback is not None: - self.pluginsCallback(info) + # this seems unused (info is not defined) + # if self.pluginsCallback is not None: + # self.pluginsCallback(info) def test(): diff --git a/PyMca5/PyMcaGui/pymca/QHDF5Stack1D.py b/PyMca5/PyMcaGui/pymca/QHDF5Stack1D.py index 04c7952..4097948 100644 --- a/PyMca5/PyMcaGui/pymca/QHDF5Stack1D.py +++ b/PyMca5/PyMcaGui/pymca/QHDF5Stack1D.py @@ -27,11 +27,10 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import sys + from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaIO import HDF5Stack1D from PyMca5.PyMcaGui.pymca import QHDF5StackWizard -DEBUG = 0 class QHDF5Stack1D(HDF5Stack1D.HDF5Stack1D): def __init__(self, filelist=None, diff --git a/PyMca5/PyMcaGui/pymca/QHDF5StackWizard.py b/PyMca5/PyMcaGui/pymca/QHDF5StackWizard.py index 103c115..908a4e5 100644 --- a/PyMca5/PyMcaGui/pymca/QHDF5StackWizard.py +++ b/PyMca5/PyMcaGui/pymca/QHDF5StackWizard.py @@ -36,6 +36,7 @@ from PyMca5.PyMcaGui.io.hdf5 import QNexusWidget from PyMca5.PyMcaCore import NexusDataSource from PyMca5 import PyMcaDirs + class IntroductionPage(qt.QWizardPage): def __init__(self, parent): qt.QWizardPage.__init__(self, parent) @@ -44,6 +45,7 @@ class IntroductionPage(qt.QWizardPage): text += "appropriate dataset(s) belonging to your stack" self.setSubTitle(text) + class FileListPage(qt.QWizardPage): def __init__(self, parent): qt.QWizardPage.__init__(self, parent) @@ -187,7 +189,7 @@ class DatasetSelectionPage(qt.QWizardPage): attrs = list(entry.attrs) if 'NX_class' in attrs: attr = entry.attrs['NX_class'] - if sys.version > '2.9': + if hasattr(attr, "decode"): try: attr = attr.decode('utf-8') except: @@ -197,7 +199,7 @@ class DatasetSelectionPage(qt.QWizardPage): attr = None if attr is None: return - if attr != 'NXentry': + if attr not in ['NXentry', b'NXentry']: return #check if there is only one NXdata @@ -206,62 +208,98 @@ class DatasetSelectionPage(qt.QWizardPage): attr = entry[key].attrs.get('NX_class', None) if attr is None: continue - if sys.version > '2.9': + if hasattr(attr, "decode"): try: attr = attr.decode('utf-8') except: print("WARNING: Cannot decode NX_class attribute") continue - if attr in ['NXdata']: + if attr in ['NXdata', b'NXdata']: nxDataList.append(key) if len(nxDataList) != 1: return nxData = entry[nxDataList[0]] - #try to get the signals + ddict = {'counters': [], + 'aliases': []} signalList = [] axesList = [] interpretation = "" - for key in nxData.keys(): - if 'signal' in nxData[key].attrs.keys(): - if int(nxData[key].attrs['signal']) == 1: - signalList.append(key) - if len(signalList) == 1: - if 'interpretation' in nxData[key].attrs.keys(): - interpretation = nxData[key].attrs['interpretation'] - if sys.version > '2.9': - try: - interpretation = interpretation.decode('utf-8') - except: - print("WARNING: Cannot decode interpretation") - if interpretation == "image": - self.stackIndexWidget.setIndex(0) - if 'axes' in nxData[key].attrs.keys(): - axes = nxData[key].attrs['axes'] - if sys.version > '2.9': - try: - axes = axes.decode('utf-8') - except: - print("WARNING: Cannot decode axes") - axes = axes.split(":") - for axis in axes: - if axis in nxData.keys(): - axesList.append(axis) - - if not len(signalList): - return - ddict = {} - ddict['counters'] = [] - ddict['aliases'] = [] + signal_key = nxData.attrs.get("signal") + if signal_key is not None: + # recent NXdata specification + if hasattr(signal_key, "decode"): + try: + signal_key = signal_key.decode('utf-8') + except AttributeError: + print("WARNING: Cannot decode NX_class attribute") + + signal_dataset = nxData.get(signal_key) + if signal_dataset is None: + return - for signal in signalList: - path = posixpath.join("/",nxDataList[0], signal) + interpretation = signal_dataset.attrs.get("interpretation", "") + if hasattr(interpretation, "decode"): + try: + interpretation = interpretation.decode('utf-8') + except AttributeError: + print("WARNING: Cannot decode interpretation") + + axesList = list(nxData.attrs.get("axes", [])) + if not axesList: + # try the old method, still documented on nexusformat.org: + # colon-delimited "array" of dataset names as a signal attr + axes = signal_dataset.attrs.get('axes') + if axes is not None: + if hasattr(axes, "decode"): + try: + axes = axes.decode('utf-8') + except AttributeError: + print("WARNING: Cannot decode axes") + axes = axes.split(":") + axesList = [ax for ax in axes if ax in nxData] + signalList.append(signal_key) + else: + # old specification + for key in nxData.keys(): + if 'signal' in nxData[key].attrs.keys(): + if int(nxData[key].attrs['signal']) == 1: + signalList.append(key) + if len(signalList) == 1: + if 'interpretation' in nxData[key].attrs.keys(): + interpretation = nxData[key].attrs['interpretation'] + if sys.version > '2.9': + try: + interpretation = interpretation.decode('utf-8') + except: + print("WARNING: Cannot decode interpretation") + + if 'axes' in nxData[key].attrs.keys(): + axes = nxData[key].attrs['axes'] + if sys.version > '2.9': + try: + axes = axes.decode('utf-8') + except: + print("WARNING: Cannot decode axes") + axes = axes.split(":") + for axis in axes: + if axis in nxData.keys(): + axesList.append(axis) + + if not len(signalList): + return + + if interpretation in ["image", b"image"]: + self.stackIndexWidget.setIndex(0) + + for signal_key in signalList: + path = posixpath.join("/", nxDataList[0], signal_key) ddict['counters'].append(path) - ddict['aliases'].append(posixpath.basename(signal)) + ddict['aliases'].append(posixpath.basename(signal_key)) for axis in axesList: - path = posixpath.join("/",nxDataList[0], axis) + path = posixpath.join("/", nxDataList[0], axis) ddict['counters'].append(path) ddict['aliases'].append(posixpath.basename(axis)) @@ -274,15 +312,13 @@ class DatasetSelectionPage(qt.QWizardPage): return self.nexusWidget.setWidgetConfiguration(ddict) - if len(signalList): - if len(axesList) == 0: - self.nexusWidget.cntTable.setCounterSelection({'y':[0]}) - elif interpretation == "image": - self.nexusWidget.cntTable.setCounterSelection({'y':[0], 'x':[1]}) - elif interpretation == "spectrum": - self.nexusWidget.cntTable.setCounterSelection({'y':[0], 'x':[len(axesList)]}) - else: - self.nexusWidget.cntTable.setCounterSelection({'y':[0]}) + + if axesList and (interpretation in ["image", b"image"]): + self.nexusWidget.cntTable.setCounterSelection({'y': [0], 'x': [1]}) + elif axesList and (interpretation in ["spectrum", b"spectrum"]): + self.nexusWidget.cntTable.setCounterSelection({'y': [0], 'x': [len(axesList)]}) + else: + self.nexusWidget.cntTable.setCounterSelection({'y': [0]}) def validatePage(self): cntSelection = self.nexusWidget.cntTable.getCounterSelection() @@ -313,6 +349,7 @@ class DatasetSelectionPage(qt.QWizardPage): msg.setText(text) msg.exec_() + class ShapePage(qt.QWizardPage): def __init__(self, parent): qt.QWizardPage.__init__(self, parent) @@ -320,6 +357,7 @@ class ShapePage(qt.QWizardPage): text = "Adjust the shape of your map if necessary" self.setSubTitle(text) + class LocalQNexusWidget(QNexusWidget.QNexusWidget): def __init__(self, parent=None, mca=False): QNexusWidget.QNexusWidget.__init__(self, parent=parent, @@ -332,6 +370,7 @@ class LocalQNexusWidget(QNexusWidget.QNexusWidget): w.setWindowModality(qt.Qt.ApplicationModal) w.show() + class QHDF5StackWizard(qt.QWizard): def __init__(self, parent=None): qt.QWizard.__init__(self, parent) @@ -377,6 +416,7 @@ class QHDF5StackWizard(qt.QWizard): self._datasetSelection.selection,\ [x[0] for x in self._datasetSelection.nexusWidget.getSelectedEntries()] + if __name__ == "__main__": import sys app = qt.QApplication(sys.argv) diff --git a/PyMca5/PyMcaGui/pymca/QPyMcaMatplotlibSave.py b/PyMca5/PyMcaGui/pymca/QPyMcaMatplotlibSave.py index cbfa1fb..540f547 100644 --- a/PyMca5/PyMcaGui/pymca/QPyMcaMatplotlibSave.py +++ b/PyMca5/PyMcaGui/pymca/QPyMcaMatplotlibSave.py @@ -1,6 +1,6 @@ #!/usr/bin/env python #/*########################################################################## -# Copyright (C) 2004-2016 V.A. Sole, European Synchrotron Radiation Facility +# Copyright (C) 2004-2018 V.A. Sole, European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -24,6 +24,7 @@ # THE SOFTWARE. # #############################################################################*/ +from __future__ import absolute_import __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" @@ -32,13 +33,16 @@ import sys import os import numpy import traceback +from io import StringIO +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaCore import PyMcaMatplotlibSave from PyMca5.PyMcaGui import IconDict -from PyMca5.PyMcaGui import PyMcaPrintPreview from PyMca5 import PyMcaDirs +from silx.gui.widgets.PrintPreview import SingletonPrintPreviewDialog + from matplotlib import cm from matplotlib.font_manager import FontProperties from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas @@ -46,7 +50,7 @@ from matplotlib.figure import Figure from matplotlib.colors import LinearSegmentedColormap, LogNorm, Normalize from matplotlib.ticker import MaxNLocator, AutoLocator -DEBUG = 0 +_logger = logging.getLogger(__name__) class TopWidget(qt.QWidget): @@ -108,7 +112,7 @@ class SaveImageSetup(qt.QWidget): self.setWindowTitle("PyMca - Matplotlib save image") self.setWindowIcon(qt.QIcon(qt.QPixmap(IconDict['gioconda16']))) self.lastOutputDir = None - self.printPreview = PyMcaPrintPreview.PyMcaPrintPreview(modal = 0) + self.printPreview = SingletonPrintPreviewDialog(parent=self) #top self.top = TopWidget(self) @@ -178,20 +182,34 @@ class SaveImageSetup(qt.QWidget): msg.setWindowTitle('Matplotlib Save Image') msg.exec_() - def printClicked(self): try: - pixmap = qt.QPixmap.grabWidget(self.imageWidget) - self.printPreview.addPixmap(pixmap) - if self.printPreview.isHidden(): - self.printPreview.show() - self.printPreview.raise_() + imgData = StringIO() + self.imageWidget.figure.savefig(imgData, format="svg") # dpi=...) + imgData.flush() + imgData.seek(0) + svgData = imgData.read() + svgRenderer = qt.QSvgRenderer() + svgRenderer.load(qt.QXmlStreamReader(svgData.encode(errors="replace"))) + self.printPreview.addSvgItem(svgRenderer) except: - msg = qt.QMessageBox(self) - msg.setIcon(qt.QMessageBox.Critical) - msg.setText("Error printing image: %s" % sys.exc_info()[1]) - msg.setWindowTitle('Matplotlib Save Image') - msg.exec_() + try: + if hasattr(qt.QPixmap,"grabWidget"): + pixmap = qt.QPixmap.grabWidget(self.imageWidget) + else: + pixmap = self.imageWidget.grab() + self.printPreview.addPixmap(pixmap) + except: + msg = qt.QMessageBox(self) + msg.setIcon(qt.QMessageBox.Critical) + msg.setText("Error printing image: %s" % sys.exc_info()[1]) + msg.setWindowTitle('Matplotlib Save Image') + msg.exec_() + return + if self.printPreview.isHidden(): + self.printPreview.show() + self.printPreview.raise_() + def saveClicked(self): outfile = qt.QFileDialog(self) @@ -274,7 +292,7 @@ class SaveImageSetup(qt.QWidget): format=finalFile[-3:], dpi=self.imageWidget.config['outputdpi']) except: - print("WARNING: trying to save using obsolete method") + _logger.warning("trying to save using obsolete method") config = self.imageWidget.getParameters() try: s=PyMcaMatplotlibSave.PyMcaMatplotlibSaveImage(self.imageWidget.imageData) @@ -728,8 +746,7 @@ class QPyMcaMatplotlibImage(FigureCanvas): elif self.config['colormap'] == 'ylgnbu_r': cmap = cm.YlGnBu_r else: - print("Unsupported colormap %s" % self.config['colormap']) - + _logger.warning("Unsupported colormap %s", self.config['colormap']) if self.config['extent'] is None: h, w = self.imageData.shape @@ -802,7 +819,7 @@ class QPyMcaMatplotlibImage(FigureCanvas): self._colorbar.locator = tick_locator self._colorbar.update_ticks() except: - print("Colorbar error", sys.exc_info()) + _logger.warning("Colorbar error %s", sys.exc_info()) pass else: self._colorbar = self.figure.colorbar(self._image, diff --git a/PyMca5/PyMcaGui/pymca/QPyMcaMatplotlibSave1D.py b/PyMca5/PyMcaGui/pymca/QPyMcaMatplotlibSave1D.py index 6b32e92..39cd550 100644 --- a/PyMca5/PyMcaGui/pymca/QPyMcaMatplotlibSave1D.py +++ b/PyMca5/PyMcaGui/pymca/QPyMcaMatplotlibSave1D.py @@ -31,14 +31,12 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import numpy +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import PyMca_Icons IconDict = PyMca_Icons.IconDict -from PyMca5.PyMcaGui import PyMcaPrintPreview -from PyMca5.PyMcaCore import PyMcaDirs -from matplotlib import cm from matplotlib import __version__ as matplotlib_version from matplotlib.font_manager import FontProperties if "PyQt5" in sys.modules: @@ -49,7 +47,7 @@ else: from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg as FigureCanvas from matplotlib.figure import Figure -DEBUG = 0 +_logger = logging.getLogger(__name__) colordict = {} colordict['blue'] = '#0000ff' @@ -494,8 +492,7 @@ class QPyMcaMatplotlibSave(FigureCanvas): n = max(x.shape) if n == 0: #nothing to plot - if DEBUG: - print("nothing to plot") + _logger.debug("nothing to plot") return style = None diff --git a/PyMca5/PyMcaGui/pymca/QSource.py b/PyMca5/PyMcaGui/pymca/QSource.py index a05263f..1f2be17 100644 --- a/PyMca5/PyMcaGui/pymca/QSource.py +++ b/PyMca5/PyMcaGui/pymca/QSource.py @@ -27,10 +27,11 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import sys + +import logging from PyMca5.PyMcaGui import PyMcaQt as qt QTVERSION = qt.qVersion() -DEBUG = 0 +_logger = logging.getLogger(__name__) SOURCE_EVENT = qt.QEvent.User class SourceEvent(qt.QEvent): @@ -80,10 +81,9 @@ class QSource(qt.QObject): reference = id(dataObject) def dataObjectDestroyed(ref, dataObjectKey=key, dataObjectRef=reference): - if DEBUG: - print('data object destroyed, key was %s' % dataObjectKey) - print('data object destroyed, ref was 0x%x' % dataObjectRef) - print("self.surveyDict[key] = ",self.surveyDict[key]) + _logger.debug('data object destroyed, key was %s', dataObjectKey) + _logger.debug('data object destroyed, ref was 0x%x', dataObjectRef) + _logger.debug("self.surveyDict[key] = %s", self.surveyDict[key]) n = len(self.surveyDict[dataObjectKey]) if n > 0: @@ -96,8 +96,7 @@ class QSource(qt.QObject): if len(self.surveyDict[dataObjectKey]) == 0: del self.surveyDict[dataObjectKey] - if DEBUG: - print("SURVEY DICT AFTER DELETION = ", self.surveyDict) + _logger.debug("SURVEY DICT AFTER DELETION = %s", self.surveyDict) return # create a weak reference to the dataObject and we call it dataObjectRef @@ -111,12 +110,10 @@ class QSource(qt.QObject): self.surveyDict[key] = [dataObjectRef] self.selections[key] = [(id(dataObjectRef), dataObjectRef.info)] except ReferenceError: - if DEBUG: - print("NOT ADDED TO THE POLL dataObject = ", dataObject) + _logger.debug("NOT ADDED TO THE POLL dataObject = %s", dataObject) return - if DEBUG: - print("SURVEY DICT AFTER ADDITION = ", self.surveyDict) + _logger.debug("SURVEY DICT AFTER ADDITION = %s", self.surveyDict) if self.pollerThreadId is None: # start a new polling thread @@ -130,8 +127,7 @@ class QSource(qt.QObject): #for key in self.surveyDict is dangerous # runtime error: dictionnary changed during iteration # a mutex is needed - if DEBUG: - print("In loop") + _logger.debug("In loop") dummy = list(self.surveyDict.keys()) eventsToPost = {} #for key in self.surveyDict: @@ -139,8 +135,7 @@ class QSource(qt.QObject): if key not in eventsToPost: eventsToPost[key] = [] if self.isUpdated(self.sourceName, key): - if DEBUG: - print(self.sourceName,key,"is updated") + _logger.debug("%s %s is updated", self.sourceName, key) try: if len(self.surveyDict[key]): #there are still instances of dataObjects @@ -161,16 +156,14 @@ class QSource(qt.QObject): del self.surveyDict[key] del self.selections[key] except KeyError: - if DEBUG: - print("key error in loop") + _logger.debug("key error in loop") pass for key in eventsToPost: for event in eventsToPost[key]: qt.QApplication.postEvent(self, event) qt.QApplication.instance().processEvents() time.sleep(self._pollTime) - if DEBUG: - print("woke up") + _logger.debug("woke up") self.pollerThreadId = None self.selections = {} diff --git a/PyMca5/PyMcaGui/pymca/QStackWidget.py b/PyMca5/PyMcaGui/pymca/QStackWidget.py index 36fe82a..ce20839 100644 --- a/PyMca5/PyMcaGui/pymca/QStackWidget.py +++ b/PyMca5/PyMcaGui/pymca/QStackWidget.py @@ -33,6 +33,7 @@ import os import traceback import numpy import weakref +import logging if __name__ == "__main__": # we have to get the Qt binding prior to import PyMcaQt @@ -41,7 +42,7 @@ if __name__ == "__main__": longoptions = ["fileindex=","old", "filepattern=", "begin=", "end=", "increment=", "nativefiledialogs=", "imagestack=", "image=", - "backend=", "binding="] + "backend=", "binding=", "logging=", "debug="] opts, args = getopt.getopt( sys.argv[1:], options, @@ -56,8 +57,13 @@ if __name__ == "__main__": if sys.version_info < (3,): try: import sip - sip.setapi("QString", 2) - sip.setapi("QVariant", 2) + sip.setapi('QString', 2) + sip.setapi('QVariant', 2) + sip.setapi('QDate', 2) + sip.setapi('QDateTime', 2) + sip.setapi('QTextStream', 2) + sip.setapi('QTime', 2) + sip.setapi('QUrl', 2) except: print("Cannot set sip API") import PyQt4.QtCore @@ -87,7 +93,6 @@ from PyMca5.PyMcaGui import CloseEventNotifyingWidget from PyMca5.PyMcaGui import MaskImageWidget convertToRowAndColumn = MaskImageWidget.convertToRowAndColumn -from PyMca5.PyMcaGui.pymca import StackROIWindow from PyMca5.PyMcaGui.pymca import RGBCorrelator from PyMca5.PyMcaGui.pymca.RGBCorrelatorWidget import ImageShapeDialog from PyMca5.PyMcaGui import IconDict @@ -96,19 +101,20 @@ from PyMca5 import PyMcaDirs from PyMca5.PyMcaIO import ArraySave HDF5 = ArraySave.HDF5 -DEBUG = 0 +_logger = logging.getLogger(__name__) +# _logger.setLevel(logging.DEBUG) QTVERSION = qt.qVersion() -if DEBUG: - StackBase.DEBUG = DEBUG +if _logger.getEffectiveLevel() == logging.DEBUG: + StackBase.logger.setLevel(logging.DEBUG) class QStackWidget(StackBase.StackBase, CloseEventNotifyingWidget.CloseEventNotifyingWidget): - def __init__(self, parent = None, - mcawidget = None, - rgbwidget = None, - vertical = False, - master = True): + def __init__(self, parent=None, + mcawidget=None, + rgbwidget=None, + vertical=False, + master=True): StackBase.StackBase.__init__(self) CloseEventNotifyingWidget.CloseEventNotifyingWidget.__init__(self, parent) @@ -202,16 +208,16 @@ class QStackWidget(StackBase.StackBase, standalonesave=standaloneSaving, profileselection=True, aspect=True) - infotext = 'Toggle background subtraction from current image\n' + infotext = 'Toggle background subtraction from current image\n' infotext += 'subtracting a straight line between the ROI limits.' self.roiBackgroundIcon = qt.QIcon(qt.QPixmap(IconDict["subtract"])) - self.roiBackgroundButton = self.roiWidget.graphWidget._addToolButton(\ + self.roiBackgroundButton = self.roiWidget.graphWidget._addToolButton( self.roiBackgroundIcon, self._roiSubtractBackgroundClicked, infotext, - toggle = True, - state = False, - position = 6) + toggle=True, + state=False, + position=6) self.roiGraphWidget = self.roiWidget.graphWidget self.stackWindow.mainLayout.addWidget(self.stackWidget) self.roiWindow.mainLayout.addWidget(self.roiWidget) @@ -224,37 +230,37 @@ class QStackWidget(StackBase.StackBase, #add some missing icons offset = 6 - infotext = 'If checked, spectra will be added normalized to the number\n' + infotext = 'If checked, spectra will be added normalized to the number\n' infotext += 'of pixels. Be carefull if you are preparing a batch and you\n' infotext += 'fit the normalized spectra because the data in the batch will\n' infotext += 'have a different weight because they are not normalized.' self.normalizeIcon = qt.QIcon(qt.QPixmap(IconDict["normalize16"])) - self.normalizeButton = self.stackGraphWidget._addToolButton(\ + self.normalizeButton = self.stackGraphWidget._addToolButton( \ self.normalizeIcon, self.normalizeIconChecked, infotext, - toggle = True, - state = False, - position = 6) + toggle=True, + state=False, + position=6) offset += 1 if self.master: self.loadIcon = qt.QIcon(qt.QPixmap(IconDict["fileopen"])) - self.loadStackButton = self.stackGraphWidget._addToolButton(\ + self.loadStackButton = self.stackGraphWidget._addToolButton( \ self.loadIcon, self.loadSlaveStack, 'Load another stack of same shape', - position = offset) + position=offset) offset += 1 - self.pluginIcon = qt.QIcon(qt.QPixmap(IconDict["plugin"])) + self.pluginIcon = qt.QIcon(qt.QPixmap(IconDict["plugin"])) infotext = "Call/Load Stack Plugins" self.stackGraphWidget._addToolButton(self.pluginIcon, self._pluginClicked, infotext, - toggle = False, - state = False, - position = offset) + toggle=False, + state=False, + position=offset) def setStack(self, *var, **kw): self.stackWidget.setImageData(None) @@ -263,7 +269,7 @@ class QStackWidget(StackBase.StackBase, if (1 in self._stack.data.shape) and\ isinstance(self._stack.data, numpy.ndarray): oldshape = self._stack.data.shape - dialog = ImageShapeDialog(self, shape = oldshape[0:2]) + dialog = ImageShapeDialog(self, shape=oldshape[0:2]) dialog.setModal(True) ret = dialog.exec_() if ret: @@ -304,14 +310,14 @@ class QStackWidget(StackBase.StackBase, xScale = self._stack.info.get("xScale", None) yScale = self._stack.info.get("yScale", None) if self.roiBackgroundButton.isChecked(): - self.roiWidget.graphWidget.graph.setGraphTitle(\ - self._ROIImageNames[0]+" Net") - self.roiWidget.setImageData(self._ROIImageList[0]-\ + self.roiWidget.graphWidget.graph.setGraphTitle( \ + self._ROIImageNames[0] + " Net") + self.roiWidget.setImageData(self._ROIImageList[0] - \ self._ROIImageList[-1], xScale=xScale, yScale=yScale) else: - self.roiWidget.graphWidget.graph.setGraphTitle(\ + self.roiWidget.graphWidget.graph.setGraphTitle( \ self._ROIImageNames[0]) self.roiWidget.setImageData(self._ROIImageList[0], xScale=xScale, @@ -398,7 +404,7 @@ class QStackWidget(StackBase.StackBase, ArraySave.save3DArrayAsMonochromaticTiff(dataView, filename, - labels = None, + labels=None, dtype=dtype, mcaindex=mcaIndex) @@ -442,7 +448,7 @@ class QStackWidget(StackBase.StackBase, row1 = int(min([row1+0.5, self._stack.data.shape[0]])) col0 = int(max([col0+0.5, 0])) col1 = int(min([col1+0.5, self._stack.data.shape[1]])) - view = self._stack.data[row0:row1+1, col0:col1+1,:] + view = self._stack.data[row0:row1+1, col0:col1+1, :] return view def saveStackAsNeXus(self, dtype=None, interpretation=None, compression=False): @@ -528,14 +534,14 @@ class QStackWidget(StackBase.StackBase, if not len(filename): return ArraySave.save3DArrayAsHDF5(self._stack.data, filename, - labels = None, dtype=None, mode='nexus+') + labels=None, dtype=None, mode='nexus+') def saveStackAsSimpleHDF5(self): filename = self._getOutputHDF5Filename() if not len(filename): return ArraySave.save3DArrayAsHDF5(self._stack.data, filename, - labels = None, dtype=None, mode='simple') + labels=None, dtype=None, mode='simple') def saveStackAsSimplestHDF5(self): filename = self._getOutputHDF5Filename() @@ -543,15 +549,15 @@ class QStackWidget(StackBase.StackBase, return view = self._getCroppedView() ArraySave.save3DArrayAsHDF5(view, filename, - labels = None, dtype=None, mode='simplest') + labels=None, dtype=None, mode='simplest') def loadStack(self): if self._stackImageData is not None: #clear with a small stack stack = DataObject.DataObject() - stack.data = numpy.zeros((100,100,100), numpy.float32) + stack.data = numpy.zeros((100, 100, 100), numpy.float32) self.setStack(stack) - if self.stackSelector is None: + if self.stackSelector is None: self.stackSelector = StackSelector.StackSelector(self) stack = self.stackSelector.getStack() if (type(stack) == type([])) or isinstance(stack, list): @@ -635,7 +641,7 @@ class QStackWidget(StackBase.StackBase, else: self._closeSlave() return - if self.stackSelector is None: + if self.stackSelector is None: self.stackSelector = StackSelector.StackSelector(self) try: @@ -725,8 +731,8 @@ class QStackWidget(StackBase.StackBase, text = QString("Set User Plugin Directory") menu.addAction(text) actionList.append(text) - global DEBUG - if DEBUG: + global _logger + if _logger.getEffectiveLevel() == logging.DEBUG: text = QString("Toggle DEBUG mode OFF") else: text = QString("Toggle DEBUG mode ON") @@ -785,11 +791,12 @@ class QStackWidget(StackBase.StackBase, self.setPluginDirectoryList(pluginsDirList) return if idx == 2: - if DEBUG: - DEBUG = 0 + if _logger.getEffectiveLevel() == logging.DEBUG: + _logger.setLevel(logging.DEBUG) + StackBase.logger.setLevel(logging.DEBUG) else: - DEBUG = 1 - StackBase.DEBUG = DEBUG + _logger.setLevel(logging.NOTSET) + StackBase.logger.setLevel(logging.NOTSET) return key = callableKeys[idx] methods = self.pluginInstanceDict[key].getMethods() @@ -832,7 +839,7 @@ class QStackWidget(StackBase.StackBase, msg.setInformativeText(qt.safe_str(sys.exc_info()[1])) msg.setDetailedText(traceback.format_exc()) msg.exec_() - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise def _actionHovered(self, action): @@ -858,7 +865,7 @@ class QStackWidget(StackBase.StackBase, self.mainLayout.addWidget(self.rgbWidget) elif n == 2: self.tab = qt.QTabWidget(self) - self.mcaWidget = McaWindow.McaWindow()#vertical=False) + self.mcaWidget = McaWindow.McaWindow() #vertical=False #self.mcaWidget.graph.setMinimumWidth(0.5 * \ # qt.QWidget.sizeHint(self).width()) self.tab.setMaximumHeight(1.3 * qt.QWidget.sizeHint(self).height()) @@ -867,7 +874,11 @@ class QStackWidget(StackBase.StackBase, self.rgbWidget = RGBCorrelator.RGBCorrelator() self.tab.addTab(self.rgbWidget, "RGB Correlator") self.mainLayout.addWidget(self.tab) - self.mcaWidget.setMiddleROIMarkerFlag(True) + + curvesRoiWidget = self.mcaWidget.getCurvesRoiDockWidget().roiWidget + if hasattr(curvesRoiWidget, "setMiddleROIMarkerFlag"): + # Not implemented prior to silx >= 0.7 + curvesRoiWidget.setMiddleROIMarkerFlag(True) def _buildAndConnectButtonBox(self): #the MCA selection @@ -909,10 +920,11 @@ class QStackWidget(StackBase.StackBase, # self.stackGraphWidget.setInfoText(" X = ???? Y = ???? Z = ????") # self.stackGraphWidget.showInfo() - self.stackGraphWidget.graph.sigPlotSignal.connect(\ + self.stackGraphWidget.graph.sigPlotSignal.connect( \ self._stackGraphSignal) + self.mcaWidget.sigROISignal.connect(self._mcaWidgetSignal) - self.roiWidget.graphWidget.graph.sigPlotSignal.connect(\ + self.roiWidget.graphWidget.graph.sigPlotSignal.connect( \ self._stackGraphSignal) def showOriginalImage(self): @@ -923,8 +935,8 @@ class QStackWidget(StackBase.StackBase, xScale = self._stack.info.get("xScale", None) yScale = self._stack.info.get("yScale", None) self.stackWidget.setImageData(self._stackImageData, - xScale=xScale, - yScale=yScale) + xScale=xScale, + yScale=yScale) def showOriginalMca(self): goodData = numpy.isfinite(self._mcaData0.y[0].sum()) @@ -936,7 +948,7 @@ class QStackWidget(StackBase.StackBase, msg = qt.QMessageBox(self) msg.setIcon(qt.QMessageBox.Information) msg.setWindowTitle("Non finite data") - text = "Your data contain infinite values or nans.\n" + text = "Your data contain infinite values or nans.\n" text += "Pixels containing those values will be ignored." msg.setText(text) msg.exec_() @@ -950,7 +962,7 @@ class QStackWidget(StackBase.StackBase, msg = qt.QMessageBox(self) msg.setIcon(qt.QMessageBox.Information) msg.setWindowTitle("No active curve selected") - text = "Please select the MCA active curve." + text = "Please select the MCA active curve." msg.setText(text) msg.exec_() return @@ -965,8 +977,8 @@ class QStackWidget(StackBase.StackBase, xScale = self._stack.info.get("xScale", None) yScale = self._stack.info.get("yScale", None) if self.roiBackgroundButton.isChecked(): - self.roiWidget.graphWidget.graph.setGraphTitle(image_names[0]+\ - " Net") + self.roiWidget.graphWidget.graph.setGraphTitle(image_names[0] + \ + " Net") self.roiWidget.setImageData(imageList[0]-imageList[-1], xScale=xScale, yScale=yScale) @@ -1037,18 +1049,18 @@ class QStackWidget(StackBase.StackBase, if self.normalizeButton.isChecked(): if self._selectionMask is None: - npixels = self._stackImageData.shape[0] *\ + npixels = self._stackImageData.shape[0] * \ self._stackImageData.shape[1] else: npixels = self._selectionMask.sum() if npixels == 0: - npixels = self._stackImageData.shape[0] *\ + npixels = self._stackImageData.shape[0] * \ self._stackImageData.shape[1] legend += "/%d" % npixels return self.sendMcaSelection(dataObject, - key = "Selection", - legend =legend, - action = action) + key="Selection", + legend=legend, + action=action) def _removeMcaClicked(self): #remove the mca @@ -1063,7 +1075,7 @@ class QStackWidget(StackBase.StackBase, if curve.startswith(legend): legend = curve break - self.sendMcaSelection(dataObject, legend = legend, action = "REMOVE") + self.sendMcaSelection(dataObject, legend=legend, action="REMOVE") def _replaceMcaClicked(self): #replace the mca @@ -1071,7 +1083,7 @@ class QStackWidget(StackBase.StackBase, self._addMcaClicked(action="REPLACE") self.__ROIConnected = True - def sendMcaSelection(self, mcaObject, key = None, legend = None, action = None): + def sendMcaSelection(self, mcaObject, key=None, legend=None, action=None): if action is None: action = "ADD" if key is None: @@ -1084,9 +1096,9 @@ class QStackWidget(StackBase.StackBase, legend += "/%d" % npixels sel = {} sel['SourceName'] = "EDF Stack" - sel['Key'] = key - sel['legend'] = legend - sel['dataobject'] = mcaObject + sel['Key'] = key + sel['legend'] = legend + sel['dataobject'] = mcaObject if action == "ADD": self.mcaWidget._addSelection([sel]) elif action == "REMOVE": @@ -1106,11 +1118,10 @@ class QStackWidget(StackBase.StackBase, if instance_id == id(self): return - if DEBUG: - if self._slaveList is not None: - print("MASTER setSelectionMask CALLED") - elif self._masterStack is not None: - print("SLAVE setSelectionMask CALLED") + if self._slaveList is not None: + _logger.debug("MASTER setSelectionMask CALLED") + elif self._masterStack is not None: + _logger.debug("SLAVE setSelectionMask CALLED") #inform built in widgets for widget in [self.stackWidget, self.roiWidget]: @@ -1131,8 +1142,7 @@ class QStackWidget(StackBase.StackBase, instanceList.append(id(slave.pluginInstanceDict[key])) if instance_id not in instanceList: #Originated by the master - if DEBUG: - print("INFORMING SLAVE") + _logger.warning("INFORMING SLAVE") slave.setSelectionMask(mask, instance_id=id(self)) if self._masterStack is not None: @@ -1143,8 +1153,7 @@ class QStackWidget(StackBase.StackBase, instanceList.append(id(self.pluginInstanceDict[key])) if instance_id in instanceList: #Originated by the slave - if DEBUG: - print("INFORMING MASTER") + _logger.debug("INFORMING MASTER") self._masterStack.setSelectionMask(mask, instance_id=id(self)) #Inform plugins @@ -1177,11 +1186,9 @@ class QStackWidget(StackBase.StackBase, return if ddict['event'] == "hFlipSignal": if ddict['id'] != id(self.stackWidget): - self.stackWidget.graph.invertYAxis(ddict['current']) - self.stackWidget.graph.replot() + self.stackWidget.graph.getYAxis().setInverted(ddict['current']) if ddict['id'] != id(self.roiWidget): - self.roiWidget.graph.invertYAxis(ddict['current']) - self.roiWidget.graph.replot() + self.roiWidget.graph.getYAxis().setInverted(ddict['current']) return def _stackGraphSignal(self, ddict): @@ -1198,8 +1205,8 @@ class QStackWidget(StackBase.StackBase, x = min(int(x), limits[0]-1) y = min(int(y), limits[1]-1) z = self._stackImageData[x, y] - self.stackGraphWidget.setInfoText(" X = %d Y = %d Z = %.4g" %\ - (y, x, z)) + self.stackGraphWidget.setInfoText( \ + " X = %d Y = %d Z = %.4g" % (y, x, z)) def _mcaWidgetSignal(self, ddict): if not self.__ROIConnected: @@ -1248,7 +1255,7 @@ def test(): stackData = numpy.zeros((nrows, ncols, nchannels), numpy.float) for i in range(nchannels): stackData[:, :, i] = a * i - stackData[0:10,:,:] = 0 + stackData[0:10, :, :] = 0 w = QStackWidget() w.setStack(stackData, mcaindex=2) w.show() @@ -1256,6 +1263,7 @@ def test(): if __name__ == "__main__": + from PyMca5.PyMcaCore.LoggingLevel import getLoggingLevel sys.excepthook = qt.exceptionHandler try: opts, args = getopt.getopt( @@ -1263,7 +1271,7 @@ if __name__ == "__main__": options, longoptions) except: - print(sys.exc_info()[1]) + print("%s" % sys.exc_info()[1]) sys.exit(1) fileindex = 0 filepattern=None @@ -1273,6 +1281,7 @@ if __name__ == "__main__": increment=None backend=None PyMcaDirs.nativeFileDialogs=True + for opt, arg in opts: if opt in '--begin': if "," in arg: @@ -1290,22 +1299,25 @@ if __name__ == "__main__": else: increment = int(arg) elif opt in '--filepattern': - filepattern = arg.replace('"','') - filepattern = filepattern.replace("'","") + filepattern = arg.replace('"', '') + filepattern = filepattern.replace("'", "") elif opt in '--fileindex': fileindex = int(arg) elif opt in ['--imagestack', "--image"]: imagestack = int(arg) elif opt in '--nativefiledialogs': if int(arg): - PyMcaDirs.nativeFileDialogs=True + PyMcaDirs.nativeFileDialogs = True else: - PyMcaDirs.nativeFileDialogs=False + PyMcaDirs.nativeFileDialogs = False elif opt in '--backend': backend = arg #elif opt in '--old': # import QEDFStackWidget # sys.exit(QEDFStackWidget.runAsMain()) + + logging.basicConfig(level=getLoggingLevel(opts)) + if filepattern is not None: if (begin is None) or (end is None): raise ValueError("A file pattern needs at least a set of begin and end indices") @@ -1316,10 +1328,10 @@ if __name__ == "__main__": if backend is not None: # set the default backend try: - from PyMca5.PyMcaGraph.Plot import Plot - Plot.defaultBackend = backend + from silx.gui.plot import PlotWidget + PlotWidget.setDefaultBackend(backend) except: - print("WARNING: Cannot set backend to %s" % backend) + _logger.warning("WARNING: Cannot set backend to %s", backend) widget = QStackWidget() w = StackSelector.StackSelector(widget) if filepattern is not None: diff --git a/PyMca5/PyMcaGui/pymca/RGBCorrelator.py b/PyMca5/PyMcaGui/pymca/RGBCorrelator.py index 06d876b..500bb6b 100644 --- a/PyMca5/PyMcaGui/pymca/RGBCorrelator.py +++ b/PyMca5/PyMcaGui/pymca/RGBCorrelator.py @@ -157,6 +157,7 @@ class RGBCorrelator(qt.QWidget): qt.QWidget.show(self) def test(): + import logging app = qt.QApplication([]) app.lastWindowClosed.connect(app.quit) if 0: @@ -167,14 +168,15 @@ def test(): w = RGBCorrelator() w.resize(800, 600) import getopt - options='' - longoptions=[] + from PyMca5.PyMcaCore.LoggingLevel import getLoggingLevel + options = '' + longoptions = ["logging=", "debug="] opts, args = getopt.getopt( sys.argv[1:], options, longoptions) - for opt,arg in opts: - pass + + logging.basicConfig(level=getLoggingLevel(opts)) filelist=args if len(filelist): try: diff --git a/PyMca5/PyMcaGui/pymca/RGBCorrelatorSlider.py b/PyMca5/PyMcaGui/pymca/RGBCorrelatorSlider.py index d2995cf..59c85a0 100644 --- a/PyMca5/PyMcaGui/pymca/RGBCorrelatorSlider.py +++ b/PyMca5/PyMcaGui/pymca/RGBCorrelatorSlider.py @@ -27,11 +27,13 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import logging from PyMca5.PyMcaGui import DoubleSlider qt = DoubleSlider.qt QTVERSION = qt.qVersion() -DEBUG = 0 +_logger = logging.getLogger(__name__) + class RGBCorrelatorSlider(qt.QWidget): sigRGBCorrelatorSliderSignal = qt.pyqtSignal(object) @@ -145,22 +147,19 @@ class RGBCorrelatorSlider(qt.QWidget): self.sigRGBCorrelatorSliderSignal.emit(ddict) def _redSliderChanged(self, ddict): - if DEBUG: - print("RGBCorrelatorSlider._redSliderChanged()") + _logger.debug("RGBCorrelatorSlider._redSliderChanged()") if self.__emitSignals: ddict['event'] = "redChanged" self.sigRGBCorrelatorSliderSignal.emit(ddict) def _greenSliderChanged(self, ddict): - if DEBUG: - print("RGBCorrelatorSlider._greenSliderChanged()") + _logger.debug("RGBCorrelatorSlider._greenSliderChanged()") if self.__emitSignals: ddict['event'] = "greenChanged" self.sigRGBCorrelatorSliderSignal.emit(ddict) def _blueSliderChanged(self, ddict): - if DEBUG: - print("RGBCorrelatorSlider._blueSliderChanged()") + _logger.debug("RGBCorrelatorSlider._blueSliderChanged()") if self.__emitSignals: ddict['event'] = "blueChanged" self.sigRGBCorrelatorSliderSignal.emit(ddict) diff --git a/PyMca5/PyMcaGui/pymca/RGBCorrelatorWidget.py b/PyMca5/PyMcaGui/pymca/RGBCorrelatorWidget.py index 5057b12..596d494 100644 --- a/PyMca5/PyMcaGui/pymca/RGBCorrelatorWidget.py +++ b/PyMca5/PyMcaGui/pymca/RGBCorrelatorWidget.py @@ -31,6 +31,7 @@ import sys import os import numpy import traceback +import logging from . import RGBCorrelatorSlider from . import RGBCorrelatorTable from PyMca5.PyMcaGui.pymca import RGBImageCalculator @@ -73,7 +74,7 @@ try: except: TOMOGUI_FLAG = False -DEBUG = 0 +_logger = logging.getLogger(__name__) class RGBCorrelatorWidget(qt.QWidget): @@ -306,8 +307,7 @@ class RGBCorrelatorWidget(qt.QWidget): self.toggleSlidersButton.setIcon(self._slidersOnIcon) def _sliderSlot(self, ddict): - if DEBUG: - print("RGBCorrelatorWidget._sliderSlot()") + _logger.debug("RGBCorrelatorWidget._sliderSlot()") if self.__imageLength is None: return tableDict = self.tableWidget.getElementSelection() if ddict['event'] == 'redChanged': @@ -339,8 +339,7 @@ class RGBCorrelatorWidget(qt.QWidget): self.__recolor(['r', 'g', 'b']) def _tableSlot(self, ddict): - if DEBUG: - print("RGBCorrelatorWidget._tableSlot()") + _logger.debug("RGBCorrelatorWidget._tableSlot()") if self.__imageLength is None: return if ddict['r'] == []:ddict['r'] = None if ddict['g'] == []:ddict['g'] = None @@ -881,8 +880,7 @@ class RGBCorrelatorWidget(qt.QWidget): self.addImage(imgData, title) continue except: - if DEBUG: - print("Built-in tif support unsuccessful") + _logger.debug("Built-in tif support unsuccessful") pass #try a pure image format from PyQt qimage = qt.QImage(fname) @@ -939,7 +937,7 @@ class RGBCorrelatorWidget(qt.QWidget): msg.setInformativeText(str(sys.exc_info()[1])) msg.setDetailedText(traceback.format_exc()) msg.exec_() - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise def addBatchDatFile(self, filename, ignoresigma=None, csv=False): diff --git a/PyMca5/PyMcaGui/pymca/RGBImageCalculator.py b/PyMca5/PyMcaGui/pymca/RGBImageCalculator.py index a9cace6..20c304e 100644 --- a/PyMca5/PyMcaGui/pymca/RGBImageCalculator.py +++ b/PyMca5/PyMcaGui/pymca/RGBImageCalculator.py @@ -29,6 +29,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import numpy +import logging from PyMca5.PyMcaGui import MaskImageWidget from PyMca5.PyMcaGui import ColormapDialog from PyMca5 import spslut @@ -43,7 +44,8 @@ COLORMAPLIST = [spslut.GREYSCALE, spslut.REVERSEGREY, spslut.TEMP, spslut.RED, spslut.GREEN, spslut.BLUE, spslut.MANY] -DEBUG = 0 +_logger = logging.getLogger(__name__) + class RGBImageCalculator(qt.QWidget): sigAddImageClicked = qt.pyqtSignal(object) @@ -170,8 +172,7 @@ class RGBImageCalculator(qt.QWidget): return self.graphWidget.plotImage(update=update) def _calculateClicked(self): - if DEBUG: - print("Calculate clicked") + _logger.debug("Calculate clicked") text = "%s" % self.mathExpression.text() if not len(text): qt.QMessageBox.critical(self, "Calculation Error", @@ -217,8 +218,7 @@ class RGBImageCalculator(qt.QWidget): self.graphWidget.graph.setGraphTitle("%s" % name) def _addImageClicked(self): - if DEBUG: - print("Add image clicked") + _logger.debug("Add image clicked") if self._imageData is None: return if self._imageData == []: @@ -234,8 +234,7 @@ class RGBImageCalculator(qt.QWidget): self.sigAddImageClicked.emit(ddict) def _removeImageClicked(self): - if DEBUG: - print("remove image clicked") + _logger.debug("remove image clicked") text = "%s" % self.name.text() if not len(text): qt.QMessageBox.critical(self, "Name Error", @@ -244,8 +243,7 @@ class RGBImageCalculator(qt.QWidget): self.sigRemoveImageClicked.emit(text) def _replaceImageClicked(self): - if DEBUG: - print("replace image clicked") + _logger.debug("replace image clicked") text = "%s" % self.name.text() if not len(text): qt.QMessageBox.critical(self, "Name Error", diff --git a/PyMca5/PyMcaGui/pymca/ScanFitToolButton.py b/PyMca5/PyMcaGui/pymca/ScanFitToolButton.py new file mode 100644 index 0000000..150a3ad --- /dev/null +++ b/PyMca5/PyMcaGui/pymca/ScanFitToolButton.py @@ -0,0 +1,158 @@ +#/*########################################################################## +# Copyright (C) 2004-2017 V.A. Sole, European Synchrotron Radiation Facility +# +# This file is part of the PyMca X-ray Fluorescence Toolkit developed at +# the ESRF by the Software group. +# +# 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. +# +#############################################################################*/ +"""This module defines a QToolButton opening a fit menu when clicked: + + - :class:`ScanFitToolButton` + +This button takes a plot object as constructor parameter. +""" + + +import silx.gui.icons + +from PyMca5.PyMcaGui import PyMcaQt as qt +from PyMca5.PyMcaGui.math.fitting import SimpleFitGui +from PyMca5.PyMcaGui.pymca import ScanFit + + +if hasattr(qt, 'QString'): + QString = qt.QString +else: + QString = qt.safe_str + + +class ScanFitToolButton(qt.QToolButton): + def __init__(self, plot, parent=None): + """QAction offering a menu with two fit options: simple fit + and custom fit. + + :param plot: :class:`ScanWindow` instance on which to operate + :param parent: Parent QObject. If parent is an action group the action + will be automatically inserted into the group. + """ + qt.QToolButton.__init__(self, parent) + self.setIcon(silx.gui.icons.getQIcon('math-fit')) + self.setToolTip("Fit of Active Curve") + self.clicked.connect(self._buttonClicked) + + self.plot = plot + + if not hasattr(self.plot, "scanFit"): + self.scanFit = ScanFit.ScanFit() + else: + # ScanWindow can define a customized scanFit with custom fit functions + self.scanFit = self.plot.scanFit + self.scanFit.sigScanFitSignal.connect( + self._scanFitSignalReceived) + + self.customFit = SimpleFitGui.SimpleFitGui() + self.customFit.sigSimpleFitSignal.connect( + self._customFitSignalReceived) + + self.fitButtonMenu = qt.QMenu() + self.fitButtonMenu.addAction( + QString("Simple Fit"), + self._scanFitSignal) + self.fitButtonMenu.addAction( + QString("Customized Fit"), + self._customFitSignal) + + self._scanFitLegend = None + self._customFitLegend = None + + def _buttonClicked(self): + """Display a menu to select simple fit or custom fit. + + Selecting simple fit calls :meth:`_scanFitSignal`. + Selecting customized fit calls :meth:`_customFitSignal`. + """ + self.fitButtonMenu.exec_(self.plot.cursor().pos()) + + def _getOneCurve(self): + """Return active curve if any. Else return first curve, if any. + Else return None + :return: [x, y, legend, info, params] or None""" + curve = self.plot.getActiveCurve() + if curve is None: + curves = self.plot.getAllCurves() + if len(curves): + curve = curves[0] + return curve + + def _showFitWidget(self): + """Initialize fit dialog widget and raise it. + + :attr:`_activeFitDialog` must be set to :attr:`scanFit` or + :attr:`customFit` before this method is called. + """ + curve = self._getOneCurve() + if curve is None: + return + x, y, legend, info, params = curve + + xmin, xmax = self.plot.getGraphXLimits() + + fitLegend = legend + " Fit" + if fitLegend in self.plot.getAllCurves(just_legend=True): + self.plot.removeCurve(fitLegend) + + if self._activeFitDialog is self.scanFit: + self._scanFitLegend = fitLegend + elif self._activeFitDialog is self.customFit: + self._customFitLegend = fitLegend + + self._activeFitDialog.setData(x=x, + y=y, + xmin=xmin, + xmax=xmax, + legend=legend) + if self._activeFitDialog.isHidden(): + self._activeFitDialog.show() + self._activeFitDialog.raise_() + + def _scanFitSignal(self): + self._activeFitDialog = self.scanFit + self._showFitWidget() + + def _customFitSignal(self): + self._activeFitDialog = self.customFit + self._showFitWidget() + + def _scanFitSignalReceived(self, ddict): + if ddict['event'] == "FitFinished": + xplot = self.scanFit.specfit.xdata * 1.0 + yplot = self.scanFit.specfit.gendata(parameters=ddict['data']) + + self.plot.addCurve(x=xplot, y=yplot, legend=self._scanFitLegend, + resetzoom=False) + + def _customFitSignalReceived(self, ddict): + if ddict['event'] == "FitFinished": + xplot = ddict['x'] + yplot = ddict['yfit'] + + self.plot.addCurve(xplot, yplot, legend=self._customFitLegend, + resetzoom=False) diff --git a/PyMca5/PyMcaGui/pymca/ScanWindow.py b/PyMca5/PyMcaGui/pymca/ScanWindow.py index 4f025de..37ea258 100644 --- a/PyMca5/PyMcaGui/pymca/ScanWindow.py +++ b/PyMca5/PyMcaGui/pymca/ScanWindow.py @@ -23,219 +23,343 @@ # THE SOFTWARE. # #############################################################################*/ -__author__ = "V.A. Sole - ESRF Data Analysis" -__contact__ = "sole@esrf.fr" -__license__ = "MIT" -__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import sys +"""This module defines a :class:`ScanWindow` inheriting a *silx* +:class:`PlotWindow` with additional tools and actions. +The main addition is a :class:`PluginsToolButton` button added to the toolbar, +to open a menu with plugins.""" + import os +import copy +import logging import numpy -#from numpy import argsort, nonzero, take -import time -import traceback +import sys + +from silx.gui.plot import PlotWindow +from silx.gui.plot.PrintPreviewToolButton import SingletonPrintPreviewToolButton + +import PyMca5 +from PyMca5.PyMcaGui.pymca import ScanWindowInfoWidget from PyMca5.PyMcaGui import PyMcaQt as qt +from PyMca5.PyMcaGui.PluginsToolButton import PluginsToolButton +from PyMca5.PyMcaGui.math import SimpleActions +from PyMca5.PyMcaGui.pymca import ScanFit +from PyMca5.PyMcaGui.pymca.ScanFitToolButton import ScanFitToolButton +from PyMca5.PyMcaCore import DataObject +from PyMca5.PyMcaGui.pymca import QPyMcaMatplotlibSave1D +from PyMca5.PyMcaGui.plotting.PyMca_Icons import change_icons + if hasattr(qt, 'QString'): QString = qt.QString else: QString = qt.safe_str -if __name__ == "__main__": - app = qt.QApplication([]) - -from PyMca5.PyMcaGui.io import PyMcaFileDialogs -from PyMca5.PyMcaGui.plotting import PlotWindow -from . import ScanFit -from PyMca5.PyMcaMath import SimpleMath -from PyMca5.PyMcaCore import DataObject -import copy -from PyMca5.PyMcaGui import PyMcaPrintPreview -from PyMca5.PyMcaCore import PyMcaDirs -from . import ScanWindowInfoWidget -#implement the plugins interface -from PyMca5.PyMcaGui import QPyMcaMatplotlibSave1D -MATPLOTLIB = True -#force understanding of utf-8 encoding -#otherways it cannot generate svg output -try: - import encodings.utf_8 -except: - #not a big problem - pass PLUGINS_DIR = None -try: - import PyMca5 - if os.path.exists(os.path.join(os.path.dirname(PyMca5.__file__), "PyMcaPlugins")): - from PyMca5 import PyMcaPlugins - PLUGINS_DIR = os.path.dirname(PyMcaPlugins.__file__) + +if os.path.exists(os.path.join(os.path.dirname(PyMca5.__file__), "PyMcaPlugins")): + from PyMca5 import PyMcaPlugins + PLUGINS_DIR = os.path.dirname(PyMcaPlugins.__file__) +else: + directory = os.path.dirname(__file__) + while True: + if os.path.exists(os.path.join(directory, "PyMcaPlugins")): + PLUGINS_DIR = os.path.join(directory, "PyMcaPlugins") + break + directory = os.path.dirname(directory) + if len(directory) < 5: + break +userPluginsDirectory = PyMca5.getDefaultUserPluginsDirectory() +if userPluginsDirectory is not None: + if PLUGINS_DIR is None: + PLUGINS_DIR = userPluginsDirectory else: - directory = os.path.dirname(__file__) - while True: - if os.path.exists(os.path.join(directory, "PyMcaPlugins")): - PLUGINS_DIR = os.path.join(directory, "PyMcaPlugins") - break - directory = os.path.dirname(directory) - if len(directory) < 5: - break - userPluginsDirectory = PyMca5.getDefaultUserPluginsDirectory() - if userPluginsDirectory is not None: - if PLUGINS_DIR is None: - PLUGINS_DIR = userPluginsDirectory - else: - PLUGINS_DIR = [PLUGINS_DIR, userPluginsDirectory] -except: - pass - -DEBUG = 0 - -class ScanWindow(PlotWindow.PlotWindow): - def __init__(self, parent=None, name="Scan Window", specfit=None, backend=None, - plugins=True, newplot=True, roi=True, fit=True, - control=True, position=True, info=False, **kw): - - super(ScanWindow, self).__init__(parent, - newplot=newplot, - plugins=plugins, - backend=backend, - roi=roi, - fit=fit, - control=control, - position=position, - **kw) + PLUGINS_DIR = [PLUGINS_DIR, userPluginsDirectory] + + +_logger = logging.getLogger(__name__) +# _logger.setLevel(logging.DEBUG) + + +class BaseScanWindow(PlotWindow): + """:class:`PlotWindow` augmented with plugins, fitting actions, + a widget for displaying scan metadata and simple curve processing actions. + """ + def __init__(self, parent=None, name="Scan Window", fit=True, backend=None, + plugins=True, control=True, position=True, roi=True, + specfit=None, info=False, save=True): + super(BaseScanWindow, self).__init__(parent, + backend=backend, + roi=roi, + control=control, + position=position, + save=save, + mask=False, + colormap=False, + aspectRatio=False, + yInverted=False, + copy=True, + print_=False) self.setDataMargins(0, 0, 0.025, 0.025) - #self._togglePointsSignal() + self.setPanWithArrowKeys(True) - self.setWindowType("SCAN") - # this two objects are the same - self.dataObjectsList = self._curveList - # but this is tricky - self.dataObjectsDict = {} + self._plotType = "SCAN" # needed by legacy plugins self.setWindowTitle(name) - self.matplotlibDialog = None - if PLUGINS_DIR is not None: - if type(PLUGINS_DIR) == type([]): - pluginDir = PLUGINS_DIR - else: - pluginDir = [PLUGINS_DIR] - self.getPlugins(method="getPlugin1DInstance", - directoryList=pluginDir) + # No context menu by default, execute zoomBack on right click + plotArea = self.getWidgetHandle() + plotArea.setContextMenuPolicy(qt.Qt.CustomContextMenu) + plotArea.customContextMenuRequested.connect(self._zoomBack) + + # Toolbar: + # hide interactive toolbar (zoom and pan mode buttons) + self.getInteractiveModeToolBar().setVisible(False) + # additional buttons + self._mathToolBar = qt.QToolBar(self) + + self.addToolBar(self._mathToolBar) + + self.fitToolButton = None + self.scanFit = None + if fit: + self.scanFit = ScanFit.ScanFit(specfit=specfit) + self.fitToolButton = ScanFitToolButton(self) + self.toolBar().insertWidget(self.getMaskAction(), # before MaskAction (hidden) + self.fitToolButton) + + self.avgAction = SimpleActions.AverageAction(plot=self) + self.derivativeAction = SimpleActions.DerivativeAction(plot=self) + self.smoothAction = SimpleActions.SmoothAction(plot=self) + self.swapSignAction = SimpleActions.SwapSignAction(plot=self) + self.yMinToZero = SimpleActions.YMinToZeroAction(plot=self) + self.subtractAction = SimpleActions.SubtractAction(plot=self) + + self._mathToolBar.addAction(self.avgAction) + self._mathToolBar.addAction(self.derivativeAction) + self._mathToolBar.addAction(self.smoothAction) + self._mathToolBar.addAction(self.swapSignAction) + self._mathToolBar.addAction(self.yMinToZero) + self._mathToolBar.addAction(self.subtractAction) + + self.pluginsToolButton = None + """Plugins tool button, used to load and call plugins. + It inherits the PluginLoader API: + + - getPlugins + - getPluginDirectoryList + - setPluginDirectoryList + + It can be None, if plugins are disabled when initializing + the ScanWindow. + """ + + if plugins: + self.pluginsToolButton = PluginsToolButton(plot=self) + + if PLUGINS_DIR is not None: + if isinstance(PLUGINS_DIR, list): + pluginDir = PLUGINS_DIR + else: + pluginDir = [PLUGINS_DIR] + self.pluginsToolButton.getPlugins( + method="getPlugin1DInstance", + directoryList=pluginDir) + self.pluginsAction = self._mathToolBar.addWidget(self.pluginsToolButton) + + self._printPreviewToolBar = qt.QToolBar(self) + self._printPreviewToolBar.setMovable(False) + self._printPreviewToolBar.setFloatable(False) + self.addToolBar(self._printPreviewToolBar) + self._printPreviewToolBar.addWidget(qt.HorizontalSpacer(self._printPreviewToolBar)) + self.printPreview = SingletonPrintPreviewToolButton(parent=self._printPreviewToolBar, + plot=self) + self.printPreviewAction = self._printPreviewToolBar.addWidget(self.printPreview) + + self.scanWindowInfoWidget = None + self.infoDockWidget = None if info: self.scanWindowInfoWidget = ScanWindowInfoWidget.\ ScanWindowInfoWidget() self.infoDockWidget = qt.QDockWidget(self) self.infoDockWidget.layout().setContentsMargins(0, 0, 0, 0) self.infoDockWidget.setWidget(self.scanWindowInfoWidget) - self.infoDockWidget.setWindowTitle(self.windowTitle()+(" Info")) + self.infoDockWidget.setWindowTitle("Scan Info") self.addDockWidget(qt.Qt.BottomDockWidgetArea, - self.infoDockWidget) - controlMenu = qt.QMenu() - controlMenu.addAction(QString("Show/Hide Legends"), - self.toggleLegendWidget) - controlMenu.addAction(QString("Show/Hide Info"), - self._toggleInfoWidget) - controlMenu.addAction(QString("Toggle Crosshair"), - self.toggleCrosshairCursor) - controlMenu.addAction(QString("Toggle Arrow Keys Panning"), - self.toggleArrowKeysPanning) - self.setControlMenu(controlMenu) - else: - self.scanWindowInfoWidget = None - #self.fig = None - if fit: - self.scanFit = ScanFit.ScanFit(specfit=specfit) - self.printPreview = PyMcaPrintPreview.PyMcaPrintPreview(modal = 0) - self.simpleMath = SimpleMath.SimpleMath() - self.outputDir = None - self.outputFilter = None - - #signals - # this one was made in the base class - #self.setCallback(self.graphCallback) - if fit: - from PyMca5.PyMcaGui.math.fitting import SimpleFitGui - self.customFit = SimpleFitGui.SimpleFitGui() - self.scanFit.sigScanFitSignal.connect(self._scanFitSignalReceived) - self.customFit.sigSimpleFitSignal.connect( \ - self._customFitSignalReceived) - - self.fitButtonMenu = qt.QMenu() - self.fitButtonMenu.addAction(QString("Simple Fit"), - self._simpleFitSignal) - self.fitButtonMenu.addAction(QString("Customized Fit") , - self._customFitSignal) - - def _toggleInfoWidget(self): - if self.infoDockWidget.isHidden(): - self.infoDockWidget.show() - legend = self.getActiveCurve(just_legend=True) - if legend is not None: - ddict ={} - ddict['event'] = "curveClicked" - ddict['label'] = legend - ddict['legend'] = legend - self.graphCallback(ddict) - else: - self.infoDockWidget.hide() - - def _buildLegendWidget(self): - if self.legendWidget is None: - super(ScanWindow, self)._buildLegendWidget() - if hasattr(self, "infoDockWidget") and \ - hasattr(self, "roiDockWidget"): - self.tabifyDockWidget(self.infoDockWidget, - self.roiDockWidget, - self.legendDockWidget) - elif hasattr(self, "infoDockWidget"): - self.tabifyDockWidget(self.infoDockWidget, - self.legendDockWidget) - - def _toggleROI(self, position=None): - super(ScanWindow, self)._toggleROI(position=position) - if hasattr(self, "infoDockWidget"): - self.tabifyDockWidget(self.infoDockWidget, - self.roiDockWidget) + self.infoDockWidget) + + self.sigActiveCurveChanged.connect(self.__updateInfoWidget) + + self.sigActiveCurveChanged.connect(self.__updateGraphTitle) + self.matplotlibDialog = None + + saveAction = self.getOutputToolBar().getSaveAction() + for ext in ["png", "eps", "svg"]: + name_filter = 'Customized graphics (*.%s)' % ext + # if silx-kit/silx#2013 is merged, the following line can be removed for silx 0.9 + saveAction.setFileFilter(dataKind='curve', # single curve case + nameFilter=name_filter, + func=self._graphicsSave) + saveAction.setFileFilter(dataKind='curves', + nameFilter=name_filter, + func=self._graphicsSave) + + change_icons(self) + + def _customControlButtonMenu(self): + """Display Options button sub-menu. Overloaded to add + _toggleInfoAction""" + # overloaded from PlotWindow to add "Show/Hide Info" + controlMenu = self.controlButton.menu() + controlMenu.clear() + controlMenu.addAction(self.getLegendsDockWidget().toggleViewAction()) + + if self.infoDockWidget is not None: + controlMenu.addAction(self.infoDockWidget.toggleViewAction()) + controlMenu.addAction(self.getRoiAction()) + controlMenu.addAction(self.getMaskAction()) + controlMenu.addAction(self.getConsoleAction()) + + controlMenu.addSeparator() + controlMenu.addAction(self.getCrosshairAction()) + controlMenu.addAction(self.getPanWithArrowKeysAction()) + + def __updateInfoWidget(self, previous_legend, legend): + """Called on active curve changed, to update the info widget""" + x, y, legend, info, params = self.getCurve(legend) + self.scanWindowInfoWidget.updateFromXYInfo(x, y, info) + + def __updateGraphTitle(self, previous_legend, legend): + """Called on active curve changed, to update the graph title""" + if legend is None and previous_legend is not None: + self.setGraphTitle() + elif legend is not None: + self.setGraphTitle(legend) + + def setWindowType(self, wtype=None): + if wtype not in [None, "SCAN", "MCA"]: + raise AttributeError("Unsupported window type %s." % wtype) + self._plotType = wtype + + def _zoomBack(self, pos): + self.getLimitsHistory().pop() + + def _graphicsSave(self, plot, filename, nameFilter=""): + # note: the method's signature must conform to + # saveAction.setFileFilter requirements + x, y, legend, info = plot.getActiveCurve()[:4] + curveList = plot.getAllCurves() + size = (6, 3) # in inches + legends = len(curveList) > 1 + if self.matplotlibDialog is None: + self.matplotlibDialog = QPyMcaMatplotlibSave1D.\ + QPyMcaMatplotlibSaveDialog(size=size, + logx=plot.isXAxisLogarithmic(), + logy=plot.isYAxisLogarithmic(), + legends=legends, + bw=False) + + mtplt = self.matplotlibDialog.plot + + mtplt.setParameters({'logy': plot.isXAxisLogarithmic(), + 'logx': plot.isYAxisLogarithmic(), + 'legends': legends, + 'bw': False}) + xmin, xmax = plot.getGraphXLimits() + ymin, ymax = plot.getGraphYLimits() + mtplt.setLimits(xmin, xmax, ymin, ymax) + + legend0 = legend + dataCounter = 1 + alias = "%c" % (96 + dataCounter) + mtplt.addDataToPlot(x, y, legend=legend0, alias=alias) + for curve in curveList: + x, y, legend, info = curve[0:4] + if legend == legend0: + continue + dataCounter += 1 + alias = "%c" % (96 + dataCounter) + mtplt.addDataToPlot(x, y, legend=legend, alias=alias) + + self.matplotlibDialog.setXLabel(plot.getGraphXLabel()) + self.matplotlibDialog.setYLabel(plot.getGraphYLabel()) + + if legends: + mtplt.plotLegends() + ret = self.matplotlibDialog.exec_() + if ret == qt.QDialog.Accepted: + mtplt.saveFile(filename) + return + + +class ScanWindow(BaseScanWindow): + """ScanWindow, adding dataObject management to BaseScanWindow + """ + + def __init__(self, parent=None, name="Scan Window", fit=True, backend=None, + plugins=True, control=True, position=True, roi=True, + specfit=None, info=False, save=True): + BaseScanWindow.__init__(self, + parent, name, fit, backend, + plugins, control, position, roi, + specfit, info, save) + + self.dataObjectsDict = {} + + self.sigContentChanged.connect(self._handleContentChanged) + + @property + def dataObjectsList(self): + return self.getAllCurves(just_legend=True) + + @property + def _curveList(self): + return self.getAllCurves(just_legend=True) + + def _handleContentChanged(self, action, kind, legend): + if action == 'remove' and kind == "curve": + self.removeCurves([legend]) def setDispatcher(self, w): w.sigAddSelection.connect(self._addSelection) w.sigRemoveSelection.connect(self._removeSelection) w.sigReplaceSelection.connect(self._replaceSelection) - def _addSelection(self, selectionlist, replot=True): - if DEBUG: - print("_addSelection(self, selectionlist)",selectionlist) - if type(selectionlist) == type([]): - sellist = selectionlist - else: - sellist = [selectionlist] + def _addSelection(self, selectionlist, resetzoom=True, replot=None): + """Add curves to plot and data objects to :attr:`dataObjectsDict` + """ + _logger.debug("_addSelection(self, selectionlist) " + + str(selectionlist)) + if replot is not None: + _logger.warning( + 'deprecated replot argument, use resetzoom instead') + resetzoom = replot and resetzoom + + sellist = selectionlist if isinstance(selectionlist, list) else \ + [selectionlist] - if len(self._curveList): + if len(self.getAllCurves(just_legend=True)): activeCurve = self.getActiveCurve(just_legend=True) else: activeCurve = None nSelection = len(sellist) for selectionIndex in range(nSelection): sel = sellist[selectionIndex] - if selectionIndex == (nSelection - 1): - actualReplot = replot - else: - actualReplot = False - source = sel['SourceName'] - key = sel['Key'] - legend = sel['legend'] #expected form sourcename + scan key - if not ("scanselection" in sel): continue - if sel['scanselection'] == "MCA": + key = sel['Key'] + legend = sel['legend'] # expected form sourcename + scan key + if "scanselection" not in sel or not sel["scanselection"] or \ + sel['scanselection'] == "MCA": + continue + if len(key.split(".")) > 2: continue - if not sel["scanselection"]:continue - if len(key.split(".")) > 2: continue dataObject = sel['dataobject'] - #only one-dimensional selections considered - if dataObject.info["selectiontype"] != "1D": continue + # only one-dimensional selections considered + if dataObject.info["selectiontype"] != "1D": + continue - #there must be something to plot + # there must be something to plot if not hasattr(dataObject, 'y'): continue + if len(dataObject.y) == 0: # nothing to be plot continue @@ -250,27 +374,21 @@ class ScanWindow(PlotWindow.PlotWindow): else: #nothing to be plot continue - if dataObject.x is None: + if getattr(dataObject, 'x', None) is None: ylen = len(dataObject.y[0]) - if ylen: - xdata = numpy.arange(ylen).astype(numpy.float) - else: - #nothing to be plot + if not ylen: + # nothing to be plot continue + xdata = numpy.arange(ylen).astype(numpy.float) elif len(dataObject.x) > 1: - if DEBUG: - print("Mesh plots") + # mesh plot continue else: if numpy.isscalar(dataObject.x[0]): dataObject.x[0] = numpy.array([dataObject.x[0]]) xdata = dataObject.x[0] - sps_source = False - if 'SourceType' in sel: - if sel['SourceType'] == 'SPS': - sps_source = True - if sps_source: + if sel.get('SourceType') == "SPS": ycounter = -1 if 'selection' not in dataObject.info: dataObject.info['selection'] = copy.deepcopy(sel['selection']) @@ -278,66 +396,60 @@ class ScanWindow(PlotWindow.PlotWindow): xlabel = None ylabel = None ycounter += 1 - if dataObject.m is None: - mdata = [numpy.ones(len(ydata)).astype(numpy.float)] - elif len(dataObject.m[0]) > 0: - if len(dataObject.m[0]) == len(ydata): - index = numpy.nonzero(dataObject.m[0])[0] - if not len(index): - continue - xdata = numpy.take(xdata, index) - ydata = numpy.take(ydata, index) - mdata = numpy.take(dataObject.m[0], index) - #A priori the graph only knows about plots - ydata = ydata/mdata - else: + # normalize ydata with monitor + if dataObject.m is not None and len(dataObject.m[0]) > 0: + if len(dataObject.m[0]) != len(ydata): raise ValueError("Monitor data length different than counter data") - else: - mdata = [numpy.ones(len(ydata)).astype(numpy.float)] + index = numpy.nonzero(dataObject.m[0])[0] + if not len(index): + continue + xdata = numpy.take(xdata, index) + ydata = numpy.take(ydata, index) + mdata = numpy.take(dataObject.m[0], index) + # A priori the graph only knows about plots + ydata = ydata / mdata ylegend = 'y%d' % ycounter - if dataObject.info['selection'] is not None: - if type(dataObject.info['selection']) == type({}): - if 'x' in dataObject.info['selection']: - #proper scan selection - ilabel = dataObject.info['selection']['y'][ycounter] - ylegend = dataObject.info['LabelNames'][ilabel] - ylabel = ylegend - if sel['selection']['x'] is not None: - if len(dataObject.info['selection']['x']): - xlabel = dataObject.info['LabelNames'] \ - [dataObject.info['selection']['x'][0]] + if isinstance(dataObject.info['selection'], dict): + if 'x' in dataObject.info['selection']: + # proper scan selection + ilabel = dataObject.info['selection']['y'][ycounter] + ylegend = dataObject.info['LabelNames'][ilabel] + ylabel = ylegend + if sel['selection']['x'] is not None: + if len(dataObject.info['selection']['x']): + xlabel = dataObject.info['LabelNames'] \ + [dataObject.info['selection']['x'][0]] dataObject.info["xlabel"] = xlabel dataObject.info["ylabel"] = ylabel newLegend = legend + " " + ylegend self.dataObjectsDict[newLegend] = dataObject self.addCurve(xdata, ydata, legend=newLegend, info=dataObject.info, - xlabel=xlabel, ylabel=ylabel, replot=False) - # replot=actualReplot) + xlabel=xlabel, ylabel=ylabel, resetzoom=False) if self.scanWindowInfoWidget is not None: if not self.infoDockWidget.isHidden(): activeLegend = self.getActiveCurve(just_legend=True) - if activeLegend is not None: - if activeLegend == newLegend: - self.scanWindowInfoWidget.updateFromDataObject\ - (dataObject) + if activeLegend == newLegend: + self.scanWindowInfoWidget.updateFromDataObject \ + (dataObject) else: + # TODO: better to implement scanWindowInfoWidget.clear dummyDataObject = DataObject.DataObject() - dummyDataObject.y=[numpy.array([])] - dummyDataObject.x=[numpy.array([])] + dummyDataObject.y = [numpy.array([])] + dummyDataObject.x = [numpy.array([])] self.scanWindowInfoWidget.updateFromDataObject(dummyDataObject) else: - #we have to loop for all y values + # we have to loop for all y values ycounter = -1 for ydata in dataObject.y: ylen = len(ydata) - if ylen == 1: - if len(xdata) > 1: - ydata = ydata[0] * numpy.ones(len(xdata)).astype(numpy.float) + if ylen == 1 and len(xdata) > 1: + ydata = ydata[0] * numpy.ones(len(xdata)).astype(numpy.float) elif len(xdata) == 1: xdata = xdata[0] * numpy.ones(ylen).astype(numpy.float) ycounter += 1 - newDataObject = DataObject.DataObject() + newDataObject = DataObject.DataObject() newDataObject.info = copy.deepcopy(dataObject.info) + if dataObject.m is not None: for imon in range(len(dataObject.m)): if numpy.isscalar(dataObject.m[imon]): @@ -345,31 +457,29 @@ class ScanWindow(PlotWindow.PlotWindow): numpy.array([dataObject.m[imon]]) if dataObject.m is None: mdata = numpy.ones(len(ydata)).astype(numpy.float) - elif len(dataObject.m[0]) > 0: - if len(dataObject.m[0]) == len(ydata): - index = numpy.nonzero(dataObject.m[0])[0] - if not len(index): - continue - xdata = numpy.take(xdata, index) - ydata = numpy.take(ydata, index) - mdata = numpy.take(dataObject.m[0], index) - #A priori the graph only knows about plots - ydata = ydata/mdata - elif len(dataObject.m[0]) == 1: - mdata = numpy.ones(len(ydata)).astype(numpy.float) - mdata *= dataObject.m[0][0] - index = numpy.nonzero(dataObject.m[0])[0] - if not len(index): - continue - xdata = numpy.take(xdata, index) - ydata = numpy.take(ydata, index) - mdata = numpy.take(dataObject.m[0], index) - #A priori the graph only knows about plots - ydata = ydata/mdata - else: - raise ValueError("Monitor data length different than counter data") - else: + elif len(dataObject.m[0]) == len(ydata): + index = numpy.nonzero(dataObject.m[0])[0] + if not len(index): + continue + xdata = numpy.take(xdata, index) + ydata = numpy.take(ydata, index) + mdata = numpy.take(dataObject.m[0], index) + # A priori the graph only knows about plots + ydata = ydata / mdata + elif len(dataObject.m[0]) == 1: mdata = numpy.ones(len(ydata)).astype(numpy.float) + mdata *= dataObject.m[0][0] + index = numpy.nonzero(dataObject.m[0])[0] + if not len(index): + continue + xdata = numpy.take(xdata, index) + ydata = numpy.take(ydata, index) + mdata = numpy.take(dataObject.m[0], index) + # A priori the graph only knows about plots + ydata = ydata / mdata + else: + raise ValueError("Monitor data length different than counter data") + newDataObject.x = [xdata] newDataObject.y = [ydata] newDataObject.m = [mdata] @@ -377,26 +487,24 @@ class ScanWindow(PlotWindow.PlotWindow): ylegend = 'y%d' % ycounter xlabel = None ylabel = None - if sel['selection'] is not None: - if type(sel['selection']) == type({}): - if 'x' in sel['selection']: - #proper scan selection - newDataObject.info['selection']['x'] = sel['selection']['x'] - newDataObject.info['selection']['y'] = [sel['selection']['y'][ycounter]] - newDataObject.info['selection']['m'] = sel['selection']['m'] - ilabel = newDataObject.info['selection']['y'][0] - ylegend = newDataObject.info['LabelNames'][ilabel] - ylabel = ylegend - if len(newDataObject.info['selection']['x']): - ilabel = newDataObject.info['selection']['x'][0] - xlabel = newDataObject.info['LabelNames'][ilabel] - else: - xlabel = "Point number" + if isinstance(sel['selection'], dict) and 'x' in sel['selection']: + # proper scan selection + newDataObject.info['selection']['x'] = sel['selection']['x'] + newDataObject.info['selection']['y'] = [sel['selection']['y'][ycounter]] + newDataObject.info['selection']['m'] = sel['selection']['m'] + ilabel = newDataObject.info['selection']['y'][0] + ylegend = newDataObject.info['LabelNames'][ilabel] + ylabel = ylegend + if len(newDataObject.info['selection']['x']): + ilabel = newDataObject.info['selection']['x'][0] + xlabel = newDataObject.info['LabelNames'][ilabel] + else: + xlabel = "Point number" if ('operations' in dataObject.info) and len(dataObject.y) == 1: newDataObject.info['legend'] = legend symbol = 'x' else: - symbol=None + symbol = None newDataObject.info['legend'] = legend + " " + ylegend newDataObject.info['selectionlegend'] = legend yaxis = None @@ -405,925 +513,153 @@ class ScanWindow(PlotWindow.PlotWindow): elif 'operations' in dataObject.info: if dataObject.info['operations'][-1] == 'derivate': yaxis = 'right' - #print("sending legend = ", newDataObject.info['legend'], "replot = ", False) self.dataObjectsDict[newDataObject.info['legend']] = newDataObject self.addCurve(xdata, ydata, legend=newDataObject.info['legend'], - info=newDataObject.info, - symbol=symbol, - yaxis=yaxis, - xlabel=xlabel, - ylabel=ylabel, - replot=False) - self.dataObjectsList = self._curveList + info=newDataObject.info, + symbol=symbol, + yaxis=yaxis, + xlabel=xlabel, + ylabel=ylabel, + resetzoom=False) try: - if activeCurve is None: - if len(self._curveList) > 0: - activeCurve = self._curveList[0] - ddict = {} - ddict['event'] = "curveClicked" - ddict['label'] = activeCurve - self.graphCallback(ddict) + if activeCurve is None and self._curveList: + self.setActiveCurve(self._curveList[0]) finally: - if replot: - #self.replot() + if resetzoom: self.resetZoom() - self.updateLegends() def _removeSelection(self, selectionlist): - if DEBUG: - print("_removeSelection(self, selectionlist)",selectionlist) - if type(selectionlist) == type([]): - sellist = selectionlist - else: - sellist = [selectionlist] + _logger.debug("_removeSelection(self, selectionlist) " + + str(selectionlist)) + + sellist = selectionlist if isinstance(selectionlist, list) else \ + [selectionlist] removelist = [] for sel in sellist: - source = sel['SourceName'] - key = sel['Key'] - if not ("scanselection" in sel): continue + key = sel['Key'] + if "scanselection" not in sel or not sel["scanselection"]: + continue if sel['scanselection'] == "MCA": continue - if not sel["scanselection"]:continue - if len(key.split(".")) > 2: continue - - legend = sel['legend'] #expected form sourcename + scan key - if type(sel['selection']) == type({}): - if 'y' in sel['selection']: - for lName in ['cntlist', 'LabelNames']: - if lName in sel['selection']: - for index in sel['selection']['y']: - removelist.append(legend +" "+\ - sel['selection'][lName][index]) + if len(key.split(".")) > 2: + continue + + legend = sel['legend'] # expected form sourcename + scan key + if isinstance(sel['selection'], dict) and 'y' in sel['selection']: + for lName in ['cntlist', 'LabelNames']: + if lName in sel['selection']: + for index in sel['selection']['y']: + removelist.append(legend + " " + + sel['selection'][lName][index]) if len(removelist): self.removeCurves(removelist) - def removeCurves(self, removeList, replot=True): - for legend in removeList: - if legend == removeList[-1]: - self.removeCurve(legend, replot=replot) - else: - self.removeCurve(legend, replot=False) - if legend in self.dataObjectsDict: - del self.dataObjectsDict[legend] - self.dataObjectsList = self._curveList - def _replaceSelection(self, selectionlist): - if DEBUG: - print("_replaceSelection(self, selectionlist)",selectionlist) - if type(selectionlist) == type([]): - sellist = selectionlist - else: - sellist = [selectionlist] + """Delete existing curves and data objects, then add new selection. + """ + _logger.debug("_replaceSelection(self, selectionlist) " + + str(selectionlist)) + + sellist = selectionlist if isinstance(selectionlist, list) else \ + [selectionlist] - doit = 0 + doit = False for sel in sellist: - if not ("scanselection" in sel): continue + if "scanselection" not in sel or not sel["scanselection"]: + continue if sel['scanselection'] == "MCA": continue - if not sel["scanselection"]:continue - if len(sel["Key"].split(".")) > 2: continue + if len(sel["Key"].split(".")) > 2: + continue dataObject = sel['dataobject'] if dataObject.info["selectiontype"] == "1D": if hasattr(dataObject, 'y'): - doit = 1 + doit = True break if not doit: return self.clearCurves() - self.dataObjectsDict={} - self.dataObjectsList=self._curveList - self._addSelection(selectionlist, replot=True) - - def _handleMarkerEvent(self, ddict): - if ddict['event'] == 'markerMoved': - label = ddict['label'] - if label.startswith('ROI'): - return self._handleROIMarkerEvent(ddict) - else: - if DEBUG: - print("Unhandled marker %s" % label) - return - - def graphCallback(self, ddict): - if DEBUG: - print("graphCallback", ddict) - if ddict['event'] in ['markerMoved', 'markerSelected']: - self._handleMarkerEvent(ddict) - elif ddict['event'] in ["mouseMoved", "MouseAt"]: - if self._toggleCounter > 0: - activeCurve = self.getActiveCurve() - if activeCurve in [None, []]: - self._handleMouseMovedEvent(ddict) - else: - x, y, legend, info = activeCurve[0:4] - # calculate the maximum distance - xMin, xMax = self.getGraphXLimits() - maxXDistance = abs(xMax - xMin) - yMin, yMax = self.getGraphYLimits() - maxYDistance = abs(yMax - yMin) - if (maxXDistance > 0.0) and (maxYDistance > 0.0): - closestIndex = (pow((x - ddict['x'])/maxXDistance, 2) + \ - pow((y - ddict['y'])/maxYDistance, 2)) - else: - closestIndex = (pow(x - ddict['x'], 2) + \ - pow(y - ddict['y'], 2)) - xText = '----' - yText = '----' - if len(closestIndex): - closestIndex = closestIndex.argmin() - xCurve = x[closestIndex] - if abs(xCurve - ddict['x']) < (0.05 * maxXDistance): - yCurve = y[closestIndex] - if abs(yCurve - ddict['y']) < (0.05 * maxYDistance): - xText = '%.7g' % xCurve - yText = '%.7g' % yCurve - if xText == '----': - if self.getGraphCursor(): - self._xPos.setStyleSheet("color: rgb(255, 0, 0);") - self._yPos.setStyleSheet("color: rgb(255, 0, 0);") - xText = '%.7g' % ddict['x'] - yText = '%.7g' % ddict['y'] - else: - self._xPos.setStyleSheet("color: rgb(0, 0, 0);") - self._yPos.setStyleSheet("color: rgb(0, 0, 0);") - else: - self._xPos.setStyleSheet("color: rgb(0, 0, 0);") - self._yPos.setStyleSheet("color: rgb(0, 0, 0);") - self._xPos.setText(xText) - self._yPos.setText(yText) - else: - self._xPos.setStyleSheet("color: rgb(0, 0, 0);") - self._yPos.setStyleSheet("color: rgb(0, 0, 0);") - self._handleMouseMovedEvent(ddict) - elif ddict['event'] in ["curveClicked", "legendClicked"]: - legend = ddict["label"] - if legend is None: - if len(self.dataObjectsList): - legend = self.dataObjectsList[0] - else: - return - if legend not in self.dataObjectsList: - if DEBUG: - print("unknown legend %s" % legend) - return - - #force the current x label to the appropriate value - dataObject = self.dataObjectsDict[legend] - if 'selection' in dataObject.info: - ilabel = dataObject.info['selection']['y'][0] - ylabel = dataObject.info['LabelNames'][ilabel] - if len(dataObject.info['selection']['x']): - ilabel = dataObject.info['selection']['x'][0] - xlabel = dataObject.info['LabelNames'][ilabel] - else: - xlabel = "Point Number" - if len(dataObject.info['selection']['m']): - ilabel = dataObject.info['selection']['m'][0] - ylabel += "/" + dataObject.info['LabelNames'][ilabel] - else: - xlabel = dataObject.info.get('xlabel', None) - ylabel = dataObject.info.get('ylabel', None) - if xlabel is not None: - self.setGraphXLabel(xlabel) - if ylabel is not None: - self.setGraphYLabel(ylabel) - self.setGraphTitle(legend) - self.setActiveCurve(legend) - #self.setGraphTitle(legend) - if self.scanWindowInfoWidget is not None: - if not self.infoDockWidget.isHidden(): - self.scanWindowInfoWidget.updateFromDataObject\ - (dataObject) - elif ddict['event'] == "removeCurveEvent": - legend = ddict['legend'] - self.removeCurves([legend]) - elif ddict['event'] == "renameCurveEvent": - legend = ddict['legend'] - newlegend = ddict['newlegend'] - if legend in self.dataObjectsDict: - self.dataObjectsDict[newlegend]= copy.deepcopy(self.dataObjectsDict[legend]) - self.dataObjectsDict[newlegend].info['legend'] = newlegend - self.dataObjectsList.append(newlegend) - self.removeCurves([legend], replot=False) - self.newCurve(self.dataObjectsDict[newlegend].x[0], - self.dataObjectsDict[newlegend].y[0], - legend=self.dataObjectsDict[newlegend].info['legend']) - - #make sure the plot signal is forwarded because we have overwritten - #its handling - self.sigPlotSignal.emit(ddict) - - - def _customFitSignalReceived(self, ddict): - if ddict['event'] == "FitFinished": - newDataObject = self.__customFitDataObject - - xplot = ddict['x'] - yplot = ddict['yfit'] - newDataObject.x = [xplot] - newDataObject.y = [yplot] - newDataObject.m = [numpy.ones(len(yplot)).astype(numpy.float)] - - #here I should check the log or linear status - self.dataObjectsDict[newDataObject.info['legend']] = newDataObject - self.addCurve(xplot, - yplot, - legend=newDataObject.info['legend']) - - def _scanFitSignalReceived(self, ddict): - if DEBUG: - print("_scanFitSignalReceived", ddict) - if ddict['event'] == "EstimateFinished": - return - if ddict['event'] == "FitFinished": - newDataObject = self.__fitDataObject - - xplot = self.scanFit.specfit.xdata * 1.0 - yplot = self.scanFit.specfit.gendata(parameters=ddict['data']) - newDataObject.x = [xplot] - newDataObject.y = [yplot] - newDataObject.m = [numpy.ones(len(yplot)).astype(numpy.float)] - - self.dataObjectsDict[newDataObject.info['legend']] = newDataObject - self.addCurve(x=xplot, y=yplot, legend=newDataObject.info['legend']) - - def _fitIconSignal(self): - if DEBUG: - print("_fitIconSignal") - self.fitButtonMenu.exec_(self.cursor().pos()) - - def _simpleFitSignal(self): - if DEBUG: - print("_simpleFitSignal") - self._QSimpleOperation("fit") - - def _customFitSignal(self): - if DEBUG: - print("_customFitSignal") - self._QSimpleOperation("custom_fit") - - def _saveIconSignal(self): - if DEBUG: - print("_saveIconSignal") - self._QSimpleOperation("save") - - def _averageIconSignal(self): - if DEBUG: - print("_averageIconSignal") - self._QSimpleOperation("average") - - def _smoothIconSignal(self): - if DEBUG: - print("_smoothIconSignal") - self._QSimpleOperation("smooth") - - def _getOutputFileName(self): - #get outputfile - self.outputDir = PyMcaDirs.outputDir - if self.outputDir is None: - self.outputDir = os.getcwd() - wdir = os.getcwd() - elif os.path.exists(self.outputDir): - wdir = self.outputDir - else: - self.outputDir = os.getcwd() - wdir = self.outputDir - - filterlist = ['Specfile MCA *.mca', - 'Specfile Scan *.dat', - 'Specfile MultiScan *.dat', - 'Raw ASCII *.txt', - '","-separated CSV *.csv', - '";"-separated CSV *.csv', - '"tab"-separated CSV *.csv', - 'OMNIC CSV *.csv', - 'Widget PNG *.png', - 'Widget JPG *.jpg', - 'Graphics PNG *.png', - 'Graphics EPS *.eps', - 'Graphics SVG *.svg'] - fileList, fileFilter = PyMcaFileDialogs.getFileList(self, - filetypelist=filterlist, - message="Output File Selection", - currentdir=wdir, - single=True, - mode="SAVE", - getfilter=True, - currentfilter=self.outputFilter) - if not len(fileList): - return - filterused = fileFilter.split() - filetype = filterused[1] - extension = filterused[2] - outdir = qt.safe_str(fileList[0]) - try: - self.outputDir = os.path.dirname(outdir) - PyMcaDirs.outputDir = os.path.dirname(outdir) - except: - print("setting output directory to default") - self.outputDir = os.getcwd() - try: - outputFile = os.path.basename(outdir) - except: - outputFile = outdir - if len(outputFile) < 5: - outputFile = outputFile + extension[-4:] - elif outputFile[-4:] != extension[-4:]: - outputFile = outputFile + extension[-4:] - return os.path.join(self.outputDir, outputFile), filetype, filterused - - def _QSimpleOperation(self, operation): - try: - self._simpleOperation(operation) - except: - msg = qt.QMessageBox(self) - msg.setIcon(qt.QMessageBox.Critical) - msg.setInformativeText(str(sys.exc_info()[1])) - msg.setDetailedText(traceback.format_exc()) - msg.exec_() - - def _saveOperation(self, fileName, fileType, fileFilter): - filterused = fileFilter - filetype = fileType - filename = fileName - if os.path.exists(filename): - os.remove(filename) - if filterused[0].upper() == "WIDGET": - fformat = filename[-3:].upper() - pixmap = qt.QPixmap.grabWidget(self) - if not pixmap.save(filename, fformat): - qt.QMessageBox.critical(self, - "Save Error", - "%s" % sys.exc_info()[1]) - return - try: - if filename[-3:].upper() in ['EPS', 'PNG', 'SVG']: - self.graphicsSave(filename) - return - except: - msg = qt.QMessageBox(self) - msg.setIcon(qt.QMessageBox.Critical) - msg.setText("Graphics Saving Error: %s" % (sys.exc_info()[1])) - msg.exec_() - return - systemline = os.linesep - os.linesep = '\n' - try: - if sys.version < "3.0": - ffile=open(filename, "wb") - else: - ffile=open(filename, "w", newline='') - except IOError: - msg = qt.QMessageBox(self) - msg.setIcon(qt.QMessageBox.Critical) - msg.setText("Input Output Error: %s" % (sys.exc_info()[1])) - msg.exec_() - return - x, y, legend, info = self.getActiveCurve() - xlabel = info.get("xlabel", "X") - ylabel = info.get("ylabel", "Y") - if 0: - if "selection" in info: - if type(info['selection']) == type({}): - if 'x' in info['selection']: - #proper scan selection - ilabel = info['selection']['y'][0] - ylegend = info['LabelNames'][ilabel] - ylabel = ylegend - if info['selection']['x'] is not None: - if len(info['selection']['x']): - xlabel = info['LabelNames'] [info['selection']['x'][0]] - else: - xlabel = "Point number" - try: - if filetype in ['Scan', 'MultiScan']: - ffile.write("#F %s\n" % filename) - savingDate = "#D %s\n"%(time.ctime(time.time())) - ffile.write(savingDate) - ffile.write("\n") - ffile.write("#S 1 %s\n" % legend) - ffile.write(savingDate) - ffile.write("#N 2\n") - ffile.write("#L %s %s\n" % (xlabel, ylabel) ) - for i in range(len(y)): - ffile.write("%.7g %.7g\n" % (x[i], y[i])) - ffile.write("\n") - if filetype == 'MultiScan': - scan_n = 1 - curveList = self.getAllCurves() - for x, y, key, info in curveList: - if key == legend: - continue - xlabel = info.get("xlabel", "X") - ylabel = info.get("ylabel", "Y") - if 0: - if "selection" in info: - if type(info['selection']) == type({}): - if 'x' in info['selection']: - #proper scan selection - ilabel = info['selection']['y'][0] - ylegend = info['LabelNames'][ilabel] - ylabel = ylegend - if info['selection']['x'] is not None: - if len(info['selection']['x']): - xlabel = info['LabelNames'] [info['selection']['x'][0]] - else: - xlabel = "Point number" - scan_n += 1 - ffile.write("#S %d %s\n" % (scan_n, key)) - ffile.write(savingDate) - ffile.write("#N 2\n") - ffile.write("#L %s %s\n" % (xlabel, ylabel) ) - for i in range(len(y)): - ffile.write("%.7g %.7g\n" % (x[i], y[i])) - ffile.write("\n") - elif filetype == 'ASCII': - for i in range(len(y)): - ffile.write("%.7g %.7g\n" % (x[i], y[i])) - elif filetype == 'CSV': - if "," in filterused[0]: - csvseparator = "," - elif ";" in filterused[0]: - csvseparator = ";" - elif "OMNIC" in filterused[0]: - csvseparator = "," - else: - csvseparator = "\t" - if "OMNIC" not in filterused[0]: - ffile.write('"%s"%s"%s"\n' % (xlabel,csvseparator,ylabel)) - for i in range(len(y)): - ffile.write("%.7E%s%.7E\n" % (x[i], csvseparator,y[i])) - else: - ffile.write("#F %s\n" % filename) - ffile.write("#D %s\n"%(time.ctime(time.time()))) - ffile.write("\n") - ffile.write("#S 1 %s\n" % legend) - ffile.write("#D %s\n"%(time.ctime(time.time()))) - ffile.write("#@MCA %16C\n") - ffile.write("#@CHANN %d %d %d 1\n" % (len(y), x[0], x[-1])) - ffile.write("#@CALIB %.7g %.7g %.7g\n" % (0, 1, 0)) - ffile.write(self.array2SpecMca(y)) - ffile.write("\n") - ffile.close() - os.linesep = systemline - except: - os.linesep = systemline - raise - return - - - def _simpleOperation(self, operation): - if operation == 'subtract': - self._subtractOperation() - return - if operation == "save": - #getOutputFileName - filename = self._getOutputFileName() - if filename is None: - return - self._saveOperation(filename[0], filename[1], filename[2]) - return - if operation != "average": - #get active curve - legend = self.getActiveCurveLegend() - if legend is None:return - - found = False - for key in self.dataObjectsList: - if key == legend: - found = True - break - - if found: - dataObject = self.dataObjectsDict[legend] - else: - print("I should not be here") - print("active curve =",legend) - print("but legend list = ",self.dataObjectsList) - return - y = dataObject.y[0] - if dataObject.x is not None: - x = dataObject.x[0] - else: - x = numpy.arange(len(y)).astype(numpy.float) - ilabel = dataObject.info['selection']['y'][0] - ylabel = dataObject.info['LabelNames'][ilabel] - if len(dataObject.info['selection']['x']): - ilabel = dataObject.info['selection']['x'][0] - xlabel = dataObject.info['LabelNames'][ilabel] - else: - xlabel = "Point Number" - else: - x = [] - y = [] - legend = "" - i = 0 - ndata = 0 - for key in self._curveList: - if DEBUG: - print("key -> ", key) - if key in self.dataObjectsDict: - x.append(self.dataObjectsDict[key].x[0]) #only the first X - if len(self.dataObjectsDict[key].y) == 1: - y.append(self.dataObjectsDict[key].y[0]) - else: - sel_legend = self.dataObjectsDict[key].info['legend'] - ilabel = 0 - #I have to get the proper y associated to the legend - if sel_legend in key: - if key.index(sel_legend) == 0: - label = key[len(sel_legend):] - while (label.startswith(' ')): - label = label[1:] - if not len(label): - break - if label in self.dataObjectsDict[key].info['LabelNames']: - ilabel = self.dataObjectsDict[key].info['LabelNames'].index(label) - if DEBUG: - print("LABEL = ", label) - print("ilabel = ", ilabel) - y.append(self.dataObjectsDict[key].y[ilabel]) - if i == 0: - legend = key - firstcurve = key - i += 1 - else: - legend += " + " + key - lastcurve = key - ndata += 1 - if ndata == 0: return #nothing to average - dataObject = self.dataObjectsDict[firstcurve] - - #create the output data object - newDataObject = DataObject.DataObject() - newDataObject.data = None - newDataObject.info = copy.deepcopy(dataObject.info) - if 'selectionlegend' in newDataObject.info: - del newDataObject.info['selectionlegend'] - if not ('operations' in newDataObject.info): - newDataObject.info['operations'] = [] - newDataObject.info['operations'].append(operation) - - sel = {} - sel['SourceType'] = "Operation" - #get new x and new y - if operation == "derivate": - #xmin and xmax - xlimits=self.getGraphXLimits() - xplot, yplot = self.simpleMath.derivate(x, y, xlimits=xlimits) - ilabel = dataObject.info['selection']['y'][0] - ylabel = dataObject.info['LabelNames'][ilabel] - newDataObject.info['LabelNames'][ilabel] = ylabel+"'" - newDataObject.info['plot_yaxis'] = "right" - sel['SourceName'] = legend - sel['Key'] = "'" - sel['legend'] = legend + sel['Key'] - outputlegend = legend + sel['Key'] - elif operation == "average": - xplot, yplot = self.simpleMath.average(x, y) - if len(legend) < 80: - sel['SourceName'] = legend - sel['Key'] = "" - sel['legend'] = "(%s)/%d" % (legend, ndata) - outputlegend = "(%s)/%d" % (legend, ndata) - else: - sel['SourceName'] = legend - legend = "Average of %d from %s to %s" % (ndata, firstcurve, lastcurve) - sel['Key'] = "" - sel['legend'] = legend - outputlegend = legend - elif operation == "swapsign": - xplot = x * 1 - yplot = -y - sel['SourceName'] = legend - sel['Key'] = "" - sel['legend'] = "-(%s)" % legend - outputlegend = "-(%s)" % legend - elif operation == "smooth": - xplot = x * 1 - yplot = self.simpleMath.smooth(y) - sel['SourceName'] = legend - sel['Key'] = "" - sel['legend'] = "%s Smooth" % legend - outputlegend = "%s Smooth" % legend - if 'operations' in dataObject.info: - if len(dataObject.info['operations']): - if dataObject.info['operations'][-1] == "smooth": - sel['legend'] = legend - outputlegend = legend - elif operation == "forceymintozero": - xplot = x * 1 - yplot = y - min(y) - sel['SourceName'] = legend - sel['Key'] = "" - sel['legend'] = "(%s) - ymin" % legend - outputlegend = "(%s) - ymin" % legend - elif operation == "fit": - #remove a existing fit if present - xmin,xmax=self.getGraphXLimits() - outputlegend = legend + " Fit" - for key in self._curveList: - if key == outputlegend: - self.removeCurves([outputlegend], replot=False) - break - self.scanFit.setData(x = x, - y = y, - xmin = xmin, - xmax = xmax, - legend = legend) - if self.scanFit.isHidden(): - self.scanFit.show() - self.scanFit.raise_() - elif operation == "custom_fit": - #remove a existing fit if present - xmin, xmax=self.getGraphXLimits() - outputlegend = legend + "Custom Fit" - keyList = list(self._curveList) - for key in keyList: - if key == outputlegend: - self.removeCurves([outputlegend], replot=False) - break - self.customFit.setData(x = x, - y = y, - xmin = xmin, - xmax = xmax, - legend = legend) - if self.customFit.isHidden(): - self.customFit.show() - self.customFit.raise_() - else: - raise ValueError("Unknown operation %s" % operation) - if operation not in ["fit", "custom_fit"]: - newDataObject.x = [xplot] - newDataObject.y = [yplot] - newDataObject.m = [numpy.ones(len(yplot)).astype(numpy.float)] - - #and add it to the plot - if True and (operation not in ['fit', 'custom_fit']): - sel['dataobject'] = newDataObject - sel['scanselection'] = True - sel['selection'] = copy.deepcopy(dataObject.info['selection']) - sel['selectiontype'] = "1D" - if operation == 'average': - self._replaceSelection([sel]) - elif operation != 'fit': - self._addSelection([sel]) - else: - self.__fitDataObject = newDataObject - return - else: - newDataObject.info['legend'] = outputlegend - if operation == 'fit': - self.__fitDataObject = newDataObject - return - if operation == 'custom_fit': - self.__customFitDataObject = newDataObject - return - - self.dataObjectsDict[newDataObject.info['legend']] = newDataObject - #here I should check the log or linear status - self.addCurve(x=xplot, y=yplot, legend=newDataObject.info['legend'], replot=False) - self.replot() - - def graphicsSave(self, filename): - #use the plugin interface - x, y, legend, info = self.getActiveCurve()[:4] - curveList = self.getAllCurves() - size = (6, 3) #in inches - bw = False - if len(curveList) > 1: - legends = True - else: - legends = False - if self.matplotlibDialog is None: - self.matplotlibDialog = QPyMcaMatplotlibSave1D.\ - QPyMcaMatplotlibSaveDialog(size=size, - logx=self._logX, - logy=self._logY, - legends=legends, - bw = bw) - mtplt = self.matplotlibDialog.plot - mtplt.setParameters({'logy':self._logY, - 'logx':self._logX, - 'legends':legends, - 'bw':bw}) - xmin, xmax = self.getGraphXLimits() - ymin, ymax = self.getGraphYLimits() - mtplt.setLimits(xmin, xmax, ymin, ymax) - - legend0 = legend - xdata = x - ydata = y - dataCounter = 1 - alias = "%c" % (96+dataCounter) - mtplt.addDataToPlot( xdata, ydata, legend=legend0, alias=alias ) - for curve in curveList: - xdata, ydata, legend, info = curve[0:4] - if legend == legend0: - continue - dataCounter += 1 - alias = "%c" % (96+dataCounter) - mtplt.addDataToPlot( xdata, ydata, legend=legend, alias=alias ) - - if sys.version < '3.0': - self.matplotlibDialog.setXLabel(qt.safe_str(self.getGraphXLabel())) - self.matplotlibDialog.setYLabel(qt.safe_str(self.getGraphYLabel())) - else: - self.matplotlibDialog.setXLabel(self.getGraphXLabel()) - self.matplotlibDialog.setYLabel(self.getGraphYLabel()) - - if legends: - mtplt.plotLegends() - ret = self.matplotlibDialog.exec_() - if ret == qt.QDialog.Accepted: - mtplt.saveFile(filename) - return - - def getActiveCurveLegend(self): - return super(ScanWindow,self).getActiveCurve(just_legend=True) - - def _deriveIconSignal(self): - if DEBUG: - print("_deriveIconSignal") - self._QSimpleOperation('derivate') - - def _swapSignIconSignal(self): - if DEBUG: - print("_swapSignIconSignal") - self._QSimpleOperation('swapsign') - - def _yMinToZeroIconSignal(self): - if DEBUG: - print("_yMinToZeroIconSignal") - self._QSimpleOperation('forceymintozero') - - def _subtractIconSignal(self): - if DEBUG: - print("_subtractIconSignal") - self._QSimpleOperation('subtract') - - def _subtractOperation(self): - #identical to twice the average with the negative active curve - #get active curve - legend = self.getActiveCurveLegend() - if legend is None: - return + self.dataObjectsDict = {} + self._addSelection(selectionlist, resetzoom=True) - found = False - for key in self.dataObjectsList: - if key == legend: - found = True - break + def removeCurves(self, removeList): + for legend in removeList: + self.removeCurve(legend) + if legend in self.dataObjectsDict: + del self.dataObjectsDict[legend] - if found: - dataObject = self.dataObjectsDict[legend] - else: - print("I should not be here") - print("active curve =",legend) - print("but legend list = ",self.dataObjectsList) - return - x = dataObject.x[0] - y = dataObject.y[0] - ilabel = dataObject.info['selection']['y'][0] - ylabel = dataObject.info['LabelNames'][ilabel] - if len(dataObject.info['selection']['x']): - ilabel = dataObject.info['selection']['x'][0] - xlabel = dataObject.info['LabelNames'][ilabel] - else: - xlabel = "Point Number" - - xActive = x - yActive = y - yActiveLegend = legend - yActiveLabel = ylabel - xActiveLabel = xlabel - - operation = "subtract" - sel_list = [] - i = 0 - ndata = 0 - keyList = list(self._curveList) - for key in keyList: - legend = "" - x = [xActive] - y = [-yActive] - if DEBUG: - print("key -> ", key) - if key in self.dataObjectsDict: - x.append(self.dataObjectsDict[key].x[0]) #only the first X - if len(self.dataObjectsDict[key].y) == 1: - y.append(self.dataObjectsDict[key].y[0]) - ilabel = self.dataObjectsDict[key].info['selection']['y'][0] - else: - sel_legend = self.dataObjectsDict[key].info['legend'] - ilabel = self.dataObjectsDict[key].info['selection']['y'][0] - #I have to get the proper y associated to the legend - if sel_legend in key: - if key.index(sel_legend) == 0: - label = key[len(sel_legend):] - while (label.startswith(' ')): - label = label[1:] - if not len(label): - break - if label in self.dataObjectsDict[key].info['LabelNames']: - ilabel = self.dataObjectsDict[key].info['LabelNames'].index(label) - if DEBUG: - print("LABEL = ", label) - print("ilabel = ", ilabel) - y.append(self.dataObjectsDict[key].y[ilabel]) - outputlegend = "(%s - %s)" % (key, yActiveLegend) - ndata += 1 - xplot, yplot = self.simpleMath.average(x, y) - yplot *= 2 - #create the output data object - newDataObject = DataObject.DataObject() - newDataObject.data = None - newDataObject.info.update(self.dataObjectsDict[key].info) - if not ('operations' in newDataObject.info): - newDataObject.info['operations'] = [] - newDataObject.info['operations'].append(operation) - newDataObject.info['LabelNames'][ilabel] = "(%s - %s)" % \ - (newDataObject.info['LabelNames'][ilabel], yActiveLabel) - newDataObject.x = [xplot] - newDataObject.y = [yplot] - newDataObject.m = None - sel = {} - sel['SourceType'] = "Operation" - sel['SourceName'] = key - sel['Key'] = "" - sel['legend'] = outputlegend - sel['dataobject'] = newDataObject - sel['scanselection'] = True - sel['selection'] = copy.deepcopy(dataObject.info['selection']) - #sel['selection']['y'] = [ilabel] - sel['selectiontype'] = "1D" - sel_list.append(sel) - if True: - #The legend menu was not working with the next line - #but if works if I add the list - self._replaceSelection(sel_list) - else: - oldlist = list(self.dataObjectsDict) - self._addSelection(sel_list) - self.removeCurves(oldlist) - - #The plugins interface - def getGraphYLimits(self): - #if the active curve is mapped to second axis - #I should give the second axis limits - return super(ScanWindow, self).getGraphYLimits() - - #end of plugins interface - def addCurve(self, x, y, legend=None, info=None, replace=False, replot=True, - color=None, symbol=None, linestyle=None, - xlabel=None, ylabel=None, yaxis=None, + def addCurve(self, x, y, legend=None, info=None, replace=False, + resetzoom=True, color=None, symbol=None, + linestyle=None, xlabel=None, ylabel=None, yaxis=None, xerror=None, yerror=None, **kw): + """Add a curve. If a curve with the same legend already exists, + the unspecified parameters (color, symbol, linestyle, yaxis) are + assumed to be identical to the parameters of the existing curve.""" + if "replot" in kw: + _logger.warning("addCurve deprecated replot argument, " + "use resetzoom instead") + resetzoom = kw["replot"] and resetzoom if legend in self._curveList: if info is None: info = {} oldStuff = self.getCurve(legend) - if len(oldStuff): - oldX, oldY, oldLegend, oldInfo = oldStuff + if oldStuff is not None: + oldX, oldY, oldLegend, oldInfo, oldParams = oldStuff else: oldInfo = {} if color is None: - color = info.get("plot_color", oldInfo.get("plot_color", None)) + color = info.get("plot_color", + oldInfo.get("plot_color", None)) if symbol is None: - symbol = info.get("plot_symbol",oldInfo.get("plot_symbol", None)) + symbol = info.get("plot_symbol", + oldInfo.get("plot_symbol", None)) if linestyle is None: - linestyle = info.get("plot_linestyle",oldInfo.get("plot_linestyle", None)) + linestyle = info.get("plot_linestyle", + oldInfo.get("plot_linestyle", None)) if yaxis is None: - yaxis = info.get("plot_yaxis",oldInfo.get("plot_yaxis", None)) + yaxis = info.get("plot_yaxis", + oldInfo.get("plot_yaxis", None)) else: if info is None: info = {} if color is None: color = info.get("plot_color", None) if symbol is None: - symbol = info.get("plot_symbol", None) + symbol = info.get("plot_symbol", None) if linestyle is None: - linestyle = info.get("plot_linestyle", None) + linestyle = info.get("plot_linestyle", None) if yaxis is None: - yaxis = info.get("plot_yaxis", None) + yaxis = info.get("plot_yaxis", None) if legend in self.dataObjectsDict: # the info is changing - super(ScanWindow, self).addCurve(x, y, legend=legend, info=info, - replace=replace, replot=replot, color=color, symbol=symbol, - linestyle=linestyle, xlabel=xlabel, ylabel=ylabel, yaxis=yaxis, - xerror=xerror, yerror=yerror, **kw) + super(ScanWindow, self).addCurve( + x, y, legend=legend, info=info, + replace=replace, color=color, symbol=symbol, + linestyle=linestyle, xlabel=xlabel, ylabel=ylabel, + yaxis=yaxis, xerror=xerror, yerror=yerror, + resetzoom=resetzoom, **kw) else: # create the data object - self.newCurve(x, y, legend=legend, info=info, - replace=replace, replot=replot, color=color, symbol=symbol, - linestyle=linestyle, xlabel=xlabel, ylabel=ylabel, yaxis=yaxis, - xerror=xerror, yerror=yerror, **kw) - - def newCurve(self, x, y, legend=None, info=None, replace=False, replot=True, - color=None, symbol=None, linestyle=None, - xlabel=None, ylabel=None, yaxis=None, + self.newCurve( + x, y, legend=legend, info=info, + replace=replace, color=color, symbol=symbol, + linestyle=linestyle, xlabel=xlabel, ylabel=ylabel, + yaxis=yaxis, xerror=xerror, yerror=yerror, + resetzoom=resetzoom, **kw) + + def newCurve(self, x, y, legend=None, info=None, replace=False, + resetzoom=True, color=None, symbol=None, + linestyle=None, xlabel=None, ylabel=None, yaxis=None, xerror=None, yerror=None, **kw): + """ + Create and add a data object to :attr:`dataObjectsDict` + """ + if "replot" in kw: + _logger.warning("addCurve deprecated replot argument, " + "use resetzoom instead") + resetzoom = kw["replot"] and resetzoom if legend is None: legend = "Unnamed curve 1.1" if xlabel is None: @@ -1332,7 +668,6 @@ class ScanWindow(PlotWindow.PlotWindow): ylabel = "Y" if info is None: info = {} - # this is awfull but I have no other way to pass the plot information ... if color is not None: info["plot_color"] = color if symbol is not None: @@ -1352,243 +687,44 @@ class ScanWindow(PlotWindow.PlotWindow): newDataObject.info['Key'] = "" newDataObject.info['selectiontype'] = "1D" newDataObject.info['LabelNames'] = [xlabel, ylabel] - newDataObject.info['selection'] = {'x':[0], 'y':[1]} - sel_list = [] - sel = {} - sel['SourceType'] = "Operation" - sel['SourceName'] = legend - sel['Key'] = "" - sel['legend'] = legend - sel['dataobject'] = newDataObject - sel['scanselection'] = True - sel['selection'] = {'x':[0], 'y':[1], 'm':[], 'cntlist':[xlabel, ylabel]} - #sel['selection']['y'] = [ilabel] - sel['selectiontype'] = "1D" - sel_list.append(sel) + newDataObject.info['selection'] = {'x': [0], 'y': [1]} + + sel = {'SourceType': "Operation", + 'SourceName': legend, + 'Key': "", + 'legend': legend, + 'dataobject': newDataObject, + 'scanselection': True, + 'selection': {'x': [0], 'y': [1], 'm': [], + 'cntlist': [xlabel, ylabel]}, + 'selectiontype': "1D"} + sel_list = [sel] if replace: self._replaceSelection(sel_list) else: - self._addSelection(sel_list, replot=replot) - - def printGraph(self): - if self.printPreview.printer is None: - # setup needed - self.printPreview.setup() - self._printer = self.printPreview.printer - if self._printer is None: - # printer was not selected - return - #self._printer = None - if PlotWindow.PlotWidget.SVG: - svg = True - self._svgRenderer = self.getSvgRenderer() - else: - svg = False - if hasattr(self, "getWidgetHandle"): - widget = self.getWidgetHandle() - else: - widget = self.centralWidget() - if hasattr(widget, "grab"): - pixmap = widget.grab() - else: - pixmap = qt.QPixmap.grabWidget(widget) - - title = None - comment = None - if self.scanWindowInfoWidget is not None: - if not self.infoDockWidget.isHidden(): - info = self.scanWindowInfoWidget.getInfo() - title = info['scan'].get('source', None) - comment = info['scan'].get('scan', None)+"\n" - h, k, l = info['scan'].get('hkl') - if h != "----": - comment += "H = %s K = %s L = %s\n" % (h, k, l) - peak = info['graph']['peak'] - peakAt = info['graph']['peakat'] - fwhm = info['graph']['fwhm'] - fwhmAt = info['graph']['fwhmat'] - com = info['graph']['com'] - mean = info['graph']['mean'] - std = info['graph']['std'] - minimum = info['graph']['min'] - maximum = info['graph']['max'] - delta = info['graph']['delta'] - xLabel = self.getGraphXLabel() - comment += "Peak %s at %s = %s\n" % (peak, xLabel, peakAt) - comment += "FWHM %s at %s = %s\n" % (fwhm, xLabel, fwhmAt) - comment += "COM = %s Mean = %s STD = %s\n" % (com, mean, std) - comment += "Min = %s Max = %s Delta = %s\n" % (minimum, - maximum, - delta) - - if hasattr(self, "scanFit"): - if not self.scanFit.isHidden(): - if comment is None: - comment = "" - comment += "\n" - comment += self.scanFit.getText() - - if svg: - self.printPreview.addSvgItem(self._svgRenderer, - title=None, - comment=comment, - commentPosition="LEFT") - else: - self.printPreview.addPixmap(pixmap, - title=None, - comment=comment, - commentPosition="LEFT") - if self.printPreview.isHidden(): - self.printPreview.show() - self.printPreview.raise_() - - def getSvgRenderer(self, printer=None): - if printer is None: - if self.printPreview.printer is None: - # setup needed - self.printPreview.setup() - self._printer = self.printPreview.printer - printer = self._printer - if printer is None: - # printer was not selected - # return a renderer without adjusting the viewbox - if sys.version < '3.0': - import cStringIO as StringIO - imgData = StringIO.StringIO() - else: - from io import StringIO - imgData = StringIO() - self.saveGraph(imgData, fileFormat='svg') - imgData.flush() - imgData.seek(0) - svgData = imgData.read() - imgData = None - svgRenderer = qt.QSvgRenderer() - svgRenderer._svgRawData = svgData - svgRenderer._svgRendererData = qt.QXmlStreamReader(svgData) - if not svgRenderer.load(svgRenderer._svgRendererData): - raise RuntimeError("Cannot interpret svg data") - return svgRenderer - - # we have what is to be printed - if sys.version < '3.0': - import cStringIO as StringIO - imgData = StringIO.StringIO() - else: - from io import StringIO - imgData = StringIO() - self.saveGraph(imgData, fileFormat='svg') - imgData.flush() - imgData.seek(0) - svgData = imgData.read() - imgData = None - svgRenderer = qt.QSvgRenderer() - - #svgRenderer = PlotWindow.PlotWindow.getSvgRenderer(self) - - # we have to specify the bounding box - config = self.getPrintConfiguration() - width = config['width'] - height = config['height'] - xOffset = config['xOffset'] - yOffset = config['yOffset'] - units = config['units'] - keepAspectRatio = config['keepAspectRatio'] - - - dpix = printer.logicalDpiX() - dpiy = printer.logicalDpiY() - - # get the available space - availableWidth = printer.width() - availableHeight = printer.height() - - # convert the offsets to dpi - if units.lower() in ['inch', 'inches']: - xOffset = xOffset * dpix - yOffset = yOffset * dpiy - if width is not None: - width = width * dpix - if height is not None: - height = height * dpiy - elif units.lower() in ['cm', 'centimeters']: - xOffset = (xOffset/2.54) * dpix - yOffset = (yOffset/2.54) * dpiy - if width is not None: - width = (width/2.54) * dpix - if height is not None: - height = (height/2.54) * dpiy - else: - # page units - xOffset = availableWidth * xOffset - yOffset = availableHeight * yOffset - if width is not None: - width = availableWidth * width - if height is not None: - height = availableHeight * height - - availableWidth -= xOffset - availableHeight -= yOffset - - if width is not None: - if (availableWidth + 0.1) < width: - txt = "Available width %f is less than requested width %f" % \ - (availableWidth, width) - raise ValueError(txt) - availableWidth = width - if height is not None: - if (availableHeight + 0.1) < height: - txt = "Available height %f is less than requested height %f" % \ - (availableHeight, height) - raise ValueError(txt) - availableHeight = height - - if keepAspectRatio: - #get the aspect ratio - widget = self.getWidgetHandle() - if widget is None: - # does this make sense? - graphWidth = availableWidth - graphHeight = availableHeight - else: - graphWidth = float(widget.width()) - graphHeight = float(widget.height()) - - graphRatio = graphHeight / graphWidth - # that ratio has to be respected + self._addSelection(sel_list, resetzoom=resetzoom) - bodyWidth = availableWidth - bodyHeight = availableWidth * graphRatio - - if bodyHeight > availableHeight: - bodyHeight = availableHeight - bodyWidth = bodyHeight / graphRatio - else: - bodyWidth = availableWidth - bodyHeight = availableHeight - - body = qt.QRectF(xOffset, - yOffset, - bodyWidth, - bodyHeight) - # this does not work if I set the svgData before - svgRenderer.setViewBox(body) - svgRenderer._viewBox = body - if not sys.version.startswith("2"): - svgData = svgData.encode(encoding="utf-8", - errors="replace") - svgRenderer._svgRawData = svgData - svgRenderer._svgRendererData = qt.QXmlStreamReader(svgData) - - if not svgRenderer.load(svgRenderer._svgRendererData): - raise RuntimeError("Cannot interpret svg data") - return svgRenderer def test(): - w = ScanWindow() + import numpy + app = qt.QApplication([]) + w = ScanWindow(info=True) x = numpy.arange(1000.) - y = 10 * x + 10000. * numpy.exp(-0.5*(x-500)*(x-500)/400) - w.addCurve(x, y, legend="dummy", replot=True, replace=True) + y1 = 10 * x + 10000. * numpy.exp(-0.5*(x-500)*(x-500)/400) + y2 = y1 + 5000. * numpy.exp(-0.5*(x-700)*(x-700)/200) + y3 = y1 + 7000. * numpy.exp(-0.5*(x-200)*(x-200)/1000) + w.addCurve(x, y1, legend="dummy1", + info={"SourceName": "Synthetic data 1 (linear+gaussian)", + "hkl": [1.1, 1.2, 1.3], + "Header": ["#S 1 toto"]}) + w.addCurve(x, y2, legend="dummy2", + info={"SourceName": "Synthetic data 2", + "hkl": [2.1, 2.2, 2.3], + "Header": ["#S 2"]}) + w.addCurve(x, y3, legend="dummy3", + info={"SourceName": "Synthetic data 3", + "hkl": ["3.1", 3.2, 3.3], + "Header": ["#S 3"]}) w.resetZoom() app.lastWindowClosed.connect(app.quit) w.show() diff --git a/PyMca5/PyMcaGui/pymca/ScanWindowInfoWidget.py b/PyMca5/PyMcaGui/pymca/ScanWindowInfoWidget.py index cff8ec9..d0e5133 100644 --- a/PyMca5/PyMcaGui/pymca/ScanWindowInfoWidget.py +++ b/PyMca5/PyMcaGui/pymca/ScanWindowInfoWidget.py @@ -1,6 +1,6 @@ #!/usr/bin/env python #/*########################################################################## -# Copyright (C) 2004-2014 V.A. Sole, European Synchrotron Radiation Facility +# Copyright (C) 2004-2017 V.A. Sole, European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -28,7 +28,7 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import sys + import numpy from PyMca5.PyMcaGui import PyMcaQt as qt QTVERSION = qt.qVersion() @@ -43,44 +43,46 @@ This module implements an info widget containing : """ -DEBUG=0 -STATISTICS=1 +DEBUG = 0 +STATISTICS = 1 + + class SpecArithmetic(object): """ This class tries to mimic SPEC operations. Correct peak positions and fwhm information have to be made via a fit. """ + def search_peak(self, xdata, ydata): - """ - Search a peak and its position in arrays xdata ad ydata. - Return three integer: - - peak position - - peak value - - index of peak position in array xdata - This result may accelerate the fwhm search. - """ - ydata = numpy.array(ydata, copy=False) - ymax = ydata[numpy.isfinite(ydata)].max() - idx = self.__give_index(ymax, ydata) - return xdata[idx], ymax, idx - - - def search_com(self, xdata,ydata): + """ + Search a peak and its position in arrays xdata ad ydata. + Return three integer: + - peak position + - peak value + - index of peak position in array xdata + This result may accelerate the fwhm search. + """ + ydata = numpy.array(ydata, copy=False) + ymax = ydata[numpy.isfinite(ydata)].max() + idx = self.__give_index(ymax, ydata) + return xdata[idx], ymax, idx + + @staticmethod + def search_com(xdata, ydata): """ Return the center of mass in arrays xdata and ydata """ - num = numpy.sum(xdata*ydata) - denom = numpy.sum(ydata) + num = numpy.sum(xdata * ydata) + denom = numpy.sum(ydata) if abs(denom) > 0: - result = num/denom + result = num / denom else: - result = 0 + result = 0 return result - - def search_fwhm(self, xdata,ydata,peak=None,index=None): + def search_fwhm(self, xdata, ydata, peak=None, index=None): """ Search a fwhm and its center in arrays xdata and ydatas. If no fwhm is found, (0,0) is returned. @@ -88,23 +90,23 @@ class SpecArithmetic(object): accelerate calculation """ if peak is None or index is None: - x,mypeak,index_peak = self.search_peak(xdata,ydata) + x, mypeak, index_peak = self.search_peak(xdata, ydata) else: - mypeak = peak + mypeak = peak index_peak = index - hm = mypeak/2 + hm = mypeak / 2 idx = index_peak try: while ydata[idx] >= hm: - idx = idx-1 + idx -= 1 x0 = float(xdata[idx]) - x1 = float(xdata[idx+1]) + x1 = float(xdata[idx + 1]) y0 = float(ydata[idx]) - y1 = float(ydata[idx+1]) + y1 = float(ydata[idx + 1]) - lhmx = (hm*(x1-x0) - (y0*x1)+(y1*x0)) / (y1-y0) + lhmx = (hm * (x1 - x0) - (y0 * x1) + (y1 * x0)) / (y1 - y0) except ZeroDivisionError: lhmx = 0 except IndexError: @@ -113,39 +115,40 @@ class SpecArithmetic(object): idx = index_peak try: while ydata[idx] >= hm: - idx = idx+1 + idx += 1 - x0 = float(xdata[idx-1]) + x0 = float(xdata[idx - 1]) x1 = float(xdata[idx]) - y0 = float(ydata[idx-1]) + y0 = float(ydata[idx - 1]) y1 = float(ydata[idx]) - uhmx = (hm*(x1-x0) - (y0*x1)+(y1*x0)) / (y1-y0) + uhmx = (hm * (x1 - x0) - (y0 * x1) + (y1 * x0)) / (y1 - y0) except ZeroDivisionError: uhmx = 0 except IndexError: uhmx = xdata[-1] - FWHM = uhmx - lhmx - CFWHM = (uhmx+lhmx)/2 - return FWHM,CFWHM + fwhm = uhmx - lhmx + cfwhm = (uhmx + lhmx) / 2 + return fwhm, cfwhm + @staticmethod + def __give_index(elem, array): + """ + Return the index of elem in array + """ + mylist = array.tolist() + return mylist.index(elem) - def __give_index(self, elem,array): - """ - Return the index of elem in array - """ - mylist = array.tolist() - return mylist.index(elem) class HKL(qt.QWidget): - def __init__(self, parent = None, h= "", k= "", l=""): + + def __init__(self, parent=None, h="", k="", l=""): qt.QWidget.__init__(self, parent) layout = qt.QHBoxLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(2) - hlabel = qt.QLabel(self) hlabel.setText('H:') self.h = qt.QLineEdit(self) @@ -176,91 +179,98 @@ class HKL(qt.QWidget): def setHKL(self, h="", k="", l=""): dformat = "%.4f" - if type(h) == type (""): + if isinstance(h, str): self.h.setText(h) else: self.h.setText(dformat % h) - if type(k) == type (""): + if isinstance(k, str): self.k.setText(k) else: self.k.setText(dformat % k) - if type(l) == type (""): + if isinstance(l, str): self.l.setText(l) else: self.l.setText(dformat % l) + class GraphInfoWidget(qt.QWidget): + """Widget displaying statistics about curve data: + peak info (x position, y value, fwhm, center of fwhm), max y value, + min y value, delta y, mean y, center of mass of y values, standard + deviation of y. + + This information is extracted directly from the curve data.""" def __init__(self, parent): qt.QWidget.__init__(self, parent) layout = qt.QGridLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(2) - #peak - peak = qt.QLabel(self) + # peak + peak = qt.QLabel(self) peak.setText("Peak: ") self.peak = qt.QLineEdit(self) self.peak.setReadOnly(True) - hboxPeak = qt.QWidget(self) + hboxPeak = qt.QWidget(self) hboxPeak.l = qt.QHBoxLayout(hboxPeak) hboxPeak.l.setContentsMargins(0, 0, 0, 0) hboxPeak.l.setSpacing(0) - peakAt = qt.QLabel(hboxPeak) + peakAt = qt.QLabel(hboxPeak) peakAt.setText(" at:") self.peakAt = qt.QLineEdit(hboxPeak) self.peak.setReadOnly(True) hboxPeak.l.addWidget(peakAt) hboxPeak.l.addWidget(self.peakAt) - #fwhm - fwhm = qt.QLabel(self) + # fwhm + fwhm = qt.QLabel(self) fwhm.setText("Fwhm: ") self.fwhm = qt.QLineEdit(self) self.fwhm.setReadOnly(True) - hboxFwhm = qt.QWidget(self) + hboxFwhm = qt.QWidget(self) hboxFwhm.l = qt.QHBoxLayout(hboxFwhm) hboxFwhm.l.setContentsMargins(0, 0, 0, 0) hboxFwhm.l.setSpacing(0) - fwhmAt = qt.QLabel(hboxFwhm) + fwhmAt = qt.QLabel(hboxFwhm) fwhmAt.setText(" at:") self.fwhmAt = qt.QLineEdit(hboxFwhm) self.fwhm.setReadOnly(True) hboxFwhm.l.addWidget(fwhmAt) hboxFwhm.l.addWidget(self.fwhmAt) - #statistics - #COM - com = qt.QLabel(self) + # statistics + # COM + com = qt.QLabel(self) com.setText("COM:") self.com = qt.QLineEdit(self) self.com.setReadOnly(True) - #mean - mean = qt.QLabel(self) + # mean + mean = qt.QLabel(self) mean.setText("Mean:") self.mean = qt.QLineEdit(self) self.mean.setReadOnly(True) - #STD - std = qt.QLabel(self) + # STD + std = qt.QLabel(self) std.setText("STD:") self.std = qt.QLineEdit(self) self.std.setReadOnly(True) - #Max - maximum = qt.QLabel(self) + # Max + maximum = qt.QLabel(self) maximum.setText("Max:") - self.maximum= qt.QLineEdit(self) + self.maximum = qt.QLineEdit(self) self.maximum.setReadOnly(True) - #mean - minimum = qt.QLabel(self) + # mean + minimum = qt.QLabel(self) minimum.setText("Min:") - self.minimum= qt.QLineEdit(self) + self.minimum = qt.QLineEdit(self) self.minimum.setReadOnly(True) - #STD - delta = qt.QLabel(self) + # STD + delta = qt.QLabel(self) delta.setText("Delta:") self.delta = qt.QLineEdit(self) self.delta.setReadOnly(True) @@ -286,59 +296,46 @@ class GraphInfoWidget(qt.QWidget): layout.addWidget(self.delta, 1, 8) self.specArithmetic = SpecArithmetic() - def updateFromDataObject(self, dataObject): - ydata = numpy.ravel(dataObject.y[0]) - ylen = len(ydata) - if ylen: - if dataObject.x is None: - xdata = numpy.arange(ylen).astype(numpy.float) - elif not len(dataObject.x): - xdata = numpy.arange(ylen).astype(numpy.float) - else: - xdata = numpy.ravel(dataObject.x[0]) - else: - xdata = None - self.updateFromXY(xdata, ydata) - - def updateFromXY(self, xdata, ydata): if len(ydata): - peakpos,peak,myidx = self.specArithmetic.search_peak(xdata,ydata) - com = self.specArithmetic.search_com(xdata,ydata) - fwhm,cfwhm = self.specArithmetic.search_fwhm(xdata,ydata, - peak=peak,index=myidx) - ymax = max(ydata) - ymin = min(ydata) + peakpos, peak, myidx = self.specArithmetic.search_peak( + xdata, ydata) + com = self.specArithmetic.search_com(xdata, ydata) + fwhm, cfwhm = self.specArithmetic.search_fwhm(xdata, ydata, + peak=peak, index=myidx) + ymax = max(ydata) + ymin = min(ydata) ymean = sum(ydata) / len(ydata) if len(ydata) > 1: - ystd = numpy.sqrt(sum((ydata-ymean)*(ydata-ymean))/len(ydata)) + ystd = numpy.sqrt( + sum((ydata - ymean) * (ydata - ymean)) / len(ydata)) else: ystd = 0 - delta = ymax - ymin + delta = ymax - ymin fformat = "%.7g" peakpos = fformat % peakpos - peak = fformat % peak - myidx = "%d" % myidx - com = fformat % com - fwhm = fformat % fwhm - cfwhm = fformat % cfwhm - ymean = fformat % ymean - ystd = fformat % ystd - ymax = fformat % ymax - ymin = fformat % ymin - delta = fformat % delta + peak = fformat % peak + # myidx = "%d" % myidx + com = fformat % com + fwhm = fformat % fwhm + cfwhm = fformat % cfwhm + ymean = fformat % ymean + ystd = fformat % ystd + ymax = fformat % ymax + ymin = fformat % ymin + delta = fformat % delta else: peakpos = "----" - peak = "----" - myidx = "----" - com = "----" - fwhm = "----" - cfwhm = "----" - ymean = "----" - ystd = "----" - ymax = "----" - ymin = "----" - delta = "----" + peak = "----" + # myidx = "----" + com = "----" + fwhm = "----" + cfwhm = "----" + ymean = "----" + ystd = "----" + ymax = "----" + ymin = "----" + delta = "----" self.peak.setText(peak) self.peakAt.setText(peakpos) self.fwhm.setText(fwhm) @@ -351,29 +348,32 @@ class GraphInfoWidget(qt.QWidget): self.delta.setText(delta) def getInfo(self): - ddict={} - ddict['peak'] = self.peak.text() - ddict['peakat'] = self.peakAt.text() - ddict['fwhm'] = self.fwhm.text() - ddict['fwhmat'] = self.fwhmAt.text() - ddict['com'] = self.com.text() - ddict['mean'] = self.mean.text() - ddict['std'] = self.std.text() - ddict['min'] = self.minimum.text() - ddict['max'] = self.maximum.text() - ddict['delta'] = self.delta.text() - return ddict - + return { + 'peak': self.peak.text(), + 'peakat': self.peakAt.text(), + 'fwhm': self.fwhm.text(), + 'fwhmat': self.fwhmAt.text(), + 'com': self.com.text(), + 'mean': self.mean.text(), + 'std': self.std.text(), + 'min': self.minimum.text(), + 'max': self.maximum.text(), + 'delta': self.delta.text(), + } class ScanInfoWidget(qt.QWidget): - def __init__(self, parent = None): + """Widget displaying curve metadata: + data source, first scan header line, H, K, L + + This information is extracted from the curve info dict.""" + def __init__(self, parent=None): qt.QWidget.__init__(self, parent) layout = qt.QGridLayout(self) layout.setContentsMargins(0, 0, 0, 0) layout.setSpacing(2) - #scan info + # scan info hBox = qt.QWidget(self) hBoxLayout = qt.QHBoxLayout(hBox) hBoxLayout.setContentsMargins(0, 0, 0, 0) @@ -392,22 +392,21 @@ class ScanInfoWidget(qt.QWidget): self.hkl = HKL(self) layout.addWidget(hBox, 0, 0, 1, 7) - #layout.addWidget(self.sourceLabel, 0, 1)#, 1, 9) + # layout.addWidget(self.sourceLabel, 0, 1)#, 1, 9) layout.addWidget(scanLabel, 1, 0) layout.addWidget(self.scanLabel, 1, 1) layout.addWidget(self.hkl, 1, 4, 1, 3) - def updateFromDataObject(self, dataObject): - info = dataObject.info + def updateFromInfoDict(self, info): source = info.get('SourceName', None) if source is None: self.sourceLabel.setText("") else: - if type(source) == type(""): + if isinstance(source, str): self.sourceLabel.setText(source) else: self.sourceLabel.setText(source[0]) - scan = info.get('Header', None) + scan = info.get('Header', None) if scan is None: scan = "" if "envdict" in info: @@ -415,63 +414,55 @@ class ScanInfoWidget(qt.QWidget): self.scanLabel.setText(scan) else: self.scanLabel.setText(scan[0]) - hkl = info.get('hkl', None) + hkl = info.get('hkl', None) if hkl is None: self.hkl.setHKL("----", "----", "----") else: self.hkl.setHKL(*hkl) def getInfo(self): - ddict = {} - ddict['source'] = self.sourceLabel.text() - ddict['scan'] = self.scanLabel.text() - ddict['hkl'] = ["%s" % self.hkl.h.text(), - "%s" % self.hkl.k.text(), - "%s" % self.hkl.l.text()] - return ddict + return { + 'source': self.sourceLabel.text(), + 'scan': self.scanLabel.text(), + 'hkl': ["%s" % self.hkl.h.text(), + "%s" % self.hkl.k.text(), + "%s" % self.hkl.l.text()] + } + class ScanWindowInfoWidget(qt.QWidget): - def __init__(self, parent = None): + + def __init__(self, parent=None): qt.QWidget.__init__(self, parent) layout = qt.QVBoxLayout(self) layout.setContentsMargins(2, 2, 2, 2) layout.setSpacing(2) - self.scanInfo = ScanInfoWidget(self) + self.scanInfo = ScanInfoWidget(self) self.graphInfo = GraphInfoWidget(self) layout.addWidget(self.scanInfo) layout.addWidget(self.graphInfo) - #print "hiding graph info" - #self.graphInfo.hide() - def updateFromDataObject(self, dataObject): - self.scanInfo.updateFromDataObject(dataObject) - self.graphInfo.updateFromDataObject(dataObject) + def updateFromXYInfo(self, xdata, ydata, info): + self.scanInfo.updateFromInfoDict(info) + self.graphInfo.updateFromXY(xdata, ydata) def getInfo(self): - ddict = {} - ddict['scan'] = self.scanInfo.getInfo() - ddict['graph'] = self.graphInfo.getInfo() - return ddict + return { + 'scan': self.scanInfo.getInfo(), + 'graph': self.graphInfo.getInfo() + } + def test(): - app = qt.QApplication([]) - w = ScanWindowInfoWidget() - app.lastWindowClosed.connect(app.quit) - """ - winfo.grid(sticky='wesn') - if STATISTICS: - winfo.configure(h=65,k=45621,l=32132,peak=6666876, - fwhm=0.2154,com=544, - ymax=10.,ymin=4,ystd=1,ymean=5) - else: - winfo.configure(h=65,k=45621,l=32132,peak=6666876, - fwhm=0.2154,com=544) - """ - w.show() - app.exec_() + app = qt.QApplication([]) + w = ScanWindowInfoWidget() + app.lastWindowClosed.connect(app.quit) + w.show() + app.exec_() if __name__ == '__main__': - test() + test() + diff --git a/PyMca5/PyMcaGui/pymca/StackBrowser.py b/PyMca5/PyMcaGui/pymca/StackBrowser.py index 1fe9885..d903014 100644 --- a/PyMca5/PyMcaGui/pymca/StackBrowser.py +++ b/PyMca5/PyMcaGui/pymca/StackBrowser.py @@ -29,13 +29,15 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import numpy +import logging from PyMca5.PyMcaGui import MaskImageWidget from PyMca5.PyMcaGui import FrameBrowser from PyMca5.PyMcaCore import DataObject qt = MaskImageWidget.qt IconDict = MaskImageWidget.IconDict -DEBUG = 0 +_logger = logging.getLogger(__name__) + class StackBrowser(MaskImageWidget.MaskImageWidget): def __init__(self, *var, **kw): @@ -184,8 +186,7 @@ class StackBrowser(MaskImageWidget.MaskImageWidget): if background is None: background = self._backgroundSubtraction if not len(self.dataObjectsList): - if DEBUG: - print("nothing to show") + _logger.debug("nothing to show") return legend = self.dataObjectsList[0] if type(legend) == type([]): diff --git a/PyMca5/PyMcaGui/pymca/StackSelector.py b/PyMca5/PyMcaGui/pymca/StackSelector.py index 8cddcb1..c3e6432 100644 --- a/PyMca5/PyMcaGui/pymca/StackSelector.py +++ b/PyMca5/PyMcaGui/pymca/StackSelector.py @@ -32,6 +32,7 @@ import sys import os import copy import traceback +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5 import PyMcaDirs from PyMca5 import DataObject @@ -59,7 +60,7 @@ except ImportError: pass QTVERSION = qt.qVersion() -DEBUG = 0 +_logger = logging.getLogger(__name__) class StackSelector(object): @@ -206,7 +207,7 @@ class StackSelector(object): msg.setInformativeText("%s" % sys.exc_info()[1]) msg.setDetailedText(traceback.format_exc()) msg.exec_() - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise elif len(filelist): if not omnicfile: @@ -220,7 +221,7 @@ class StackSelector(object): msg.exec_loop() else: msg.exec_() - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise if aifirafile: masterStack = DataObject.DataObject() @@ -425,7 +426,7 @@ if __name__ == "__main__": options, longoptions) except: - print(sys.exc_info()[1]) + _logger.error(sys.exc_info()[1]) sys.exit(1) fileindex = 0 filepattern = None diff --git a/PyMca5/PyMcaGui/pymca/SumRulesTool.py b/PyMca5/PyMcaGui/pymca/SumRulesTool.py index e0414dd..fd0b859 100644 --- a/PyMca5/PyMcaGui/pymca/SumRulesTool.py +++ b/PyMca5/PyMcaGui/pymca/SumRulesTool.py @@ -28,6 +28,7 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys +import logging import os from os.path import isdir as osPathIsDir from os.path import basename as osPathBasename @@ -37,14 +38,16 @@ import numpy from PyMca5.PyMcaMath.fitting.SpecfitFuns import upstep, downstep from PyMca5.PyMca import PyMcaQt as qt -from PyMca5.PyMca import PlotWindow as DataDisplay + from PyMca5.PyMca import Elements from PyMca5.PyMca import ConfigDict from PyMca5.PyMca import PyMcaDataDir, PyMcaDirs from PyMca5.PyMca import QSpecFileWidget from PyMca5.PyMca import SpecFileDataSource -from PyMca5.PyMcaGui import IconDict + +from silx.gui.plot import PlotWindow as DataDisplay +from silx.gui.plot.LegendSelector import LegendsDockWidget if hasattr(qt, "QString"): QString = qt.QString @@ -58,7 +61,7 @@ else: QStringList = list -DEBUG = 0 +_logger = logging.getLogger(__name__) NEWLINE = '\n' class Calculations(object): @@ -115,9 +118,9 @@ class Calculations(object): # Determine number of states in outer shell if econf == '3d': - if DEBUG >= 1: - print('Calculations.magneticMoment -- considering 3d material:') - print('\tp: %s, q: %s, r:%s'%(str(p),str(q),str(r))) + _logger.debug( + 'Calculations.magneticMoment -- considering 3d material:' + '\n\tp: %s, q: %s, r:%s', str(p), str(q), str(r)) nMax = 10. # Calculate Integrals if q is not None: @@ -128,9 +131,8 @@ class Calculations(object): mSpin = abs((3.*p - 2.*q) * (nMax - n) / r) mRatio = abs(2.*q/(9.*p-6.*q)) elif econf == '4f': - if DEBUG >= 1: - print('Calculations.magneticMoment -- considering 4f material:') - print('\tp: %s, q: %s, r:%s'%(str(p),str(q),str(r))) + _logger.debug('Calculations.magneticMoment -- considering 4f material:' + '\n\tp: %s, q: %s, r:%s', str(p), str(q), str(r)) nMax = 14. if q is not None: mOrbt = abs(q * (nMax - n) / r) @@ -154,9 +156,9 @@ class MarkerSpinBox(qt.QDoubleSpinBox): self.window = window self.plotWindow = plotWindow #self.graph = graph - self.markerID = self.plotWindow.insertXMarker(0., - legend=label, - text=label) + self.markerID = self.plotWindow.addXMarker(0., + legend=label, + text=label) # Initialize self.setMinimum(0.) @@ -198,7 +200,7 @@ class MarkerSpinBox(qt.QDoubleSpinBox): def showMarker(self): self.plotWindow.removeMarker(self.label) - self.markerID = self.plotWindow.insertXMarker( + self.markerID = self.plotWindow.addXMarker( self.value(), legend=self.label, text=self.label, @@ -218,7 +220,7 @@ class MarkerSpinBox(qt.QDoubleSpinBox): draggable = False # Make shure that the marker is deleted # If marker is not present, removeMarker just passes.. - self.markerID = self.plotWindow.insertXMarker( + self.markerID = self.plotWindow.addXMarker( self.value(), legend=self.label, text=self.label, @@ -241,11 +243,10 @@ class MarkerSpinBox(qt.QDoubleSpinBox): try: val = float(val) except ValueError: - if DEBUG == 1: - print('_valueChanged -- Sorry, it ain\'t gonna float: %s'%str(val)) + _logger.debug('_valueChanged -- Sorry, it ain\'t gonna float: %s', str(val)) return # Marker of same label as self.label gets replaced.. - self.markerID = self.plotWindow.insertXMarker( + self.markerID = self.plotWindow.addXMarker( val, legend=self.label, text=self.label, @@ -289,8 +290,7 @@ class LineEditDisplay(qt.QLineEdit): elif isinstance(self.controller, qt.QDoubleSpinBox): tmp = self.controller.value() else: - if DEBUG == 1: - print('LineEditDisplay.checkController -- Reached untreated case, setting empty string') + _logger.debug('LineEditDisplay.checkController -- Reached untreated case, setting empty string') tmp = '' self.setText(tmp) @@ -311,8 +311,7 @@ class LineEditDisplay(qt.QLineEdit): elif isinstance(self.controller, qt.QDoubleSpinBox): text = inp + ' ' + self.unit else: - if DEBUG == 1: - print('LineEditDisplay.setText -- Reached untreated case, setting empty string') + _logger.debug('LineEditDisplay.setText -- Reached untreated case, setting empty string') text = '' qt.QLineEdit.setText(self, text) @@ -370,45 +369,22 @@ class SumRulesWindow(qt.QMainWindow): def __init__(self, parent=None): qt.QMainWindow.__init__(self, parent) self.setWindowTitle('Sum Rules Tool') - if hasattr(DataDisplay,'PlotWindow'): - self.plotWindow = DataDisplay.PlotWindow( - parent=self, - backend=None, - plugins=False, # Hide plugin tool button - newplot=False, # Hide mirror active curve, ... functionality - roi=False, # No ROI widget - control=False, # Hide option button - position=True, # Show x,y position display - kw={'logx': False, # Hide logarithmic x-scale tool button - 'logy': False, # Hide logarithmic y-scale tool button - 'flip': False, # Hide whatever this does - 'fit': False}) # Hide simple fit tool button - self.plotWindow._buildLegendWidget() - else: - self.plotWindow = DataDisplay.ScanWindow(self) - - # Hide Buttons in the toolbar - if hasattr(self.plotWindow,'scanWindowInfoWidget'): - # Get rid of scanInfoWidget - self.plotWindow.scanWindowInfoWidget.hide() - self.plotWindow.graph.enablemarkermode() - # Hide unnecessary buttons in the toolbar - toolbarChildren = self.plotWindow.toolBar - # QWidget.findChildren(<qt-type>) matches - # all child widgets with the specified type - toolbarButtons = toolbarChildren.findChildren(qt.QToolButton) - toolbarButtons[3].hide() # LogX - toolbarButtons[4].hide() # LogY - toolbarButtons[6].hide() # Simple Fit - toolbarButtons[7].hide() # Average Plotted Curves - toolbarButtons[8].hide() # Derivative - toolbarButtons[9].hide() # Smooth - toolbarButtons[11].hide() # Set active to zero - toolbarButtons[12].hide() # Subtract active curve - toolbarButtons[13].hide() # Save active curve - toolbarButtons[14].hide() # Plugins - else: - self.plotWindow + self.plotWindow = DataDisplay( + parent=self, + roi=False, # No ROI widget + control=False, # hide option button, legend widget added later + position=True, # Show x,y position display + fit=False, # Hide simple fit tool button + colormap=False, + aspectRatio=False, + yInverted=False, + copy=True, + print_=False, + mask=False) + + self._legendsDockWidget = LegendsDockWidget(plot=self.plotWindow) + self.plotWindow.addDockWidget(qt.Qt.RightDockWidgetArea, + self._legendsDockWidget) self.__savedConf = False self.__savedData = False @@ -822,8 +798,8 @@ class SumRulesWindow(qt.QMainWindow): self.buttonEstimate.setShortcut(qt.Qt.CTRL+qt.Qt.Key_E) self.buttonEstimate.clicked.connect(self.estimate) self.buttonEstimate.setEnabled(False) - self.plotWindow.toolBar.addSeparator() - self.plotWindow.toolBar.addWidget(self.buttonEstimate) + self.plotWindow.toolBar().addSeparator() + self.plotWindow.toolBar().addWidget(self.buttonEstimate) self.plotWindow.sigPlotSignal.connect(self._handlePlotSignal) @@ -1000,16 +976,14 @@ class SumRulesWindow(qt.QMainWindow): try: n = float(electronOccupation.text()) except ValueError: - if DEBUG == 1: - print('calcMM -- Could not convert electron occupation') + _logger.debug('calcMM -- Could not convert electron occupation') return electronConfiguration = ddict[self.__tabElem]['electron configuration'] econf = str(electronConfiguration.currentText()) try: mmO, mmS, mmR = mathObj.magneticMoment(p,q,r,n,econf) except ValueError as e: - if DEBUG == 1: - print(e) + _logger.debug(e) mmO, mmS, mmR = 3*['---'] self.mmOrbt.setText(str(mmO)) self.mmSpin.setText(str(mmS)) @@ -1220,14 +1194,13 @@ class SumRulesWindow(qt.QMainWindow): #keysLoaded = confDict.keys() #keysValues = self.valuesDict.keys() except KeyError as e: - if DEBUG: - print('loadConfiguration -- Key Error in \'%s\''%filename) - print('\tMessage:', e) - else: - msg = qt.QMessageBox() - msg.setWindowTitle('Sum Rules Analysis Error') - msg.setIcon(qt.QMessageBox.Warning) - msg.setText('Malformed configuration file \'%s\''%filename) + _logger.debug('loadConfiguration -- Key Error in \'%s\'', filename) + _logger.debug('\tMessage: %s', e) + + msg = qt.QMessageBox() + msg.setWindowTitle('Sum Rules Analysis Error') + msg.setIcon(qt.QMessageBox.Warning) + msg.setText('Malformed configuration file \'%s\''%filename) return self.__savedConf = True @@ -1265,7 +1238,7 @@ class SumRulesWindow(qt.QMainWindow): except KeyError: msg = ('setElement -- \'%s\' not found in '%symbol) msg += 'Elements.Element dictionary' - print(msg) + _logger.error(msg) # Update valuesDict self.valuesDict[self.__tabElem]['info'] = ddict # Update the EdgeCBs @@ -1349,15 +1322,12 @@ class SumRulesWindow(qt.QMainWindow): tmp = float(value) obj.setValue(tmp) except ValueError: - if hasattr(self.plotWindow,'graph'): - xmin, xmax = self.plotWindow.graph.getX1AxisLimits() - else: - xmin, xmax = self.plotWindow.getGraphXLimits() + xmin, xmax = self.plotWindow.getGraphXLimits() tmp = xmin + (xmax-xmin)/10. - if DEBUG: - msg = 'setValuesDict -- Float conversion failed' - msg += ' while setting marker positions. Value:', value - print(msg) + _logger.debug( + 'setValuesDict -- Float conversion failed' + ' while setting marker positions. Value: %s', + value) elif isinstance(obj, qt.QCheckBox): if value == 'True': state = qt.Qt.Checked @@ -1369,10 +1339,10 @@ class SumRulesWindow(qt.QMainWindow): tmp = float(value) obj.setValue(tmp) except ValueError: - if DEBUG: - msg = 'setValuesDict -- Float conversion failed' - msg += ' while setting QDoubleSpinBox value. Value:', value - print(msg) + _logger.debug( + 'setValuesDict -- Float conversion failed' + ' while setting QDoubleSpinBox value. Value: %s', + value) elif isinstance(obj, qt.QComboBox): idx = obj.findText(QString(value)) obj.setCurrentIndex(idx) @@ -1416,7 +1386,6 @@ class SumRulesWindow(qt.QMainWindow): # Add spectrum to plotWindow using the if identifier == 'xmcd': self.xmcdData = (xSorted, ySorted) - #self.plotWindow.graph.mapToY2(intLegend) elif identifier == 'xas': self.xasData = (xSorted, ySorted) # Trigger replot when data is added @@ -1553,8 +1522,7 @@ class SumRulesWindow(qt.QMainWindow): idxPost = numpy.nonzero((postMin <= x) & (x <= postMax))[0] if (len(idxPre) == 0) or (len(idxPost) == 0): - if DEBUG: - print('estimateBG -- Somethings wrong with pre/post edge markers') + _logger.debug('estimateBG -- Somethings wrong with pre/post edge markers') return xPreMin = x[idxPre.min()] @@ -1578,16 +1546,15 @@ class SumRulesWindow(qt.QMainWindow): if x02 < x01: par1 = (ratio, x02, width) par2 = ((1.-ratio), x01, width) - if DEBUG: - print('estimateBG -- x02 < x01, using par1: %s and par2: %s'\ - %(str(par1),str(par2))) + _logger.debug('estimateBG -- x02 < x01, using par1: %s and par2: %s', + par1, par2) model = bottom + sign * diff * (erf(par1, x) + erf(par2, x)) else: par1 = (ratio, x01, width) par2 = ((1.-ratio), x02, width) - if DEBUG: - print('estimateBG -- x01 < x02, using par1: %s and par2: %s'\ - %(str(par1),str(par2))) + + _logger.debug('estimateBG -- x01 < x02, using par1: %s and par2: %s', + par1, par2) model = bottom + sign * diff * (erf(par1, x) + erf(par2, x)) preModel = numpy.asarray(len(x)*[avgPre]) @@ -1599,46 +1566,33 @@ class SumRulesWindow(qt.QMainWindow): model, self.__xasBGmodel, {}, - replot=False) + resetzoom=False) self.plotWindow.addCurve(x, preModel, 'Pre BG model', {}, - replot=False) + resetzoom=False) self.plotWindow.addCurve(x, postModel, 'Post BG model', {}, - replot=False) - if hasattr(self.plotWindow, 'graph'): - self.plotWindow.graph.replot() - else: - self.plotWindow.replot() - self.plotWindow.updateLegends() + resetzoom=False) def plotOnDemand(self, window): # Remove all curves - if hasattr(self.plotWindow,'graph'): - legends = self.plotWindow.getAllCurves(just_legend=True) - for legend in legends: - self.plotWindow.removeCurve(legend, replot=False) - else: - self.plotWindow.clearCurves() + self.plotWindow.clearCurves() if (self.xmcdData is None) or (self.xasData is None): # Nothing to do return xyList = [] - mapToY2 = False window = window.lower() if window == self.__tabElem: if self.xmcdCorrData is not None: - if DEBUG == 1: - print('plotOnDemand -- __tabElem: Using self.xmcdCorrData') + _logger.debug('plotOnDemand -- __tabElem: Using self.xmcdCorrData') xmcdX, xmcdY = self.xmcdCorrData xmcdLabel = 'xmcd corr' else: - if DEBUG == 1: - print('plotOnDemand -- __tabElem: Using self.xmcdData') + _logger.debug('plotOnDemand -- __tabElem: Using self.xmcdData') xmcdX, xmcdY = self.xmcdData xmcdLabel = 'xmcd' xasX, xasY = self.xasData @@ -1663,13 +1617,11 @@ class SumRulesWindow(qt.QMainWindow): x, y = self.xasData self.xasDataCorr = x, y-yBG if self.xmcdCorrData is not None: - if DEBUG: - print('plotOnDemand -- __tabInt: Using self.xmcdCorrData') + _logger.debug('plotOnDemand -- __tabInt: Using self.xmcdCorrData') xmcdX, xmcdY = self.xmcdCorrData xmcdIntLabel = 'xmcd corr Int' else: - if DEBUG: - print('plotOnDemand -- __tabInt: Using self.xmcdData') + _logger.debug('plotOnDemand -- __tabInt: Using self.xmcdData') xmcdX, xmcdY = self.xmcdData xmcdIntLabel = 'xmcd Int' mathObj = Calculations() @@ -1690,8 +1642,7 @@ class SumRulesWindow(qt.QMainWindow): xmax = max(xmax, x.max()) ymin = min(ymin, y.min()) ymax = max(ymax, y.max()) - if DEBUG == 1: - print('plotOnDemand -- adding Curve..') + _logger.debug('plotOnDemand -- adding Curve..') """ if mapToY2: if hasattr(self.plotWindow, 'graph'): @@ -1706,27 +1657,21 @@ class SumRulesWindow(qt.QMainWindow): legend=legend, info=info, replace=False, - replot=True) - # Assure margins in plot when using matplotlibbacken - if not hasattr(self.plotWindow, 'graph'): - if DEBUG == 1: - print('plotOnDemand -- Setting margins..') - print('\txmin:',xmin,'xmax:',xmax) - print('\tymin:',ymin,'ymax:',ymax) - # Pass if no curves present - curves = self.plotWindow.getAllCurves(just_legend=True) - if len(curves) == 0: - # At this point xymin, xymax should be infinite.. - pass - xmargin = 0.1 * (xmax - xmin) - ymargin = 0.1 * (ymax - ymin) - self.plotWindow.setGraphXLimits(xmin-xmargin, - xmax+xmargin) - self.plotWindow.setGraphYLimits(ymin-ymargin, - ymax+ymargin) - # Need to force replot here for correct display - self.plotWindow.replot() - self.plotWindow.updateLegends() + resetzoom=True) + # Assure margins in plot when using matplotlibbackend + _logger.debug('plotOnDemand -- Setting margins..\n' + '\txmin: %s xmax: %s\n\tymin: %s ymax: %s', + xmin, xmax , ymin, ymax) + # Pass if no curves present + if not len(self.plotWindow.getAllCurves(just_legend=True)): + # At this point xymin, xymax should be infinite.. + pass + xmargin = 0.1 * (xmax - xmin) + ymargin = 0.1 * (ymax - ymin) + self.plotWindow.setGraphXLimits(xmin-xmargin, + xmax+xmargin) + self.plotWindow.setGraphYLimits(ymin-ymargin, + ymax+ymargin) def addMarker(self, window, label='X MARKER', xpos=None, unit=''): # Add spinbox controlling the marker @@ -1758,7 +1703,7 @@ class SumRulesWindow(qt.QMainWindow): def _handleTabChangedSignal(self, idx): if idx >= len(self.tabList): - print('Tab changed -- Index out of range') + _logger.info('Tab changed -- Index out of range') return tab = self.tabList[idx] self.plotOnDemand(window=tab) @@ -1909,8 +1854,7 @@ class LoadDichorismDataDialog(qt.QFileDialog): # contents in the top right widget filename = str(filename) if osPathIsDir(filename): - if DEBUG == 1: - print('LoadDichorismDataDialog.setDataSource -- Invalid path or filename..') + _logger.debug('LoadDichorismDataDialog.setDataSource -- Invalid path or filename..') return try: src = SpecFileDataSource.SpecFileDataSource(filename) diff --git a/PyMca5/PyMcaGui/pymca/XMCDWindow.py b/PyMca5/PyMcaGui/pymca/XMCDWindow.py index 293d848..8d109a1 100644 --- a/PyMca5/PyMcaGui/pymca/XMCDWindow.py +++ b/PyMca5/PyMcaGui/pymca/XMCDWindow.py @@ -28,6 +28,7 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import numpy, copy +import logging import sys from os.path import splitext, basename, dirname, exists, join as pathjoin from PyMca5.PyMcaGui import IconDict @@ -37,7 +38,7 @@ from PyMca5.PyMcaIO import ConfigDict from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaIO import specfilewrapper as specfile from PyMca5 import PyMcaDataDir -from PyMca5.PyMcaGui import ScanWindow as sw +from PyMca5.PyMcaGui.pymca import ScanWindow if hasattr(qt, "QString"): QString = qt.QString @@ -46,8 +47,9 @@ else: QString = str QStringList = list -DEBUG = 0 -if DEBUG: +_logger = logging.getLogger(__name__) + +if _logger.getEffectiveLevel() == logging.DEBUG: numpy.set_printoptions(threshold=50) NEWLINE = '\n' @@ -257,8 +259,7 @@ class XMCDOptions(qt.QDialog): except IndexError: # Returned list is empty return - if DEBUG: - print('saveOptions -- Filename: "%s"' % filename) + _logger.debug('saveOptions -- Filename: "%s"', filename) if len(filename) == 0: self.saved = False return False @@ -304,25 +305,22 @@ class XMCDOptions(qt.QDialog): try: self.setOptions(confDict['XMCDOptions']) except ValueError as e: - if DEBUG: - print('loadOptions -- int conversion failed:',) - print('Invalid value for option \'%s\'' % e) - else: - msg = qt.QMessageBox() - msg.setWindowTitle('XMCD Options Error') - msg.setText('Configuration file \'%s\' corruted' % filename) - msg.exec_() - return + _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.exec_() + return except KeyError as e: - if DEBUG: - print('loadOptions -- invalid identifier:',) - print('option \'%s\' not found' % e) - else: - msg = qt.QMessageBox() - msg.setWindowTitle('XMCD Options Error') - msg.setText('Configuration file \'%s\' corruted' % filename) - msg.exec_() - return + _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.exec_() + return self.saved = True def getOptions(self): @@ -367,15 +365,8 @@ class XMCDOptions(qt.QDialog): if type(button) == type(qt.QRadioButton()): button.setChecked(True) -class XMCDScanWindow(sw.ScanWindow): - xmcdToolbarOptions = { - 'logx': False, - 'logy': False, - 'flip': False, - 'fit': False, - 'roi': False, - } +class XMCDScanWindow(ScanWindow.ScanWindow): plotModifiedSignal = qt.pyqtSignal() saveOptionsSignal = qt.pyqtSignal('QString') @@ -389,22 +380,22 @@ class XMCDScanWindow(sw.ScanWindow): :param parent: Parent Widget, None per default :type parent: QWidget """ - sw.ScanWindow.__init__(self, - parent, - name='XLD/XMCD Analysis', - specfit=None, - plugins=False, - newplot=False, - **self.xmcdToolbarOptions) - if hasattr(self, 'pluginsIconFlag'): - self.pluginsIconFlag = False + ScanWindow.ScanWindow.__init__(self, + parent, + name='XLD/XMCD Analysis', + specfit=None, + plugins=False, + roi=False, fit=False) self.plotWindow = origin - if hasattr(self, 'scanWindowInfoWidget'): - if self.scanWindowInfoWidget: - self.scanWindowInfoWidget.hide() + + # hide the first 2 actions + self.xAxisLogarithmicAction.setVisible(False) + self.yAxisLogarithmicAction.setVisible(False) + + # hide additional toolbar with simple math actions + self._mathToolBar.setVisible(False) # 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) @@ -419,11 +410,16 @@ class XMCDScanWindow(sw.ScanWindow): buttonReplaceAll.setToolTip( '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) - self.graphBottomLayout.addWidget(buttonReplace) - self.graphBottomLayout.addWidget(buttonReplaceAll) + + # this is a hack: silx does not provide an attribute for the + # bottom bar, but it has a handle for the last widget in the bar + # which uses a HBoxLayout. + bottomBarHLayout = self.positionWidget.layout() + bottomBarHLayout.addWidget(qt.HorizontalSpacer()) + bottomBarHLayout.addWidget(buttonAdd) + bottomBarHLayout.addWidget(buttonAddAll) + bottomBarHLayout.addWidget(buttonReplace) + bottomBarHLayout.addWidget(buttonReplaceAll) buttonAdd.clicked.connect(self.add) buttonReplace.clicked.connect(self.replace) @@ -448,8 +444,9 @@ class XMCDScanWindow(sw.ScanWindow): self.xmcd = None self.xas = None - if hasattr(self, '_buildLegendWidget'): - self._buildLegendWidget() + self.getLegendsDockWidget().show() + self.addDockWidget(qt.Qt.RightDockWidgetArea, + self.getLegendsDockWidget()) def sizeHint(self): if self.parent(): @@ -551,8 +548,7 @@ class XMCDScanWindow(sw.ScanWindow): keys = sorted(self.curvesDict.keys()) xRangeList = [self.curvesDict[k].x[0] for k in keys] if not len(xRangeList): - if DEBUG: - print('interpXRange -- Nothing to do') + _logger.debug('interpXRange -- Nothing to do') return None num = 0 @@ -586,11 +582,10 @@ class XMCDScanWindow(sw.ScanWindow): mask = numpy.nonzero((x > xmin) & (x < xmax))[0] out = numpy.sort(numpy.take(x, mask)) - if DEBUG: - print('interpXRange -- Resulting xrange:') - print('\tmin = %f' % out.min()) - print('\tmax = %f' % out.max()) - print('\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): @@ -612,8 +607,8 @@ class XMCDScanWindow(sw.ScanWindow): self.curvesDict = self.copyCurves(groupA + groupB) if (len(self.curvesDict) == 0) or\ - ((len(self.selectionDict['A']) == 0) and\ - (len(self.selectionDict['B']) == 0)): + ((len(self.selectionDict['A']) == 0) and + (len(self.selectionDict['B']) == 0)): # Nothing to do return @@ -622,19 +617,16 @@ class XMCDScanWindow(sw.ScanWindow): # Get active curve active = self.plotWindow.getActiveCurve() if active: - if DEBUG: - print('processSelection -- xrange: use active') - x, y, leg, info = active[0:4] + _logger.debug('processSelection -- xrange: use active') + x = active.getXData() xRange = self.interpXRange(xRange=x) else: return elif self.optsDict['equidistant']: - if DEBUG: - print('processSelection -- xrange: use equidistant') + _logger.debug('processSelection -- xrange: use equidistant') xRange = self.interpXRange(equidistant=True) else: - if DEBUG: - print('processSelection -- xrange: use first') + _logger.debug('processSelection -- xrange: use first') xRange = self.interpXRange() if hasattr(self.plotWindow, 'graph'): activeLegend = self.plotWindow.graph.getActiveCurve(justlegend=True) @@ -687,9 +679,9 @@ class XMCDScanWindow(sw.ScanWindow): ylabel=ylabel, color=color) if idx == 'A': - self.avgA = self.dataObjectsList[-1] + self.avgA = self.getAllCurves(just_legend=True)[-1] if idx == 'B': - self.avgB = self.dataObjectsList[-1] + self.avgB = self.getAllCurves(just_legend=True)[-1] if (self.avgA and self.avgB): self.performXMCD() @@ -740,8 +732,7 @@ class XMCDScanWindow(sw.ScanWindow): out[legend] = tmp else: # TODO: Errorhandling, curve not found - if DEBUG: - print("copyCurves -- Retrieved none type curve") + _logger.debug("copyCurves -- Retrieved none type curve") continue return out @@ -769,9 +760,8 @@ class XMCDScanWindow(sw.ScanWindow): """ if (len(xarr) != len(yarr)) or\ (len(xarr) == 0) or (len(yarr) == 0): - if DEBUG: - print('specAverage -- invalid input!') - print('Array lengths do not match or are 0') + _logger.debug('specAverage -- invalid input!') + _logger.debug('Array lengths do not match or are 0') return None, None same = True @@ -819,9 +809,8 @@ class XMCDScanWindow(sw.ScanWindow): if xmax < xmax0: xmax0 = xmax if xmax <= xmin: - if DEBUG: - print('specAverage -- ') - print('No overlap between spectra!') + _logger.debug('specAverage --\n' + 'No overlap between spectra!') return numpy.array([]), numpy.array([]) # Clip xRange to maximal overlap in spectra @@ -869,17 +858,15 @@ class XMCDScanWindow(sw.ScanWindow): a = self.dataObjectsDict[self.avgA] b = self.dataObjectsDict[self.avgB] else: - if DEBUG: - print('performXAS -- Data not found: ') - print('\tavg_m = %f' % self.avgA) - print('\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]) else: - if DEBUG: - print('performXAS -- x ranges are not the same! ') - print('Force interpolation') + _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]) @@ -893,7 +880,7 @@ class XMCDScanWindow(sw.ScanWindow): xlabel=xlabel, ylabel=ylabel, color="pink") - self.xas = self.dataObjectsList[-1] + self.xas = self.getAllCurves(just_legend=True)[-1] def performXMCD(self): keys = self.dataObjectsDict.keys() @@ -901,15 +888,13 @@ class XMCDScanWindow(sw.ScanWindow): a = self.dataObjectsDict[self.avgA] b = self.dataObjectsDict[self.avgB] else: - if DEBUG: - print('performXMCD -- Data not found:') + _logger.debug('performXMCD -- Data not found:') return if numpy.all( a.x[0] == b.x[0] ): diff = b.y[0] - a.y[0] else: - if DEBUG: - print('performXMCD -- x ranges are not the same! ') - print('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]], @@ -927,8 +912,8 @@ class XMCDScanWindow(sw.ScanWindow): ylabel=ylabel, yaxis="right") # DELETE ME self.graph.mapToY2(' '.join([xmcdLegend, ylabel])) - self._zoomReset() - self.xmcd = self.dataObjectsList[-1] + self.resetZoom() + self.xmcd = self.getAllCurves(just_legend=True)[-1] def selectionInfo(self, idx, key): """ @@ -1012,7 +997,7 @@ class XMCDScanWindow(sw.ScanWindow): return title = '' - legends = self.dataObjectsList + legends = self.getAllCurves(just_legend=True) tmpLegs = sorted(self.curvesDict.keys()) if len(tmpLegs) > 0: title += self.curvesDict[tmpLegs[0]].info.get('selectionlegend','') @@ -1080,12 +1065,12 @@ class XMCDScanWindow(sw.ScanWindow): self.saveOptionsSignal.emit(splitext(sepFileName)[0]) def add(self): - if len(self.dataObjectsList) == 0: + if len(self.getAllCurves(just_legend=True)) == 0: return activeCurve = self.getActiveCurve() if activeCurve is None: return - (xVal, yVal, legend, info) = activeCurve + (xVal, yVal, legend, info) = activeCurve[0:4] #if 'selectionlegend' in info: # newLegend = info['selectionlegend'] #elif 'operation' in info: @@ -1100,7 +1085,8 @@ class XMCDScanWindow(sw.ScanWindow): self.plotModifiedSignal.emit() def addAll(self): - for (xVal, yVal, legend, info) in self.getAllCurves(): + for curve in self.getAllCurves(): + (xVal, yVal, legend, info) = curve[0:4] #if 'selectionlegend' in info: # newLegend = info['selectionlegend'] #elif 'operation' in info: @@ -1115,12 +1101,12 @@ class XMCDScanWindow(sw.ScanWindow): self.plotModifiedSignal.emit() def replace(self): - if len(self.dataObjectsList) == 0: + if len(self.getAllCurves(just_legend=True)) == 0: return activeCurve = self.getActiveCurve() if activeCurve is None: return - (xVal, yVal, legend, info) = activeCurve + (xVal, yVal, legend, info) = activeCurve[0:4] if 'selectionlegend' in info: newLegend = info['selectionlegend'] elif 'operation' in info: @@ -1136,7 +1122,8 @@ class XMCDScanWindow(sw.ScanWindow): def replaceAll(self): allCurves = self.getAllCurves() - for (idx, (xVal, yVal, legend, info)) 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: @@ -1258,10 +1245,8 @@ class XMCDTreeWidget(qt.QTreeWidget): out = [] convert = (convertType != str) if ncol > (self.columnCount()-1): - if DEBUG: - print('getColum -- Selected column out of bounds') + _logger.debug('getColum -- Selected column out of bounds') raise IndexError("Selected column '%d' out of bounds" % ncol) - return out if selectedOnly: sel = self.selectedItems() else: @@ -1276,8 +1261,7 @@ class XMCDTreeWidget(qt.QTreeWidget): if convertType == float: tmp = float('NaN') else: - if DEBUG: - print('getColum -- Conversion failed!') + _logger.debug('getColum -- Conversion failed!') raise TypeError out += [tmp] return out @@ -1477,8 +1461,7 @@ class XMCDWidget(qt.QWidget): helpFileHandle.close() self.helpFileBrowser.setHtml(helpFileHTML) except IOError: - if DEBUG: - print('XMCDWindow -- init: Unable to read help file') + _logger.debug('XMCDWindow -- init: Unable to read help file') self.helpFileBrowser = None self.selectionDict = {'D': [], @@ -2004,7 +1987,7 @@ class XMCDWidget(qt.QWidget): if len(namesList) == len(valuesList): ret.append(dict(zip(namesList, valuesList))) else: - print("Number of motors and values does not match!") + _logger.warning("Number of motors and values does not match!") return ret def _setLists(self): @@ -2024,12 +2007,11 @@ class XMCDWidget(qt.QWidget): if self.plotWindow is not None: curves = self.plotWindow.getAllCurves() else: - if DEBUG: - print('_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 = [curve.getLegend() for curve in curves] + self.infoList = [curve.getInfo() for curve in curves] # Try to recover the scan number from the legend, if not set # Requires additional import: #from re import search as regexpSearch @@ -2086,7 +2068,7 @@ def main(): app = qt.QApplication([]) # Create dummy ScanWindow - swin = sw.ScanWindow() + swin = ScanWindow.ScanWindow() info0 = {'xlabel': 'foo', 'ylabel': 'arb', 'MotorNames': 'oxPS PhaseA Phase BRUKER CRYO OXFORD', @@ -2096,13 +2078,13 @@ def main(): 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.) + 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) + swin.newCurve(x, y2, legend="Curve2", xlabel='ene_st2', ylabel='Ihor', info=info2, replace=False) + swin.newCurve(x, y0, legend="Curve0", xlabel='ene_st0', ylabel='Iver', info=info0, replace=False) + swin.newCurve(x, y1, legend="Curve1", xlabel='ene_st1', ylabel='Ihor', info=info1, replace=False) # info['Key'] is overwritten when using newCurve swin.dataObjectsDict['Curve2 Ihor'].info['Key'] = '1.1' diff --git a/PyMca5/PyMcaIO/ArraySave.py b/PyMca5/PyMcaIO/ArraySave.py index 9989db6..8c4a7e1 100644 --- a/PyMca5/PyMcaIO/ArraySave.py +++ b/PyMca5/PyMcaIO/ArraySave.py @@ -2,7 +2,7 @@ # # The PyMca X-Ray Fluorescence Toolkit # -# Copyright (c) 2004-2014 European Synchrotron Radiation Facility +# Copyright (c) 2004-2018 European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -33,29 +33,50 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import os import numpy import time +import logging +_logger = logging.getLogger(__name__) +import sys try: from PyMca5.PyMcaIO import EdfFile from PyMca5.PyMcaIO import TiffIO except ImportError: - print("ArraySave.py is importing EdfFile and TiffIO from local directory") + _logger.info("ArraySave.py is importing EdfFile and TiffIO from local directory") import EdfFile import TiffIO HDF5 = True try: import h5py + if sys.version_info < (3, ): + text_dtype = h5py.special_dtype(vlen=unicode) + else: + text_dtype = h5py.special_dtype(vlen=str) except ImportError: HDF5 = False -DEBUG = 0 +def to_unicode(s): + """Return string as unicode. + + :param s: A string (bytestring or unicode string). + If s is a bytestring, it is assumed that it is utf-8 encoded text""" + if hasattr(s, "decode"): + return s.decode("utf-8") + return s + + +def to_h5py_utf8(str_list): + """Convert a string or a list of strings to a variable length utf-8 string + compatible with h5py. + """ + return numpy.array(str_list, dtype=text_dtype) def getDate(): localtime = time.localtime() gtime = time.gmtime() - #year, month, day, hour, minute, second,\ + # year, month, day, hour, minute, second,\ # week_day, year_day, delta = time.localtime() year = localtime[0] month = localtime[1] @@ -63,10 +84,10 @@ def getDate(): hour = localtime[3] minute = localtime[4] second = localtime[5] - #get the difference against Greenwich + # get the difference against Greenwich delta = hour - gtime[3] - return "%4d-%02d-%02dT%02d:%02d:%02d%+02d:00" % (year, month, day, hour, - minute, second, delta) + return u"%4d-%02d-%02dT%02d:%02d:%02d%+02d:00" % (year, month, day, hour, + minute, second, delta) def save2DArrayListAsASCII(datalist, filename, @@ -140,11 +161,11 @@ def save2DArrayListAsEDF(datalist, filename, labels=None, dtype=None): for i in range(ndata): if dtype is None: edfout.WriteImage({'Title': labels[i]}, - datalist[i], Append=1) + datalist[i], Append=1) else: edfout.WriteImage({'Title': labels[i]}, - datalist[i].astype(dtype), - Append=1) + datalist[i].astype(dtype), + Append=1) del edfout # force file close @@ -192,7 +213,7 @@ def save2DArrayListAsMonochromaticTiff(datalist, filename, try: os.remove(fname) except OSError: - print("Cannot remove file %s" % fname) + _logger.warning("Cannot remove file %s", fname) pass if (savedData == 0) or multifile: outfileInstance = TiffIO.TiffIO(fname, mode="wb+") @@ -217,6 +238,7 @@ def save2DArrayListAsMonochromaticTiff(datalist, filename, savedData += 1 outfileInstance.close() # force file close + def openHDF5File(name, mode='a', **kwargs): """ Open an HDF5 file. @@ -235,37 +257,22 @@ def openHDF5File(name, mode='a', **kwargs): h5file = h5py.File(name, mode, **kwargs) if h5file.mode != 'r' and len(h5file) == 0: if 'file_name' not in h5file.attrs: - attr = 'file_name' - txt = "%s" % name - dtype = '<S%d' % len(txt) - h5file.attrs.create(attr, txt, dtype=dtype) + h5file.attrs.create('file_name', to_h5py_utf8(name)) if 'file_time' not in h5file.attrs: - attr = 'file_time' - txt = "%s" % getDate() - dtype = '<S%d' % len(txt) - h5file.attrs.create(attr, txt, dtype=dtype) + h5file.attrs.create('file_time', to_h5py_utf8(getDate())) if 'HDF5_version' not in h5file.attrs: - attr = 'HDF5_version' txt = "%s" % h5py.version.hdf5_version - dtype = '<S%d' % len(txt) - h5file.attrs.create(attr, txt, dtype=dtype) + h5file.attrs.create('HDF5_version', to_h5py_utf8(txt)) if 'HDF5_API_version' not in h5file.attrs: - attr = 'HDF5_API_version' txt = "%s" % h5py.version.api_version - dtype = '<S%d' % len(txt) - h5file.attrs.create(attr, txt, dtype=dtype) + h5file.attrs.create('HDF5_API_version', to_h5py_utf8(txt)) if 'h5py_version' not in h5file.attrs: - attr = 'h5py_version' txt = "%s" % h5py.version.version - dtype = '<S%d' % len(txt) - h5file.attrs.create(attr, txt, dtype=dtype) + h5file.attrs.create('h5py_version', to_h5py_utf8(txt)) if 'creator' not in h5file.attrs: - attr = 'creator' - txt = "%s" % 'PyMca' - dtype = '<S%d' % len(txt) - h5file.attrs.create(attr, txt, dtype=dtype) - #if 'format_version' not in self.attrs and len(h5file) == 0: - # h5file.attrs['format_version'] = __format_version__ + h5file.attrs.create('creator', to_h5py_utf8('PyMca')) + # if 'format_version' not in self.attrs and len(h5file) == 0: + # h5file.attrs['format_version'] = __format_version__ return h5file @@ -286,24 +293,23 @@ def getHDF5FileInstanceAndBuffer(filename, shape, hdf = openHDF5File(filename, 'a') entryName = "data" - #entry + # entry nxEntry = hdf.require_group(entryName) if 'NX_class' not in nxEntry.attrs: - nxEntry.attrs['NX_class'] = 'NXentry'.encode('utf-8') - elif nxEntry.attrs['NX_class'] != 'NXentry'.encode('utf-8'): - #should I raise an error? + nxEntry.attrs['NX_class'] = u'NXentry' + elif nxEntry.attrs['NX_class'] not in [b'NXentry', u"NXentry"]: + # should I raise an error? pass - nxEntry['title'] = "PyMca saved 3D Array".encode('utf-8') - nxEntry['start_time'] = getDate().encode('utf-8') + nxEntry['title'] = u"PyMca saved 3D Array" + nxEntry['start_time'] = getDate() nxData = nxEntry.require_group('NXdata') if 'NX_class' not in nxData.attrs: - nxData.attrs['NX_class'] = 'NXdata'.encode('utf-8') - elif nxData.attrs['NX_class'] == 'NXdata'.encode('utf-8'): - #should I raise an error? + nxData.attrs['NX_class'] = u'NXdata' + elif nxData.attrs['NX_class'] in [b'NXdata', u'NXdata']: + # should I raise an error? pass if compression: - if DEBUG: - print("Saving compressed and chunked dataset") + _logger.debug("Saving compressed and chunked dataset") chunk1 = int(shape[1] / 10) if chunk1 == 0: chunk1 = shape[1] @@ -319,30 +325,33 @@ def getHDF5FileInstanceAndBuffer(filename, shape, chunk2 = int(shape[2] / i) break data = nxData.require_dataset(buffername, - shape=shape, - dtype=dtype, - chunks=(1, chunk1, chunk2), - compression=compression) + shape=shape, + dtype=dtype, + chunks=(1, chunk1, chunk2), + compression=compression) else: #no chunking - if DEBUG: - print("Saving not compressed and not chunked dataset") + _logger.debug("Saving not compressed and not chunked dataset") data = nxData.require_dataset(buffername, - shape=shape, - dtype=dtype, - compression=None) - data.attrs['signal'] = numpy.int32(1) + shape=shape, + dtype=dtype, + compression=None) + nxData.attrs['signal'] = to_unicode(buffername) if interpretation is not None: - data.attrs['interpretation'] = interpretation.encode('utf-8') + data.attrs['interpretation'] = to_unicode(interpretation) + for i in range(len(shape)): dim = numpy.arange(shape[i]).astype(numpy.float32) dset = nxData.require_dataset('dim_%d' % i, - dim.shape, - dim.dtype, - dim, - chunks=dim.shape) - dset.attrs['axis'] = numpy.int32(i + 1) - nxEntry['end_time'] = getDate().encode('utf-8') + dim.shape, + dim.dtype, + dim, + chunks=dim.shape) + + nxData.attrs["axes"] = to_h5py_utf8(['dim_%d' % i + for i in range(len(shape))]) + + nxEntry['end_time'] = getDate() return hdf, data @@ -374,6 +383,7 @@ def save3DArrayAsMonochromaticTiff(data, filename, outfileInstance.writeImage(tmpData, info={'Title': labels[i]}) if (ndata > 10): print("Saved image %d of %d" % (i + 1, ndata)) + _logger.info("Saved image %d of %d", i + 1, ndata) elif mcaindex == 1: for i in range(ndata): if i == 1: @@ -384,6 +394,7 @@ def save3DArrayAsMonochromaticTiff(data, filename, tmpData = data[:, i, :].astype(dtype) outfileInstance.writeImage(tmpData, info={'Title': labels[i]}) if (ndata > 10): + _logger.info("Saved image %d of %d", i + 1, ndata) print("Saved image %d of %d" % (i + 1, ndata)) else: for i in range(ndata): @@ -395,20 +406,23 @@ def save3DArrayAsMonochromaticTiff(data, filename, tmpData = data[i].astype(dtype) outfileInstance.writeImage(tmpData, info={'Title': labels[i]}) if (ndata > 10): + _logger.info("Saved image %d of %d", + i + 1, ndata) print("Saved image %d of %d" % (i + 1, ndata)) outfileInstance.close() # force file close + # it should be used to name the data that for the time being is named 'data'. def save3DArrayAsHDF5(data, filename, axes=None, labels=None, dtype=None, mode='nexus', mcaindex=-1, interpretation=None, compression=None): if not HDF5: raise IOError('h5py does not seem to be installed in your system') if (mcaindex == 0) and (interpretation in ["spectrum", None]): - #stack of images to be saved as stack of spectra + # stack of images to be saved as stack of spectra modify = True shape = [data.shape[1], data.shape[2], data.shape[0]] elif (mcaindex != 0) and (interpretation in ["image"]): - #stack of spectra to be saved as stack of images + # stack of spectra to be saved as stack of images modify = True shape = [data.shape[2], data.shape[0], data.shape[1]] else: @@ -417,7 +431,7 @@ def save3DArrayAsHDF5(data, filename, axes=None, labels=None, dtype=None, mode=' if dtype is None: dtype = data.dtype if mode.lower() in ['nexus', 'nexus+']: - #raise IOError, 'NeXus data saving not implemented yet' + # raise IOError, 'NeXus data saving not implemented yet' if os.path.exists(filename): try: os.remove(filename) @@ -425,27 +439,26 @@ def save3DArrayAsHDF5(data, filename, axes=None, labels=None, dtype=None, mode=' raise IOError("Cannot overwrite existing file!") hdf = openHDF5File(filename, 'a') entryName = "data" - #entry + # entry nxEntry = hdf.require_group(entryName) if 'NX_class' not in nxEntry.attrs: - nxEntry.attrs['NX_class'] = 'NXentry'.encode('utf-8') - elif nxEntry.attrs['NX_class'] != 'NXentry'.encode('utf-8'): - #should I raise an error? + nxEntry.attrs['NX_class'] = u'NXentry' + elif nxEntry.attrs['NX_class'] not in [b'NXentry', u'NXentry']: + # should I raise an error? pass - nxEntry['title'] = numpy.string_("PyMca saved 3D Array".encode('utf-8')) - nxEntry['start_time'] = numpy.string_(getDate().encode('utf-8')) + nxEntry['title'] = u"PyMca saved 3D Array" + nxEntry['start_time'] = getDate() nxData = nxEntry.require_group('NXdata') - if ('NX_class' not in nxData.attrs): - nxData.attrs['NX_class'] = 'NXdata'.encode('utf-8') - elif nxData.attrs['NX_class'] != 'NXdata'.encode('utf-8'): - #should I raise an error? + if 'NX_class' not in nxData.attrs: + nxData.attrs['NX_class'] = u'NXdata' + elif nxData.attrs['NX_class'] not in [u'NXdata', b'NXdata']: + # should I raise an error? pass if modify: - if interpretation in ["image", "image".encode('utf-8')]: + if interpretation in [b"image", u"image"]: if compression: - if DEBUG: - print("Saving compressed and chunked dataset") + _logger.debug("Saving compressed and chunked dataset") #risk of taking a 10 % more space in disk chunk1 = int(shape[1] / 10) if chunk1 == 0: @@ -460,31 +473,31 @@ def save3DArrayAsHDF5(data, filename, axes=None, labels=None, dtype=None, mode=' chunk2 = int(shape[2] / i) break dset = nxData.require_dataset('data', - shape=shape, - dtype=dtype, - chunks=(1, chunk1, chunk2), - compression=compression) + shape=shape, + dtype=dtype, + chunks=(1, chunk1, chunk2), + compression=compression) else: - if DEBUG: - print("Saving not compressed and not chunked dataset") + _logger.debug("Saving not compressed and not chunked dataset") #print not compressed -> Not chunked dset = nxData.require_dataset('data', - shape=shape, - dtype=dtype, - compression=None) + shape=shape, + dtype=dtype, + compression=None) for i in range(data.shape[-1]): tmp = data[:, :, i:i + 1] tmp.shape = 1, shape[1], shape[2] dset[i, 0:shape[1], :] = tmp - print("Saved item %d of %d" % (i + 1, data.shape[-1])) + _logger.info("Saved item %d of %d", + i + 1, data.shape[-1]) elif 0: - #if I do not match the input and output shapes it takes ages - #to save the images as spectra. However, it is much faster - #when performing spectra operations. + # if I do not match the input and output shapes it takes ages + # to save the images as spectra. However, it is much faster + # when performing spectra operations. dset = nxData.require_dataset('data', - shape=shape, - dtype=dtype, - chunks=(1, shape[1], shape[2])) + shape=shape, + dtype=dtype, + chunks=(1, shape[1], shape[2])) for i in range(data.shape[1]): # shape[0] chunk = numpy.zeros((1, data.shape[2], data.shape[0]), dtype) @@ -498,35 +511,33 @@ def save3DArrayAsHDF5(data, filename, axes=None, labels=None, dtype=None, mode=' tmpData = data[k:k + 1, i, :] tmpData.shape = -1 chunk[0, :, k] = tmpData - print("Saving item %d of %d" % (i, data.shape[1])) + _logger.info("Saving item %d of %d", + i, data.shape[1]) dset[i, :, :] = chunk else: - #if I do not match the input and output shapes it takes ages - #to save the images as spectra. This is a very fast saving, but - #the performance is awful when reading. + # if I do not match the input and output shapes it takes ages + # to save the images as spectra. This is a very fast saving, but + # the performance is awful when reading. if compression: - if DEBUG: - print("Saving compressed and chunked dataset") + _logger.debug("Saving compressed and chunked dataset") dset = nxData.require_dataset('data', shape=shape, dtype=dtype, chunks=(shape[0], shape[1], 1), compression=compression) else: - if DEBUG: - print("Saving not compressed and not chunked dataset") + _logger.debug("Saving not compressed and not chunked dataset") dset = nxData.require_dataset('data', - shape=shape, - dtype=dtype, - compression=None) + shape=shape, + dtype=dtype, + compression=None) for i in range(data.shape[0]): tmp = data[i:i + 1, :, :] tmp.shape = shape[0], shape[1], 1 dset[:, :, i:i + 1] = tmp else: if compression: - if DEBUG: - print("Saving compressed and chunked dataset") + _logger.debug("Saving compressed and chunked dataset") chunk1 = int(shape[1] / 10) if chunk1 == 0: chunk1 = shape[1] @@ -541,30 +552,31 @@ def save3DArrayAsHDF5(data, filename, axes=None, labels=None, dtype=None, mode=' if (shape[2] % i) == 0: chunk2 = int(shape[2] / i) break - if DEBUG: - print("Used chunk size = (1, %d, %d)" % (chunk1, chunk2)) + _logger.debug("Used chunk size = (1, %d, %d)", + chunk1, chunk2) dset = nxData.require_dataset('data', - shape=shape, - dtype=dtype, - chunks=(1, chunk1, chunk2), - compression=compression) + shape=shape, + dtype=dtype, + chunks=(1, chunk1, chunk2), + compression=compression) else: - if DEBUG: - print("Saving not compressed and notchunked dataset") + _logger.debug("Saving not compressed and notchunked dataset") dset = nxData.require_dataset('data', - shape=shape, - dtype=dtype, - compression=None) + shape=shape, + dtype=dtype, + compression=None) tmpData = numpy.zeros((1, data.shape[1], data.shape[2]), data.dtype) for i in range(data.shape[0]): tmpData[0:1] = data[i:i + 1] dset[i:i + 1] = tmpData[0:1] - print("Saved item %d of %d" % (i + 1, data.shape[0])) + _logger.info("Saved item %d of %d", i + 1, data.shape[0]) + + nxData.attrs["signal"] = u'data' - dset.attrs['signal'] = "1".encode('utf-8') if interpretation is not None: - dset.attrs['interpretation'] = interpretation.encode('utf-8') + dset.attrs['interpretation'] = to_unicode(interpretation) + axesAttribute = [] for i in range(len(shape)): if axes is None: @@ -584,18 +596,20 @@ def save3DArrayAsHDF5(data, filename, axes=None, labels=None, dtype=None, mode=' dimlabel = 'dim_%d' % i axesAttribute.append(dimlabel) adset = nxData.require_dataset(dimlabel, - dim.shape, - dim.dtype, - compression=None) + dim.shape, + dim.dtype, + compression=None) adset[:] = dim[:] adset.attrs['axis'] = i + 1 - dset.attrs['axes'] = (":".join(axesAttribute)).encode('utf-8') - nxEntry['end_time'] = numpy.string_(getDate().encode('utf-8')) + + nxData.attrs["axes"] = to_h5py_utf8([axAttr for axAttr in axesAttribute]) + + nxEntry['end_time'] = getDate() if mode.lower() == 'nexus+': - #create link - g = h5py.h5g.open(hdf.fid, '/'.encode('utf-8')) - g.link('/data/NXdata/data'.encode('utf-8'), - '/data/data'.encode('utf-8'), + # create link + g = h5py.h5g.open(hdf.fid, '/') + g.link('/data/NXdata/data', + '/data/data', h5py.h5g.LINK_HARD) elif mode.lower() == 'simplest': @@ -607,17 +621,17 @@ def save3DArrayAsHDF5(data, filename, axes=None, labels=None, dtype=None, mode=' hdf = h5py.File(filename, 'a') if compression: hdf.require_dataset('data', - shape=shape, - dtype=dtype, - data=data, - chunks=(1, shape[1], shape[2]), - compression=compression) + shape=shape, + dtype=dtype, + data=data, + chunks=(1, shape[1], shape[2]), + compression=compression) else: hdf.require_dataset('data', - shape=shape, - data=data, - dtype=dtype, - compression=None) + shape=shape, + data=data, + dtype=dtype, + compression=None) else: if os.path.exists(filename): try: @@ -629,13 +643,14 @@ def save3DArrayAsHDF5(data, filename, axes=None, labels=None, dtype=None, mode=' hdf = h5py.File(filename, 'a') dataGroup = hdf.require_group('data') dataGroup.require_dataset('data', - shape=shape, - dtype=dtype, - data=data, - chunks=(1, shape[1], shape[2])) + shape=shape, + dtype=dtype, + data=data, + chunks=(1, shape[1], shape[2])) hdf.flush() hdf.close() + def main(): a = numpy.arange(1000000.) a.shape = 20, 50, 1000 @@ -643,6 +658,7 @@ def main(): getHDF5FileInstanceAndBuffer('/test2.h5', (100, 100, 100)) print("Date String = ", getDate()) + if __name__ == "__main__": main() diff --git a/PyMca5/PyMcaIO/BAXSCSVFileParser.py b/PyMca5/PyMcaIO/BAXSCSVFileParser.py index b7ab9ca..0369d18 100644 --- a/PyMca5/PyMcaIO/BAXSCSVFileParser.py +++ b/PyMca5/PyMcaIO/BAXSCSVFileParser.py @@ -33,9 +33,11 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import numpy +import logging from PyMca5.PyMcaIO import SpecFileAbstractClass -DEBUG = 0 +_logger = logging.getLogger(__name__) + class BufferedFile(object): def __init__(self, filename): @@ -127,8 +129,7 @@ class BAXSCSVFileParser(object): return self.__getitem__(int(n[0])-1) def allmotors(self): - if DEBUG: - print("BAXCSVFileParser allmotors called") + _logger.debug("BAXCSVFileParser allmotors called") return [] class BAXSCSVScan(SpecFileAbstractClass.SpecFileAbstractScan): diff --git a/PyMca5/PyMcaIO/EDFStack.py b/PyMca5/PyMcaIO/EDFStack.py index c033572..2b734e7 100644 --- a/PyMca5/PyMcaIO/EDFStack.py +++ b/PyMca5/PyMcaIO/EDFStack.py @@ -37,6 +37,7 @@ from PyMca5.PyMcaMisc import PhysicalMemory import numpy import sys import os +import logging # Offer automatic conversion to HDF5 in case of lacking # memory to hold the Stack. @@ -49,7 +50,8 @@ except: SOURCE_TYPE = "EdfFileStack" -DEBUG = 0 +_logger = logging.getLogger(__name__) + X_AXIS=0 Y_AXIS=1 @@ -232,9 +234,9 @@ class EDFStack(DataObject.DataObject): samplingStep = None i = 2 while samplingStep is None: - print("**************************************************") - print(" Memory error!, attempting %dx%d sampling reduction ") % (i,i) - print("**************************************************") + _logger.warning("**************************************************") + _logger.warning(" Memory error!, attempting %dx%d sampling reduction ", i,i) + _logger.warning("**************************************************") s1, s2 = arrRet[::i, ::i].shape try: self.data = numpy.zeros((s1, s2, @@ -451,14 +453,14 @@ class EDFStack(DataObject.DataObject): self.data[self.incrProgressBar, :,:] = pieceOfStack[:,:] except: if pieceOfStack.shape[1] != arrRet.shape[1]: - print(" ERROR on file %s" % tempEdfFileName) - print(" DIM 1 error Assuming missing data were at the end!!!") + _logger.warning(" ERROR on file %s", tempEdfFileName) + _logger.warning(" DIM 1 error Assuming missing data were at the end!!!") if pieceOfStack.shape[0] != arrRet.shape[0]: - print(" ERROR on file %s" % tempEdfFileName) - print(" DIM 0 error Assuming missing data were at the end!!!") - self.data[self.incrProgressBar,\ - :pieceOfStack.shape[0],\ - :pieceOfStack.shape[1]] = pieceOfStack[:,:] + _logger.warning(" ERROR on file %s", tempEdfFileName) + _logger.warning(" DIM 0 error Assuming missing data were at the end!!!") + self.data[self.incrProgressBar, + :pieceOfStack.shape[0], + :pieceOfStack.shape[1]] = pieceOfStack[:, :] self.incrProgressBar += 1 self.onProgress(self.incrProgressBar) self.onEnd() @@ -571,8 +573,9 @@ class EDFStack(DataObject.DataObject): prefix = name[0:n-i+1] prefix = os.path.join(os.path.dirname(filename),prefix) if not os.path.exists(prefix + number + suffix): - print("Internal error in EDFStack") - print("file should exist: %s " % (prefix + number + suffix)) + _logger.error("Internal error in EDFStack " + "file should exist: %s ", + prefix + number + suffix) return i = 0 if begin is None: @@ -609,7 +612,7 @@ class EDFStack(DataObject.DataObject): sourceInfo["KeyList"]= self.__keyList def getKeyInfo(self, key): - print("Not implemented") + _logger.info("Not implemented") return {} def isIndexedStack(self): diff --git a/PyMca5/PyMcaIO/EdfFile.py b/PyMca5/PyMcaIO/EdfFile.py index 481ca6e..901655b 100644 --- a/PyMca5/PyMcaIO/EdfFile.py +++ b/PyMca5/PyMcaIO/EdfFile.py @@ -92,10 +92,10 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" section is going to be translated into an 1D, 2D or 3D Numpy Array, and accessed through GetData method call. """ -DEBUG = 0 ################################################################################ import sys import numpy +import logging import os.path #, tempfile, shutil try: import gzip @@ -130,6 +130,9 @@ try: except: CAN_USE_FASTEDF = 0 +_logger = logging.getLogger(__name__) + + ################################################################################ # constants HEADER_BLOCK_SIZE = 1024 @@ -630,7 +633,7 @@ class EdfFile(object): try: datasize = self.__GetSizeNumpyType__(datatype) except TypeError: - print("What is the meaning of this error?") + _logger.debug("What is the meaning of this error?") datasize = 8 if self.Images[Index].NumDim == 3: sizeToRead = self.Images[Index].Dim1 * \ @@ -702,7 +705,7 @@ class EdfFile(object): else: if fastedf: - print("I could not use fast routines") + _logger.info("I could not use fast routines") type_ = self.__GetDefaultNumpyType__(self.Images[Index].DataType, index=Index) size_pixel = self.__GetSizeNumpyType__(type_) Data = numpy.array([], type_) @@ -953,20 +956,16 @@ class EdfFile(object): #Internal Methods def __makeSureFileIsOpen(self): - if DEBUG: - print("Making sure file is open") + _logger.debug("Making sure file is open") if not self.__ownedOpen: return if self.ADSC or self.MARCCD or self.PILATUS_CBF or self.SPE: - if DEBUG: - print("Special case. Image is buffered") + _logger.debug("Special case. Image is buffered") return if self.File in [0, None]: - if DEBUG: - print("File is None") + _logger.debug("File is None") elif self.File.closed: - if DEBUG: - print("Reopening closed file") + _logger.debug("Reopening closed file") accessMode = self.File.mode fileName = self.File.name newFile = open(fileName, accessMode) @@ -974,20 +973,16 @@ class EdfFile(object): return def __makeSureFileIsClosed(self): - if DEBUG: - print("Making sure file is closed") + _logger.debug("Making sure file is closed") if not self.__ownedOpen: return if self.ADSC or self.MARCCD or self.PILATUS_CBF or self.SPE: - if DEBUG: - print("Special case. Image is buffered") + _logger.debug("Special case. Image is buffered") return if self.File in [0, None]: - if DEBUG: - print("File is None") + _logger.debug("File is None") elif not self.File.closed: - if DEBUG: - print("Closing file") + _logger.debug("Closing file") self.File.close() return diff --git a/PyMca5/PyMcaIO/HDF5Stack1D.py b/PyMca5/PyMcaIO/HDF5Stack1D.py index 041bf1e..b6cef57 100644 --- a/PyMca5/PyMcaIO/HDF5Stack1D.py +++ b/PyMca5/PyMcaIO/HDF5Stack1D.py @@ -33,22 +33,23 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import posixpath import numpy import h5py +import logging +_logger = logging.getLogger(__name__) try: from PyMca5.PyMcaCore import DataObject from PyMca5.PyMcaMisc import PhysicalMemory except ImportError: - print("HDF5Stack1D importing DataObject from local directory!") + _logger.info("HDF5Stack1D importing DataObject from local directory!") import DataObject import PhysicalMemory try: from PyMca5.PyMcaCore import NexusDataSource from PyMca5.PyMcaCore import NexusTools except ImportError: - print("HDF5Stack1D importing NexusDataSource from local directory!") + _logger.info("HDF5Stack1D importing NexusDataSource from local directory!") import NexusDataSource import NexusDataSource -DEBUG = 0 SOURCE_TYPE = "HDF5Stack1D" class HDF5Stack1D(DataObject.DataObject): @@ -82,10 +83,9 @@ class HDF5Stack1D(DataObject.DataObject): That means scanlist = ["/whatever1"] and selection['y'] = "/whatever2/counts" """ - if DEBUG: - print("filelist = ", filelist) - print("selection = ", selection) - print("scanlist = ", scanlist) + _logger.debug("filelist = %s", filelist) + _logger.debug("selection = %s", selection) + _logger.debug("scanlist = %s", scanlist) # all the files in the same source hdfStack = NexusDataSource.NexusDataSource(filelist) @@ -186,7 +186,7 @@ class HDF5Stack1D(DataObject.DataObject): # this is the case of a selection inside a group scanlist.append("1.%d" % i) except KeyError: - print("%s not in file, ignoring." % dirname) + _logger.warning("%s not in file, ignoring.", dirname) if not len(scanlist): if not ySelection.startswith("/"): path = "/" + ySelection @@ -231,9 +231,8 @@ class HDF5Stack1D(DataObject.DataObject): if not nScans: raise IOError("No entry contains the required data") - if DEBUG: - print("Retained number of files = %d" % nFiles) - print("Retained number of scans = %d" % nScans) + _logger.debug("Retained number of files = %d", nFiles) + _logger.debug("Retained number of scans = %d", nScans) # Now is to decide the number of mca ... # I assume all the scans contain the same number of mca @@ -264,8 +263,7 @@ class HDF5Stack1D(DataObject.DataObject): numpy.float64]: # Some datasets form CLS (origin APS?) arrive as data format # equal to ">u2" and are not triggered as integer types - if DEBUG: - print("Not basic dataset type %s" % self.__dtype) + _logger.debug("Not basic dataset type %s", self.__dtype) if ("%s" % self.__dtype).endswith("2"): self.__dtype = numpy.float32 else: @@ -279,8 +277,7 @@ class HDF5Stack1D(DataObject.DataObject): mcaIndex = selection.get('index', len(shape)-1) if mcaIndex == -1: mcaIndex = len(shape) - 1 - if DEBUG: - print("mcaIndex = %d" % mcaIndex) + _logger.debug("mcaIndex = %d", mcaIndex) considerAsImages = False dim0, dim1, mcaDim = self.getDimensions(nFiles, nScans, shape, index=mcaIndex) @@ -328,9 +325,9 @@ class HDF5Stack1D(DataObject.DataObject): except (MemoryError, ValueError): # some versions report ValueError instead of MemoryError if (nFiles == 1) and (len(shape) == 3): - print("Attempting dynamic loading") + _logger.warning("Attempting dynamic loading") if mSelection is not None: - print("Ignoring monitor") + _logger.warning("Ignoring monitor") self.data = yDataset if mSelection is not None: mdtype = tmpHdf[mpath].dtype @@ -442,7 +439,7 @@ class HDF5Stack1D(DataObject.DataObject): n = nStart if IN_MEMORY == False: # We can only deal with one dynamic dataset - print("Selection %s ignored" % ySelection) + _logger.warning("Selection %s ignored", ySelection) continue if JUST_KEYS: entryName = goodEntryNames[int(scan.split(".")[-1])-1] @@ -528,8 +525,8 @@ class HDF5Stack1D(DataObject.DataObject): case = 0 _time[nStart: nStart + nMcaInYDataset] += timeData if case == -1: - print("I do not know how to handle this time data") - print("Ignoring time information") + _logger.warning("I do not know how to handle this time data") + _logger.warning("Ignoring time information") _time= None if (len(yDataset.shape) == 3) and\ (dim1 == yDataset.shape[1]): @@ -705,8 +702,8 @@ class HDF5Stack1D(DataObject.DataObject): if i != mcaIndex: nRequiredValues *= self.data.shape[i] if _time.size != nRequiredValues: - print("I do not know how to interpret the time information") - print("Ignoring time information") + _logger.warning("I do not know how to interpret the time information") + _logger.warning("Ignoring time information") _time = None else: _time.shape = -1 @@ -732,7 +729,7 @@ class HDF5Stack1D(DataObject.DataObject): if xDataset.size == shape[self.info['McaIndex']]: self.x = [xDataset.reshape(-1)] else: - print("Ignoring xSelection") + _logger.warning("Ignoring xSelection") elif _channels is not None: _channels.shape = -1 self.x = [_channels] @@ -748,8 +745,7 @@ class HDF5Stack1D(DataObject.DataObject): index = -1 if index == -1: index = len(shape) - 1 - if DEBUG: - print("INDEX = %d" % index) + _logger.debug("INDEX = %d", index) #figure out the shape of the stack if len(shape) == 0: #a scalar? @@ -780,9 +776,8 @@ class HDF5Stack1D(DataObject.DataObject): nMca *= shape[i] mcaDim = shape[index] - if DEBUG: - print("nMca = %d" % nMca) - print("mcaDim = ", mcaDim) + _logger.debug("nMca = %d", nMca) + _logger.debug("mcaDim = %s", mcaDim) # HDF allows to work directly from the files without loading # them into memory. diff --git a/PyMca5/PyMcaIO/JcampFileParser.py b/PyMca5/PyMcaIO/JcampFileParser.py index 95b0e1b..fd8d910 100644 --- a/PyMca5/PyMcaIO/JcampFileParser.py +++ b/PyMca5/PyMcaIO/JcampFileParser.py @@ -31,32 +31,31 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import os +import logging import sys import numpy import mmap import re +import time from PyMca5.PyMcaIO import JcampReader from PyMca5.PyMcaIO import SpecFileAbstractClass if sys.version < "3": from StringIO import StringIO else: from io import StringIO -DEBUG = 0 -if DEBUG: - import time +_logger = logging.getLogger(__name__) + class JcampFileParser(SpecFileAbstractClass.SpecFileAbstractClass): def __init__(self, filename, single=False): # get the number of entries in the file self.__lastEntryData = -1 - if DEBUG: - t0 = time.time() + t0 = time.time() if sys.maxsize > 2**32: self._useMMap = True else: self._useMMap = False - if DEBUG: - print("USING MMPA = ", self._useMMap) + _logger.debug("USING MMPA = %s", self._useMMap) if self._useMMap: # 64-bit supported f = open(filename, "rb") @@ -95,18 +94,16 @@ class JcampFileParser(SpecFileAbstractClass.SpecFileAbstractClass): current = f.tell() line = f.readline() f.close() - if DEBUG: - print("Elapsed CURRENT = ", time.time() - t0) + _logger.debug("Elapsed CURRENT = %s", + time.time() - t0) self._filename = os.path.abspath(filename) - if DEBUG: - print("PARSING FIRST ") - t0 = time.time() + _logger.debug("PARSING FIRST ") + t0 = time.time() self._parseEntryData(0) - if DEBUG: - elapsed = time.time() - t0 - print("ELAPSED PER SCAN = ", elapsed) - print("N SCANS = ", self.scanno()) - print("EXPECTED = ", elapsed * self.scanno()) + elapsed = time.time() - t0 + _logger.debug("ELAPSED PER SCAN = %s", elapsed) + _logger.debug("N SCANS = %s", self.scanno()) + _logger.debug("EXPECTED = %s", elapsed * self.scanno()) def _parseEntryData(self, idx): if idx == self.__lastEntryData: @@ -141,7 +138,7 @@ class JcampFileParser(SpecFileAbstractClass.SpecFileAbstractClass): try: fileheader = instance._header except: - print("JCampFileParser cannot access '_header' attribute") + _logger.warning("JCampFileParser cannot access '_header' attribute") fileheader=None data = numpy.zeros((x.size, 2), numpy.float32) data[:, 0] = x diff --git a/PyMca5/PyMcaIO/JcampReader.py b/PyMca5/PyMcaIO/JcampReader.py index c732101..654bf3d 100644 --- a/PyMca5/PyMcaIO/JcampReader.py +++ b/PyMca5/PyMcaIO/JcampReader.py @@ -61,16 +61,19 @@ import os import sys import re import numpy +import logging + patternKey=re.compile(r'^[#][#]\s*(?P<name>[^=]+)=(?P<value>.*)$') #patternNumber = re.compile(r'([+-]?\d+\.?\d*)') patternNumber = re.compile(r'[+-]?[0-9]+\.?[0-9]*(?:[eE][+-]?[0-9]+)?') -DEBUG = 0 -if DEBUG: - text = '1.23 +2 456-7.98+5 10+3.4E+01 98-7.6E-2+3' - print("RESULT:") - print(re.findall(patternNumber, text)) - print("EXPECTED:") - print(['1.23', '+2', '456', '-7.98', '+5', '10', '+3.4E+01', '98', '-7.6E-2', '+3']) +_logger = logging.getLogger(__name__) + + +text = '1.23 +2 456-7.98+5 10+3.4E+01 98-7.6E-2+3' +_logger.debug("RESULT:") +_logger.debug("\t%s", re.findall(patternNumber, text)) +_logger.debug("EXPECTED:") +_logger.debug("\t%s", ['1.23', '+2', '456', '-7.98', '+5', '10', '+3.4E+01', '98', '-7.6E-2', '+3']) class BufferedFile(object): def __init__(self, filenameOrBuffer, block=False): @@ -193,7 +196,7 @@ class JcampReader(object): deltaX = float(self.info["DELTAX"]) nPoints = int(self.info.get("NPOINTS", 0)) if nPoints != len(yValues): - print("Number of points does not match number of values") + _logger.warning("Number of points does not match number of values") nPoints = len(yValues) # this formula is given in the article x = firstX + numpy.arange(nPoints) * \ @@ -251,7 +254,7 @@ class JcampReader(object): deltaX = float(self.info["DELTAX"]) nPoints = int(self.info.get("NPOINTS", 0)) if nPoints != len(yValues): - print("Number of points does not match number of values") + _logger.warning("Number of points does not match number of values") nPoints = len(yValues) # this formula is given in the article x = firstX + numpy.arange(nPoints) * \ diff --git a/PyMca5/PyMcaIO/LispixMap.py b/PyMca5/PyMcaIO/LispixMap.py index 28732f0..79e0b94 100644 --- a/PyMca5/PyMcaIO/LispixMap.py +++ b/PyMca5/PyMcaIO/LispixMap.py @@ -2,7 +2,7 @@ # # The PyMca X-Ray Fluorescence Toolkit # -# Copyright (c) 2004-2015 European Synchrotron Radiation Facility +# Copyright (c) 2004-2018 European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -34,9 +34,11 @@ import os import sys import struct import numpy +import logging from PyMca5 import DataObject -DEBUG = 0 +_logger = logging.getLogger(__name__) + SOURCE_TYPE = "EdfFileStack" class LispixMap(DataObject.DataObject): @@ -187,7 +189,7 @@ class LispixMap(DataObject.DataObject): self.data.shape = rows, columns, channels mcaIndex = 2 else: - print("Assuming spectra") + _logger.info("Assuming spectra") self.data.shape = rows, columns, channels mcaIndex = 2 @@ -211,16 +213,17 @@ def _getDataAndDescriptionFileName(filename): If the associated file is not existing, it returns None. """ - if filename.lower().endswith("raw"): + tmpFileName = filename.lower() + if tmpFileName.endswith("raw"): dataDile = filename headerFile = filename[:-3] + "rpl" else: headerFile = filename - if not os.path.exists(headerFile): - headerFile = None + if os.path.exists(headerFile): + dataFile = headerFile[:-3] + "raw" else: - headerFile = filename - dataFile = filename[:-3] + "raw" + headerFile = ".rpl file not found" + dataFile = ".raw file not found" return dataFile, headerFile def _parseHeaderFile(headerFile): @@ -239,15 +242,17 @@ def _parseHeaderFile(headerFile): record-by vector # image, vector, or dont-care """ - data = open(headerFile, "r").readlines() - numericKeyList = ["width", "Width", - "height", "Height", - "depth", "Depth", - "offset", "Offset", - "data-length", "Data-length"] - asciiKeyList = ["data-type", "Data-type", - "byte-order", "Byte-order", - "record-by", "Record-by"] + f = open(headerFile, "r") + data = f.readlines() + f.close() + numericKeyList = ["width", + "height", + "depth", + "offset", + "data-length"] + asciiKeyList = ["data-type", + "byte-order", + "record-by"] otherKeys = [] description = {} description["depth"] = 1 @@ -255,8 +260,9 @@ def _parseHeaderFile(headerFile): description["data-length"] = 1 description["data-type"] = "unsigned" description["byte-order"] = "little-endian" - for line in data: + for tmpLine in data: treated = False + line = tmpLine.lower() for key in numericKeyList: if line.startswith(key): cleanLine = line.replace("\t", " ") @@ -281,9 +287,7 @@ def _parseHeaderFile(headerFile): if not treated: content = line.replace("\t", " ") if len(content.strip(" ")): - if DEBUG: - print("Ignored line:") - print(line) + _logger.debug("Ignored line: %s", line) return description diff --git a/PyMca5/PyMcaIO/MRCMap.py b/PyMca5/PyMcaIO/MRCMap.py index 4d0da68..9502256 100644 --- a/PyMca5/PyMcaIO/MRCMap.py +++ b/PyMca5/PyMcaIO/MRCMap.py @@ -30,19 +30,18 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -import os import sys -import re import struct import numpy -import copy +import logging from PyMca5 import DataObject if sys.version < '2.6': def bytes(x): return str(x) -DEBUG = 0 +_logger = logging.getLogger(__name__) + SOURCE_TYPE = "EdfFileStack" class MRCMap(DataObject.DataObject): diff --git a/PyMca5/PyMcaIO/NumpyStack.py b/PyMca5/PyMcaIO/NumpyStack.py index 87cf575..a0b517e 100644 --- a/PyMca5/PyMcaIO/NumpyStack.py +++ b/PyMca5/PyMcaIO/NumpyStack.py @@ -35,10 +35,8 @@ The purpose of using this class instead of using StackBase is to simplify the use of McaAdvancedFitBatch with in-memory arrays. """ -import numpy from PyMca5.PyMcaCore import DataObject -DEBUG = 0 SOURCE_TYPE = "EdfFileStack" class NumpyStack(DataObject.DataObject): diff --git a/PyMca5/PyMcaIO/OlympusCSVFileParser.py b/PyMca5/PyMcaIO/OlympusCSVFileParser.py index e90fb9e..56fd18e 100644 --- a/PyMca5/PyMcaIO/OlympusCSVFileParser.py +++ b/PyMca5/PyMcaIO/OlympusCSVFileParser.py @@ -33,8 +33,10 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import numpy +import logging + +_logger = logging.getLogger(__name__) -DEBUG = 0 class BufferedFile(object): def __init__(self, filename): @@ -184,9 +186,8 @@ class OlympusCSVScan(object): a = "#S %d %s" % (self._number + 1, self.command()) return [a] - def header(self,key): - if DEBUG: - print("Requested key = ", key) + def header(self, key): + _logger.debug("Requested key = %s", key) if key in ['S', '#S']: return self.fileheader()[0] elif key == 'N': diff --git a/PyMca5/PyMcaIO/OmnicMap.py b/PyMca5/PyMcaIO/OmnicMap.py index 045d454..3eacc07 100644 --- a/PyMca5/PyMcaIO/OmnicMap.py +++ b/PyMca5/PyMcaIO/OmnicMap.py @@ -36,9 +36,12 @@ import re import struct import numpy import copy +import logging from PyMca5 import DataObject -DEBUG = 0 + +_logger = logging.getLogger(__name__) + SOURCE_TYPE = "EdfFileStack" @@ -79,9 +82,8 @@ class OmnicMap(DataObject.DataObject): s = data[firstByte:(firstByte + 100 - 16)] if sys.version >= '3.0': s = str(s) - if DEBUG: - print("firstByte = %d" % firstByte) - print("s1 = %s " % s) + _logger.debug("firstByte = %d", firstByte) + _logger.debug("s1 = %s ", s) exp = re.compile('(-?[0-9]+\.?[0-9]*)') tmpValues = exp.findall(s) spectrumIndex = int(tmpValues[0]) @@ -92,20 +94,17 @@ class OmnicMap(DataObject.DataObject): else: # I have to calculate them from the scan xPosition, yPosition = self.getPositionFromIndexAndInfo(0, omnicInfo) - if DEBUG: - print("spectrumIndex, nSpectra, xPosition, yPosition = %d %d %f %f" %\ - (spectrumIndex, self.nSpectra, xPosition, yPosition)) + _logger.debug("spectrumIndex, nSpectra, xPosition, yPosition = %d %d %f %f", + spectrumIndex, self.nSpectra, xPosition, yPosition) if sys.version < '3.0': chain = "Spectrum" else: chain = bytes("Spectrum", 'utf-8') secondByte = data[(firstByte + 1):].index(chain) secondByte += firstByte + 1 - if DEBUG: - print("secondByte = ", secondByte) + _logger.debug("secondByte = %s", secondByte) self.nChannels = int((secondByte - firstByte - 100) / 4) - if DEBUG: - print("nChannels = %d" % self.nChannels) + _logger.debug("nChannels = %d", self.nChannels) self.firstSpectrumOffset = firstByte - 16 #fill the header @@ -130,10 +129,9 @@ class OmnicMap(DataObject.DataObject): if (abs(yPosition - oldYPosition) > 1.0e-6) and\ (abs(xPosition - oldXPosition) < 1.0e-6): break - self.nRows = self.nRows + 1 - if DEBUG: - print("DIMENSIONS X = %f Y=%d" %\ - ((self.nSpectra * 1.0) / self.nRows, self.nRows)) + self.nRows += 1 + _logger.debug("DIMENSIONS X = %f Y=%d", + self.nSpectra * 1.0 / self.nRows, self.nRows) #arrange as an EDF Stack self.info = {} @@ -214,9 +212,8 @@ class OmnicMap(DataObject.DataObject): ddict['Laser frequency'] = vFloats[16] ddict['Data spacing'] = (lastX - firstX) / (ddict['Number of points'] - 1.0) ddict['Background gain'] = vFloats[10] - if DEBUG: - for key in ddict.keys(): - print(key, ddict[key]) + for key in ddict.keys(): + _logger.debug("%s: %s", key, ddict[key]) ddict.update(self.getMapInformation(data)) return ddict @@ -257,9 +254,8 @@ class OmnicMap(DataObject.DataObject): ddict['Mapping stage X step size'] = deltaX ddict['Mapping stage Y step size'] = deltaY ddict['Number of spectra'] = abs((1 + ((y1 - y0) / deltaY)) * (1 + ((x1 - x0) / deltaX))) - if DEBUG: - for key in ddict.keys(): - print(key, ddict[key]) + for key in ddict.keys(): + _logger.debug("%s: %s", key, ddict[key]) return ddict def getOmnicInfo(self): @@ -302,7 +298,7 @@ class OmnicMap(DataObject.DataObject): if __name__ == "__main__": filename = None if len(sys.argv) > 2: - DEBUG = int(sys.argv[2]) + _logger.setLevel(logging.DEBUG) if len(sys.argv) > 1: filename = sys.argv[1] elif os.path.exists("SambaPhg_IR.map"): diff --git a/PyMca5/PyMcaIO/PilatusCBF.py b/PyMca5/PyMcaIO/PilatusCBF.py index 9c6f260..12f7b03 100644 --- a/PyMca5/PyMcaIO/PilatusCBF.py +++ b/PyMca5/PyMcaIO/PilatusCBF.py @@ -49,7 +49,8 @@ else: import io _fileClass = io.IOBase -DEBUG = False +_logger = logging.getLogger(__name__) + DATA_TYPES = { "signed 8-bit integer" : np.int8, "signed 16-bit integer" : np.int16, @@ -135,8 +136,7 @@ class PilatusCBF(object): if item not in self.__header.keys(): missing.append(item) if len(missing) > 0: - if DEBUG: - print("CBF file misses the keys " + " ".join(missing)) + _logger.debug("CBF file misses the keys %s", " ".join(missing)) def _readbinary_byte_offset(self, inStream): """ @@ -241,7 +241,7 @@ class PilatusCBF(object): except KeyError: bytecode = np.int32 self.bpp = 32 - logging.warning("Defaulting type to int32") + _logger.warning("Defaulting type to int32") if self.__header["conversions"] == "x-CBF_BYTE_OFFSET": self.__data = self._readbinary_byte_offset(self.cif["_array_data.data"]).astype(bytecode).reshape((self.dim2, self.dim1)) @@ -298,7 +298,7 @@ class CIF(dict): @return the """ if not os.path.isfile(_strFilename): - print("I cannot find the file %s" % _strFilename) + _logger.error("I cannot find the file %s", _strFilename) raise IOError("I cannot find the file %s" % _strFilename) if _bKeepComment: self._parseCIF(open(_strFilename, "rb").read()) @@ -335,7 +335,7 @@ class CIF(dict): @rtype: string """ if not os.path.isfile(_strFilename): - print("I cannot find the file %s" % _strFilename) + _logger.error("I cannot find the file %s", _strFilename) raise IOError("I cannot find the file %s" % _strFilename) lLinesRead = open(_strFilename, "r").readlines() sText = "" @@ -345,12 +345,14 @@ class CIF(dict): if CIF.isAscii(sLine): sText += sLine[:iPos] + "\n" - if iPos > 80 : - print("Warning, this line is too long and could cause problems in PreQuest\n", sLine) + if iPos > 80: + _logger.warning("Warning, this line is too long and could cause problems in PreQuest\n" + "%s", sLine) else : sText += sLine if len(sLine.strip()) > 80 : - print("Warning, this line is too long and could cause problems in PreQuest\n", sLine) + _logger.warning("Warning, this line is too long and could cause problems in PreQuest\n" + "%s", sLine) return sText @@ -564,13 +566,13 @@ class CIF(dict): try: fFile = open(_strFilename, "w") except IOError: - print("Error during the opening of file for write : %s" % _strFilename) + _logger.error("Error during the opening of file for write : %s", _strFilename) return fFile.write(self._cif2str(_strFilename)) try: fFile.close() except IOError: - print("Error during the closing of file for write : %s" % _strFilename) + _logger.error("Error during the closing of file for write : %s", _strFilename) raise @@ -683,7 +685,7 @@ class CIF(dict): @rtype: dictionnary """ if not os.path.isfile(_strFilename): - print("I cannot find the file %s" % _strFilename) + _logger.error("I cannot find the file %s", _strFilename) raise IOError("I cannot find the file %s" % _strFilename) lInFile = open(_strFilename, "r").readlines() self["_audit_creation_method"] = 'From 2-D detector using FIT2D and CIFfile' @@ -744,8 +746,6 @@ class CIF(dict): return False if __name__ == "__main__": - import os - import sys from PyMca5 import EdfFile #fd = open('Cu_ZnO_20289.mccd', 'rb') filename = sys.argv[1] diff --git a/PyMca5/PyMcaIO/SRSFileParser.py b/PyMca5/PyMcaIO/SRSFileParser.py index c809d25..dbda936 100644 --- a/PyMca5/PyMcaIO/SRSFileParser.py +++ b/PyMca5/PyMcaIO/SRSFileParser.py @@ -33,9 +33,11 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import numpy +import logging from PyMca5.PyMcaIO import SpecFileAbstractClass -DEBUG = 0 +_logger = logging.getLogger(__name__) + class BufferedFile(object): def __init__(self, filename): @@ -105,8 +107,7 @@ class SRSFileParser(object): labels = line[:-1].replace("\t", " ").split(" ") readingData = True else: - if DEBUG: - print("Unhandled line %s" % line[:-1]) + _logger.debug("Unhandled line %s", line[:-1]) line = _fileObject.readline() header.append("#N %d" % len(labels)) txt = "#L " diff --git a/PyMca5/PyMcaIO/SpecFileAbstractClass.py b/PyMca5/PyMcaIO/SpecFileAbstractClass.py index 868e7e5..37f07fa 100644 --- a/PyMca5/PyMcaIO/SpecFileAbstractClass.py +++ b/PyMca5/PyMcaIO/SpecFileAbstractClass.py @@ -38,7 +38,12 @@ It can be used to wrap other formats as specile """ import os import numpy -DEBUG = 0 +import logging + + +_logger = logging.getLogger(__name__) + + class SpecFileAbstractClass(object): def __init__(self, filename): if not os.path.exists(filename): @@ -50,16 +55,14 @@ class SpecFileAbstractClass(object): If there is only one scan returns 1:1 with two scans returns 1:2 """ - if DEBUG: - print("list method called") + _logger.debug("list method called") return "1:1" def __getitem__(self, item): """ Returns the scan data """ - if DEBUG: - print("__getitem__ called") + _logger.debug("__getitem__ called") return self.scandata[item] def select(self, key): @@ -139,8 +142,7 @@ class SpecFileAbstractScan(object): return self.__cols def command(self): - if DEBUG: - print("command called") + _logger.debug("command called") text = "" if self.scanheader is not None: if len(self.scanheader): @@ -162,8 +164,7 @@ class SpecFileAbstractScan(object): return text def fileheader(self): - if DEBUG: - print("file header called") + _logger.debug("file header called") labels = '#L ' for label in self.labels: labels += ' '+label @@ -173,8 +174,7 @@ class SpecFileAbstractScan(object): else: return ['#S 1 Unknown command'] else: - if DEBUG: - print("returning ",self.scanheader) + _logger.debug("returning %s", self.scanheader) return self.scanheader def header(self,key): diff --git a/PyMca5/PyMcaIO/SpecFileStack.py b/PyMca5/PyMcaIO/SpecFileStack.py index 1b51b11..24ca907 100644 --- a/PyMca5/PyMcaIO/SpecFileStack.py +++ b/PyMca5/PyMcaIO/SpecFileStack.py @@ -33,6 +33,7 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import numpy +import logging from PyMca5.PyMcaCore import DataObject from PyMca5.PyMcaIO import specfilewrapper as specfile from PyMca5.PyMcaCore import SpecFileDataSource @@ -44,12 +45,14 @@ try: except: pass SOURCE_TYPE = "SpecFileStack" -DEBUG = 0 +_logger = logging.getLogger(__name__) + X_AXIS = 0 Y_AXIS = 1 Z_AXIS = 2 + class SpecFileStack(DataObject.DataObject): def __init__(self, filelist=None): DataObject.DataObject.__init__(self) @@ -299,10 +302,10 @@ class SpecFileStack(DataObject.DataObject): for i in range(5): print("\7") sampling_order += 1 - print("**************************************************") - print(" Memory error!, attempting %dx%d sub-sampling " % \ - (sampling_order, sampling_order)) - print("**************************************************") + _logger.warning("**************************************************") + _logger.warning(" Memory error!, attempting %dx%d sub-sampling ", + sampling_order, sampling_order) + _logger.warning("**************************************************") s0 = int(shape[0] / sampling_order) s1 = int(shape[1] / sampling_order) #if shape[0] % sampling_order: @@ -323,8 +326,8 @@ class SpecFileStack(DataObject.DataObject): tempInstance = specfile.Specfile(tempFileName) if tempInstance is None: if not os.path.exists(tempFileName): - print("File %s does not exists" % tempFileName) - raise IOError( \ + _logger.error("File %s does not exists", tempFileName) + raise IOError( "File %s does not exists" % tempFileName) scan = tempInstance.select(keylist[-1]) for i in iterlist: @@ -460,8 +463,8 @@ class SpecFileStack(DataObject.DataObject): prefix = name[0:n - i + 1] prefix = os.path.join(os.path.dirname(filename), prefix) if not os.path.exists(prefix + number + suffix): - print("Internal error in EDFStack") - print("file should exist: %s" % (prefix + number + suffix)) + _logger.warning("Internal error in EDFStack") + _logger.warning("file should exist: %s", prefix + number + suffix) return i = 0 if begin is None: @@ -499,7 +502,7 @@ class SpecFileStack(DataObject.DataObject): sourceInfo["KeyList"] = self.__keyList def getKeyInfo(self, key): - print("Not implemented") + _logger.info("Not implemented") return {} def isIndexedStack(self): diff --git a/PyMca5/PyMcaIO/SupaVisioMap.py b/PyMca5/PyMcaIO/SupaVisioMap.py index 8fb028c..0d92944 100644 --- a/PyMca5/PyMcaIO/SupaVisioMap.py +++ b/PyMca5/PyMcaIO/SupaVisioMap.py @@ -35,11 +35,14 @@ import os import numpy import struct import time +import logging from PyMca5 import DataObject from PyMca5.PyMcaIO import PyMcaIOHelper -DEBUG = 0 -SOURCE_TYPE="EdfFileStack" + +_logger = logging.getLogger(__name__) + +SOURCE_TYPE = "EdfFileStack" class SupaVisioMap(DataObject.DataObject): def __init__(self, filename): @@ -95,7 +98,7 @@ if __name__ == "__main__": elif os.path.exists(".\PIGE\010826.pige"): filename = ".\PIGE\010826.pige" if filename is not None: - DEBUG = 1 + _logger.setLevel(logging.DEBUG) w = SupaVisioMap(filename) else: print("Please supply input filename") diff --git a/PyMca5/PyMcaIO/TextImageStack.py b/PyMca5/PyMcaIO/TextImageStack.py index b42c254..0a66dc4 100644 --- a/PyMca5/PyMcaIO/TextImageStack.py +++ b/PyMca5/PyMcaIO/TextImageStack.py @@ -33,10 +33,12 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import numpy import sys import os +import logging from PyMca5 import DataObject SOURCE_TYPE = "EdfFileStack" -DEBUG = 0 +_logger = logging.getLogger(__name__) + class TextImageStack(DataObject.DataObject): def __init__(self, filelist = None, imagestack=None, dtype=None): @@ -82,9 +84,9 @@ class TextImageStack(DataObject.DataObject): samplingStep = None i = 2 while samplingStep is None: - print("**************************************************") - print(" Memory error!, attempting %dx%d sampling reduction ") % (i,i) - print("**************************************************") + _logger.warning("**************************************************") + _logger.warning(" Memory error!, attempting %dx%d sampling reduction ", i, i) + _logger.warning("**************************************************") s1, s2 = arrRet[::i, ::i].shape try: self.data = numpy.zeros((self.__nFiles, s1, s2), @@ -178,8 +180,8 @@ class TextImageStack(DataObject.DataObject): prefix = name[0:n-i+1] prefix = os.path.join(os.path.dirname(filename),prefix) if not os.path.exists(prefix + number + suffix): - print("Internal error in EDFStack") - print("file should exist: %s " % (prefix + number + suffix)) + _logger.warning("Internal error in EDFStack") + _logger.warning("file should exist: %s ", prefix + number + suffix) return i = 0 if begin is None: @@ -216,7 +218,7 @@ class TextImageStack(DataObject.DataObject): sourceInfo["KeyList"]= self.__keyList def getKeyInfo(self, key): - print("Not implemented") + _logger.info("Not implemented") return {} def isIndexedStack(self): diff --git a/PyMca5/PyMcaIO/ThermoEMSFileParser.py b/PyMca5/PyMcaIO/ThermoEMSFileParser.py index c2a76ec..5a76012 100644 --- a/PyMca5/PyMcaIO/ThermoEMSFileParser.py +++ b/PyMca5/PyMcaIO/ThermoEMSFileParser.py @@ -33,8 +33,10 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import numpy +import logging + +_logger = logging.getLogger(__name__) -DEBUG = 0 class BufferedFile(object): def __init__(self, filename): @@ -211,9 +213,8 @@ class ThermoEMSScan(object): #a = "#S %d %s" % (self._number + 1, self.command()) #return [a] - def header(self,key): - if DEBUG: - print("Requested key = ", key) + def header(self, key): + _logger.debug("Requested key = %s", key) if key in ['S', '#S']: return self.fileheader()[0] elif key == 'N': diff --git a/PyMca5/PyMcaIO/TiffIO.py b/PyMca5/PyMcaIO/TiffIO.py index c564135..3fc93b8 100644 --- a/PyMca5/PyMcaIO/TiffIO.py +++ b/PyMca5/PyMcaIO/TiffIO.py @@ -36,8 +36,10 @@ import sys import os import struct import numpy +import logging + +_logger = logging.getLogger(__name__) -DEBUG = 0 ALLOW_MULTIPLE_STRIPS = False TAG_ID = { 256:"NumberOfColumns", # S or L ImageWidth @@ -158,8 +160,7 @@ class TiffIO(object): if fortyTwo != 42: raise IOError("Invalid TIFF version %d" % fortyTwo) else: - if DEBUG: - print("VALID TIFF VERSION") + _logger.debug("VALID TIFF VERSION") if sys.byteorder != fileOrder: swap = True else: @@ -181,8 +182,7 @@ class TiffIO(object): def __makeSureFileIsOpen(self): if not self.fd.closed: return - if DEBUG: - print("Reopening closed file") + _logger.debug("Reopening closed file") fileName = self.fd.name if self._access is None: # we do not own the file @@ -195,8 +195,7 @@ class TiffIO(object): def __makeSureFileIsClosed(self): if self._access is None: # we do not own the file - if DEBUG: - print("Not closing not owned file") + _logger.debug("Not closing not owned file") return if not self.fd.closed: @@ -230,25 +229,21 @@ class TiffIO(object): offsetToIFD = 0 else: offsetToIFD = struct.unpack(fmt, inStr)[0] - if DEBUG: - print("Offset to first IFD = %d" % offsetToIFD) + _logger.debug("Offset to first IFD = %d", offsetToIFD) while offsetToIFD != 0: self._IFD.append(offsetToIFD) nImages += 1 fd.seek(offsetToIFD) fmt = st + 'H' numberOfDirectoryEntries = struct.unpack(fmt,fd.read(struct.calcsize(fmt)))[0] - if DEBUG: - print("Number of directory entries = %d" % numberOfDirectoryEntries) + _logger.debug("Number of directory entries = %d", numberOfDirectoryEntries) fmt = st + 'I' fd.seek(offsetToIFD + 2 + 12 * numberOfDirectoryEntries) offsetToIFD = struct.unpack(fmt,fd.read(struct.calcsize(fmt)))[0] - if DEBUG: - print("Next Offset to IFD = %d" % offsetToIFD) + _logger.debug("Next Offset to IFD = %d", offsetToIFD) # offsetToIFD = 0 - if DEBUG: - print("Number of images found = %d" % nImages) + _logger.debug("Number of images found = %d", nImages) return nImages def _parseImageFileDirectory(self, nImage): @@ -258,8 +253,7 @@ class TiffIO(object): fd.seek(offsetToIFD) fmt = st + 'H' numberOfDirectoryEntries = struct.unpack(fmt,fd.read(struct.calcsize(fmt)))[0] - if DEBUG: - print("Number of directory entries = %d" % numberOfDirectoryEntries) + _logger.debug("Number of directory entries = %d", numberOfDirectoryEntries) fmt = st + 'HHI4s' tagIDList = [] @@ -292,15 +286,15 @@ class TiffIO(object): valueOffsetList.append(actualValue) else: valueOffsetList.append(valueOffset) - if DEBUG: - if tagID in TAG_ID: - print("tagID = %s" % TAG_ID[tagID]) - else: - print("tagID = %d" % tagID) - print("fieldType = %s" % FIELD_TYPE[fieldType][0]) - print("nValues = %d" % nValues) - # if nValues == 1: - # print("valueOffset = %s" % valueOffset) + + if tagID in TAG_ID: + _logger.debug("tagID = %s", TAG_ID[tagID]) + else: + _logger.debug("tagID = %d", tagID) + _logger.debug("fieldType = %s", FIELD_TYPE[fieldType][0]) + _logger.debug("nValues = %d", nValues) + # if nValues == 1: + # print("valueOffset = %s" % valueOffset) return tagIDList, fieldTypeList, nValuesList, valueOffsetList def _readIFDEntry(self, tag, tagIDList, fieldTypeList, nValuesList, valueOffsetList): @@ -332,8 +326,7 @@ class TiffIO(object): try: text = raw.decode("utf-8") except UnicodeDecodeError: - if DEBUG: - print("TIFF file tag %d contains non ASCII/UTF-8 characters. " % tag) + _logger.debug("TIFF file tag %d contains non ASCII/UTF-8 characters. ", tag) text = raw.decode("utf-8", errors='replace') # Use a valid ASCII character to limit ferther encoding error text = text.replace(u"\ufffd", "?") @@ -363,8 +356,7 @@ class TiffIO(object): def _readInfo(self, nImage, close=True): if nImage in self._imageInfoCacheIndex: - if DEBUG: - print("Reading info from cache") + _logger.debug("Reading info from cache") return self._imageInfoCache[self._imageInfoCacheIndex.index(nImage)] # read the header @@ -424,7 +416,7 @@ class TiffIO(object): if TAG_PHOTOMETRIC_INTERPRETATION in tagIDList: interpretation = valueOffsetList[tagIDList.index(TAG_PHOTOMETRIC_INTERPRETATION)] else: - print("WARNING: Non standard TIFF. Photometric interpretation TAG missing") + _logger.warning("WARNING: Non standard TIFF. Photometric interpretation TAG missing") helpString = "" @@ -483,13 +475,13 @@ class TiffIO(object): tagIDList, fieldTypeList, nValuesList, valueOffsetList)[0] else: rowsPerStrip = nRows - print("WARNING: Non standard TIFF. Rows per strip TAG missing") + _logger.warning("WARNING: Non standard TIFF. Rows per strip TAG missing") if TAG_STRIP_BYTE_COUNTS in tagIDList: stripByteCounts = self._readIFDEntry(TAG_STRIP_BYTE_COUNTS, tagIDList, fieldTypeList, nValuesList, valueOffsetList) else: - print("WARNING: Non standard TIFF. Strip byte counts TAG missing") + _logger.warning("WARNING: Non standard TIFF. Strip byte counts TAG missing") if hasattr(nBits, 'index'): expectedSum = 0 for n in nBits: @@ -509,8 +501,7 @@ class TiffIO(object): interpretation = 1 # we cannot rely on any cache in this case useInfoCache = False - if DEBUG: - print("FORCED MONO") + _logger.debug("FORCED MONO") else: useInfoCache = True @@ -560,8 +551,7 @@ class TiffIO(object): return info def _readImage(self, nImage, **kw): - if DEBUG: - print("Reading image %d" % nImage) + _logger.debug("Reading image %d", nImage) if 'close' in kw: close = kw['close'] else: @@ -569,8 +559,7 @@ class TiffIO(object): rowMin = kw.get('rowMin', None) rowMax = kw.get('rowMax', None) if nImage in self._imageDataCacheIndex: - if DEBUG: - print("Reading image data from cache") + _logger.debug("Reading image data from cache") return self._imageDataCache[self._imageDataCacheIndex.index(nImage)] self.__makeSureFileIsOpen() @@ -592,8 +581,7 @@ class TiffIO(object): raise IOError("Compressed TIFF images not supported except packbits") else: # PackBits compression - if DEBUG: - print("Using PackBits compression") + _logger.debug("Using PackBits compression") interpretation = info["photometricInterpretation"] if interpretation == 2: @@ -688,7 +676,7 @@ class TiffIO(object): if nRows == rowsPerStrip: actualBytesPerRow = int(image.nbytes / nRows) if actualBytesPerRow != bytesPerRow: - print("Warning: Bogus StripByteCounts information") + _logger.warning("Warning: Bogus StripByteCounts information") bytesPerRow = actualBytesPerRow nBytes = (rowMax-rowMin+1) * bytesPerRow fd.seek(stripOffsets[0] + rowMin * bytesPerRow) @@ -855,8 +843,7 @@ class TiffIO(object): # get the image file directories nImages = self.getImageFileDirectories() - if DEBUG: - print("File contains %d images" % nImages) + _logger.debug("File contains %d images", nImages) if nImages == 0: fd.seek(4) fmt = st + 'I' @@ -1093,8 +1080,7 @@ class TiffIO(object): stripOffsetsString += struct.pack(fmt, value) stripByteCountsString += struct.pack(fmt, stripByteCounts) - if DEBUG: - print("IMAGE WILL START AT %d" % stripOffsets[0]) + _logger.debug("IMAGE WILL START AT %d", stripOffsets[0]) # sample format if dtype in [numpy.float32, numpy.float64] or\ diff --git a/PyMca5/PyMcaIO/specfile/src/sfinit.c b/PyMca5/PyMcaIO/specfile/src/sfinit.c index 7291427..3c3c517 100644 --- a/PyMca5/PyMcaIO/specfile/src/sfinit.c +++ b/PyMca5/PyMcaIO/specfile/src/sfinit.c @@ -1,5 +1,5 @@ # /*########################################################################## -# Copyright (C) 1995-2017 European Synchrotron Radiation Facility +# Copyright (C) 1995-2018 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 @@ -521,11 +521,12 @@ sfReadFile(SpecFile *sf,SfCursor *cursor,int *error) { free(buffer); sf->no_scans = cursor->scanno; - /* - * Save last - */ - sfSaveScan(sf,cursor,error); - + if (sf->no_scans > 0) { + /* + * Save last + */ + sfSaveScan(sf,cursor,error); + } return; } @@ -790,24 +791,21 @@ sfSaveScan(SpecFile *sf, SfCursor *cursor,int *error) { static void sfAssignScanNumbers(SpecFile *sf) { - int size,i; - char *buffer,*ptr; - - char buffer2[50]; + int i; + char *ptr; + char buffer[50]; + char buffer2[50]; register ObjectList *object, *object2; SpecScan *scan, *scan2; - size = 50; - buffer = (char *) malloc(size); - for ( object = (sf->list).first; object; object=object->next) { scan = (SpecScan *) object->contents; lseek(sf->fd,scan->offset,SEEK_SET); - read(sf->fd,buffer,size); + read(sf->fd,buffer,sizeof(buffer)); buffer[49] = '\0'; for ( ptr = buffer+3,i=0; *ptr != ' ';ptr++,i++) buffer2[i] = *ptr; diff --git a/PyMca5/PyMcaIO/specfilewrapper.py b/PyMca5/PyMcaIO/specfilewrapper.py index 44e82f9..7ba7653 100644 --- a/PyMca5/PyMcaIO/specfilewrapper.py +++ b/PyMca5/PyMcaIO/specfilewrapper.py @@ -34,6 +34,7 @@ import os import sys import numpy import re +import logging from PyMca5.PyMcaIO import specfile from PyMca5.PyMcaIO import Fit2DChiFileParser from PyMca5.PyMcaIO import APSMEDFileParser @@ -42,14 +43,16 @@ from PyMca5.PyMcaIO import BAXSCSVFileParser from PyMca5.PyMcaIO import OlympusCSVFileParser from PyMca5.PyMcaIO import ThermoEMSFileParser from PyMca5.PyMcaIO import JcampFileParser + +_logger = logging.getLogger(__name__) + try: from PyMca5.PyMcaIO import SPXFileParser SPX = True except: - print("specfilewrapper cannot import SPXFileParser") + _logger.info("specfilewrapper cannot import SPXFileParser") SPX = False -DEBUG = 0 if sys.version >= '2.6': def safe_str(bytesObject): @@ -106,16 +109,13 @@ def Specfile(filename): qxas = False if len(line): #it is a Specfile - if DEBUG: - print("This looks as a specfile") + _logger.debug("This looks as a specfile") output=specfile.Specfile(filename) elif SPX and filename.upper().endswith("SPX"): - if DEBUG: - print("This looks as an SPX file") + _logger.debug("This looks as an SPX file") output = SPXFileParser.SPXFileParser(filename) else: - if DEBUG: - print("this does not look as a specfile") + _logger.debug("this does not look as a specfile") if len(line0) > 7: if line0.startswith('$SPEC_ID') or\ line0.startswith('$DATE_MEA') or\ @@ -130,27 +130,22 @@ def Specfile(filename): if (not qxas) and (not amptek) and APSMEDFileParser.isAPSMEDFile(filename): return APSMEDFileParser.APSMEDFileParser(filename) if (not qxas) and (not amptek) and SRSFileParser.isSRSFile(filename): - if DEBUG: - print("SRSFileParser") + _logger.debug("SRSFileParser") return SRSFileParser.SRSFileParser(filename) if (not qxas) and (not amptek) and BAXSCSVFileParser.isBAXSCSVFile(filename): - if DEBUG: - print("BAXSCSVFileParser") + _logger.debug("BAXSCSVFileParser") return BAXSCSVFileParser.BAXSCSVFileParser(filename) if (not qxas) and (not amptek) and \ OlympusCSVFileParser.isOlympusCSVFile(filename): - if DEBUG: - print("OlympusCSVFileParser") + _logger.debug("OlympusCSVFileParser") return OlympusCSVFileParser.OlympusCSVFileParser(filename) if (not qxas) and (not amptek) and \ ThermoEMSFileParser.isThermoEMSFile(filename): - if DEBUG: - print("ThermoEMSFileParser") + _logger.debug("ThermoEMSFileParser") return ThermoEMSFileParser.ThermoEMSFileParser(filename) if (not qxas) and (not amptek) and \ JcampFileParser.isJcampFile(filename): - if DEBUG: - print("JcampFileParser") + _logger.debug("JcampFileParser") return JcampFileParser.JcampFileParser(filename) output = specfilewrapper(filename, amptek=amptek, qxas=qxas) return output @@ -427,8 +422,7 @@ class myscandata(object): return self.__cols def command(self): - if DEBUG: - print("command called") + _logger.debug("command called") if self.qxas is not None: if 'S' in self.qxas: text = self.qxas['S'] @@ -467,8 +461,7 @@ class myscandata(object): def fileheader(self, key=''): # key is there for compatibility - if DEBUG: - print("file header called") + _logger.debug("file header called") return self._fileheader def header(self, key): diff --git a/PyMca5/PyMcaMath/SimpleMath.py b/PyMca5/PyMcaMath/SimpleMath.py index 880c56b..e2dde53 100644 --- a/PyMca5/PyMcaMath/SimpleMath.py +++ b/PyMca5/PyMcaMath/SimpleMath.py @@ -31,8 +31,13 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import numpy +import logging from . import SGModule + +_logger = logging.getLogger(__name__) + + class SimpleMath(object): def derivate(self,xdata,ydata, xlimits=None): x=numpy.array(xdata, copy=False, dtype=numpy.float) @@ -87,9 +92,8 @@ class SimpleMath(object): """ if (len(xarr) != len(yarr)) or\ (len(xarr) == 0) or (len(yarr) == 0): - if DEBUG: - print('specAverage -- invalid input!') - print('Array lengths do not match or are 0') + _logger.debug('specAverage -- invalid input!\n' + 'Array lengths do not match or are 0') return None, None same = True @@ -139,9 +143,8 @@ class SimpleMath(object): if xmax < xmax0: xmax0 = xmax if xmax <= xmin: - if DEBUG: - print('specAverage -- ') - print('No overlap between spectra!') + _logger.debug('specAverage -- \n' + 'No overlap between spectra!') return numpy.array([]), numpy.array([]) # make sure x0 is sorted diff --git a/PyMca5/PyMcaMath/fitting/SimpleFitAll.py b/PyMca5/PyMcaMath/fitting/SimpleFitAll.py index b340e17..d469c04 100644 --- a/PyMca5/PyMcaMath/fitting/SimpleFitAll.py +++ b/PyMca5/PyMcaMath/fitting/SimpleFitAll.py @@ -36,7 +36,6 @@ import logging from PyMca5.PyMcaIO import ConfigDict import PyMca5 -DEBUG = False if sys.version_info < (3, ): text_dtype = h5py.special_dtype(vlen=unicode) @@ -177,7 +176,7 @@ class SimpleFitAll(object): except: _logger.error( "Error %s processing index = %d", sys.exc_info()[1], i) - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise self.onProcessSpectraFinished() self._status = "Ready" diff --git a/PyMca5/PyMcaMath/fitting/SimpleFitModule.py b/PyMca5/PyMcaMath/fitting/SimpleFitModule.py index 6cfde86..2a4e705 100644 --- a/PyMca5/PyMcaMath/fitting/SimpleFitModule.py +++ b/PyMca5/PyMcaMath/fitting/SimpleFitModule.py @@ -37,13 +37,16 @@ import copy import logging import glob import types +import logging from . import Gefit from . import SpecfitFuns from PyMca5 import getDefaultUserFitFunctionsDirectory _logger = logging.getLogger(__name__) -DEBUG = 0 + +_logger = logging.getLogger(__name__) + class SimpleFit(object): @@ -121,7 +124,7 @@ class SimpleFit(object): if ffile is not None: self.importFunctions(ffile) else: - print("WARNING:Function %s not among defined functions" % fName) + _logger.warning("WARNING:Function %s not among defined functions", fName) continue self._fitConfiguration['functions'][fName]['configuration']=\ ddict['functions'][fName]['configuration'] @@ -135,8 +138,7 @@ class SimpleFit(object): return if (oldConfig['fit']['xmin'] != self._fitConfiguration['fit']['xmin']) or\ (oldConfig['fit']['xmax'] != self._fitConfiguration['fit']['xmax']): - if DEBUG: - print("SETTING DATA AGAIN") + _logger.debug("SETTING DATA AGAIN") self.setData(self._x0, self._y0, xmin=self._fitConfiguration['fit']['xmin'], xmax=self._fitConfiguration['fit']['xmax']) @@ -145,15 +147,13 @@ class SimpleFit(object): for key in ['strip_flag', 'stripanchorsflag', 'stripalgorithm', 'stripwidth', 'stripiterations', 'stripconstant']: if oldConfig['fit'][key] != self._fitConfiguration['fit'][key]: - if DEBUG: - print("RECALCULATING STRIP") + _logger.debug("RECALCULATING STRIP") self._getStripBackground() break if key == 'stripanchorsflag': if len(oldConfig['fit']['stripanchorslist']) !=\ len(self._fitConfiguration['fit']['stripanchorslist']): - if DEBUG: - print("ANCHORS CHANGE, RECALCULATING STRIP") + _logger.debug("ANCHORS CHANGE, RECALCULATING STRIP") self._getStripBackground() break @@ -204,8 +204,7 @@ class SimpleFit(object): self._fitConfiguration['fit']['xmax'] = xmax * 1.0 if sigma is not None: self._sigma = self._sigma0[idx] - if DEBUG: - print("TODO: Make sure we have something to fit") + _logger.debug("TODO: Make sure we have something to fit") #get strip/SNIP background self._z = self._getStripBackground() @@ -427,8 +426,7 @@ class SimpleFit(object): #SNIP algorithm if self._fitConfiguration['fit']['stripalgorithm'] in ["SNIP", 1]: - if DEBUG: - print("CALCULATING SNIP") + _logger.debug("CALCULATING SNIP") if len(anchorslist) == 0: anchorslist = [0, len(ysmooth)-1] anchorslist.sort() @@ -448,15 +446,14 @@ class SimpleFit(object): #strip background niter = self._fitConfiguration['fit']['stripiterations'] if niter > 0: - if DEBUG: - print("CALCULATING STRIP") - print("iterations = ", niter) - print("constant = ", - self._fitConfiguration['fit']['stripconstant']) - print("width = ", - self._fitConfiguration['fit']['stripwidth']) - print("anchors = ", anchorslist) - result=SpecfitFuns.subac(ysmooth, + _logger.debug("CALCULATING STRIP") + _logger.debug("iterations = ", niter) + _logger.debug("constant = %s", + self._fitConfiguration['fit']['stripconstant']) + _logger.debug("width = %s", + self._fitConfiguration['fit']['stripwidth']) + _logger.debug("anchors = %s", anchorslist) + result = SpecfitFuns.subac(ysmooth, self._fitConfiguration['fit']['stripconstant'], niter, self._fitConfiguration['fit']['stripwidth'], @@ -476,8 +473,7 @@ class SimpleFit(object): 1, anchorslist) else: - if DEBUG: - print("NO STRIP, NO SNIP") + _logger.debug("NO STRIP, NO SNIP") result = numpy.zeros(ysmooth.shape, numpy.float) + min(ysmooth) return result @@ -538,9 +534,8 @@ class SimpleFit(object): self.estimateFunction() fitFunctionDict = self._fitConfiguration['functions']\ [fitFunction] - if DEBUG: - print("ESTIMATION parameters = ",functionParameters) - print("ESTIMATION constraints = ",functionConstraints) + _logger.debug("ESTIMATION parameters = %s", functionParameters) + _logger.debug("ESTIMATION constraints = %s", functionConstraints) self._setStatus("Fit function estimation finished") #estimations are made @@ -717,8 +712,7 @@ class SimpleFit(object): weightflag = 0 else: weightflag = 1 - if DEBUG: - print("STILL TO HANDLE DERIVATIVES") + _logger.debug("STILL TO HANDLE DERIVATIVES") model_deriv = self.modelFunctionDerivative if self._fitConfiguration['fit']['strip_flag']: y = self._y - self._z @@ -727,7 +721,7 @@ class SimpleFit(object): self._fitResult = None if not flagconstrained: param_constrains = [] - if DEBUG: + try: result = Gefit.LeastSquaresFit(self.modelFunction,param_val, xdata=self._x, ydata=y, @@ -736,26 +730,18 @@ class SimpleFit(object): weightflag=weightflag, model_deriv=model_deriv, fulloutput=True) - else: - try: - result = Gefit.LeastSquaresFit(self.modelFunction,param_val, - xdata=self._x, - ydata=y, - sigmadata=self._sigma, - constrains=param_constrains, - weightflag=weightflag, - model_deriv=model_deriv, - fulloutput=True) - except: - text = sys.exc_info()[1] - if type(text) is not type(" "): - text = text.args - if len(text): - text = text[0] - else: - text = '' - self._setStatus('Fit error : %s' %text) + except: + if _logger.getEffectiveLevel() == logging.DEBUG: raise + text = sys.exc_info()[1] + if type(text) is not type(" "): + text = text.args + if len(text): + text = text[0] + else: + text = '' + self._setStatus('Fit error : %s' %text) + raise self._fitResult = {} self._fitResult['fit_function'] = self.getFitFunction() @@ -766,16 +752,15 @@ class SimpleFit(object): self._fitResult['niter'] = result[3] self._fitResult['lastdeltachi'] = result[4] self._fitResult['n_background_parameters'] = self.__nBackgroundParameters - if DEBUG: - print("Found parameters = ", self._fitResult['fittedvalues']) - i=0 + _logger.debug("Found parameters = %s", self._fitResult['fittedvalues']) + i = 0 self._fitResult['parameters'] = [] for param in self.paramlist: - if param['code'] != 'IGNORE': - self._fitResult['parameters'].append(param['name']) - param['fitresult'] = result[0][i] - param['sigma']= result[2][i] - i = i + 1 + if param['code'] != 'IGNORE': + self._fitResult['parameters'].append(param['name']) + param['fitresult'] = result[0][i] + param['sigma'] = result[2][i] + i += 1 self._setStatus("Fit finished") return result @@ -836,7 +821,7 @@ class SimpleFit(object): try: y += self._z except: - print("Cannot add strip background") + _logger.warning("Cannot add strip background") return y def _evaluateFunction(self, x=None): @@ -917,5 +902,5 @@ def test(): a.exec_() if __name__=="__main__": - DEBUG = 1 + _logger.setLevel(logging.DEBUG) test() diff --git a/PyMca5/PyMcaMath/fitting/Specfit.py b/PyMca5/PyMcaMath/fitting/Specfit.py index d238afe..abad09b 100644 --- a/PyMca5/PyMcaMath/fitting/Specfit.py +++ b/PyMca5/PyMcaMath/fitting/Specfit.py @@ -33,10 +33,15 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import numpy +import logging from . import SpecfitFuns from .Gefit import LeastSquaresFit from PyMca5.PyMcaCore import EventHandler -DEBUG = 0 + + +_logger = logging.getLogger(__name__) + + class Specfit(object): #def __init__(self,x=None,y=None,sigmay=None): def __init__(self, *vars, **kw): @@ -576,9 +581,8 @@ class Specfit(object): try: theory=newfun.THEORY except: - if DEBUG: - print("No theory name") - theory="%s" % file + _logger.debug("No theory name") + theory = "%s" % file try: parameters=newfun.PARAMETERS except: @@ -631,7 +635,7 @@ class Specfit(object): #tkMessageBox.showerror('Error',"Problem implementing user theory") badluck=1 if badluck: - print("ERROR IMPORTING") + _logger.warning("ERROR IMPORTING") return badluck def startfit(self,mcafit=0): @@ -800,7 +804,7 @@ class Specfit(object): try: idx = numpy.nonzero((self.xdata>=x[0]) & (self.xdata<=x[-1]))[0] except: - print("ERROR ",x) + _logger.warning("ERROR %s", x) yy=numpy.take(self.ydata,idx) nrx=numpy.shape(x)[0] nry=numpy.shape(yy)[0] @@ -912,7 +916,7 @@ class Specfit(object): if self.fitconfig['fittheory'] is not None: error = self.settheory(self.fitconfig[key]) if error: - print("ERROR on background and/or theory configuration") + _logger.warning("ERROR on background and/or theory configuration") result.update(self.fitconfig) return result @@ -951,9 +955,8 @@ class Specfit(object): y = self.ydata0 if 'debug' in kw: - mcadebug = 1 - else: - mcadebug = 0 + _logger.setLevel(logging.DEBUG) + if 'Yscaling' in kw: if kw['Yscaling'] is not None: yscaling=kw['Yscaling'] @@ -1004,14 +1007,12 @@ class Specfit(object): sensitivity) for idx in peaksidx: peaks.append(self.xdata[int(idx)]) - if mcadebug: - print("MCA Found peaks = ",peaks) + _logger.debug("MCA Found peaks = %s", peaks) if len(peaks): regions=self.mcaregions(peaks,self.xdata[fwhm]-self.xdata[0]) else: regions=[] - if mcadebug: - print(" regions = ",regions) + _logger.debug(" regions = %s", regions) #if the function needs a scaling just give it #removed estimate should deal with it #self.configure(Yscaling=yscaling,yscaling=yscaling) diff --git a/PyMca5/PyMcaMath/fitting/SpecfitFunctions.py b/PyMca5/PyMcaMath/fitting/SpecfitFunctions.py index ad443af..f2ed4bd 100644 --- a/PyMca5/PyMcaMath/fitting/SpecfitFunctions.py +++ b/PyMca5/PyMcaMath/fitting/SpecfitFunctions.py @@ -32,11 +32,13 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import os import numpy +import logging arctan = numpy.arctan from PyMca5.PyMcaMath.fitting import SpecfitFuns from PyMca5.PyMcaMath.fitting.Gefit import LeastSquaresFit -DEBUG=0 +_logger = logging.getLogger(__name__) + try: HOME=os.getenv('HOME') @@ -276,7 +278,7 @@ class SpecfitFunctions(object): ngauss=1 newpar=[] for i in range(ngauss): - print("Defining Gaussian numer %d " % (i+1)) + _logger.info("Defining Gaussian numer %d ", i+1) newpar.append(input('Height = ')) newpar.append(input('Position = ')) newpar.append(input('FWHM = ')) diff --git a/PyMca5/PyMcaMath/fitting/StackSimpleFit.py b/PyMca5/PyMcaMath/fitting/StackSimpleFit.py index 41af8b8..866069e 100644 --- a/PyMca5/PyMcaMath/fitting/StackSimpleFit.py +++ b/PyMca5/PyMcaMath/fitting/StackSimpleFit.py @@ -33,12 +33,14 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import numpy +import logging from PyMca5.PyMcaIO import ConfigDict from . import SimpleFitModule from PyMca5.PyMcaIO import ArraySave from PyMca5 import PyMcaDirs -DEBUG = 0 +_logger = logging.getLogger(__name__) + class StackSimpleFit(object): def __init__(self, fit=None): @@ -181,9 +183,9 @@ class StackSimpleFit(object): if self.mask[self._row, self._column]: self.processStackData(i) except: - print("Error %s processing index = %d, row = %d column = %d" %\ - (sys.exc_info()[1], i, self._row, self._column)) - if DEBUG: + _logger.warning("Error %s processing index = %d, row = %d column = %d", + sys.exc_info()[1], i, self._row, self._column) + if _logger.getEffectiveLevel() == logging.DEBUG: raise self.onProcessStackFinished() self._status = "Ready" @@ -195,12 +197,10 @@ class StackSimpleFit(object): x, y, sigma, xmin, xmax = self.getFitInputValues(i) self.fit.setData(x, y, sigma=sigma, xmin=xmin, xmax=xmax) if self._parameters is None: - if DEBUG: - print("First estimation") + _logger.debug("First estimation") self.fit.estimate() elif self.__ALWAYS_ESTIMATE: - if DEBUG: - print("Estimation due to settings") + _logger.debug("Estimation due to settings") self.fit.estimate() self.estimateFinished() values, chisq, sigma, niter, lastdeltachi = self.fit.startFit() @@ -296,12 +296,10 @@ class StackSimpleFit(object): return x, y, sigma, self.xMin, self.xMax def estimateFinished(self): - if DEBUG: - print("Estimate finished") + _logger.debug("Estimate finished") def aboutToGetStackData(self, idx): - if DEBUG: - print("New spectrum %d" % idx) + _logger.debug("New spectrum %d", idx) self._currentFitIndex = idx if self.progressCallback is not None: self.progressCallback(idx, self._nRows * self._nColumns) @@ -313,8 +311,7 @@ class StackSimpleFit(object): os.remove(self.outputFile) def fitFinished(self): - if DEBUG: - print("fit finished") + _logger.debug("fit finished") #get parameter results fitOutput = self.fit.getResult(configuration=False) @@ -322,7 +319,7 @@ class StackSimpleFit(object): row= self._row column = self._column if result is None: - print("result not valid for row %d, column %d" % (row, column)) + _logger.warning("result not valid for row %d, column %d", row, column) return if self.fixedLenghtOutput and (self._parameters is None): @@ -402,8 +399,7 @@ class StackSimpleFit(object): return ddict def onProcessStackFinished(self): - if DEBUG: - print("Stack proccessed") + _logger.debug("Stack proccessed") self._status = "Stack Fitting finished" if self.fixedLenghtOutput: self._status = "Writing output files" @@ -457,5 +453,4 @@ def test(): instance.processStack() if __name__=="__main__": - DEBUG = 0 test() diff --git a/PyMca5/PyMcaMath/mva/NNMAModule.py b/PyMca5/PyMcaMath/mva/NNMAModule.py index 950ee9f..4e0a07e 100644 --- a/PyMca5/PyMcaMath/mva/NNMAModule.py +++ b/PyMca5/PyMcaMath/mva/NNMAModule.py @@ -184,6 +184,7 @@ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 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" @@ -196,7 +197,10 @@ except: MDP = False from . import py_nnma -DEBUG = 0 + + +_logger = logging.getLogger(__name__) + function_list = ['FNMAI', 'ALS', 'FastHALS', 'GDCLS'] function_dict = {"NNSC": py_nnma.NNSC, @@ -211,8 +215,13 @@ function_dict = {"NNSC": py_nnma.NNSC, "FastHALS": py_nnma.FastHALS, "SNMF": py_nnma.SNMF, } + +VERBOSE = _logger.getEffectiveLevel() == logging.DEBUG + + def nnma(stack, ncomponents, binning=None, - function=None, eps=5e-5, verbose=DEBUG, maxcount=1000, kmeans=False): + function=None, eps=5e-5, verbose=VERBOSE, + maxcount=1000, kmeans=False): if kmeans and (not MDP): raise ValueError("K Means not supported") #I take the defaults for the other parameters diff --git a/PyMca5/PyMcaMath/mva/PCAModule.py b/PyMca5/PyMcaMath/mva/PCAModule.py index c159bb8..af02372 100644 --- a/PyMca5/PyMcaMath/mva/PCAModule.py +++ b/PyMca5/PyMcaMath/mva/PCAModule.py @@ -32,6 +32,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import os import time +import logging import numpy import numpy.linalg try: @@ -50,13 +51,15 @@ except: from . import Lanczos from . import PCATools -DEBUG = 0 + + +_logger = logging.getLogger(__name__) + # Make these functions accept arguments not relevant to # them in order to simplify having a common graphical interface def lanczosPCA(stack, ncomponents=10, binning=None, legacy=True, **kw): - if DEBUG: - print("lanczosPCA") + _logger.debug("lanczosPCA") if binning is None: binning = 1 @@ -322,7 +325,7 @@ def multipleArrayPCA(stackList0, ncomponents=10, binning=None, legacy=True, **kw totalVariance = numpy.diag(covMatrix).sum() evalues, evectors = numpy.linalg.eigh(covMatrix) covMatrix = None - print("Total Variance = ", totalVariance.sum()) + _logger.info("Total Variance = %s", totalVariance.sum()) images = numpy.zeros((ncomponents, npixels), numpy.float32) eigenvectors = numpy.zeros((ncomponents, eigenvectorLength), numpy.float32) @@ -377,8 +380,7 @@ def expectationMaximizationPCA(stack, ncomponents=10, binning=None, legacy=True, """ This is a fast method when the number of components is small """ - if DEBUG: - print("expectationMaximizationPCA") + _logger.debug("expectationMaximizationPCA") #This part is common to all ... if binning is None: binning = 1 @@ -466,8 +468,7 @@ def numpyPCA(stack, ncomponents=10, binning=None, legacy=True, **kw): """ This is a covariance method using numpy """ - if DEBUG: - print("PCAModule.numpyPCA called") + _logger.debug("PCAModule.numpyPCA called") if hasattr(stack, "info"): index = stack.info.get('McaIndex', -1) elif "index" in kw: @@ -508,13 +509,12 @@ def mdpICAFloat64(stack, ncomponents=10, binning=None, def mdpPCA(stack, ncomponents=10, binning=None, dtype='float64', svd='True', mask=None, spectral_mask=None, legacy=True, **kw): - if DEBUG: - print("MDP Method") - print("binning =", binning) - print("dtype = ", dtype) - print("svd = ", svd) + _logger.debug("MDP Method") + _logger.debug("binning = %s", binning) + _logger.debug("dtype = %s", dtype) + _logger.debug("svd = %s", svd) for key in kw: - print("mdpPCA Key ignored: %s" % key) + _logger.info("mdpPCA Key ignored: %s", key) #This part is common to all ... if binning is None: binning = 1 @@ -620,14 +620,12 @@ def mdpPCA(stack, ncomponents=10, binning=None, dtype='float64', svd='True', pca.train(data[:i, :]) else: pca.train(data[:i, spectral_mask > 0]) - if DEBUG: - print("Half training") + _logger.debug("Half training") if spectral_mask is None: pca.train(data[i:, :]) else: pca.train(data[i:, spectral_mask > 0]) - if DEBUG: - print("Full training") + _logger.debug("Full training") else: if spectral_mask is None: pca.train(data) @@ -729,14 +727,12 @@ def mdpICA(stack, ncomponents=10, binning=None, dtype='float64', if 1: if (mdp.__version__ >= "2.5"): - if DEBUG: - print("TDSEPNone") + _logger.debug("TDSEPNone") ica = mdp.nodes.TDSEPNode(white_comp=ncomponents, verbose=False, dtype="float64", white_parm={'svd': svd}) - if DEBUG: - t0 = time.time() + t0 = time.time() shape = data.shape if len(data.shape) == 3: if r > 10: @@ -802,22 +798,19 @@ def mdpICA(stack, ncomponents=10, binning=None, dtype='float64', ica.train(data[:i, :]) else: ica.train(data[:i, spectral_mask > 0]) - if DEBUG: - print("Half training") + _logger.debug("Half training") if spectral_mask is None: ica.train(data[i:, :]) else: ica.train(data[i:, spectral_mask > 0]) - if DEBUG: - print("Full training") + _logger.debug("Full training") else: if spectral_mask is None: ica.train(data) else: ica.train(data[:, spectral_mask > 0]) ica.stop_training() - if DEBUG: - print("training elapsed = %f" % (time.time() - t0)) + _logger.debug("training elapsed = %f", time.time() - t0) else: if 0: print("ISFANode (alike)") @@ -826,14 +819,12 @@ def mdpICA(stack, ncomponents=10, binning=None, dtype='float64', dtype='float64', white_parm={'svd':svd}) elif 1: - if DEBUG: - print("FastICANode") + _logger.debug("FastICANode") ica = mdp.nodes.FastICANode(white_comp=ncomponents, verbose=False, dtype=dtype) else: - if DEBUG: - print("CuBICANode") + _logger.debug("CuBICANode") ica = mdp.nodes.CuBICANode(white_comp=ncomponents, verbose=False, dtype=dtype) @@ -858,8 +849,8 @@ def mdpICA(stack, ncomponents=10, binning=None, dtype='float64', if (len(data.shape) == 3): images = numpy.zeros((2 * ncomponents, r, c), data.dtype) for i in range(r): - print("Building images. Projecting data %d out of %d" %\ - (i + 1, r)) + _logger.info("Building images. Projecting data %d out of %d", + i + 1, r) if binning > 1: if spectral_mask is None: tmpData = data[i, :, :] diff --git a/PyMca5/PyMcaMath/mva/PCATools.py b/PyMca5/PyMcaMath/mva/PCATools.py index 6882f67..85261ef 100644 --- a/PyMca5/PyMcaMath/mva/PCATools.py +++ b/PyMca5/PyMcaMath/mva/PCATools.py @@ -31,6 +31,8 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys +import logging +import time import numpy import numpy.linalg try: @@ -41,7 +43,8 @@ except ImportError: #print("WARNING: Not using BLAS/ATLAS, PCA calculation will be slower") dotblas = numpy -DEBUG = 0 +_logger = logging.getLogger(__name__) + def getCovarianceMatrix(stack, index=None, @@ -157,8 +160,7 @@ def getCovarianceMatrix(stack, eigenvectorLength = nChannels if (not force)and isinstance(data, numpy.ndarray): - if DEBUG: - print("Memory consuming calculation") + _logger.debug("Memory consuming calculation") #make a direct calculation (memory cosuming) #take a view to the data dataView = data[:] @@ -197,8 +199,7 @@ def getCovarianceMatrix(stack, return covMatrix, sumSpectrum / usedPixels, usedPixels #we are dealing with dynamically loaded data - if DEBUG: - print("DYNAMICALLY LOADED DATA") + _logger.debug("DYNAMICALLY LOADED DATA") #create the needed storage space for the covariance matrix try: covMatrix = numpy.zeros((eigenvectorLength, eigenvectorLength), @@ -224,7 +225,7 @@ def getCovarianceMatrix(stack, except AttributeError: txt = "%s" % type(data) if 'h5py' in txt: - print("Implementing h5py workaround") + _logger.warning("Implementing h5py workaround") import h5py data = h5py.Dataset(data.id) else: @@ -246,8 +247,7 @@ def getCovarianceMatrix(stack, if divider <= 0: step = oldShape[index] break - if DEBUG: - print("Reading chunks of %d images" % step) + _logger.debug("Reading chunks of %d images", step) nImagesRead = 0 if (binning == 1) and oldShape[index] >= step: chunk1 = numpy.zeros((step, nPixels), numpy.float64) @@ -321,8 +321,7 @@ def getCovarianceMatrix(stack, i += iToRead chunk1 = None chunk2 = None - if DEBUG: - print("totalImages Read = ", nImagesRead) + _logger.debug("totalImages Read = %s", nImagesRead) elif (binning > 1) and (oldShape[index] >= step): chunk1 = numpy.zeros((step, nPixels), numpy.float64) chunk2 = numpy.zeros((nPixels, step), numpy.float64) @@ -411,8 +410,7 @@ def getCovarianceMatrix(stack, step = nPixels break step = nPixels - if DEBUG: - print("Reading chunks of %d spectra" % step) + _logger.debug("Reading chunks of %d spectra", step) cleanWeights.shape = 1, -1 if len(data.shape) == 2: @@ -537,11 +535,10 @@ def getCovarianceMatrix(stack, def numpyPCA(stack, index=-1, ncomponents=10, binning=None, center=True, scale=True, mask=None, spectral_mask=None, legacy=True, **kw): - if DEBUG: - print("PCATools.numpyPCA") - print("index = %d" % index) - print("center = %s" % center) - print("scale = %s" % scale) + _logger.debug("PCATools.numpyPCA") + _logger.debug("index = %d", index) + _logger.debug("center = %s", center) + _logger.debug("scale = %s", scale) #recover the actual data to work with if hasattr(stack, "info") and hasattr(stack, "data"): #we are dealing with a PyMca data object @@ -573,7 +570,7 @@ def numpyPCA(stack, index=-1, ncomponents=10, binning=None, except AttributeError: txt = "%s" % type(data) if 'h5py' in txt: - print("Implementing h5py workaround") + _logger.warning("Implementing h5py workaround") import h5py data = h5py.Dataset(data.id) else: @@ -609,7 +606,7 @@ def numpyPCA(stack, index=-1, ncomponents=10, binning=None, totalVariance = numpy.diag(cov) standardDeviation = numpy.sqrt(totalVariance) standardDeviation = standardDeviation + (standardDeviation == 0) - print("Total Variance = ", totalVariance.sum()) + _logger.info("Total Variance = %s", totalVariance.sum()) normalizeToUnitStandardDeviation = scale if 0: @@ -620,18 +617,18 @@ def numpyPCA(stack, index=-1, ncomponents=10, binning=None, cov[i, :] /= numpy.sqrt(totalVariance[i]) cov[:, i] /= numpy.sqrt(totalVariance[i]) - if DEBUG: - import time - t0 = time.time() + t0 = time.time() + evalues, evectors = numpy.linalg.eigh(cov) # The total variance should also be the sum of all the eigenvalues calculatedTotalVariance = evalues.sum() if abs(totalVariance.sum() - evalues.sum()) > 0.0001: - print("WARNING: Discrepancy on total variance") - print("Variance from covariance matrix = ", totalVariance.sum()) - print("Variance from sum of eigenvalues = ", calculatedTotalVariance) - if DEBUG: - print("Eig elapsed = ", time.time() - t0) + _logger.info("WARNING: Discrepancy on total variance") + _logger.info("Variance from covariance matrix = %s", + totalVariance.sum()) + _logger.info("Variance from sum of eigenvalues = %s", + calculatedTotalVariance) + _logger.debug("Eig elapsed = %s", time.time() - t0) cov = None dtype = numpy.float32 @@ -649,12 +646,13 @@ def numpyPCA(stack, index=-1, ncomponents=10, binning=None, eigenvalues[i0] = evalues[i] partialExplainedVariance = 100. * evalues[i] / \ calculatedTotalVariance - print("PC%02d Explained variance %.5f %% " %\ - (i0 + 1, partialExplainedVariance)) + _logger.info("PC%02d Explained variance %.5f %% ", + i0 + 1, partialExplainedVariance) totalExplainedVariance += partialExplainedVariance eigenvectors[i0, :] = evectors[:, i] #print("NORMA = ", numpy.dot(evectors[:, i].T, evectors[:, i])) - print("Total explained variance = %.2f %% " % totalExplainedVariance) + _logger.info("Total explained variance = %.2f %% ", + totalExplainedVariance) else: idx = numpy.argsort(evalues) eigenvalues[:] = evalues[idx] diff --git a/PyMca5/PyMcaMisc/PhysicalMemory.py b/PyMca5/PyMcaMisc/PhysicalMemory.py index 32ba9dd..39a21a0 100644 --- a/PyMca5/PyMcaMisc/PhysicalMemory.py +++ b/PyMca5/PyMcaMisc/PhysicalMemory.py @@ -34,6 +34,11 @@ import sys import os import ctypes import traceback +import logging + + +_logger = logging.getLogger(__name__) + def loadCLibrary(name="libc.so"): try: @@ -134,8 +139,8 @@ def getPhysicalMemoryOrNone(): if value <= 0: # Value makes no sense. # return None as requested in case of failure - print("WARNING: Returned physical memory does not make sense %d" % \ - value) + _logger.warning("WARNING: Returned physical memory does not make sense %d", + value) return None else: return value diff --git a/PyMca5/PyMcaPhysics/xas/XASClass.py b/PyMca5/PyMcaPhysics/xas/XASClass.py index ac12ce1..049c2d2 100644 --- a/PyMca5/PyMcaPhysics/xas/XASClass.py +++ b/PyMca5/PyMcaPhysics/xas/XASClass.py @@ -35,8 +35,7 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import copy -import os -import sys +import logging import numpy import time from PyMca5.PyMca import XASNormalization @@ -46,7 +45,8 @@ try: _XAS = True except ImportError: _XAS = False -DEBUG = 0 +_logger = logging.getLogger(__name__) + def polynom(x, parameters): if hasattr(x, 'shape'): @@ -134,7 +134,7 @@ def polspl_evaluate(set2,xl,xh,c,nc,nr): #; #; now the rest of the points #; - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: fit2 = fit *1 for i in range(len(set2[0,:])): # loop over all the points for j in range(1,int(nr+1)): # loop over the # of intervals @@ -156,8 +156,9 @@ def polspl_evaluate(set2,xl,xh,c,nc,nr): yval += c[cstart+k] * numpy.power(xval,(k-1)) fit[0, idx] = xval fit[1, idx] = yval - if DEBUG: - print("GOOD? = ", numpy.allclose(fit, fit2)) + + if _logger.getEffectiveLevel() == logging.DEBUG: + _logger.debug("GOOD? = %s", numpy.allclose(fit, fit2)) return fit def polspl(x,y,w,npts,xl,xh,nr,nc): @@ -428,8 +429,8 @@ def postEdge(set2,kmin=None,kmax=None,polDegree=[3,3,3],knots=None, full=False): c = numpy.zeros(36) nc = numpy.zeros(10, numpy.int32) if len(polDegree) > 10: - print("Error: Maximum number of intervals is 10") - print(" Number of intervals forced to 10") + _logger.warning("Error: Maximum number of intervals is 10") + _logger.warning(" Number of intervals forced to 10") polDegree = polDegree[0:9] x1 = 0.0 # set2[:,0].min() @@ -441,8 +442,7 @@ def postEdge(set2,kmin=None,kmax=None,polDegree=[3,3,3],knots=None, full=False): x2 = kmax xrange1 = [x1,x2] - if DEBUG: - print("++++++++++++++++++",xrange1) + _logger.debug("++++++++++++++++++%s", xrange1) if knots not in [None, []]: if len(knots) == len(polDegree): if knots[0] > kmin: @@ -456,8 +456,8 @@ def postEdge(set2,kmin=None,kmax=None,polDegree=[3,3,3],knots=None, full=False): if knots[-1] < kmax: knots = list(knots) + [kmax] if ( (len(polDegree)+1) != len(knots) ): - print("Error: dimension of knots must be dimension of polDegree+1") - print(" Forced automatic (equidistant) knot definition.") + _logger.warning("Error: dimension of knots must be dimension of polDegree+1") + _logger.warning(" Forced automatic (equidistant) knot definition.") knots = None else: xrange1 = knots[0],knots[-1] @@ -486,12 +486,12 @@ def postEdge(set2,kmin=None,kmax=None,polDegree=[3,3,3],knots=None, full=False): goodi = (set2[:,0] >= xrange1[0]) & (set2[:,0] <= xrange1[1]) set22 = set2[goodi,:] - if DEBUG: - print(' Number of fitting points: %d'%(len(set22[:,0]))) - print(' polynomials used for fitting: %d'%(nr)) - print('# degree min max') - for i in range(1,nr+1): - print("%d %9d %9.2f %9.2f "%(i,nc[i]-1,xl[i],xh[i])) + _logger.debug(' Number of fitting points: %d', len(set22[:,0])) + _logger.debug(' polynomials used for fitting: %d', nr) + _logger.debug('# degree min max') + for i in range(1,nr+1): + _logger.debug("%d %9d %9.2f %9.2f ", + i, nc[i]-1, xl[i], xh[i]) # ; # ; call spline @@ -506,11 +506,11 @@ def postEdge(set2,kmin=None,kmax=None,polDegree=[3,3,3],knots=None, full=False): #t0 = time.time() if _XAS: c = _xas.polspl(xx,yy,w,npts,xl,xh,nr,nc) - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: t0 = time.time() c2 = polspl(xx,yy,w,npts,xl,xh,nr,nc) - print("polspl elapsed = ", time.time() - t0) - print("OK?", numpy.allclose(c, c2)) + _logger.debug("polspl elapsed = %s", time.time() - t0) + _logger.debug("OK? %s", numpy.allclose(c, c2)) else: c = polspl(xx,yy,w,npts,xl,xh,nr,nc) @@ -589,8 +589,7 @@ def getFTWindowWeights(tk, window="Gaussian", windpar=0.2, wrange=None): window = window[0].upper() + window[1:].lower() else: window = names[window] - if DEBUG: - print("Using window ", window) + _logger.debug("Using window %s", window) if wrange == None: xmax = tk.max() @@ -791,16 +790,14 @@ def getBackFT(fourier,npoint=4096,krange=[2.0,12.0],rstep=None,rmin=None,rmax=No rstep = fourier[nn+1,0] - fourier[nn,0] rstep2 = fourier[nn+2,0] - fourier[nn+1,0] rdiff = numpy.abs (rstep - rstep2) - if DEBUG: - print(' back rstep = %f'%(rstep)) - print(' rdiff = %f'%rdiff) + _logger.debug(' back rstep = %f', rstep) + _logger.debug(' rdiff = %f', rdiff) if (rdiff >= 1e-6): raise ValueError("r griding is not regular; Use rstep keyword -> Abort") #return fou ptstart = int(rmin/rstep) - if DEBUG: - print(' ptstart = %d'%ptstart) - print(' ptstart+npt = %d'%(ptstart+npt)) + _logger.debug(' ptstart = %d', ptstart) + _logger.debug(' ptstart+npt = %d', ptstart+npt) fou[ptstart:ptstart+npt,:]=fourier else: #;--- interpolation fou[:,0] = numpy.linspace(0,0,npoint-1,npoint)*rstep @@ -1243,8 +1240,7 @@ class XASClass(object): raise ValueError("Edge energy not set") if (id(energy) == id(self._energy)) and self._equidistant: # data do not need to be interpolated - if DEBUG: - print("NO INTERPOLATION") + _logger.debug("NO INTERPOLATION") eWork = energy muWork = mu else: @@ -1412,7 +1408,7 @@ class XASClass(object): normalizedSpectrum[i:] *= (jump / \ (data["PostEdge"] - data["PreEdge"])[i:]) else: - print("WARNING: Undefined jump normalization method. Assume Flattened") + _logger.warning("WARNING: Undefined jump normalization method. Assume Flattened") jumpMethod = "Flattened" i = numpy.argmin(energy < e0) normalizedSpectrum[i:] *= (jump / \ @@ -1487,25 +1483,25 @@ if __name__ == "__main__": #sys.exit() from PyMca5.PyMca import PyMcaQt as qt app = qt.QApplication([]) - from PyMca5.PyMca import PlotWindow - w = PlotWindow.PlotWindow() - w.addCurve(energy, mu, legend="original", replot=False) + from silx.gui.plot import Plot1D + w = Plot1D() + w.addCurve(energy, mu, legend="original") w.addCurve(ddict["NormalizedEnergy"], - ddict["NormalizedMu"], legend="Mu", yaxis="right", replot=False) + ddict["NormalizedMu"], legend="Mu", yaxis="right") w.addCurve(ddict["NormalizedEnergy"], - ddict["NormalizedSignal"], legend="Post", replot=False) + ddict["NormalizedSignal"], legend="Post") w.addCurve(ddict["NormalizedEnergy"], - ddict["NormalizedBackground"], legend="Pre",replot=False) + ddict["NormalizedBackground"], legend="Pre") w.resetZoom() w.show() - exafs = PlotWindow.PlotWindow() + exafs = Plot1D() idx = (ddict["EXAFSKValues"] >= ddict["KMin"]) & \ (ddict["EXAFSKValues"] <= ddict["KMax"]) exafs.addCurve(ddict["EXAFSKValues"][idx], ddict["EXAFSNormalized"][idx], legend="Normalized EXAFS") exafs.show() #""" - ft = PlotWindow.PlotWindow() + ft = Plot1D() ft.addCurve(ddict["FT"]["FTRadius"], ddict["FT"]["FTIntensity"]) ft.resetZoom() ft.show() diff --git a/PyMca5/PyMcaPhysics/xas/XASNormalization.py b/PyMca5/PyMcaPhysics/xas/XASNormalization.py index 03be8d3..2b97bb0 100644 --- a/PyMca5/PyMcaPhysics/xas/XASNormalization.py +++ b/PyMca5/PyMcaPhysics/xas/XASNormalization.py @@ -36,11 +36,13 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import numpy +import logging from PyMca5.PyMcaMath.fitting import SpecfitFuns from PyMca5.PyMcaMath import SGModule from PyMca5.PyMcaMath.fitting.Gefit import LeastSquaresFit -DEBUG = 0 -if DEBUG: +_logger = logging.getLogger(__name__) + +if _logger.getEffectiveLevel() == logging.DEBUG: from pylab import * @@ -388,7 +390,7 @@ def XASPolynomialNormalization(spectrum, normalizedSpectrum = (spectrum - pre_edge_function(prePol, energy))\ /post_edge_function(postPol, energy) jump = post_edge_function(postPol, edge) - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: plot(energy, spectrum, 'o') plot(xPre, pre_edge_function(prePol, xPre), 'r') plot(xPost, post_edge_function(postPol, xPost)+pre_edge_function(prePol, xPost), 'y') @@ -433,8 +435,8 @@ def XASVictoreenNormalization(spectrum, weightflag=0, linear=1)[0] normalizedSpectrum = (spectrum - pre_edge_function(prePol, energy))\ /post_edge_function(postPol, energy) - if DEBUG: - print("VICTOREEN") + if _logger.getEffectiveLevel() == logging.DEBUG: + _logger.info("VICTOREEN") plot(energy, spectrum, 'o') plot(xPre, pre_edge_function(prePol, xPre), 'r') plot(xPost, @@ -460,7 +462,7 @@ if __name__ == "__main__": edge = estimateXANESEdge(spectrum+i, energy=energy) print("EDGE ELAPSED = ", (time.time() - t0)/float(n)) print("EDGE = %f" % edge) - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: n = 1 else: n = 100 @@ -490,7 +492,7 @@ if __name__ == "__main__": algorithm_parameters={'pre_edge_order':'Victoreen', 'post_edge_order':'Victoreen'})[0:2] print("ELAPSED Victoreen = ", (time.time() - t0)/float(n)) - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: #plot(energy, spectrum, 'b') plot(nEne0, nSpe0, 'k', label='Polynomial') plot(nEneP, nSpeP, 'b', label='Polynomial') diff --git a/PyMca5/PyMcaPhysics/xas/XASStackBatch.py b/PyMca5/PyMcaPhysics/xas/XASStackBatch.py index 0b363af..da80eaa 100644 --- a/PyMca5/PyMcaPhysics/xas/XASStackBatch.py +++ b/PyMca5/PyMcaPhysics/xas/XASStackBatch.py @@ -37,12 +37,14 @@ import os import numpy import h5py import posixpath +import logging from PyMca5.PyMca import XASClass from PyMca5.PyMcaIO import ConfigDict import time -DEBUG = 0 +_logger = logging.getLogger(__name__) + class XASStackBatch(object): def __init__(self, analyzer=None): @@ -82,8 +84,7 @@ class XASStackBatch(object): :return: A dictionnary with the results as keys. """ - if DEBUG: - t0 = time.time() + t0 = time.time() if configuration is not None: self._analyzer.setConfiguration(configuration) @@ -95,7 +96,7 @@ class XASStackBatch(object): # dictated by the current configuration pass else: - print("WARNING: weight not handled yet") + _logger.warning("WARNING: weight not handled yet") weightPolicy = 0 # no weight #weightPolicy = 1 # use average weight from the sum spectrum #weightPolicy = 2 # individual pixel weights (slow) @@ -311,15 +312,15 @@ class XASStackBatch(object): outputDict["images"] = output out.flush() out.close() - if DEBUG: - t = time.time() - t0 - print("First fit elapsed = %f" % t) - print("Spectra per second = %f" % (data.shape[0]*data.shape[1]/float(t))) - t0 = time.time() + + t = time.time() - t0 + _logger.debug("First fit elapsed = %f", t) + _logger.debug("Spectra per second = %f", data.shape[0]*data.shape[1]/float(t)) + t0 = time.time() return outputDict if __name__ == "__main__": - DEBUG = 1 + _logger.setLevel(logging.DEBUG) analyzer = XASClass.XASClass() instance = XASStackBatch(analyzer=analyzer) configurationFile = "test.ini" diff --git a/PyMca5/PyMcaPhysics/xrf/ClassMcaTheory.py b/PyMca5/PyMcaPhysics/xrf/ClassMcaTheory.py index 9fc309e..b1aaf8e 100644 --- a/PyMca5/PyMcaPhysics/xrf/ClassMcaTheory.py +++ b/PyMca5/PyMcaPhysics/xrf/ClassMcaTheory.py @@ -34,6 +34,7 @@ import os import sys import numpy import copy +import logging from .Strategies import STRATEGIES from . import ConcentrationsTool FISX = ConcentrationsTool.FISX @@ -44,7 +45,7 @@ from PyMca5.PyMcaMath.fitting import SpecfitFuns from PyMca5.PyMcaIO import ConfigDict from PyMca5.PyMcaMath.fitting import Gefit from PyMca5 import PyMcaDataDir -DEBUG = 0 +_logger = logging.getLogger(__name__) #"python ClassMcaTheory.py -s1.1 --file=03novs060sum.mca --pkm=McaTheory.dat --continuum=0 --strip=1 --sumflag=1 --maxiter=4" CONTINUUM_LIST = [None,'Constant','Linear','Parabolic','Linear Polynomial','Exp. Polynomial'] OLDESCAPE = 0 @@ -154,10 +155,8 @@ class McaTheory(object): def configure(self, newdict=None): if newdict in [None, {}]: if self.__toBeConfigured: - if DEBUG: - txt = "WARNING: This configuration is the one of last fit.\n" - txt += "It does not correspond to the one of next fit." - print(txt) + _logger.debug("WARNING: This configuration is the one of last fit.\n" + "It does not correspond to the one of next fit.") return copy.deepcopy(self.config) self.config.update(newdict) self.__toBeConfigured = False @@ -451,8 +450,7 @@ class McaTheory(object): _nescape_ = 0 if self.config['fit']['escapeflag']: if self.__USE_FISX_ESCAPE: - if DEBUG: - print("Using fisx escape") + _logger.debug("Using fisx escape") xcom = FisxHelper.xcom detector_composition = Elements.getMaterialMassFractions([detele], [1.0]) @@ -764,8 +762,7 @@ class McaTheory(object): _nescape_ = 0 if self.config['fit']['escapeflag']: if self.__USE_FISX_ESCAPE: - if DEBUG: - print("Using fisx escape") + _logger.debug("Using fisx escape") xcom = FisxHelper.xcom detector_composition = Elements.getMaterialMassFractions([detele], [1.0]) @@ -865,8 +862,7 @@ class McaTheory(object): _nescape_ = 0 if self.config['fit']['escapeflag']: if self.__USE_FISX_ESCAPE: - if DEBUG: - print("Using fisx escape") + _logger.debug("Using fisx escape") xcom = FisxHelper.xcom detector_composition = Elements.getMaterialMassFractions([detele], [1.0]) @@ -995,8 +991,7 @@ class McaTheory(object): if (self.lastxmin != self.config['fit']['xmin']) or\ (self.lastxmax != self.config['fit']['xmax']): if self.ydata0 is not None: - if DEBUG: - print("Limits changed") + _logger.debug("Limits changed") self.setData(x=self.xdata0, y=self.ydata0, sigmay=self.sigmay0, @@ -1008,18 +1003,15 @@ class McaTheory(object): if hasattr(self, "xdata"): if self.STRIP: if calculateStrip: - if DEBUG: - print("Calling to calculate non analytical background in config") + _logger.debug("Calling to calculate non analytical background in config") self.__getselfzz() else: - if DEBUG: - print("Using previous non analytical background in config") + _logger.debug("Using previous non analytical background in config") self.datatofit = numpy.concatenate((self.xdata, self.ydata-self.zz, self.sigmay),1) self.laststrip = 1 else: - if DEBUG: - print("Using previous data") + _logger.debug("Using previous data") self.datatofit = numpy.concatenate((self.xdata, self.ydata, self.sigmay),1) self.laststrip = 0 @@ -1049,8 +1041,7 @@ class McaTheory(object): a strategy based on concentrations """ if self.__toBeConfigured: - if DEBUG: - print("setData RESTORE ORIGINAL CONFIGURATION") + _logger.debug("setData RESTORE ORIGINAL CONFIGURATION") self.configure(self.__originalConfiguration) if 'x' in kw: x=kw['x'] @@ -1216,8 +1207,7 @@ class McaTheory(object): #SNIP algorithm if self.config['fit']['stripalgorithm'] == 1: - if DEBUG: - print("CALCULATING SNIP") + _logger.debug("CALCULATING SNIP") if len(anchorslist) == 0: anchorslist = [0, len(ysmooth)-1] anchorslist.sort() @@ -1243,8 +1233,7 @@ class McaTheory(object): #strip background niter = self.config['fit']['stripiterations'] if niter > 0: - if DEBUG: - print("CALCULATING STRIP") + _logger.debug("CALCULATING STRIP") if (niter > 1000) and (self.config['fit']['stripwidth'] == 1): self.zz=SpecfitFuns.subac(ysmooth, self.config['fit']['stripconstant'], @@ -1805,8 +1794,7 @@ class McaTheory(object): def estimate(self): if self.__toBeConfigured: - if DEBUG: - print("CONFIGURING FROM ESTIMATION") + _logger.debug("CONFIGURING FROM ESTIMATION") self.configure(self.__originalConfiguration) self.parameters, self.codes = self.specfitestimate(self.xdata, self.ydata,self.zz) #self.estimatelinpoly(self.xdata, self.ydata,self.zz) diff --git a/PyMca5/PyMcaPhysics/xrf/Elements.py b/PyMca5/PyMcaPhysics/xrf/Elements.py index 51f4aaf..0683074 100644 --- a/PyMca5/PyMcaPhysics/xrf/Elements.py +++ b/PyMca5/PyMcaPhysics/xrf/Elements.py @@ -2,7 +2,7 @@ # # The PyMca X-Ray Fluorescence Toolkit # -# Copyright (c) 2004-2017 European Synchrotron Radiation Facility +# Copyright (c) 2004-2018 European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -2410,7 +2410,7 @@ def getMaterialMassAttenuationCoefficients(compoundList0, fractionList0, energy0 elif type(energy0) == numpy.ndarray: energy = energy0.tolist() - for compound in compoundList: + for compound, compoundFraction in zip(compoundList, compoundFractionList): elts=[] #get energy list if compound in Element.keys(): @@ -2431,7 +2431,7 @@ def getMaterialMassAttenuationCoefficients(compoundList0, fractionList0, energy0 #the proportion of the element in that compound times the compound fraction fraction = [Element[elt]['mass'] *nb for (elt, nb) in zip(elts, nbs) ] - div = compoundFractionList[compoundList.index(compound)]/sum(fraction) + div = compoundFraction/sum(fraction) fraction = [x * div for x in fraction] if energy is None: #get energy list diff --git a/PyMca5/PyMcaPhysics/xrf/FastXRFLinearFit.py b/PyMca5/PyMcaPhysics/xrf/FastXRFLinearFit.py index f91f083..0b88c9e 100644 --- a/PyMca5/PyMcaPhysics/xrf/FastXRFLinearFit.py +++ b/PyMca5/PyMcaPhysics/xrf/FastXRFLinearFit.py @@ -35,6 +35,7 @@ Module to perform a fast linear fit on a stack of fluorescence spectra. """ import os import numpy +import logging from PyMca5.PyMcaMath.linalg import lstsq from . import ClassMcaTheory from PyMca5.PyMcaMath.fitting import Gefit @@ -43,7 +44,8 @@ from PyMca5.PyMcaMath.fitting import SpecfitFuns from PyMca5.PyMcaIO import ConfigDict import time -DEBUG = 0 +_logger = logging.getLogger(__name__) + class FastXRFLinearFit(object): def __init__(self, mcafit=None): @@ -101,15 +103,13 @@ class FastXRFLinearFit(object): if hasattr(y, "info"): if "McaLiveTime" in y.info: livetime = y.info["McaLiveTime"] - if DEBUG: - t0 = time.time() + t0 = time.time() if configuration is not None: self._mcaTheory.setConfiguration(configuration) elif self._config is None: raise ValueError("Fit configuration missing") else: - if DEBUG: - print("Setting default configuration") + _logger.debug("Setting default configuration") self._mcaTheory.setConfiguration(self._config) # read the current configuration # it is a copy, we can modify it at will @@ -164,8 +164,7 @@ class FastXRFLinearFit(object): # background if config['fit']['stripflag']: if config['fit']['stripalgorithm'] == 1: - if DEBUG: - print("SNIP") + _logger.debug("SNIP") else: raise RuntimeError("Please use the faster SNIP background") @@ -335,9 +334,8 @@ class FastXRFLinearFit(object): uncertainties = numpy.zeros((nFree, nRows, nColumns), numpy.float32) #perform the initial fit - if DEBUG: - print("Configuration elapsed = %f" % (time.time() - t0)) - t0 = time.time() + _logger.debug("Configuration elapsed = %f", time.time() - t0) + t0 = time.time() totalSpectra = data.shape[0] * data.shape[1] jStep = min(100, data.shape[1]) if weightPolicy == 2: @@ -397,11 +395,12 @@ class FastXRFLinearFit(object): results[:, i, jStart:jEnd] = parameters uncertainties[:, i, jStart:jEnd] = ddict['uncertainties'] jStart = jEnd - if DEBUG: - t = time.time() - t0 - print("First fit elapsed = %f" % t) - print("Spectra per second = %f" % (data.shape[0]*data.shape[1]/float(t))) - t0 = time.time() + t = time.time() - t0 + _logger.debug("First fit elapsed = %f", t) + if t > 0.: + _logger.debug("Spectra per second = %f", + data.shape[0]*data.shape[1]/float(t)) + t0 = time.time() # cleanup zeros # start with the parameter with the largest amount of negative values @@ -434,7 +433,8 @@ class FastXRFLinearFit(object): i = item[1] badMask = item[2] results[i][badMask] = 0.0 - print("WARNING: %d pixels of parameter %s forced to zero" % (item[0], freeNames[i])) + _logger.warning("WARNING: %d pixels of parameter %s forced to zero", + item[0], freeNames[i]) continue zeroList.sort() zeroList.reverse() @@ -458,12 +458,10 @@ class FastXRFLinearFit(object): for i in badParameters: results[i][badMask] = 0.0 uncertainties[i][badMask] = 0.0 - if DEBUG: - print("WARNING: %d pixels of parameter %s set to zero" % (badMask.sum(), - freeNames[i])) + _logger.debug("WARNING: %d pixels of parameter %s set to zero", + badMask.sum(), freeNames[i]) else: - if DEBUG: - print("Number of secondary fits = %d" % (nFits + 1)) + _logger.debug("Number of secondary fits = %d", nFits + 1) nFits += 1 A = derivatives[:, [i for i in range(nFree) if i not in badParameters]] #assume we'll not have too many spectra @@ -533,9 +531,9 @@ class FastXRFLinearFit(object): uncertainties[i][badMask] = ddict['uncertainties'][idx] idx += 1 - if DEBUG and refit: + if refit: t = time.time() - t0 - print("Fit of negative peaks elapsed = %f" % t) + _logger.debug("Fit of negative peaks elapsed = %f", t) t0 = time.time() outputDict = {'parameters':results, 'uncertainties':uncertainties, 'names':freeNames} @@ -550,8 +548,7 @@ class FastXRFLinearFit(object): fitFirstSpectrum = False if config['concentrations']['usematrix']: - if DEBUG: - print("USING MATRIX") + _logger.debug("USING MATRIX") if config['concentrations']['reference'].upper() == "AUTO": fitFirstSpectrum = True elif autotime: @@ -611,16 +608,14 @@ class FastXRFLinearFit(object): referenceElement = addInfo['ReferenceElement'] referenceTransitions = addInfo['ReferenceTransitions'] - if DEBUG: - print("Reference <%s> transition <%s>" % (referenceElement, referenceTransitions)) + _logger.debug("Reference <%s> transition <%s>", + referenceElement, referenceTransitions) if referenceElement in ["", None, "None"]: - if DEBUG: - print("No reference") + _logger.debug("No reference") counter = 0 for i, group in enumerate(fitresult['result']['groups']): if group.lower().startswith("scatter"): - if DEBUG: - print("skept %s" % group) + _logger.debug("skept %s", group) continue outputDict['names'].append("C(%s)" % group) if counter == 0: @@ -640,8 +635,7 @@ class FastXRFLinearFit(object): fitresult['result'][group]['fitarea']) counter += 1 else: - if DEBUG: - print("With reference") + _logger.debug("With reference") idx = None testGroup = referenceElement+ " " + referenceTransitions.split()[0] for i, group in enumerate(fitresult['result']['groups']): @@ -659,8 +653,7 @@ class FastXRFLinearFit(object): counter = 0 for i, group in enumerate(fitresult['result']['groups']): if group.lower().startswith("scatter"): - if DEBUG: - print("skept %s" % group) + _logger.debug("skept %s", group) continue outputDict['names'].append("C(%s)" % group) goodI = results[nFreeBackgroundParameters+i] > 0 @@ -677,10 +670,8 @@ class FastXRFLinearFit(object): (concentrationsResult[layer]['mass fraction'][group])) counter += 1 outputDict['concentrations'] = massFractions - if DEBUG: - t = time.time() - t0 - print("Calculation of concentrations elapsed = %f" % t) - t0 = time.time() + t = time.time() - t0 + _logger.debug("Calculation of concentrations elapsed = %f", t) #################################################### return outputDict @@ -718,7 +709,8 @@ def getFileListFromPattern(pattern, begin, end, increment=None): return fileList if __name__ == "__main__": - DEBUG = True + logging.basicConfig(level=logging.INFO) + _logger.setLevel(logging.DEBUG) import glob import sys from PyMca5.PyMca import EDFStack diff --git a/PyMca5/PyMcaPhysics/xrf/FisxHelper.py b/PyMca5/PyMcaPhysics/xrf/FisxHelper.py index e2b1af3..7f986aa 100644 --- a/PyMca5/PyMcaPhysics/xrf/FisxHelper.py +++ b/PyMca5/PyMcaPhysics/xrf/FisxHelper.py @@ -31,15 +31,18 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import os -import sys -import time +import logging from fisx import DataDir from fisx import Elements as FisxElements from fisx import Material from fisx import Detector from fisx import XRF +import time +import sys xcom = None -DEBUG = 0 + +_logger = logging.getLogger(__name__) + def getElementsInstance(dataDir=None, bindingEnergies=None, xcomFile=None): if dataDir is None: @@ -48,7 +51,7 @@ def getElementsInstance(dataDir=None, bindingEnergies=None, xcomFile=None): from PyMca5.PyMcaDataDir import PYMCA_DATA_DIR as pymcaDataDir from PyMca5 import getDataFile except: - print("Using fisx shell constants and ratios") + _logger.info("Using fisx shell constants and ratios") pymcaDataDir = None if bindingEnergies is None: if pymcaDataDir is None: @@ -60,11 +63,9 @@ def getElementsInstance(dataDir=None, bindingEnergies=None, xcomFile=None): xcomFile = os.path.join(dataDir, "XCOM_CrossSections.dat") else: xcomFile = getDataFile("XCOM_CrossSections.dat") - if DEBUG: - t0 = time.time() + t0 = time.time() instance = FisxElements(dataDir, bindingEnergies, xcomFile) - if DEBUG: - print("Shell constants") + _logger.debug("Shell constants") # the files should be taken from PyMca to make sure the same data are used for key in ["K", "L", "M"]: @@ -73,18 +74,15 @@ def getElementsInstance(dataDir=None, bindingEnergies=None, xcomFile=None): # we have to make sure we have got a string if hasattr(fname, "decode"): fname = fname.decode("latin-1") - if DEBUG: - print("Before %s" % fname) + _logger.debug("Before %s", fname) if pymcaDataDir is not None: fname = getDataFile(key + "ShellConstants.dat") else: fname = os.path.join(os.path.dirname(fname), key + "ShellConstants.dat") instance.setShellConstantsFile(key, fname) - if DEBUG: - print("After %s" % instance.getShellConstantsFile(key)) - if DEBUG: - print("Radiative transitions") + _logger.debug("After %s", instance.getShellConstantsFile(key)) + _logger.debug("Radiative transitions") for key in ["K", "L", "M"]: fname = instance.getShellRadiativeTransitionsFile(key) @@ -92,18 +90,15 @@ def getElementsInstance(dataDir=None, bindingEnergies=None, xcomFile=None): # we have to make sure we have got a string ... if hasattr(fname, "decode"): fname = fname.decode("latin-1") - if DEBUG: - print("Before %s" % fname) + _logger.debug("Before %s", fname) if pymcaDataDir is not None: fname = getDataFile(key + "ShellRates.dat") else: fname = os.path.join(os.path.dirname(fname), key + "ShellRates.dat") instance.setShellRadiativeTransitionsFile(key, fname) - if DEBUG: - print("After %s " % instance.getShellRadiativeTransitionsFile(key)) + _logger.debug("After %s ", instance.getShellRadiativeTransitionsFile(key)) - if DEBUG: - print("Reading Elapsed = ", time.time() - t0) + _logger.debug("Reading Elapsed = %s", time.time() - t0) return instance def getMultilayerFluorescence(multilayerSample, @@ -122,42 +117,42 @@ def getMultilayerFluorescence(multilayerSample, elementsFromMatrix=False, secondary=None, materials=None, - secondaryCalculationLimit=None): + secondaryCalculationLimit=None, + cache=1): if secondary is None: secondary=0 if secondaryCalculationLimit is None: secondaryCalculationLimit=0.0 + if cache: + cache = 1 + else: + cache = 0 - if DEBUG: - print("Library actually using secondary = ", secondary) - print("Library using secondary limit = ", secondaryCalculationLimit) + _logger.info("Library requested to use secondary = %s", secondary) + _logger.info("Library requested to use secondary limit = %s", secondaryCalculationLimit) + _logger.info("Library requested to use cache = %d", cache) global xcom if xcom is None: - if DEBUG: - print("Getting fisx elements instance") + _logger.debug("Getting fisx elements instance") xcom = getElementsInstance() if materials is not None: - if DEBUG: - print("Deleting materials") + _logger.debug("Deleting materials") xcom.removeMaterials() for material in materials: - if DEBUG: - print("Adding material making sure no duplicates") + _logger.debug("Adding material making sure no duplicates") xcom.addMaterial(material, errorOnReplace=1) # the instance - if DEBUG: - print("creating XRF instance") + _logger.debug("creating XRF instance") xrf = XRF() # the beam energies if not len(energyList): raise ValueError("Empty list of beam energies!!!") - if DEBUG: - print("setting beam") + _logger.debug("setting beam") xrf.setBeam(energyList, weights=weightList, characteristic=flagList) # the beam filters (if any) @@ -172,8 +167,7 @@ def getMultilayerFluorescence(multilayerSample, Unless you know what you are doing, the funny factors must be 1.0 """ - if DEBUG: - print("setting beamFilters") + _logger.debug("setting beamFilters") xrf.setBeamFilters(beamFilters) # the sample description @@ -186,20 +180,17 @@ def getMultilayerFluorescence(multilayerSample, Unless you know what you are doing, the funny factors must be 1.0 """ - if DEBUG: - print("setting sample") + _logger.debug("setting sample") xrf.setSample(multilayerSample) # the attenuators if attenuatorList is not None: if len(attenuatorList) > 0: - if DEBUG: - print("setting attenuators") + _logger.debug("setting attenuators") xrf.setAttenuators(attenuatorList) # the geometry - if DEBUG: - print("setting Geometry") + _logger.debug("setting Geometry") if alphaIn is None: alphaIn = 45 if alphaOut is None: @@ -207,8 +198,7 @@ def getMultilayerFluorescence(multilayerSample, xrf.setGeometry(alphaIn, alphaOut) # the detector - if DEBUG: - print("setting Detector") + _logger.debug("setting Detector") if detector is not None: # Detector can be a list as [material, density, thickness] # or a Detector instance @@ -259,38 +249,61 @@ def getMultilayerFluorescence(multilayerSample, if elementsFromMatrix: elementsList = matrixElementsList - # enabling the cascade cache gets a (miserable) 15 % speed up - if DEBUG: - print("Using cascade cache") - t0 = time.time() + t0 = time.time() + if cache: + # enabling the cascade cache gets a (miserable) 15 % speed up + _logger.debug("FisxHelper Using cache") + else: + _logger.debug("FisxHelper Not using cache") treatedElements = [] emittedLines = [] + for actualElement in actualElementsList: + element = actualElement.split()[0] + if element not in treatedElements: + if cache: + lines = xcom.getEmittedXRayLines(element) + sampleEnergies = [lines[key] for key in lines] + for e in sampleEnergies: + if e not in emittedLines: + emittedLines.append(e) + treatedElements.append(element) + for layer in multilayerSample: composition = xcom.getComposition(layer[0]) for element in composition.keys(): - xcom.setElementCascadeCacheEnabled(element, 1) - if hasattr(xcom, "updateCache"): - if element not in treatedElements: + if element not in treatedElements: + if cache: lines = xcom.getEmittedXRayLines(element) sampleEnergies = [lines[key] for key in lines] for e in sampleEnergies: if e not in emittedLines: emittedLines.append(e) + treatedElements.append(element) + + if attenuatorList is not None: + for layer in attenuatorList: + composition = xcom.getComposition(layer[0]) + for element in composition.keys(): + if element not in treatedElements: + if cache: + lines = xcom.getEmittedXRayLines(element) + sampleEnergies = [lines[key] for key in lines] + for e in sampleEnergies: + if e not in emittedLines: + emittedLines.append(e) treatedElements.append(element) if hasattr(xcom, "updateCache"): - if DEBUG: - print("Filling atenuation cache") composition = detectorInstance.getComposition(xcom) for element in composition.keys(): - xcom.setElementCascadeCacheEnabled(element, 1) if element not in treatedElements: - lines = xcom.getEmittedXRayLines(element) - sampleEnergies = [lines[key] for key in lines] - for e in sampleEnergies: - if e not in emittedLines: - emittedLines.append(e) + if cache: + lines = xcom.getEmittedXRayLines(element) + sampleEnergies = [lines[key] for key in lines] + for e in sampleEnergies: + if e not in emittedLines: + emittedLines.append(e) treatedElements.append(element) for element in actualElementsList: @@ -300,28 +313,40 @@ def getMultilayerFluorescence(multilayerSample, for element in treatedElements: # this limit seems overestimated but still reasonable if xcom.getCacheSize(element) > 5000: - print("Clearing cache") + _logger.info("Clearing cache for %s" % element) xcom.clearCache(element) - xcom.updateCache(element, energyList) - xcom.updateCache(element, emittedLines) - xcom.setCacheEnabled(element, 1) - if DEBUG: - print("Element %s cache size = %d" % (element, xcom.getCacheSize(element))) - + if cache: + _logger.info("Updating cache for %s" % element) + xcom.updateCache(element, energyList) + xcom.updateCache(element, emittedLines) + else: + # should I clear the cache to be sure? + # for the time being, yes. + _logger.info("No cache. Clearing cache for %s" % element) + xcom.clearCache(element) + xcom.setCacheEnabled(element, cache) + _logger.info("Element %s cache size = %d", + element, xcom.getCacheSize(element)) for element in actualElementsList: - xcom.setElementCascadeCacheEnabled(element.split()[0], 1) + xcom.setElementCascadeCacheEnabled(element.split()[0], cache) if hasattr(xcom, "updateEscapeCache") and \ hasattr(xcom, "setEscapeCacheEnabled"): if detector is not None: for element in actualElementsList: - lines = xcom.getEmittedXRayLines(element.split()[0]) - lines_energy = [lines[key] for key in lines] - for e in lines_energy: - if e not in emittedLines: - emittedLines.append(e) - xcom.setEscapeCacheEnabled(1) - xcom.updateEscapeCache(detectorInstance.getComposition(xcom), + if cache: + lines = xcom.getEmittedXRayLines(element.split()[0]) + lines_energy = [lines[key] for key in lines] + for e in lines_energy: + if e not in emittedLines: + emittedLines.append(e) + if not cache: + if hasattr(xcom, "clearEscapeCache"): + # the method is there but nor wrapped yet + xcom.clearEscapeCache() + xcom.setEscapeCacheEnabled(cache) + if cache: + xcom.updateEscapeCache(detectorInstance.getComposition(xcom), emittedLines, energyThreshold=detectorInstance.getEscapePeakEnergyThreshold(), \ intensityThreshold=detectorInstance.getEscapePeakIntensityThreshold(), \ @@ -329,23 +354,19 @@ def getMultilayerFluorescence(multilayerSample, alphaIn=detectorInstance.getEscapePeakAlphaIn(), thickness=0) # No escape by the back considered yet else: - if DEBUG: - print("NOT CALLING UPDATE CACHE") - if DEBUG: - print("C++ elapsed filling cache = ", time.time() - t0) + _logger.debug("NOT CALLING UPDATE CACHE") + _logger.info("C++ elapsed filling cache = %s", + time.time() - t0) - if DEBUG: - print("Calling getMultilayerFluorescence") - t0 = time.time() + _logger.debug("Calling getMultilayerFluorescence") + t0 = time.time() expectedFluorescence = xrf.getMultilayerFluorescence(actualElementsList, xcom, secondary=secondary, useGeometricEfficiency=useGeometricEfficiency, useMassFractions=elementsFromMatrix, secondaryCalculationLimit=secondaryCalculationLimit) - if DEBUG: - print("C++ elapsed TWO = ", time.time() - t0) - + _logger.info("C++ elapsed TWO = %s", time.time() - t0) if not elementsFromMatrix: # If one element was present in one layer and not on others, PyMca only # calculated contributions from the layers in which the element was @@ -417,7 +438,8 @@ def _getFisxMaterials(fitConfiguration): if fractionList[n] > 0.0: composition[compoundList[n]] = fractionList[n] else: - print("ignoring ", compoundList[n], "fraction = ", fractionList[n]) + _logger.info("ignoring %s, fraction = %s", + compoundList[n], fractionList[n]) # check the composition is expressed in terms of elements # and not in terms of other undefined materials totallyDefined = True @@ -457,7 +479,7 @@ def _getFisxMaterials(fitConfiguration): txt += "contains <%s> (defined as <%s>), " % (compound, compound + " ") else: txt += "contains <%s> (undefined)," % compound - print(txt) + _logger.info(txt) raise KeyError(txt) return fisxMaterials @@ -570,7 +592,8 @@ def _getFisxDetector(fitConfiguration, attenuatorsDetector=None): else: # make sure information is consistent if attenuatorsDetector[0] not in [detectorMaterial, detectorMaterial+"1"]: - print("%s not equal to %s" % (attenuatorsDetector[0], detectorMaterial)) + _logger.warning("%s not equal to %s", + attenuatorsDetector[0], detectorMaterial) msg = "Inconsistent detector material between DETECTOR and ATTENUATORS tab" msg += "\n%s not equal to %s" % (attenuatorsDetector[0], detectorMaterial) raise ValueError(msg) @@ -595,8 +618,7 @@ def _getSecondaryCalculationLimitFromFitConfiguration(fitConfiguration): limit = float(\ fitConfiguration["concentrations"]["secondarycalculationlimit"]) except: - if DEBUG: - print("Exception. Forcing no limit") + _logger.debug("Exception. Forcing no limit") limit = 0.0 return limit @@ -651,7 +673,7 @@ def _fisxFromFitConfigurationAction(fitConfiguration, try: secondary = fitConfiguration["concentrations"]["usemultilayersecondary"] except: - print("Exception. Forcing tertiary") + _logger.warning("Exception. Forcing tertiary") secondary = 2 if action.upper() == "FLUORESCENCE": @@ -761,7 +783,7 @@ def getFisxCorrectionFactors(*var, **kw): secondOrder = ddict[element][family][layerKey]["counts"][1] if firstOrder <= 0: if secondOrder > 0.0: - print("Inconsistency? secondary with no primary?") + _logger.warning("Inconsistency? secondary with no primary?") ddict[element][family][layerKey]["correction_factor"][1] = 1 if nItems == 3: ddict[element][family][layerKey]["correction_factor"][2] = 1 @@ -787,9 +809,7 @@ def getFisxCorrectionFactorsFromFitConfigurationFile(fileName, secondaryCalculationLimit) if __name__ == "__main__": - DEBUG = 1 - import time - import sys + _logger.setLevel(logging.DEBUG) if len(sys.argv) < 2: print("Usage: python FisxHelper FitConfigurationFile [element] [matrix_flag]") sys.exit(0) diff --git a/PyMca5/PyMcaPhysics/xrf/SingleLayerStrategy.py b/PyMca5/PyMcaPhysics/xrf/SingleLayerStrategy.py index b55a74b..c077c13 100644 --- a/PyMca5/PyMcaPhysics/xrf/SingleLayerStrategy.py +++ b/PyMca5/PyMcaPhysics/xrf/SingleLayerStrategy.py @@ -31,10 +31,12 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import copy +import logging from . import Elements from . import ConcentrationsTool -DEBUG = 0 +_logger = logging.getLogger(__name__) + class SingleLayerStrategy(object): def __init__(self): @@ -48,8 +50,7 @@ class SingleLayerStrategy(object): Returning an empty fit configuration, or a number of iterations equal 0 will indicate the process is over. """ - if DEBUG: - print("SingleLayerStrategy called with iteration ", currentIteration) + _logger.debug("SingleLayerStrategy called with iteration %s", currentIteration) newConfiguration = copy.deepcopy(fitResult['config']) strategyConfiguration = newConfiguration['SingleLayerStrategy'] if currentIteration is None: @@ -122,8 +123,7 @@ class SingleLayerStrategy(object): if "-" in group: continue if strategyConfiguration["flags"][materialCounter] in ["0", 0]: - if DEBUG: - print("ignoring ", group) + _logger.debug("ignoring %s", group) continue ele = group.split()[0] material = strategyConfiguration["materials"][materialCounter] @@ -157,7 +157,6 @@ class SingleLayerStrategy(object): "Comment":"Last Single Layer Strategy iteration"} # and update it newConfiguration[parentKey][daughterKey][1] = materialName - if DEBUG: - print("Updated sample material: ", \ - newConfiguration["materials"][materialName]) + _logger.debug("Updated sample material: %s", + newConfiguration["materials"][materialName]) return newConfiguration, currentIteration - 1 diff --git a/PyMca5/PyMcaPhysics/xrf/XRFMC/XMSOParser.py b/PyMca5/PyMcaPhysics/xrf/XRFMC/XMSOParser.py index 5255231..1925b6a 100644 --- a/PyMca5/PyMcaPhysics/xrf/XRFMC/XMSOParser.py +++ b/PyMca5/PyMcaPhysics/xrf/XRFMC/XMSOParser.py @@ -32,9 +32,11 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os +import logging import xml.etree.ElementTree as ElementTree -DEBUG = 0 +_logger = logging.getLogger(__name__) + def getXMSOFileFluorescenceInformation(xmsoFile): f = ElementTree.parse(xmsoFile) @@ -42,10 +44,9 @@ def getXMSOFileFluorescenceInformation(xmsoFile): root = f.getroot() transitions = ['K', 'Ka', 'Kb', 'L', 'L1', 'L2', 'L3', 'M'] for i in root.iter('fluorescence_line_counts'): - if DEBUG: - print(i.attrib) - for key in ['symbol', 'total_counts']: - print(key, '= ', i.get(key)) + _logger.debug("%s", i.attrib) + for key in ['symbol', 'total_counts']: + _logger.debug('%s = %s', key, i.get(key)) element = i.get('symbol') ddict[element] = {} #ddict[element]['z'] = i.get('atomic_number') @@ -54,10 +55,9 @@ def getXMSOFileFluorescenceInformation(xmsoFile): 'counts': [], 'correction_factor':[]} for a in i.iter('fluorescence_line'): - if DEBUG: - print(a.attrib) - for key in ['type', 'total_counts']: - print(key, '= ', a.get(key)) + _logger.debug("%s", a.attrib) + for key in ['type', 'total_counts']: + _logger.debug('%s = %s', key, a.get(key)) line = a.get('type') ddict[element][line] = {} #ddict[element][line]['total'] = float(a.get('total_counts')) @@ -74,8 +74,7 @@ def getXMSOFileFluorescenceInformation(xmsoFile): transitionsAffected.append(key) cumulator = 0 for b in a.iter('counts'): - if DEBUG: - print(b.attrib) + _logger.debug("%s", b.attrib) value = float(b.text) ddict[element][line]['counts'].append(value) cumulator += value @@ -117,7 +116,7 @@ def test(xmsoFile='t.xmso'): if line == "z": #atomic number continue - if 1 or DEBUG or line in ['K', 'Ka', 'Kb', 'L', 'L1', 'L2', 'L3', 'M']: + if 1 or line in ['K', 'Ka', 'Kb', 'L', 'L1', 'L2', 'L3', 'M']: correction1 = ddict[element][line]['correction_factor'][1] correctionn = ddict[element][line]['correction_factor'][-1] print("Element %s Line %s Correction 2 = %f Correction n = %f" %\ diff --git a/PyMca5/PyMcaPlugins/AdvancedAlignmentScanPlugin.py b/PyMca5/PyMcaPlugins/AdvancedAlignmentScanPlugin.py index 441b635..3a310c3 100644 --- a/PyMca5/PyMcaPlugins/AdvancedAlignmentScanPlugin.py +++ b/PyMca5/PyMcaPlugins/AdvancedAlignmentScanPlugin.py @@ -27,7 +27,111 @@ __author__ = "Tonn Rueter & V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +__doc__ = """ +Due to uncertainties in the experimental set-up, recorded data might be shifted unrelated +to physical effects probed in the experiment. The present plug-in calculates this shift and +corrects the data using a variety of different methods. + +Usage and Description +--------------------- + +Data that is subject to a shift must be loaded into the plot window of the main application. +The plug-in offers two ways to treat the data: + + - A shortcut options, called *Perform FFT Shift*, calculates the shift and directly corrects + the data. + - The *Show Alignment Window* option, showing a window that allows for specification of the + shift and alignment methods, as well as offering the possibility to save calculated shifts + and load previously calculated shifts from a file. + It is also possible to enter shift values by hand. + + +Once the *Alignment Window* is opened, the alignment method and the shift method must be specified. +The alignment method specifies how the shift is calculated, while the shift method determines +how the shift is applied to the data. + +The table shows three columns: + + - The first one shows the plot legend of the data that will be + corrected by the shift method. + - The second column shows the plot legend from which the shift + is calculated. + - The third column shows the shift values calculated by the alignment method in + units of the plot windows x-axis. + +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. + +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 +to use a reference signal recorded during the experiment, to determine the shift and then apply +the shift values to a different set of data. + +.. note:: + + In order to match different sets of data to another, as necessary in the case of a + reference signal, the order in which the data is added to the plot window is crucial. If one + switches between two sets of data, where one set aligns the other one, it is highly encouraged + to consult the table in the *Alignment window* to check if every element in the two different + sets of data is assigned to its correct counterpart before applying the shift. + + +If the data in the plot window is zoomed-in to a distinct feature, only this range of the data +is used to calculate the shift. + +Methods used by the plug-in +--------------------------- + +Alignment methods are used to calculate the shift. Present methods include FFT, MAX, FIT and +FIT DRV. + +*FFT*: + + Uses the Fourier Transform of the curves to calculate their cross-correlation. The maximum + of the correlation is determined, and yields the shift value. This method is the default option. + Since it is not affected by the peak shape, it is fast and numerically robust. + + .. note:: The shifts are given in real space values. + +*MAX*: + + Determines the maximum of each curve. The shift is given by the differences in the x-position + of the maxima. Note that this method is highly vulnerable to noise in the data and spikes. + +*FIT*: + + This method subtracts a background from the data using the SNIP algorithm and searches for peaks + in the data. For every curve, the single most pronounced feature is selected. + The peak is fitted by a Gaussian model. The shifts are then given by differences in the x-offsets + of the fitted Gaussians. + +*FIT DRV*: + + Uses the same procedure as the FIT method. However the fit is applied to the first derivative of + the data. This method is only recommended for X-ray absorption data. + +Shift methods are used to apply the calculated shift to the data. Present methods include *Shift x-range* +and *Inverse FFT shift*. + +*Shift x-range*: + + This method adds the calculated shift value to every point. + +*Inverse FFT shift*: + + Takes the Fourier Transform of a curve and multiplies the shift as a phase factor. The multiplication + of a phase factor in Fourier space translates to a shift in the x-range in real space. The shifted data + is given by the inverse Fourier transform. + + .. note:: + + For this process, the data needs to have a equidistant x-range. If this is not the case, the data + will be interpolated on a equidistant x-range. Due to the cyclic nature of the Fourier transform, this + method is recommended for data that has linear background. +""" import numpy +import logging import sys import traceback from PyMca5.PyMcaGui import PyMcaQt as qt @@ -41,13 +145,15 @@ from PyMca5.PyMcaMath.fitting.SpecfitFuns import gauss from PyMca5.PyMcaMath.fitting import SpecfitFuns from os.path import join as pathjoin +_logger = logging.getLogger(__name__) + try: from PyMca5 import Plugin1DBase except ImportError: - print("WARNING:AlignmentScanPlugin import from somewhere else") + _logger.warning("WARNING:AlignmentScanPlugin import from somewhere else") from . import Plugin1DBase -DEBUG = 0 + class AlignmentWidget(qt.QDialog): _storeCode = 2 @@ -257,8 +363,7 @@ class AlignmentWidget(qt.QDialog): return False if not str(filename).endswith('.shift'): filename += '.shift' - if DEBUG == 1: - print('saveOptions -- Filename: "%s"' % filename) + _logger.debug('saveOptions -- Filename: "%s"', filename) currentOrder = self.plugin.getOrder() outDict = ConfigDict.ConfigDict() llist, ddict = self.getDict() @@ -482,8 +587,7 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): xmin = xmin0 if xmax0 < xmax: xmax = xmax0 - if DEBUG == 1: - print('calculateShiftsFit -- xmin = %.3f, xmax = %.3f'%(xmin, xmax)) + _logger.debug('calculateShiftsFit -- xmin = %.3f, xmax = %.3f', xmin, xmax) # Get active curve activeCurve = self.getActiveCurve() @@ -515,11 +619,11 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): numpy.asarray([yp0, xp0, fwhm0]), xdata=x0[fitrange0], ydata=y0[fitrange0]) - if DEBUG == 1: - if derivative: - print('calculateShiftsFit -- Results (Leg, PeakPos, Shift):') - else: - print('calculateShiftsFitDerivative -- Results (Leg, PeakPos, Shift):') + + if derivative: + _logger.debug('calculateShiftsFit -- Results (Leg, PeakPos, Shift):') + else: + _logger.debug('calculateShiftsFitDerivative -- Results (Leg, PeakPos, Shift):') for x,y,legend,info in curves: idx = numpy.nonzero((xmin <= x) & (x <= xmax))[0] x = numpy.take(x, idx) @@ -553,8 +657,7 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): key = legend retList.append(key) retDict[key] = shift - if DEBUG == 1: - print( '\t%s\t%.3f\t%.3f'%(legend, fitp[1], shift)) + _logger.debug('\t%s\t%.3f\t%.3f', legend, fitp[1], shift) return retList, retDict def calculateShiftsMax(self): @@ -595,10 +698,9 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): # Determine the index of maximum in active curve shift0 = numpy.argmax(y0) - if DEBUG == 1: - print('calculateShiftsMax -- Results:') - print('\targmax(y) shift') - for x,y,legend,info in curves: + _logger.debug('calculateShiftsMax -- Results:') + _logger.debug('\targmax(y) shift') + for x, y, legend, info in curves: idx = numpy.nonzero((xmin <= x) & (x <= xmax))[0] x = numpy.take(x, idx) y = numpy.take(y, idx) @@ -608,8 +710,7 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): key = legend retList.append(key) retDict[key] = shift - if DEBUG == 1: - print('\t%d %.3f'%(x[shifty],shift)) + _logger.debug('\t%d %.3f', x[shifty], shift) return retList, retDict def calculateShiftsFFT(self, portion=.95): @@ -629,8 +730,7 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): xmin = xmin0 if xmax0 < xmax: xmax = xmax0 - if DEBUG == 1: - print('calculateShiftsFFT -- xmin = %.3f, xmax = %.3f'%(xmin, xmax)) + _logger.debug('calculateShiftsFFT -- xmin = %.3f, xmax = %.3f', xmin, xmax) # Get active curve activeCurve = self.getActiveCurve() @@ -650,8 +750,7 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): y0 = numpy.take(y0, idx) fft0 = numpy.fft.fft(y0) - if DEBUG == 1: - print('calculateShiftsFFT -- results (Legend len(idx) shift):') + _logger.debug('calculateShiftsFFT -- results (Legend len(idx) shift):') for x,y,legend,info in curves: idx = numpy.nonzero((x >= xmin) & (x <= xmax))[0] x = numpy.take(x, idx) @@ -681,8 +780,7 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): key = legend retList.append(key) retDict[key] = shift - if DEBUG == 1: - print('\t%s\t%d\t%f'%(legend,len(idx),shift)) + _logger.debug('\t%s\t%d\t%f', legend, len(idx), shift) return retList, retDict # END Alignment Methods @@ -723,19 +821,16 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): if msg.exec_() != qt.QMessageBox.Ok: return False - if DEBUG == 1: - print('applyShifts -- Shifting ...') + _logger.debug('applyShifts -- Shifting ...') for idx, (x, y, legend, info) in enumerate(curves): shift = self.shiftDict[legend] if shift is None: - if DEBUG == 1: - print('\tCurve \'%s\' not found in shiftDict\n%s'\ - %(legend,str(self.shiftDict))) + _logger.debug('\tCurve \'%s\' not found in shiftDict\n%s', + legend, str(self.shiftDict)) continue if shift == float('NaN'): - if DEBUG == 1: - print('\tCurve \'%s\' has NaN shift'%legend) + _logger.debug('\tCurve \'%s\' has NaN shift', legend) continue # Limit shift to zoomed in area @@ -752,8 +847,7 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): else: replot = False # Check if scan number is adopted by new curve - if DEBUG == 1: - print('\'%s\' -- shifts -> \'%s\' by %f'%(self.shiftList[idx], legend, shift)) + _logger.debug('\'%s\' -- shifts -> \'%s\' by %f', self.shiftList[idx], legend, shift) #selectionlegend = info.get('selectionlegend', legend) selectionlegend = legend self.addCurve(xShifted, yShifted, @@ -813,8 +907,7 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): Method receives methodName from AlignmentWidget instance and assigns the according shift method. """ - if DEBUG == 1: - print('setShiftMethod -- %s'%methodName) + _logger.debug('setShiftMethod -- %s', methodName) methodName = str(methodName) if methodName == 'Inverse FFT shift': self.shiftMethod = self.fftShift @@ -829,8 +922,7 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): Method receives methodName from AlignmentWidget instance and assigns the according alignment method. """ - if DEBUG == 1: - print('setAlignmentMethod -- %s'%methodName) + _logger.debug('setAlignmentMethod -- %s', methodName) methodName = str(methodName) if methodName == 'FFT': self.alignmentMethod = self.calculateShiftsFFT @@ -894,8 +986,7 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): """ curves = self.getAllCurves() if len(curves) < 1: - if DEBUG == 1: - print('interpolate -- no curves present') + _logger.debug('interpolate -- no curves present') raise ValueError("At least 1 curve needed") return @@ -960,9 +1051,8 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): xmin0 = xmin if xmax > xmax0: xmax0 = xmax - if DEBUG == 1: - print('getXLimits -- overlap = %s, xmin = %.3f, xmax =%.3f'\ - %(overlap,xmin0,xmax0)) + _logger.debug('getXLimits -- overlap = %s, xmin = %.3f, xmax =%.3f', + overlap, xmin0, xmax0) return xmin0, xmax0 def normalize(self, y): @@ -1019,12 +1109,10 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): # Extract highest peak sortIdx = y[peakIdx].argsort()[-1] except IndexError: - if DEBUG == 1: - print('No peaks found..') + _logger.debug('No peaks found..') return None except SystemError: - if DEBUG == 1: - print('Peak search failed. Continue with y maximum') + _logger.debug('Peak search failed. Continue with y maximum') peakIdx = [ybg.argmax()] sortIdx = 0 xpeak = float(x[peakIdx][sortIdx]) @@ -1064,8 +1152,7 @@ class AdvancedAlignmentScanPlugin(Plugin1DBase.Plugin1DBase): msg.setWindowTitle('Alignment Scan Error') msg.setText('No help file found.') msg.exec_() - if DEBUG == 1: - print('XMCDWindow -- init: Unable to read help file') + _logger.debug('XMCDWindow -- init: Unable to read help file') self.helpFileBrowser = None if self.helpFileBrowser is not None: self.helpFileBrowser.show() @@ -1083,7 +1170,7 @@ if __name__ == "__main__": a.show() x = numpy.arange(250, 750, 2, dtype=float) - y1 = 1.0 + 50.0 * numpy.exp(-0.001*(x-500)**2) + 2.*numpy.random.random(250.) - y2 = 1.0 + 20.5 * numpy.exp(-0.005*(x-600)**2) + 2.*numpy.random.random(250.) + y1 = 1.0 + 50.0 * numpy.exp(-0.001*(x-500)**2) + 2.*numpy.random.random(250) + y2 = 1.0 + 20.5 * numpy.exp(-0.005*(x-600)**2) + 2.*numpy.random.random(250) app.exec_() diff --git a/PyMca5/PyMcaPlugins/AlignmentScanPlugin.py b/PyMca5/PyMcaPlugins/AlignmentScanPlugin.py index ee33c2e..e12aa47 100644 --- a/PyMca5/PyMcaPlugins/AlignmentScanPlugin.py +++ b/PyMca5/PyMcaPlugins/AlignmentScanPlugin.py @@ -207,18 +207,18 @@ if __name__ == "__main__": import os try: from PyMca5.PyMcaGui import PyMcaQt as qt - from PyMca5.PyMcaGui.plotting import PlotWindow + from PyMca5.PyMcaGui.pymca import ScanWindow app = qt.QApplication([]) QT = True - plot = PlotWindow.PlotWindow() + plot = ScanWindow.ScanWindow() except: # test without graphical interface QT = False from PyMca5.PyMcaGraph import Plot plot = Plot.Plot() pluginDir = [os.path.dirname(__file__)] - plot.getPlugins(method="getPlugin1DInstance", - directoryList=pluginDir) + plot.pluginsToolButton.getPlugins(method="getPlugin1DInstance", + directoryList=pluginDir) i = numpy.arange(1000.) y1 = 10.0 + 5000.0 * numpy.exp(-0.01*(i-50)**2) y2 = 10.0 + 5000.0 * numpy.exp(-((i-55)/5.)**2) diff --git a/PyMca5/PyMcaPlugins/BackgroundStackPlugin.py b/PyMca5/PyMcaPlugins/BackgroundStackPlugin.py index 1357d39..5362658 100644 --- a/PyMca5/PyMcaPlugins/BackgroundStackPlugin.py +++ b/PyMca5/PyMcaPlugins/BackgroundStackPlugin.py @@ -38,6 +38,7 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import logging from PyMca5 import StackPluginBase from PyMca5.PyMcaGui import SGWindow from PyMca5.PyMcaGui import SNIPWindow @@ -45,12 +46,13 @@ from PyMca5.PyMcaGui import PyMca_Icons as PyMca_Icons import numpy -DEBUG = 0 +_logger = logging.getLogger(__name__) class BackgroundStackPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) SGtext = "Replace current stack by a\n" SGtext += "Savitsky-Golay treated one." diff --git a/PyMca5/PyMcaPlugins/ExternalImagesStackPlugin.py b/PyMca5/PyMcaPlugins/ExternalImagesStackPlugin.py index b78da58..34e8e50 100644 --- a/PyMca5/PyMcaPlugins/ExternalImagesStackPlugin.py +++ b/PyMca5/PyMcaPlugins/ExternalImagesStackPlugin.py @@ -43,6 +43,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import os +import logging from PyMca5 import StackPluginBase from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaIO import EDFStack @@ -51,10 +52,13 @@ from PyMca5.PyMcaGui import StackPluginResultsWindow from PyMca5.PyMcaGui import ExternalImagesWindow from PyMca5.PyMcaGui import PyMca_Icons as PyMca_Icons -DEBUG = 0 +_logger = logging.getLogger(__name__) + + class ExternalImagesStackPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {'Load':[self._loadImageFiles, "Load Images", @@ -77,8 +81,7 @@ class ExternalImagesStackPlugin(StackPluginBase.StackPluginBase): self.widget.setSelectionMask(mask) def mySlot(self, ddict): - if DEBUG: - print("mySlot ", ddict['event'], ddict.keys()) + _logger.debug("mySlot %s %s", ddict['event'], ddict.keys()) if ddict['event'] == "selectionMaskChanged": self.setStackSelectionMask(ddict['current']) elif ddict['event'] == "addImageClicked": diff --git a/PyMca5/PyMcaPlugins/FastXRFLinearFitStackPlugin.py b/PyMca5/PyMcaPlugins/FastXRFLinearFitStackPlugin.py index 90ad304..fb974a3 100644 --- a/PyMca5/PyMcaPlugins/FastXRFLinearFitStackPlugin.py +++ b/PyMca5/PyMcaPlugins/FastXRFLinearFitStackPlugin.py @@ -60,7 +60,7 @@ These plugins will be compatible with any stack window that provides the functio import sys import os import numpy -import time +import logging import traceback from PyMca5 import StackPluginBase from PyMca5.PyMcaPhysics import FastXRFLinearFit @@ -71,11 +71,13 @@ from PyMca5.PyMcaGui import PyMca_Icons as PyMca_Icons from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaIO import ArraySave -DEBUG = 0 +_logger = logging.getLogger(__name__) + class FastXRFLinearFitStackPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {} function = self.calculate @@ -97,8 +99,7 @@ class FastXRFLinearFitStackPlugin(StackPluginBase.StackPluginBase): self.thread = None def stackUpdated(self): - if DEBUG: - print("FastXRFLinearFitStackPlugin.stackUpdated() called") + _logger.debug("FastXRFLinearFitStackPlugin.stackUpdated() called") self._widget = None def selectionMaskUpdated(self): @@ -110,8 +111,7 @@ class FastXRFLinearFitStackPlugin(StackPluginBase.StackPluginBase): self._widget.setSelectionMask(mask) def mySlot(self, ddict): - if DEBUG: - print("mySlot ", ddict['event'], ddict.keys()) + _logger.debug("mySlot ", ddict['event'], ddict.keys()) if ddict['event'] == "selectionMaskChanged": self.setStackSelectionMask(ddict['current']) elif ddict['event'] == "addImageClicked": @@ -158,7 +158,7 @@ class FastXRFLinearFitStackPlugin(StackPluginBase.StackPluginBase): self.fitInstance = FastXRFLinearFit.FastXRFLinearFit() #self._fitConfigurationFile="E:\DATA\COTTE\CH1777\G4-4720eV-NOWEIGHT-Constant-batch.cfg" - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: self.thread = CalculationThread.CalculationThread(\ calculation_method=self.actualCalculation) self.thread.result = self.actualCalculation() @@ -248,8 +248,8 @@ class FastXRFLinearFitStackPlugin(StackPluginBase.StackPluginBase): parameters = self.configurationWidget.getParameters() outputDir = parameters["output_dir"] if outputDir in [None, ""]: - if DEBUG: - print("Nothing to be saved") + _logger.debug("Nothing to be saved") + if _logger.getEffectiveLevel() == logging.DEBUG: return if parameters["file_root"] is None: fileRoot = "" diff --git a/PyMca5/PyMcaPlugins/FitStackPlugin.py b/PyMca5/PyMcaPlugins/FitStackPlugin.py index e11205b..10c627c 100644 --- a/PyMca5/PyMcaPlugins/FitStackPlugin.py +++ b/PyMca5/PyMcaPlugins/FitStackPlugin.py @@ -38,19 +38,21 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" - +import logging +_logger = logging.getLogger(__name__) try: from PyMca5 import StackPluginBase from PyMca5.PyMcaGui import StackSimpleFitWindow from PyMca5.PyMcaGui import PyMca_Icons except ImportError: - print("FitStackPlugin importing from somewhere else") + _logger.warning("FitStackPlugin importing from somewhere else") -DEBUG = 0 class FitStackPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) + StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {} function = self.fitStack diff --git a/PyMca5/PyMcaPlugins/ImageAlignmentStackPlugin.py b/PyMca5/PyMcaPlugins/ImageAlignmentStackPlugin.py index 7ede608..fa005be 100644 --- a/PyMca5/PyMcaPlugins/ImageAlignmentStackPlugin.py +++ b/PyMca5/PyMcaPlugins/ImageAlignmentStackPlugin.py @@ -40,6 +40,7 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys import os import numpy +import logging from PyMca5 import StackPluginBase from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import FFTAlignmentWindow @@ -51,12 +52,15 @@ from PyMca5.PyMcaGui import PyMcaFileDialogs from PyMca5.PyMcaIO import specfilewrapper from PyMca5.PyMcaGui import HDF5Widget +_logger = logging.getLogger(__name__) + try: from PyMca5.PyMcaGui import SIFTAlignmentWindow sift = SIFTAlignmentWindow.sift + ocl = SIFTAlignmentWindow.silx.opencl.ocl SIFT = True except: - print("SIFTAlignmentWindow not successful") + _logger.warning("SIFTAlignmentWindow not successful") SIFT = False try: @@ -65,10 +69,11 @@ try: except: HDF5 = False -DEBUG = 0 + class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {'FFT Alignment':[self._fftAlignment, "Align using FFT", @@ -132,7 +137,7 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): if filename is not None: self.__hdf5 = self.initializeHDF5File(filename) - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: shifts = self.calculateShiftsFFT(stack, reference, offsets=offsets, @@ -198,7 +203,7 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): dataGroup[info['xlabel']].attrs['axis'] = numpy.int32(1) axesAttribute = '%s:dim_1:dim_2' % info['xlabel'] except: - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise dataGroup['dim_0'] = numpy.arange(stack.data.shape[mcaIndex]).astype(numpy.float32) dataGroup['dim_0'].attrs['axis'] = numpy.int32(1) @@ -267,14 +272,14 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): import pyopencl except: raise ImportError("PyOpenCL does not seem to be installed on your system") - if sift.opencl.ocl is None: + if ocl is None: raise ImportError("PyOpenCL does not seem to be installed on your system") stack = self.getStackDataObject() if stack is None: return mcaIndex = stack.info.get('McaIndex') if not (mcaIndex in [0, 2, -1]): - raise IndexError("Unsupported 1D index %d" % mcaIndex) + raise IndexError("Unsupported 1D index %d" % mcaIndex) widget = SIFTAlignmentWindow.SIFTAlignmentDialog() widget.setStack(stack) mask = self.getStackSelectionMask() @@ -293,7 +298,7 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): self.__hdf5 = self.initializeHDF5File(filename) crop = False device = ddict['opencl_device'] - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: result = self.calculateShiftsSIFT(stack, reference, mask=mask, device=device, crop=crop, filename=filename) else: @@ -315,28 +320,13 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): if mask.sum() == 0: mask = None if device is None: - if sys.platform == 'darwin': - max_workgroup_size = 1 - siftInstance = sift.LinearAlign(reference.astype(numpy.float32), - max_workgroup_size=max_workgroup_size, - devicetype="cpu", - init_sigma=sigma) - else: - siftInstance = sift.LinearAlign(reference.astype(numpy.float32), - devicetype="cpu", - init_sigma=sigma) + siftInstance = sift.LinearAlign(reference.astype(numpy.float32), + devicetype="cpu", + init_sigma=sigma) else: - deviceType = sift.opencl.ocl.platforms[device[0]].devices[device[1]].type - if deviceType.lower() == "cpu" and sys.platform == 'darwin': - max_workgroup_size = 1 - siftInstance = sift.LinearAlign(reference.astype(numpy.float32), - max_workgroup_size=max_workgroup_size, - device=device, - init_sigma=sigma) - else: - siftInstance = sift.LinearAlign(reference.astype(numpy.float32), - device=device, - init_sigma=sigma) + siftInstance = sift.LinearAlign(reference.astype(numpy.float32), + deviceid=device, + init_sigma=sigma) data = stack.data mcaIndex = stack.info['McaIndex'] if not (mcaIndex in [0, 2, -1]): @@ -358,11 +348,10 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): shifts = numpy.zeros((data.shape[mcaIndex], 2), dtype=numpy.float32) if mcaIndex == 0: for i in range(data.shape[mcaIndex]): - if DEBUG: - print("SIFT Shifting image %d" % i) + _logger.debug("SIFT Shifting image %d", i) result = siftInstance.align(data[i].astype(numpy.float32), shift_only=True, return_all=True) - if DEBUG: - print("Index = %d shift = %.4f, %.4f" % (i, result['offset'][0], result['offset'][1])) + _logger.debug("Index = %d shift = %.4f, %.4f", + i, result['offset'][0], result['offset'][1]) if filename is None: stack.data[i] = result['result'] else: @@ -373,12 +362,11 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): else: image2 = numpy.zeros(reference.shape, dtype=numpy.float32) for i in range(data.shape[mcaIndex]): - if DEBUG: - print("SIFT Shifting image %d" % i) + _logger.debug("SIFT Shifting image %d", i) image2[:, :] = data[:, :, i] result = siftInstance.align(image2, shift_only=True, return_all=True) - if DEBUG: - print("Index = %d shift = %.4f, %.4f" % (i, result['offset'][0], result['offset'][1])) + _logger.debug("Index = %d shift = %.4f, %.4f", + i, result['offset'][0], result['offset'][1]) if filename is None: stack.data[:, :, i] = result['result'] else: @@ -423,7 +411,7 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): dataGroup[info['xlabel']].attrs['axis'] = numpy.int32(1) axesAttribute = '%s:dim_1:dim_2' % info['xlabel'] except: - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise dataGroup['dim_0'] = numpy.arange(stack.data.shape[mcaIndex]).astype(numpy.float32) dataGroup['dim_0'].attrs['axis'] = numpy.int32(1) @@ -437,9 +425,8 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): self.finishHDF5File(hdf) def calculateShiftsFFT(self, stack, reference, offsets=None, widths=None, crop=False): - if DEBUG: - print("Offsets = ", offsets) - print("Widths = ", widths) + _logger.debug("Offsets = %s", offsets) + _logger.debug("Widths = %s", widths) data = stack.data if offsets is None: offsets = [0.0, 0.0] @@ -479,8 +466,8 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): image1fft2 = fft2Function(image1) shifts[i] = ImageRegistration.measure_offset_from_ffts(image2fft2, image1fft2) - if DEBUG: - print("Index = %d shift = %.4f, %.4f" % (i, shifts[i][0], shifts[i][1])) + _logger.debug("Index = %d shift = %.4f, %.4f", + i, shifts[i][0], shifts[i][1]) self._progress = (100 * i) / total elif mcaIndex in [2, -1]: for i in range(data.shape[mcaIndex]): @@ -490,8 +477,8 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): image1fft2 = fft2Function(image1) shifts[i] = ImageRegistration.measure_offset_from_ffts(image2fft2, image1fft2) - if DEBUG: - print("Index = %d shift = %.4f, %.4f" % (i, shifts[i][0], shifts[i][1])) + _logger.debug("Index = %d shift = %.4f, %.4f", + i, shifts[i][0], shifts[i][1]) self._progress = (100 * i) / total else: raise IndexError("Only stacks of images or spectra supported. 1D index should be 0 or 2") @@ -515,11 +502,9 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): getfilter=True, currentfilter=filefilter[0]) if len(filename): - if DEBUG: - print("file name = %s file filter = %s" % (filename, ffilter)) + _logger.debug("file name = %s file filter = %s", filename, ffilter) else: - if DEBUG: - print("nothing selected") + _logger.debug("nothing selected") return filename = filename[0] if ffilter.startswith('HDF5'): @@ -575,14 +560,13 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): currentfilter=filefilter[0]) if len(filename): filename = filename[0] - if DEBUG: - print("file name = %s" % filename) + _logger.debug("file name = %s", filename) else: raise IOError("No output file selected") if filename is not None: self.__hdf5 = self.initializeHDF5File(filename) crop = False - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: result = self.shiftStack(stack, shifts, crop=crop, @@ -620,7 +604,7 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): dataGroup[info['xlabel']].attrs['axis'] = numpy.int32(1) axesAttribute = '%s:dim_1:dim_2' % info['xlabel'] except: - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: raise dataGroup['dim_0'] = numpy.arange(stack.data.shape[mcaIndex]).astype(numpy.float32) dataGroup['dim_0'].attrs['axis'] = numpy.int32(1) @@ -688,8 +672,7 @@ class ImageAlignmentStackPlugin(StackPluginBase.StackPluginBase): stack.data[:, :, i] = tmpImage * window else: outputStack[i] = tmpImage * window - if DEBUG: - print("Index %d bilinear shifted" % i) + _logger.debug("Index %d bilinear shifted", i) self._progress = (100 * i) / total def initializeHDF5File(self, fname): diff --git a/PyMca5/PyMcaPlugins/KineticsPlugin.py b/PyMca5/PyMcaPlugins/KineticsPlugin.py index 0c8dd3c..291c320 100644 --- a/PyMca5/PyMcaPlugins/KineticsPlugin.py +++ b/PyMca5/PyMcaPlugins/KineticsPlugin.py @@ -176,10 +176,9 @@ def getPlugin1DInstance(plotWindow, **kw): return ob if __name__ == "__main__": - import sys import os from PyMca5.PyMcaGui import PyMcaQt as qt - from PyMca5.PyMcaGui import PlotWindow + from PyMca5.PyMcaGui.pymca import ScanWindow # first order, k = 4.820e-04 x = [0, 600, 1200, 1800, 2400, 3000, 3600] y = [0.0365, 0.0274, 0.0206, 0.0157, 0.0117, 0.00860, 0.00640] @@ -189,10 +188,11 @@ if __name__ == "__main__": print("Expected slope: 0.000482") sigmay = None app = qt.QApplication([]) - plot = PlotWindow.PlotWindow() - plot.setPluginDirectoryList([os.path.dirname(__file__)]) - plot.getPlugins() - plot.addCurve(x, y, "Test Data") + plot = ScanWindow.ScanWindow() + plot.pluginsToolButton.setPluginDirectoryList([os.path.dirname(__file__)]) + plot.pluginsToolButton.getPlugins() + plot.addCurve(x, y, "Test Data", + resetzoom=True) plot.show() plugin = getPlugin1DInstance(plot) for method in plugin.getMethods(): diff --git a/PyMca5/PyMcaPlugins/LoadPositionersStackPlugin.py b/PyMca5/PyMcaPlugins/LoadPositionersStackPlugin.py index d6bd9b5..b906356 100644 --- a/PyMca5/PyMcaPlugins/LoadPositionersStackPlugin.py +++ b/PyMca5/PyMcaPlugins/LoadPositionersStackPlugin.py @@ -43,6 +43,7 @@ Data loaded with this plugin can then be used by other tools, such as the __authors__ = ["P. Knobel"] __license__ = "MIT" +import logging from PyMca5 import StackPluginBase from PyMca5.PyMcaGui.io.hdf5.HDF5Widget import getGroupNameDialog @@ -76,15 +77,15 @@ except ImportError: # suppress errors and warnings if fabio is missing if silx_open is not None: - import logging logging.getLogger("silx.io.fabioh5").setLevel(logging.CRITICAL) -DEBUG = 0 +_logger = logging.getLogger(__name__) class LoadPositionersStackPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow) self.methodDict = {'Load positioners': [self._loadFromFile, "Load positioners from file"]} @@ -129,11 +130,9 @@ class LoadPositionersStackPlugin(StackPluginBase.StackPluginBase): getfilter=True, currentfilter=filefilter[0]) if len(filename): - if DEBUG: - print("file name = %s file filter = %s" % (filename, ffilter)) + _logger.debug("file name = %s file filter = %s", filename, ffilter) else: - if DEBUG: - print("nothing selected") + _logger.debug("nothing selected") return filename = filename[0] diff --git a/PyMca5/PyMcaPlugins/Medfilt2DPlugin.py b/PyMca5/PyMcaPlugins/Medfilt2DPlugin.py new file mode 100644 index 0000000..dc79dfa --- /dev/null +++ b/PyMca5/PyMcaPlugins/Medfilt2DPlugin.py @@ -0,0 +1,208 @@ +#/*########################################################################## +# Copyright (C) 2004-2018 V.A. Sole, European Synchrotron Radiation Facility +# +# This file is part of the PyMca X-ray Fluorescence Toolkit developed at +# the ESRF by the Software group. +# +# 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. +# +#############################################################################*/ + +from PyMca5.PyMcaCore.Plugin2DBase import Plugin2DBase +from PyMca5.PyMcaGui import PyMcaQt as qt +from silx.gui.plot import Plot2D +from silx.math.medianfilter import medfilt2d + + +class Medfilt2DPlugin(Plugin2DBase): + def __init__(self, plotWindow): + Plugin2DBase.__init__(self, plotWindow) + self.methods = { + "Median filter": [self._medfilt2D, + "Open a plot showing a filtered image", + None] + } + self.widget = None + + def getMethods(self, plottype=None): + """ + + :return: A list with the NAMES associated to the callable methods + that are applicable to the specified type plot. The list can be empty. + :rtype: list[string] + """ + return list(self.methods.keys()) + + def _getMethod(self, name): + method = self.methods.get(name) + if method is None: + raise NameError("method %s not found" % name) + return method + + def getMethodToolTip(self, name): + """ + Returns the help associated to the particular method name or None. + + :param name: The method for which a tooltip is asked + :rtype: string + """ + return self._getMethod(name)[1] + + def getMethodPixmap(self, name): + """ + :param name: The method for which a pixmap is asked + :rtype: QPixmap or None + """ + return self._getMethod(name)[2] + + def applyMethod(self, name): + """ + The plugin is asked to apply the method associated to name. + """ + return self._getMethod(name)[0]() + + def _medfilt2D(self): + if self.widget is None: + self.widget = Plot2DMedFilt() + self.widget.show() + + active_image = self._plotWindow.getActiveImage() + if active_image is None: + return + data = active_image.getData() + if data.ndim > 2: + raise NotImplementedError("Median filter not implemented for RGB images") + self.widget.setColormap(active_image.getColormap()) + self.widget.setRawData(data) + self.widget.setLegend("medfilt2d(%s)" % active_image.getLegend()) + + def activeImageChanged(self, prev, new): + if new is None: + return + self._medfilt2D() + + +class Plot2DMedFilt(Plot2D): + # TODO: we could allow setting different X- and Y-filter width. + def __init__(self, parent=None): + Plot2D.__init__(self, parent=parent) + self.toolBar().addSeparator() + label = qt.QLabel(self) + label.setText("Median filter width:") + + self.spinbox = qt.QSpinBox(self) + self.spinbox.setMinimum(1) + self.spinbox.setValue(1) + self.spinbox.setSingleStep(2) + self.spinbox.valueChanged[int].connect(self._medfiltWidthChanged) + self.spinbox.setEnabled(False) + + self.toolBar().addWidget(label) + self.toolBar().addWidget(self.spinbox) + + self._data = None + self._legend = None + self._colormap = None + + def setLegend(self, legend): + """ + + :param str legend: + :return: + """ + self._legend = legend + + def setRawData(self, data, legend=None): + """Set raw image data to be filtered. + + :param numpy.ndarray data: + :param str legend: + :return: + """ + if data is None: + self.spinbox.setEnabled(False) + self._data = None + return + if data.ndim != 2: + raise TypeError("Data must be a 2D array") + self._data = data + self.spinbox.setMaximum(max(data.shape)) + self.spinbox.setEnabled(True) + self._applyFilter() + + def setColormap(self, colormap): + self._colormap = colormap + + @property + def medfilt_width(self): + return self.spinbox.value() + + def _medfiltWidthChanged(self, width): + self._applyFilter() + + def _applyFilter(self): + self.addImage(medfilt2d(self._data, kernel_size=self.medfilt_width), + colormap=self._colormap, + legend="medfilt2d(%s)" % self._legend) + + +MENU_TEXT = "2D median filter" + + +def getPlugin2DInstance(plotWindow, **kw): + """ + """ + ob = Medfilt2DPlugin(plotWindow) + return ob + + +if __name__ == "__main__": + # python -m PyMca5.PyMcaPlugins.Medfilt2DPlugin + from PyMca5.PyMcaGui.PluginsToolButton import PluginsToolButton + from PyMca5 import PyMcaPlugins + import numpy + import os + from silx.test.utils import add_relative_noise + from silx.gui.plot import PlotWidget + + # build a plot widget with a plugin toolbar button + app = qt.QApplication([]) + master_plot = PlotWidget() + toolb = qt.QToolBar(master_plot) + plugins_tb_2d = PluginsToolButton(plot=master_plot, + parent=toolb, + method="getPlugin2DInstance") + plugins_tb_2d.getPlugins( + method="getPlugin2DInstance", + directoryList=[os.path.dirname(PyMcaPlugins.__file__)]) + toolb.addWidget(plugins_tb_2d) + master_plot.addToolBar(toolb) + master_plot.show() + + # add a noisy image + a, b = numpy.meshgrid(numpy.linspace(-10, 10, 500), + numpy.linspace(-10, 5, 400), + indexing="ij") + myimg = numpy.asarray(numpy.sin(a * b) / (a * b), + dtype='float32') + myimg = add_relative_noise(myimg, + max_noise=10.) # % + master_plot.addImage(myimg) + + app.exec_() diff --git a/PyMca5/PyMcaPlugins/MedianFilterScanDeglitchPlugin.py b/PyMca5/PyMcaPlugins/MedianFilterScanDeglitchPlugin.py index f350a5c..cce25dc 100644 --- a/PyMca5/PyMcaPlugins/MedianFilterScanDeglitchPlugin.py +++ b/PyMca5/PyMcaPlugins/MedianFilterScanDeglitchPlugin.py @@ -37,8 +37,11 @@ from PyMca5 import Plugin1DBase from PyMca5.PyMcaMath.PyMcaSciPy.signal import medfilt1d from PyMca5.PyMcaGui import PyMcaQt as qt import numpy +import logging + +_logger = logging.getLogger(__name__) + -DEBUG = 0 class MedianFilterScanDeglitchPlugin(Plugin1DBase.Plugin1DBase): def __init__(self, plotWindow, **kw): Plugin1DBase.Plugin1DBase.__init__(self, plotWindow, **kw) @@ -150,12 +153,10 @@ class MedianFilterScanDeglitchPlugin(Plugin1DBase.Plugin1DBase): if not (self.width%2): self.width += 1 if self._widget.buttonAll.isChecked(): - if DEBUG: - print('AllChecked') + _logger.debug('AllChecked') self.removeSpikesAll() elif self._widget.buttonActive.isChecked(): - if DEBUG: - print('ActiveChecked') + _logger.debug('ActiveChecked') self.removeSpikesActive() def removeSpikesAll(self): @@ -190,7 +191,7 @@ class MedianFilterScanDeglitchPlugin(Plugin1DBase.Plugin1DBase): self.addCurve(x,ynew,legend,info, replace=False, replot=True) else: self.addCurve(x,ynew,legend,info, replace=False, replot=False) - #self._plotWindow.replot() + MENU_TEXT = "Remove glitches from curves" def getPlugin1DInstance(plotWindow, **kw): @@ -198,10 +199,10 @@ def getPlugin1DInstance(plotWindow, **kw): return ob if __name__ == "__main__": - from PyMca5.PyMcaGui import PlotWindow + from silx.gui.plot import Plot1D app = qt.QApplication([]) - sw = PlotWindow.PlotWindow() + sw = Plot1D() x = numpy.linspace(0, 1999, 2000) y0 = x/100. + 100.*numpy.exp(-(x-500)**2/1000.) + 50.*numpy.exp(-(x-1200)**2/5000.) + 100.*numpy.exp(-(x-1700)**2/500.) + 10 * numpy.random.random(2000) @@ -213,6 +214,7 @@ if __name__ == "__main__": sw.addCurve(x, y0, legend="Curve0") sw.addCurve(x, y1, legend="Curve1") + sw.setActiveCurve("Curve0") plugin = getPlugin1DInstance(sw) plugin.configureFilter() diff --git a/PyMca5/PyMcaPlugins/MedianFilterStackPlugin.py b/PyMca5/PyMcaPlugins/MedianFilterStackPlugin.py index b414c56..b445588 100644 --- a/PyMca5/PyMcaPlugins/MedianFilterStackPlugin.py +++ b/PyMca5/PyMcaPlugins/MedianFilterStackPlugin.py @@ -41,15 +41,18 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import logging from PyMca5 import StackPluginBase from PyMca5.PyMcaGui.pymca import Median2DBrowser from PyMca5.PyMcaGui import PyMca_Icons -DEBUG = 0 +_logger = logging.getLogger(__name__) + class MedianFilterStackPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {'Show':[self._showWidget, "Image Browser with Median Filter", @@ -58,8 +61,7 @@ class MedianFilterStackPlugin(StackPluginBase.StackPluginBase): self.widget = None def stackUpdated(self): - if DEBUG: - print("StackBrowserPlugin.stackUpdated() called") + _logger.debug("StackBrowserPlugin.stackUpdated() called") if self.widget is None: return if self.widget.isHidden(): @@ -92,8 +94,7 @@ class MedianFilterStackPlugin(StackPluginBase.StackPluginBase): self.widget.setBackgroundImage(self._getBackgroundImage()) def mySlot(self, ddict): - if DEBUG: - print("mySlot ", ddict['event'], ddict.keys()) + _logger.debug("mySlot %s %s", ddict['event'], ddict.keys()) if ddict['event'] == "selectionMaskChanged": self.setStackSelectionMask(ddict['current']) elif ddict['event'] == "addImageClicked": diff --git a/PyMca5/PyMcaPlugins/MotorInfoPlugin.py b/PyMca5/PyMcaPlugins/MotorInfoPlugin.py index ed55964..f3d9089 100644 --- a/PyMca5/PyMcaPlugins/MotorInfoPlugin.py +++ b/PyMca5/PyMcaPlugins/MotorInfoPlugin.py @@ -47,7 +47,10 @@ except ImportError: print("MotorInfoPlugin importing from somewhere else") import MotorInfoWindow -DEBUG = 0 +import logging +_logger = logging.getLogger(__name__) + + class MotorInfo(Plugin1DBase.Plugin1DBase): def __init__(self, plotWindow, **kw): Plugin1DBase.Plugin1DBase.__init__(self, plotWindow, **kw) @@ -86,11 +89,10 @@ class MotorInfo(Plugin1DBase.Plugin1DBase): def _getLists(self): curves = self.getAllCurves() nCurves = len(curves) - if DEBUG: - print ("Received %d curve(s).." % nCurves) + _logger.debug("Received %d curve(s)..", nCurves) legendList = [leg for (xvals, yvals, leg, info) in curves] infoList = [info for (xvals, yvals, leg, info) in curves] - motorValuesList = self._convertInfoDictionary( infoList ) + motorValuesList = self._convertInfoDictionary(infoList) return legendList, motorValuesList def _convertInfoDictionary(self, infosList): diff --git a/PyMca5/PyMcaPlugins/MotorInfoWindow.py b/PyMca5/PyMcaPlugins/MotorInfoWindow.py index 696de9b..6c4fd5f 100644 --- a/PyMca5/PyMcaPlugins/MotorInfoWindow.py +++ b/PyMca5/PyMcaPlugins/MotorInfoWindow.py @@ -27,6 +27,7 @@ __author__ = "Tonn Rueter" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import logging from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import IconDict @@ -40,7 +41,7 @@ if hasattr(qt, 'QString'): else: QString = qt.safe_str -DEBUG = 0 +_logger = logging.getLogger(__name__) class MotorInfoComboBox(qt.QComboBox): @@ -184,17 +185,15 @@ class MotorInfoTable(TableWidget): self.setItem(currentRow, 0, legend ) def updateTable(self, legList, motList): - if DEBUG: - print("updateTable received lengths = ", len(legList), len(motList)) - print("updateTable received legList = ", legList) - print("updateTable received motList = ", motList) + _logger.debug("updateTable received lengths = %d %d", len(legList), len(motList)) + _logger.debug("updateTable received legList = %s", legList) + _logger.debug("updateTable received motList = %s", motList) if legList is None: nItems = 0 else: nItems = len(legList) if self.legendsList == legList and self.motorsList == motList: - if DEBUG: - print("Ignoring update, no changes") + _logger.debug("Ignoring update, no changes") else: nRows = self.rowCount() if nRows != nItems: @@ -268,7 +267,7 @@ class MotorInfoDialog(qt.QWidget): qt.QWidget.__init__(self, parent) self.setWindowTitle("Motor Info Plugin") if len(legends) != len(motorValues): - print('Consistency error: legends and motorValues do not have same length!') + _logger.warning('Consistency error: legends and motorValues do not have same length!') self.numCurves = len(legends) # Buttons self.buttonAddColumn = qt.QPushButton("Add", self) diff --git a/PyMca5/PyMcaPlugins/MultipleScanToMeshPlugin.py b/PyMca5/PyMcaPlugins/MultipleScanToMeshPlugin.py index 7da49c2..a18b06c 100644 --- a/PyMca5/PyMcaPlugins/MultipleScanToMeshPlugin.py +++ b/PyMca5/PyMcaPlugins/MultipleScanToMeshPlugin.py @@ -28,15 +28,16 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import sys -import os import numpy +import logging from matplotlib.mlab import griddata from PyMca5 import Plugin1DBase from PyMca5.PyMcaGui import MaskImageWidget from PyMca5.PyMcaGui import PyMcaQt as qt -DEBUG = 0 +_logger = logging.getLogger(__name__) + class MultipleScanToMeshPlugin(Plugin1DBase.Plugin1DBase): def __init__(self, plotWindow, **kw): @@ -76,14 +77,11 @@ class MultipleScanToMeshPlugin(Plugin1DBase.Plugin1DBase): """ The plugin is asked to apply the method associated to name. """ - if DEBUG: - self.methodDict[name][0]() - else: - try: - self.methodDict[name][0]() - except: - print(sys.exc_info()) - raise + try: + self.methodDict[name][0]() + except: + _logger.error(sys.exc_info()) + raise def _rixsID26(self): allCurves = self.getAllCurves() @@ -264,7 +262,7 @@ if __name__ == "__main__": #w.exec_() #sys.exit(0) - DEBUG = 1 + _logger.setLevel(logging.DEBUG) x = numpy.arange(100.) y = x * x plot = Plot.Plot() diff --git a/PyMca5/PyMcaPlugins/NNMAStackPlugin.py b/PyMca5/PyMcaPlugins/NNMAStackPlugin.py index 3d96db5..40a7a41 100644 --- a/PyMca5/PyMcaPlugins/NNMAStackPlugin.py +++ b/PyMca5/PyMcaPlugins/NNMAStackPlugin.py @@ -59,6 +59,7 @@ functions: selectionMaskUpdated """ import numpy +import logging from PyMca5 import StackPluginBase from PyMca5.PyMcaGui import CalculationThread @@ -67,12 +68,13 @@ from PyMca5.PyMcaGui import StackPluginResultsWindow from PyMca5.PyMcaGui import PyMca_Icons qt = StackPluginResultsWindow.qt -DEBUG = 0 +_logger = logging.getLogger(__name__) class NNMAStackPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {'Calculate': [self.calculate, "Perform NNMA", @@ -86,8 +88,7 @@ class NNMAStackPlugin(StackPluginBase.StackPluginBase): self.thread = None def stackUpdated(self): - if DEBUG: - print("NNMAStackPlugin.stackUpdated() called") + _logger.debug("NNMAStackPlugin.stackUpdated() called") self.configurationWidget = None self.widget = None @@ -100,8 +101,7 @@ class NNMAStackPlugin(StackPluginBase.StackPluginBase): self.widget.setSelectionMask(mask) def mySlot(self, ddict): - if DEBUG: - print("mySlot ", ddict['event'], ddict.keys()) + _logger.debug("mySlot %s %s", ddict['event'], ddict.keys()) if ddict['event'] == "selectionMaskChanged": self.setStackSelectionMask(ddict['current']) elif ddict['event'] == "addImageClicked": @@ -177,28 +177,23 @@ class NNMAStackPlugin(StackPluginBase.StackPluginBase): self._executeFunctionAndParameters() def _executeFunctionAndParameters(self): - if DEBUG: - print("NNMAStackPlugin _executeFunctionAndParameters") + _logger.debug("NNMAStackPlugin _executeFunctionAndParameters") self.widget = None self.thread = CalculationThread.CalculationThread(\ calculation_method=self.actualCalculation) self.thread.finished.connect(self.threadFinished) self.configurationWidget.show() message = "Please wait. NNMA Calculation going on." - if DEBUG: - print("NNMAStackPlugin starting thread") + _logger.debug("NNMAStackPlugin starting thread") self.thread.start() - if DEBUG: - print("NNMAStackPlugin waitingMessageDialog") + _logger.debug("NNMAStackPlugin waitingMessageDialog") CalculationThread.waitingMessageDialog(self.thread, message=message, parent=self.configurationWidget) - if DEBUG: - print("NNMAStackPlugin waitingMessageDialog passed") + _logger.debug("NNMAStackPlugin waitingMessageDialog passed") def actualCalculation(self): - if DEBUG: - print("NNMAStackPlugin actualCalculation") + _logger.debug("NNMAStackPlugin actualCalculation") nnmaParameters = self.configurationWidget.getParameters() self._status.setText("Calculation going on") self.configurationWidget.setEnabled(False) @@ -215,7 +210,7 @@ class NNMAStackPlugin(StackPluginBase.StackPluginBase): stack = self.getStackDataObject() if isinstance(stack, numpy.ndarray): if stack.data.dtype not in [numpy.float, numpy.float32]: - print("WARNING: Non floating point data") + _logger.warning("WARNING: Non floating point data") text = "Calculation going on." text += " WARNING: Non floating point data." self._status.setText(text) @@ -227,8 +222,7 @@ class NNMAStackPlugin(StackPluginBase.StackPluginBase): return result def threadFinished(self): - if DEBUG: - print("NNMAStackPlugin threadFinished") + _logger.debug("NNMAStackPlugin threadFinished") result = self.thread.result self.thread = None if type(result) == type((1,)): @@ -261,12 +255,10 @@ class NNMAStackPlugin(StackPluginBase.StackPluginBase): vectorNames.append("NNMA Component %02d" % i) vectorTitles.append("%g %% explained intensity" %\ eigenValues[i]) - if DEBUG: - print("NNMAStackPlugin threadFinished. Create widget") + _logger.debug("NNMAStackPlugin threadFinished. Create widget") self.widget = StackPluginResultsWindow.StackPluginResultsWindow(\ usetab=True) - if DEBUG: - print("NNMAStackPlugin threadFinished. Widget created") + _logger.debug("NNMAStackPlugin threadFinished. Widget created") self.widget.buildAndConnectImageButtonBox(replace=True, multiple=True) qt = StackPluginResultsWindow.qt diff --git a/PyMca5/PyMcaPlugins/PCAStackPlugin.py b/PyMca5/PyMcaPlugins/PCAStackPlugin.py index e03fc90..27e5683 100644 --- a/PyMca5/PyMcaPlugins/PCAStackPlugin.py +++ b/PyMca5/PyMcaPlugins/PCAStackPlugin.py @@ -48,6 +48,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import numpy +import logging from PyMca5 import StackPluginBase from PyMca5.PyMcaGui import CalculationThread @@ -57,12 +58,13 @@ from PyMca5.PyMcaGui import StackPluginResultsWindow from PyMca5.PyMcaGui import PyMca_Icons qt = StackPluginResultsWindow.qt -DEBUG = 0 +_logger = logging.getLogger(__name__) class PCAStackPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {'Calculate': [self.calculate, "Perform PCA", None], 'Show': [self._showWidget, @@ -74,8 +76,7 @@ class PCAStackPlugin(StackPluginBase.StackPluginBase): self.thread = None def stackUpdated(self): - if DEBUG: - print("PCAStackPlugin.stackUpdated() called") + _logger.debug("PCAStackPlugin.stackUpdated() called") self.configurationWidget = None self.widget = None @@ -88,8 +89,7 @@ class PCAStackPlugin(StackPluginBase.StackPluginBase): self.widget.setSelectionMask(mask) def mySlot(self, ddict): - if DEBUG: - print("mySlot ", ddict['event'], ddict.keys()) + _logger.debug("mySlot %s %s", ddict['event'], ddict.keys()) if ddict['event'] == "selectionMaskChanged": self.setStackSelectionMask(ddict['current']) elif ddict['event'] == "addImageClicked": @@ -166,7 +166,7 @@ class PCAStackPlugin(StackPluginBase.StackPluginBase): def _executeFunctionAndParameters(self): self.widget = None self.configurationWidget.show() - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: self.thread = CalculationThread.CalculationThread(\ calculation_method=self.actualCalculation) self.thread.result = self.actualCalculation() @@ -223,7 +223,7 @@ class PCAStackPlugin(StackPluginBase.StackPluginBase): stack = self.getStackDataObject() if isinstance(stack, numpy.ndarray): if stack.data.dtype not in [numpy.float, numpy.float32]: - print("WARNING: Non floating point data") + _logger.warning("WARNING: Non floating point data") text = "Calculation going on." text += " WARNING: Non floating point data." self._status.setText(text) diff --git a/PyMca5/PyMcaPlugins/ROIStackPlugin.py b/PyMca5/PyMcaPlugins/ROIStackPlugin.py index 0a73164..4b42fb7 100644 --- a/PyMca5/PyMcaPlugins/ROIStackPlugin.py +++ b/PyMca5/PyMcaPlugins/ROIStackPlugin.py @@ -1,5 +1,5 @@ #/*########################################################################## -# Copyright (C) 2004-2014 V.A. Sole, European Synchrotron Radiation Facility +# Copyright (C) 2004-2018 V.A. Sole, European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -26,13 +26,16 @@ """ This plugin opens a stack ROI window providing alternative views: - - ICR Energy at Max. - - ICR Energy at Min. - - 3 energy slices - - ICR Background + - Usual sum of counts in the region + - Channel/Energy at data Max in the region. + - Channel/Energy at data Min in the region. + - Map of the counts at the first channel of the region + - Map of the counts at the middle cahnnel of the region + - Map of the counts at the last channel of the region + - Background counts This window also provides a median filter tool, with a configurable filter -width, to smooth the stack image. +width, to get rid of outlier pixel. The mask of this plot widget is synchronized with the master stack widget. @@ -42,15 +45,18 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import logging from PyMca5 import StackPluginBase from PyMca5.PyMcaGui.pymca import StackROIWindow from PyMca5.PyMcaGui import PyMca_Icons as PyMca_Icons -DEBUG = 0 +_logger = logging.getLogger(__name__) + class ROIStackPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {'Show':[self._showWidget, "Show ROIs", @@ -59,8 +65,7 @@ class ROIStackPlugin(StackPluginBase.StackPluginBase): self.roiWindow = None def stackUpdated(self): - if DEBUG: - print("ROIStackPlugin.stackUpdated() called") + _logger.debug("ROIStackPlugin.stackUpdated() called") if self.roiWindow is None: return if self.roiWindow.isHidden(): @@ -86,8 +91,7 @@ class ROIStackPlugin(StackPluginBase.StackPluginBase): self.stackUpdated() def mySlot(self, ddict): - if DEBUG: - print("mySlot ", ddict['event'], ddict.keys()) + _logger.debug("mySlot %s %s", ddict['event'], ddict.keys()) if ddict['event'] == "selectionMaskChanged": self.setStackSelectionMask(ddict['current']) elif ddict['event'] == "addImageClicked": diff --git a/PyMca5/PyMcaPlugins/RegularMeshPlugin.py b/PyMca5/PyMcaPlugins/RegularMeshPlugin.py index a98c797..64bfb52 100644 --- a/PyMca5/PyMcaPlugins/RegularMeshPlugin.py +++ b/PyMca5/PyMcaPlugins/RegularMeshPlugin.py @@ -28,15 +28,15 @@ __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import numpy -from numpy import cos, sin import sys +import logging from PyMca5 import Plugin1DBase -import os from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaGui import MaskImageWidget -DEBUG = 0 +_logger = logging.getLogger(__name__) + class RegularMeshPlugins(Plugin1DBase.Plugin1DBase): def __init__(self, plotWindow, **kw): @@ -76,14 +76,11 @@ class RegularMeshPlugins(Plugin1DBase.Plugin1DBase): """ The plugin is asked to apply the method associated to name. """ - if DEBUG: - self.methodDict[name][0]() - else: - try: - self.methodDict[name][0]() - except: - print(sys.exc_info()) - raise + try: + self.methodDict[name][0]() + except: + _logger.error(sys.exc_info()) + raise def _convert(self): x, y, legend, info = self.getActiveCurve() @@ -112,7 +109,7 @@ class RegularMeshPlugins(Plugin1DBase.Plugin1DBase): #Didier's contribution: Try to do something if scan has been interrupted if y.size < (int(item[6])+1) * (int(item[10])+1): - print("WARNING: Incomplete mesh scan") + _logger.warning("WARNING: Incomplete mesh scan") self._motor1 = numpy.resize(self._motor1,(y.size/(int(item[6])+1),1)) y = numpy.resize(y,((y.size/(int(item[6])+1)*(int(item[6])+1)),1)) @@ -130,8 +127,7 @@ class RegularMeshPlugins(Plugin1DBase.Plugin1DBase): self._motor1 = self._x self._motor1Mne = self._xLabel except: - if DEBUG: - print("XLabel should be one of the scanned motors") + _logger.debug("XLabel should be one of the scanned motors") self._legend = legend self._info = info @@ -162,7 +158,7 @@ def getPlugin1DInstance(plotWindow, **kw): if __name__ == "__main__": from PyMca5.PyMcaGraph import Plot app = qt.QApplication([]) - DEBUG = 1 + _logger.setLevel(logging.DEBUG) x = numpy.arange(100.) y = x * x plot = Plot.Plot() diff --git a/PyMca5/PyMcaPlugins/ReverseStackPlugin.py b/PyMca5/PyMcaPlugins/ReverseStackPlugin.py index 2b3f65a..d74eed3 100644 --- a/PyMca5/PyMcaPlugins/ReverseStackPlugin.py +++ b/PyMca5/PyMcaPlugins/ReverseStackPlugin.py @@ -40,13 +40,16 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import numpy +import logging from PyMca5 import StackPluginBase -DEBUG = 0 +_logger = logging.getLogger(__name__) + class ReverseStackPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {} text = "Replace current stack by one\n" diff --git a/PyMca5/PyMcaPlugins/SilxRoiStackPlugin.py b/PyMca5/PyMcaPlugins/SilxRoiStackPlugin.py index 978f5d5..7d1416a 100644 --- a/PyMca5/PyMcaPlugins/SilxRoiStackPlugin.py +++ b/PyMca5/PyMcaPlugins/SilxRoiStackPlugin.py @@ -1,5 +1,5 @@ # /*######################################################################### -# Copyright (C) 2004-2017 V.A. Sole, European Synchrotron Radiation Facility +# Copyright (C) 2004-2018 European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -26,10 +26,13 @@ """ This plugin opens a stack ROI window providing alternative views: - - ICR Energy at Max. - - ICR Energy at Min. - - 3 energy slices - - ICR Background + - Usual sum of counts in the region + - Channel/Energy at data Max in the region. + - Channel/Energy at data Min in the region. + - Map of the counts at the first channel of the region + - Map of the counts at the middle cahnnel of the region + - Map of the counts at the last channel of the region + - Background counts The background image can be subtracted from the other images to show a net count. diff --git a/PyMca5/PyMcaPlugins/StackAxesPlugin.py b/PyMca5/PyMcaPlugins/StackAxesPlugin.py index ed1fe26..e096038 100644 --- a/PyMca5/PyMcaPlugins/StackAxesPlugin.py +++ b/PyMca5/PyMcaPlugins/StackAxesPlugin.py @@ -38,15 +38,18 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import numpy +import logging from PyMca5 import StackPluginBase from PyMca5.PyMcaGui import PyMcaFileDialogs from PyMca5.PyMcaGui import PyMca_Icons -DEBUG = 0 +_logger = logging.getLogger(__name__) + class StackAxesPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) text = "Replace current 1D axis by list of numbers found in ASCII file" self.methodDict = {} diff --git a/PyMca5/PyMcaPlugins/StackBrowserPlugin.py b/PyMca5/PyMcaPlugins/StackBrowserPlugin.py index 1091af3..f265a59 100644 --- a/PyMca5/PyMcaPlugins/StackBrowserPlugin.py +++ b/PyMca5/PyMcaPlugins/StackBrowserPlugin.py @@ -23,7 +23,11 @@ # THE SOFTWARE. # #############################################################################*/ -""" +__author__ = "V.A. Sole - ESRF Data Analysis" +__contact__ = "sole@esrf.fr" +__license__ = "MIT" +__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +__doc__ = """ This plugin open a plot window with a browser to browse all images in the stack. @@ -33,21 +37,20 @@ average of several consecutive frames rather than a single frame. The plot has also mask tools synchronized with the mask in the master window. """ -__author__ = "V.A. Sole - ESRF Data Analysis" -__contact__ = "sole@esrf.fr" -__license__ = "MIT" -__copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" +import logging from PyMca5 import StackPluginBase from PyMca5.PyMcaGui.pymca import StackBrowser from PyMca5.PyMcaGui import PyMca_Icons -DEBUG = 0 +_logger = logging.getLogger(__name__) + class StackBrowserPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {'Show':[self._showWidget, "Show Stack Image Browser", @@ -56,8 +59,7 @@ class StackBrowserPlugin(StackPluginBase.StackPluginBase): self.widget = None def stackUpdated(self): - if DEBUG: - print("StackBrowserPlugin.stackUpdated() called") + _logger.debug("StackBrowserPlugin.stackUpdated() called") if self.widget is None: return if self.widget.isHidden(): @@ -90,8 +92,7 @@ class StackBrowserPlugin(StackPluginBase.StackPluginBase): self.widget.setBackgroundImage(self._getBackgroundImage()) def mySlot(self, ddict): - if DEBUG: - print("mySlot ", ddict['event'], ddict.keys()) + _logger.debug("mySlot %s %s", ddict['event'], ddict.keys()) if ddict['event'] == "selectionMaskChanged": self.setStackSelectionMask(ddict['current']) elif ddict['event'] == "addImageClicked": diff --git a/PyMca5/PyMcaPlugins/StackNormalizationPlugin.py b/PyMca5/PyMcaPlugins/StackNormalizationPlugin.py index de9caae..3fe8c26 100644 --- a/PyMca5/PyMcaPlugins/StackNormalizationPlugin.py +++ b/PyMca5/PyMcaPlugins/StackNormalizationPlugin.py @@ -54,6 +54,7 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import numpy +import logging from PyMca5 import StackPluginBase # Add support for normalization by data from PyMca5.PyMca import PyMcaFileDialogs @@ -66,11 +67,13 @@ try: except: HDF5 = False -DEBUG = 0 +_logger = logging.getLogger(__name__) + class StackNormalizationPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {} text = "Stack/I0 where I0 is the active curve\n" @@ -160,7 +163,7 @@ class StackNormalizationPlugin(StackPluginBase.StackPluginBase): if edf.GetNumImages() > 1: # TODO: A dialog showing the different images # based on the external images browser needed - print("WARNING: Taking first image") + _logger.warning("WARNING: Taking first image") data = edf.GetData(0) edf = None elif ffilter.startswith("ASCII"): diff --git a/PyMca5/PyMcaPlugins/StackROIBatchPlugin.py b/PyMca5/PyMcaPlugins/StackROIBatchPlugin.py index 455abde..ac80c38 100644 --- a/PyMca5/PyMcaPlugins/StackROIBatchPlugin.py +++ b/PyMca5/PyMcaPlugins/StackROIBatchPlugin.py @@ -60,7 +60,7 @@ These plugins will be compatible with any stack window that provides the functio import sys import os import numpy -import time +import logging import traceback from PyMca5 import StackPluginBase from PyMca5.PyMcaCore import StackROIBatch @@ -71,11 +71,14 @@ from PyMca5.PyMcaGui import PyMca_Icons as PyMca_Icons from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMcaIO import ArraySave -DEBUG = 0 +_logger = logging.getLogger(__name__) + + class StackROIBatchPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {} function = self.calculate @@ -97,8 +100,7 @@ class StackROIBatchPlugin(StackPluginBase.StackPluginBase): self.thread = None def stackUpdated(self): - if DEBUG: - print("StackROIBatchPlugin.stackUpdated() called") + _logger.debug("StackROIBatchPlugin.stackUpdated() called") self._widget = None def selectionMaskUpdated(self): @@ -110,8 +112,7 @@ class StackROIBatchPlugin(StackPluginBase.StackPluginBase): self._widget.setSelectionMask(mask) def mySlot(self, ddict): - if DEBUG: - print("mySlot ", ddict['event'], ddict.keys()) + _logger.debug("mySlot %s %s", ddict['event'], ddict.keys()) if ddict['event'] == "selectionMaskChanged": self.setStackSelectionMask(ddict['current']) elif ddict['event'] == "addImageClicked": @@ -156,7 +157,7 @@ class StackROIBatchPlugin(StackPluginBase.StackPluginBase): self._widget = None if self.workerInstance is None: self.workerInstance = StackROIBatch.StackROIBatch() - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: self.thread = CalculationThread.CalculationThread(\ calculation_method=self.actualCalculation) self.thread.result = self.actualCalculation() @@ -228,13 +229,13 @@ class StackROIBatchPlugin(StackPluginBase.StackPluginBase): parameters = self.configurationWidget.getParameters() outputDir = parameters["output_dir"] if outputDir in [None, ""]: - if DEBUG: - print("Nothing to be saved") + _logger.debug("Nothing to be saved") + if _logger.getEffectiveLevel() == logging.DEBUG: return if parameters["file_root"] is None: fileRoot = "" else: - fileRoot = parameters["file_root"].replace(" ","") + fileRoot = parameters["file_root"].replace(" ", "") if fileRoot in [None, ""]: fileRoot = "images" if not os.path.exists(outputDir): diff --git a/PyMca5/PyMcaPlugins/StackScanWindowPlugin.py b/PyMca5/PyMcaPlugins/StackScanWindowPlugin.py index c898ab6..386cea9 100644 --- a/PyMca5/PyMcaPlugins/StackScanWindowPlugin.py +++ b/PyMca5/PyMcaPlugins/StackScanWindowPlugin.py @@ -34,15 +34,18 @@ __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" import numpy +import logging from PyMca5.PyMcaGui import ScanWindow from PyMca5 import StackPluginBase -DEBUG = 0 +_logger = logging.getLogger(__name__) + class StackScanWindowPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {} text = "Add active curve to plugin scan window\n" @@ -90,7 +93,7 @@ class StackScanWindowPlugin(StackPluginBase.StackPluginBase): x, y, legend, info = self.getActiveCurve() if self.widget is None: self.widget = ScanWindow.ScanWindow() - self.widget.addCurve(x, y, legend=legend, replot=True, replace=replace) + self.widget.addCurve(x, y, legend=legend, resetzoom=True, replace=replace) self.widget.show() self.widget.raise_() diff --git a/PyMca5/PyMcaPlugins/StackShowSpectra.py b/PyMca5/PyMcaPlugins/StackShowSpectra.py index b50dbab..6d46f82 100644 --- a/PyMca5/PyMcaPlugins/StackShowSpectra.py +++ b/PyMca5/PyMcaPlugins/StackShowSpectra.py @@ -35,12 +35,15 @@ __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" from PyMca5 import StackPluginBase from PyMca5.PyMca import ScanWindow import numpy +import logging + +_logger = logging.getLogger(__name__) -DEBUG = 0 class ShowSpectra(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {} function = self.showSpectra @@ -112,7 +115,7 @@ class ShowSpectra(StackPluginBase.StackPluginBase): if self.widget is None: self.widget = ScanWindow.ScanWindow() data = stack.data - replot = False + resetzoom = False if step in [None, 1]: for i in range(data.shape[0]): for j in range(data.shape[1]): @@ -120,7 +123,8 @@ class ShowSpectra(StackPluginBase.StackPluginBase): replace = True else: replace = False - self.widget.addCurve(x, data[i, j], legend="Row %03d Col %03d" % (i, j), replace=replace, replot=replot) + self.widget.addCurve(x, data[i, j], legend="Row %03d Col %03d" % (i, j), + replace=replace, resetzoom=resetzoom) else: counter = 0 for i in range(data.shape[0]): @@ -130,15 +134,16 @@ class ShowSpectra(StackPluginBase.StackPluginBase): replace = True else: replace = False - self.widget.addCurve(x, data[i, j], - legend="Row %03d Col %03d" % (i, j), - replace=replace, replot=replot) + self.widget.addCurve( + x, data[i, j], + legend="Row %03d Col %03d" % (i, j), + replace=replace, resetzoom=resetzoom) counter += 1 self.widget.resetZoom() self.widget.show() self.widget.raise_() -MENU_TEXT="Show Spectra" +MENU_TEXT = "Show Spectra" def getStackPluginInstance(plotWindow, **kw): ob = ShowSpectra(plotWindow) return ob diff --git a/PyMca5/PyMcaPlugins/XASPlugin.py b/PyMca5/PyMcaPlugins/XASPlugin.py index c1d049c..4f6c520 100644 --- a/PyMca5/PyMcaPlugins/XASPlugin.py +++ b/PyMca5/PyMcaPlugins/XASPlugin.py @@ -380,7 +380,7 @@ if __name__ == "__main__": import sys import os from PyMca5.PyMcaGui import PyMcaQt as qt - from PyMca5.PyMcaGui import PlotWindow + from PyMca5.PyMcaGui.pymca import ScanWindow from PyMca5.PyMcaIO import specfilewrapper as specfile from PyMca5.PyMcaDataDir import PYMCA_DATA_DIR if len(sys.argv) > 1: @@ -391,10 +391,11 @@ if __name__ == "__main__": energy = data[0, :] mu = data[1, :] app = qt.QApplication([]) - plot = PlotWindow.PlotWindow() - plot.setPluginDirectoryList([os.path.dirname(__file__)]) - plot.getPlugins() - plot.addCurve(energy, mu, os.path.basename(fileName)) + plot = ScanWindow.ScanWindow() + plot.pluginsToolButton.setPluginDirectoryList([os.path.dirname(__file__)]) + plot.pluginsToolButton.getPlugins() + plot.addCurve(energy, mu, os.path.basename(fileName), + resetzoom=True) plot.show() plugin = getPlugin1DInstance(plot) for method in plugin.getMethods(): diff --git a/PyMca5/PyMcaPlugins/XASStackBatchPlugin.py b/PyMca5/PyMcaPlugins/XASStackBatchPlugin.py index db9d11f..64c59ef 100644 --- a/PyMca5/PyMcaPlugins/XASStackBatchPlugin.py +++ b/PyMca5/PyMcaPlugins/XASStackBatchPlugin.py @@ -59,8 +59,7 @@ These plugins will be compatible with any stack window that provides the functio """ import sys import os -import numpy -import time +import logging import traceback from PyMca5 import StackPluginBase from PyMca5.PyMcaGui import CalculationThread @@ -71,11 +70,13 @@ from PyMca5.PyMcaGui import PyMca_Icons as PyMca_Icons from PyMca5.PyMcaGui import PyMcaQt as qt from PyMca5.PyMca import XASStackBatch -DEBUG = 0 +_logger = logging.getLogger(__name__) + class XASStackBatchPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {} function = self.calculate @@ -91,8 +92,7 @@ class XASStackBatchPlugin(StackPluginBase.StackPluginBase): self.thread = None def stackUpdated(self): - if DEBUG: - print("StackXASBatchPlugin.stackUpdated() called") + _logger.debug("StackXASBatchPlugin.stackUpdated() called") if self._widget is not None: self._widget.close() self._widget = None @@ -106,8 +106,7 @@ class XASStackBatchPlugin(StackPluginBase.StackPluginBase): self._widget.setSelectionMask(mask) def mySlot(self, ddict): - if DEBUG: - print("mySlot ", ddict['event'], ddict.keys()) + _logger.debug("mySlot %s %s", ddict['event'], ddict.keys()) if ddict['event'] == "selectionMaskChanged": self.setStackSelectionMask(ddict['current']) elif ddict['event'] == "addImageClicked": @@ -150,7 +149,7 @@ class XASStackBatchPlugin(StackPluginBase.StackPluginBase): def _executeFunctionAndParameters(self): self._parameters = self.configurationWidget.getParameters() self._widget = None - if DEBUG: + if _logger.getEffectiveLevel() == logging.DEBUG: self.thread = CalculationThread.CalculationThread(\ calculation_method=self.actualCalculation) self.thread.result = self.actualCalculation() diff --git a/PyMca5/PyMcaPlugins/XASStackNormalizationPlugin.py b/PyMca5/PyMcaPlugins/XASStackNormalizationPlugin.py index 05dbbf3..29ae7de 100644 --- a/PyMca5/PyMcaPlugins/XASStackNormalizationPlugin.py +++ b/PyMca5/PyMcaPlugins/XASStackNormalizationPlugin.py @@ -58,12 +58,15 @@ These plugins will be compatible with any stack window that provides the functio selectionMaskUpdated """ import numpy +import logging + +_logger = logging.getLogger(__name__) try: from PyMca5 import StackPluginBase from PyMca5.PyMcaGui import CalculationThread except ImportError: - print("XASStackNormalizationPlugin importing bases from somewhere else") + _logger.warning("XASStackNormalizationPlugin importing bases from somewhere else") from . import StackPluginBase from . import CalculationThread @@ -72,11 +75,11 @@ from PyMca5.PyMcaGui import StackPluginResultsWindow from PyMca5.PyMcaPhysics.xas import XASNormalization from PyMca5.PyMcaGui.physics.xas import XASNormalizationWindow -DEBUG = 0 class XASStackNormalizationPlugin(StackPluginBase.StackPluginBase): def __init__(self, stackWindow, **kw): - StackPluginBase.DEBUG = DEBUG + if _logger.getEffectiveLevel() == logging.DEBUG: + StackPluginBase.pluginBaseLogger.setLevel(logging.DEBUG) StackPluginBase.StackPluginBase.__init__(self, stackWindow, **kw) self.methodDict = {} text = "Replace current stack by a normalized one." @@ -126,8 +129,7 @@ class XASStackNormalizationPlugin(StackPluginBase.StackPluginBase): # own stuff def mySlot(self, ddict): - if DEBUG: - print("mySlot ", ddict['event'], ddict.keys()) + _logger.debug("mySlot %s %s", ddict['event'], ddict.keys()) if ddict['event'] == "selectionMaskChanged": self.setStackSelectionMask(ddict['current']) elif ddict['event'] == "addImageClicked": diff --git a/PyMca5/PyMcaPlugins/XMCDPlugin.py b/PyMca5/PyMcaPlugins/XMCDPlugin.py index dadf1a9..fd141af 100644 --- a/PyMca5/PyMcaPlugins/XMCDPlugin.py +++ b/PyMca5/PyMcaPlugins/XMCDPlugin.py @@ -32,8 +32,11 @@ from PyMca5 import Plugin1DBase from PyMca5.PyMcaGui.pymca import XMCDWindow from platform import node as gethostname +import logging + +_logger = logging.getLogger(__name__) + -DEBUG = 0 class XMCDAnalysis(Plugin1DBase.Plugin1DBase): def __init__(self, plotWindow, **kw): Plugin1DBase.Plugin1DBase.__init__(self, plotWindow, **kw) @@ -75,8 +78,7 @@ class XMCDAnalysis(Plugin1DBase.Plugin1DBase): if guess.startswith(hostname): beamline = 'ID08' break - if DEBUG: - print('_createWidget -- beamline = "%s"' % beamline) + _logger.debug('_createWidget -- beamline = "%s"', beamline) parent = None self.widget = XMCDWindow.XMCDWidget(parent, self._plotWindow, @@ -106,13 +108,13 @@ if __name__ == "__main__": info2 = {'MotorNames': 'PhaseD oxPS Motor10 Motor8', 'MotorValues': '2 0.44400576644 0.613870067852 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.) + 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='zratio2', info=info2, replot=False, replace=False) - swin.newCurve(x, y0, legend="Curve0", xlabel='ene_st0', ylabel='zratio0', info=info0, replot=False, replace=False) - swin.newCurve(x, y1, legend="Curve1", xlabel='ene_st1', ylabel='zratio1', info=info1, replot=False, replace=False) + swin.newCurve(x, y2, legend="Curve2", xlabel='ene_st2', ylabel='zratio2', info=info2) + swin.newCurve(x, y0, legend="Curve0", xlabel='ene_st0', ylabel='zratio0', info=info0) + swin.newCurve(x, y1, legend="Curve1", xlabel='ene_st1', ylabel='zratio1', info=info1) plugin = getPlugin1DInstance(swin) plugin.applyMethod(plugin.getMethods()[0]) diff --git a/PyMca5/PyMcaPlugins/optional/JsonRpc1DPlugin.py b/PyMca5/PyMcaPlugins/optional/JsonRpc1DPlugin.py index 974513d..7181d26 100644 --- a/PyMca5/PyMcaPlugins/optional/JsonRpc1DPlugin.py +++ b/PyMca5/PyMcaPlugins/optional/JsonRpc1DPlugin.py @@ -548,7 +548,7 @@ if __name__ == "__main__": import time import sys import os.path - from PyMca5.PyMcaGui.plotting.PlotWindow import PlotWindow + from PyMca5.PyMcaGui.pymca.ScanWindow import ScanWindow if len(sys.argv) == 1 or \ sys.argv[1] not in ('plugin', 'demoServer', 'demoClient', 'auto'): @@ -568,17 +568,17 @@ Options: plugin, demoServer demoClient, auto app = qt.QApplication([]) # Create plot window - plot = PlotWindow(roi=True) + plot = ScanWindow(roi=True) plot.show() # Load plugin pluginDir = os.path.dirname(os.path.abspath(__file__)) pluginName = os.path.splitext(os.path.basename(__file__))[0] - plot.setPluginDirectoryList([pluginDir]) - nbPlugins = plot.getPlugins() # Update plug-in list + plot.pluginsToolButton.setPluginDirectoryList([pluginDir]) + nbPlugins = plot.pluginsToolButton.getPlugins() # Update plug-in list assert nbPlugins >= 1 - plugin = plot.pluginInstanceDict[pluginName] + plugin = plot.pluginsToolButton.pluginInstanceDict[pluginName] if sys.argv[1] == 'auto': # Run automated demos diff --git a/PyMca5/PyMcaPlugins/optional/TaurusPlugin1D.py b/PyMca5/PyMcaPlugins/optional/TaurusPlugin1D.py index c2cd53d..491e794 100644 --- a/PyMca5/PyMcaPlugins/optional/TaurusPlugin1D.py +++ b/PyMca5/PyMcaPlugins/optional/TaurusPlugin1D.py @@ -1,5 +1,5 @@ #/*########################################################################## -# Copyright (C) 2004-2015 V.A. Sole, T. Coutinho, European Synchrotron Radiation Facility +# Copyright (C) 2004-2018 V.A. Sole, T. Coutinho, European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -37,10 +37,11 @@ __doc__ = """ You can also run it as a stand alone script. """ import numpy -from PyMca5 import Plugin1DBase +from PyMca5.PyMcaCore import Plugin1DBase from PyMca5.PyMcaGui import PyMcaQt as qt Qt = qt from taurus import Attribute +from taurus import Release from taurus.core import TaurusEventType from taurus.qt.qtcore.taurusqlistener import QObjectTaurusListener from taurus.qt.qtgui.panel import TaurusModelChooser @@ -75,6 +76,7 @@ class TaurusPlugin1D(Plugin1DBase.Plugin1DBase, QObjectTaurusListener): def onSelectionChanged(self, models): if self._oldModels in [None, []]: + self._attrDict = {} for model in models: try: attr = Attribute(model) @@ -83,6 +85,8 @@ class TaurusPlugin1D(Plugin1DBase.Plugin1DBase, QObjectTaurusListener): attr = Attribute(str(model)) #force a read -> attr.read() attr.addListener(self) + legend = qt.safe_str(attr.getNormalName()) + self._attrDict[legend] = attr self._oldModels = models else: keptModels = [] @@ -93,16 +97,20 @@ class TaurusPlugin1D(Plugin1DBase.Plugin1DBase, QObjectTaurusListener): else: newModels.append(model) for model in self._oldModels: - if model not in newModels: + if model not in keptModels: attr = Attribute(model) attr.removeListener(self) - legend = attr.getNormalName() + legend = qt.safe_str(attr.getNormalName()) + if legend in self._attrDict: + del self._attrDict[legend] print("Trying to remove ", legend) self.removeCurve(legend, replot=False) for model in newModels: attr = Attribute(model) # attr.read() attr.addListener(self) + legend = qt.safe_str(attr.getNormalName()) + self._attrDict[legend] = attr self._oldModels = keptModels + newModels #Methods to be implemented by the plugin @@ -145,9 +153,12 @@ class TaurusPlugin1D(Plugin1DBase.Plugin1DBase, QObjectTaurusListener): if self._widget is None: self._widget = TaurusModelChooser() #self._adapter = TaurusPyMcaAdapter() - Qt.QObject.connect(self._widget, - Qt.SIGNAL("updateModels"), - self.onSelectionChanged) + if Release.version_info >= (4,): + self._widget.updateModels.connect(self.onSelectionChanged) + else: + Qt.QObject.connect(self._widget, + Qt.SIGNAL("updateModels"), + self.onSelectionChanged) self._widget.show() MENU_TEXT = "Taurus Device Browser" @@ -160,7 +171,7 @@ if __name__ == "__main__": import os from PyMca5.PyMcaGui import ScanWindow plot = ScanWindow.ScanWindow() - plot.setPluginDirectoryList([os.path.dirname(__file__)]) - plot.getPlugins() + plot.pluginsToolButton.setPluginDirectoryList([os.path.dirname(os.path.abspath(__file__))]) + plot.pluginsToolButton.getPlugins() plot.show() app.exec_() diff --git a/PyMca5/__init__.py b/PyMca5/__init__.py index 9c131c6..16b2fbc 100644 --- a/PyMca5/__init__.py +++ b/PyMca5/__init__.py @@ -27,17 +27,21 @@ __author__ = "V.A. Sole - ESRF Data Analysis" __contact__ = "sole@esrf.fr" __license__ = "MIT" __copyright__ = "European Synchrotron Radiation Facility, Grenoble, France" -__version__ = "5.3.2" +__version__ = "5.4.0" import os import sys + from PyMca5.PyMcaDataDir import PYMCA_DATA_DIR try: from fisx.DataDir import FISX_DATA_DIR except ImportError: FISX_DATA_DIR = None -DEBUG = 0 +import logging as _logging +_logging.getLogger(__name__).addHandler(_logging.NullHandler()) +_logger = _logging.getLogger(__name__) + if sys.platform.startswith("win"): import ctypes @@ -123,7 +127,7 @@ def getDefaultUserPluginsDirectory(): else: return None except: - print("WARNING: Cannot initialize plugins directory") + _logger.info("WARNING: Cannot initialize plugins directory") return None def getDefaultUserFitFunctionsDirectory(): @@ -157,7 +161,7 @@ def getUserDataFile(fileName, directory=""): if not os.path.exists(userDataDir): os.mkdir(userDataDir) except: - print("WARNING: cannot initialize user data directory") + _logger.info("WARNING: cannot initialize user data directory") if userDataDir is None: return fileName @@ -168,12 +172,10 @@ def getUserDataFile(fileName, directory=""): else: userDataFile = os.path.join(userDataDir, baseName) if os.path.exists(userDataFile): - if DEBUG: - print("Using user data file: %s" % userDataFile) + _logger.debug("Using user data file: %s", userDataFile) return userDataFile else: - if DEBUG: - print("Using data file: %s" % fileName) + _logger.debug("Using data file: %s", fileName) return fileName def getDataFile(fileName, directory=None): @@ -188,8 +190,7 @@ def getDataFile(fileName, directory=None): # return the input file name if exists if os.path.exists(fileName): - if DEBUG: - print("Filename as supplied <%s>" % fileName) + _logger.debug("Filename as supplied <%s>", fileName) return fileName # the list of sub-directories where to look for the file @@ -202,8 +203,7 @@ def getDataFile(fileName, directory=None): for subdirectory in directoryList: newFileName = getUserDataFile(fileName, directory=subdirectory) if os.path.exists(newFileName): - if DEBUG: - print("Filename from user <%s>" % newFileName) + _logger.debug("Filename from user <%s>", newFileName) return newFileName # PyMca @@ -212,8 +212,7 @@ def getDataFile(fileName, directory=None): subdirectory, os.path.basename(fileName)) if os.path.exists(newFileName): - if DEBUG: - print("Filename from PyMca Data Directory <%s>" % newFileName) + _logger.debug("Filename from PyMca Data Directory <%s>", newFileName) return newFileName # fisx @@ -223,9 +222,8 @@ def getDataFile(fileName, directory=None): subdirectory, os.path.basename(fileName)) if os.path.exists(newFileName): - if DEBUG: - print("Filename from fisx Data Directory <%s>" % \ - newFileName) + _logger.debug("Filename from fisx Data Directory <%s>", + newFileName) return newFileName # file not found @@ -241,7 +239,7 @@ if sys.platform.startswith("win"): if os.getenv("MPLCONFIGDIR") is None: os.environ['MPLCONFIGDIR'] = getDefaultSettingsDirectory() except: - print("WARNING: Could not set MPLCONFIGDIR.", sys.exc_info()[1]) + _logger.info("WARNING: Could not set MPLCONFIGDIR. %s", sys.exc_info()[1]) # mandatory modules for backwards compatibility from .PyMcaCore import Plugin1DBase, StackPluginBase, PyMcaDirs, DataObject @@ -251,7 +249,7 @@ from .PyMcaCore import Plugin1DBase, StackPluginBase, PyMcaDirs, DataObject try: from .PyMcaIO import specfilewrapper, EdfFile, specfile, ConfigDict except: - print("WARNING importing IO directly") + _logger.info("WARNING importing IO directly") from PyMcaIO import specfilewrapper, EdfFile, specfile, ConfigDict from .PyMcaMath.fitting import SpecfitFuns, Gefit, Specfit diff --git a/PyMca5/tests/ElementsTest.py b/PyMca5/tests/ElementsTest.py index d518638..0bab872 100644 --- a/PyMca5/tests/ElementsTest.py +++ b/PyMca5/tests/ElementsTest.py @@ -2,7 +2,7 @@ # # The PyMca X-Ray Fluorescence Toolkit # -# Copyright (c) 2004-2014 European Synchrotron Radiation Facility +# Copyright (c) 2004-2018 European Synchrotron Radiation Facility # # This file is part of the PyMca X-ray Fluorescence Toolkit developed at # the ESRF by the Software group. @@ -295,6 +295,19 @@ class testElements(unittest.TestCase): self.assertTrue((100.0 * abs(yTest-yRef)/yRef) < 0.01) energyIndex += 1 + def testMaterialCompositionCalculation(self): + if DEBUG: + print() + print("Testing Material Composition Calculation (issue #298)") + mat1 = "Kapton" + mat2 = "Mylar" + + c1 = self._elements.getMaterialMassFractions([mat1, mat2], [0.5, 0.5]) + c2 = self._elements.getMaterialMassFractions([mat2, mat1], [0.5, 0.5]) + + for key in c1: + self.assertTrue(abs(c1[key] - c2[key]) < 1.0e-7, + "Inconsistent calculation for element %s" % key) def getSuite(auto=True): testSuite = unittest.TestSuite() @@ -307,6 +320,7 @@ def getSuite(auto=True): testSuite.addTest(testElements("testElementCrossSectionsReadout")) testSuite.addTest(testElements("testElementCrossSectionsCalculation")) testSuite.addTest(testElements("testMaterialCrossSectionsCalculation")) + testSuite.addTest(testElements("testMaterialCompositionCalculation")) return testSuite def test(auto=False): diff --git a/PyMca5/tests/WidgetsTest.py b/PyMca5/tests/WidgetsTest.py new file mode 100644 index 0000000..18b658f --- /dev/null +++ b/PyMca5/tests/WidgetsTest.py @@ -0,0 +1,111 @@ +#/*########################################################################## +# Copyright (C) 2004-2018 European Synchrotron Radiation Facility +# +# This file is part of the PyMca X-ray Fluorescence Toolkit developed at +# the ESRF by the Software group. +# +# 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. +# +#############################################################################*/ + +import logging +import os +import sys +import unittest +import PyMca5.PyMcaGui.PyMcaQt as qt +from silx.gui.test.utils import TestCaseQt + +from PyMca5.PyMcaGui.pymca import ScanWindow +from PyMca5.PyMcaGui.pymca import McaWindow +from PyMca5.PyMcaGui.physics.xrf import McaAdvancedFit + + +_logger = logging.getLogger(__name__) + + +class TestQtWrapper(unittest.TestCase): + """Minimalistic test to check that Qt has been loaded.""" + + def testQObject(self): + """Test that QObject is there.""" + obj = qt.QObject() + self.assertTrue(obj is not None) + + +class TestScanWindow(TestCaseQt): + def setUp(self): + super(TestScanWindow, self).setUp() + + def testShow(self): + widget = ScanWindow.ScanWindow() + widget.show() + self.qapp.processEvents() + + +class TestMcaWindow(TestCaseQt): + def setUp(self): + super(TestMcaWindow, self).setUp() + + def testShow(self): + widget = McaWindow.McaWindow() + widget.show() + self.qapp.processEvents() + + +class TestMcaAdvancedFit(TestCaseQt): + def setUp(self): + super(TestMcaAdvancedFit, self).setUp() + + def testShow(self): + widget = McaAdvancedFit.McaAdvancedFit() + widget.show() + self.qapp.processEvents() + + +def getSuite(auto=True): + test_suite = unittest.TestSuite() + + with_qt_test = True + skip_msg = "" + if sys.platform.startswith('linux') and not os.environ.get('DISPLAY', ''): + # On Linux and no DISPLAY available (e.g., ssh without -X) + skip_msg = 'Widgets tests disabled (DISPLAY env. variable not set)' + with_qt_test = False + + elif os.environ.get('WITH_QT_TEST', 'True') == 'False': + skip_msg = "Widgets tests skipped by WITH_QT_TEST env var" + with_qt_test = False + + if not with_qt_test: + class SkipGUITest(unittest.TestCase): + def runTest(self): + self.skipTest( + skip_msg) + test_suite.addTest(SkipGUITest()) + return test_suite + + for TestCaseCls in (TestQtWrapper, TestScanWindow, + TestMcaWindow, TestMcaAdvancedFit): + test_suite.addTest( + unittest.defaultTestLoader.loadTestsFromTestCase(TestCaseCls)) + return test_suite + + +if __name__ == '__main__': + unittest.main(defaultTest='getSuite') diff --git a/changelog.txt b/changelog.txt index 2ff9a9d..0e68f5b 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,22 @@ +VERSION 5.4.0 +------------- + +- Add training exercises to the tutorials + +- Support quantification accounting for live time when using HDF5 files + +- Add higher order excitations example to the training data + +- Use silx toolkit for graphics + +- Allow the user to select a particular Qt binding (--binding option, default is PyQt5) + +- Implement a user selectable logging level (--logging option, default is warning) + +- Correct handling of repeated elements in the sample matrix + +- Correct readout of lispix data + VERSION 5.3.2 ------------- diff --git a/cx_setup.py b/cx_setup.py index 7852c38..8e43d06 100644 --- a/cx_setup.py +++ b/cx_setup.py @@ -145,8 +145,7 @@ MATPLOTLIB = True try: import pyopencl OPENCL = True - from PyMca5.PyMcaMath import sift -except : +except: OPENCL = False if sys.platform.lower().startswith("linux"): @@ -170,7 +169,7 @@ elif h5py.version.version < '2.0.0': 'h5py._conv', 'h5py._proxy'] else: H5PY_SPECIAL = True -includes = [] + includes = [] import fisx FISX = True @@ -182,14 +181,16 @@ includes.append('encodings.latin_1') import PyMca5 import hdf5plugin import silx +import pkg_resources SILX = True special_modules = [os.path.dirname(PyMca5.__file__), - os.path.dirname(matplotlib.__file__), + os.path.dirname(matplotlib.__file__), os.path.dirname(ctypes.__file__), os.path.dirname(fisx.__file__), os.path.dirname(hdf5plugin.__file__), - os.path.dirname(silx.__file__)] + os.path.dirname(silx.__file__), + os.path.dirname(pkg_resources.__file__)] try: import tomogui diff --git a/doc/man/pymca.1 b/doc/man/pymca.1 index cfebe58..d055597 100644 --- a/doc/man/pymca.1 +++ b/doc/man/pymca.1 @@ -3,7 +3,7 @@ .\" -.TH pymca 1 "March 2012" "ESRF" "PyMca X-Ray Fluorescence Toolkit" +.TH pymca 1 "September 2018" "ESRF" "PyMca X-Ray Fluorescence Toolkit" .SH NAME @@ -50,7 +50,12 @@ Start the program using the selected graphics backend (mpl for matplotlib, gl fo i .B pymca --binding=XX .P -Start the program using the selected Qt binding. It has ot be one of PyQt5, PyQt4, PySide or PySide2. +Start the program using the selected Qt binding. It has to be one of PyQt5 (default), PyQt4, PySide or PySide2. +i +.B pymca --logging=XX +.P +Set the logging level. Allowed values are, in increasing order of verbosity: critical, error, warning (default), info, debug. +Alternatively, you can specify an integer in range 0 (critical) to 4 (debug). .SH SEE ALSO HDF5, h5py diff --git a/doc/man/pymcaroitool.1 b/doc/man/pymcaroitool.1 index c7d9ec6..7f9d17d 100644 --- a/doc/man/pymcaroitool.1 +++ b/doc/man/pymcaroitool.1 @@ -43,7 +43,7 @@ Start the program with a file browser to select the input files. .B pymcaroitool --binding=XX .P -Start the program using the selected Qt binding. It has ot be one of PyQt5, PyQt4, PySide or PySide2. +Start the program using the selected Qt binding. It has to be one of PyQt5 (default), PyQt4, PySide or PySide2. .B pymcaroitool file_0001.edf .P @@ -69,6 +69,13 @@ file_00200.edf Load the double indexed files from row10_col0100.dat, row10_col0101.dat, ... to row20_col0199.dat, row20_col0200.dat +.B pymcaroitool --logging=XX +.P +Set the logging level. Allowed values are, in increasing order of verbosity: critical, error, warning (default), info, debug. +Alternatively, you can specify a value in range 0 (critical) to 4 (debug). + + + .SH CAVEATS If files f_000.xxx and f_001.xxx are present in the same directory, and only one of them is selected, the program will always try to load both of diff --git a/doc/source/customization/index.rst b/doc/source/customization/index.rst index 02b2707..20a3a41 100644 --- a/doc/source/customization/index.rst +++ b/doc/source/customization/index.rst @@ -5,5 +5,5 @@ Customizing PyMca .. toctree:: :maxdepth: 3 - settings + settings/index.rst plugins/index.rst diff --git a/doc/source/customization/plugins/plugins1d.rst b/doc/source/customization/plugins/plugins1d.rst index 77bb7b9..14bd217 100644 --- a/doc/source/customization/plugins/plugins1d.rst +++ b/doc/source/customization/plugins/plugins1d.rst @@ -23,8 +23,8 @@ Overview .. autofunction:: getPlugin1DInstance -Builtin 1D plugins -****************** +Built-in 1D plugins +******************* Background subtraction tools ++++++++++++++++++++++++++++ @@ -50,11 +50,15 @@ Alignment plugin .. automodule:: PyMca5.PyMcaPlugins.AlignmentScanPlugin +Advanced alignment plugin ++++++++++++++++++++++++++ + +.. automodule:: PyMca5.PyMcaPlugins.AdvancedAlignmentScanPlugin + .. TODO: Kinetic tools .. TODO: XASNormalization -.. TODO: AdvancedAlignmentScanPlugin (see .html file in PYMCA_DOC_DIR) Remove glitches from curves +++++++++++++++++++++++++++ diff --git a/doc/source/customization/plugins/stackplugins.rst b/doc/source/customization/plugins/stackplugins.rst index b9cc9fc..6bbd8fe 100644 --- a/doc/source/customization/plugins/stackplugins.rst +++ b/doc/source/customization/plugins/stackplugins.rst @@ -23,8 +23,8 @@ Stack plugin API .. autofunction:: getStackPluginInstance -Builtin stack plugins -********************* +Built-in stack plugins +********************** Alternative ROI options +++++++++++++++++++++++ diff --git a/doc/source/customization/settings.rst b/doc/source/customization/settings.rst deleted file mode 100644 index d39314a..0000000 --- a/doc/source/customization/settings.rst +++ /dev/null @@ -1,4 +0,0 @@ - -Settings --------- - diff --git a/doc/source/customization/settings/img/settings_01.png b/doc/source/customization/settings/img/settings_01.png Binary files differnew file mode 100644 index 0000000..027859a --- /dev/null +++ b/doc/source/customization/settings/img/settings_01.png diff --git a/doc/source/customization/settings/img/settings_02.png b/doc/source/customization/settings/img/settings_02.png Binary files differnew file mode 100644 index 0000000..9cf15f1 --- /dev/null +++ b/doc/source/customization/settings/img/settings_02.png diff --git a/doc/source/customization/settings/index.rst b/doc/source/customization/settings/index.rst new file mode 100644 index 0000000..4f1302f --- /dev/null +++ b/doc/source/customization/settings/index.rst @@ -0,0 +1,71 @@ +Settings +======== + +.. |img_01| image:: ./img/settings_01.png + :align: middle + :alt: Settings Directory + +.. |img_02| image:: ./img/settings_02.png + :align: middle + :alt: File mneu + +.. contents:: + :local: + +PyMca allows a certain level of customization via user settings. + +Settings Directory +------------------ + +The first time *PyMca* is started, it creates a user accessible settings directory to allow user customization up to a certain extent. + +The location and name of this settings directory is different following the operation system. + +The typical layout of the directory is shown below + +|img_01| + +Windows +....... + +The name of the folder is PyMca and it is located in the Documents folder of the user. The idea is that this folder should be easily accessible by the user and this location seems preferable to the use of a hidden folder. + +MacOS, Linux,... +................ + +The settings directory is created in the user $HOME directory. + +The name of the directory was PyMca in older versions of the program. Recent versionstry to use the more standard way of using a .pymca directory. Nevertheless, if they find a PyMca directory at he $HOME level, they will keep using it. + + +GUI settings +------------ + +Graphical user interfaces are nice to start but sometimes they require a lot of interaction from the user. + +In an attempt to minimize the amount of user interaction, *PyMca* allows to save some settings like main window geometry, open files, last used directory, fit configuration, ROI table configuration... + + +|img_02| + + +This feature is accessible from the File menu either by choosing *File->Save default settings* or *File->Save->PyMca Configuration* This creates a .ini file in the user accessible settings folder of PyMca created when first using the program. + +The PyMca.ini file contains the default settings used by the application on start up. They can be bypassed by a fresh start of PyMca (typing *pymca -f* from the command line or selecting "*PyMca Fresh Start* from the Windows start menu). + +XRF Database +------------ + +The subdirectory data allows the user to modify the data used by *PyMca* when performing X-ray fluorescence calculations. It is enough to copy to this directory any of the original ASCII files contained in the `fisx_data <https://github.com/vasole/fisx/tree/master/fisx_data>`_ directory of the fisx module to force the program to use that file. The user can then proceed to edit the file and PyMca will use the modified file the next time is started. + +If you are interested in modifying the data used by *PyMca* the is an `exercise <../../training/xraydata/index.html>`_ to teach you how to proceed. + +CAUTION: At this point it is not advisable to modify the EADL97_* or the XCOM_CrossSections.dat files. + +CAUTION: Those files use unix line endings (LF) and not windows line endings (CR/LF). If you are under windows you have to make sure you do not use an editor modifying line endings. Convenient and free editors for windows are `Notepad++ <https://notepad-plus-plus.org>`_ or `Vim for windows <https://www.vim.org>`_ + +User Plugins +------------ + +The subdirectory plugins contains the user plugins to be used in the application. Please refer to the plugins documentation for details. + diff --git a/doc/source/index.rst b/doc/source/index.rst index 4f1ce15..8ad93f2 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -14,7 +14,7 @@ The proper way to cite PyMca is: V.A. Sole, E. Papillon, M. Cotte, Ph. Walter, J. Susini, A multiplatform code for the analysis of energy-dispersive X-ray fluorescence spectra, Spectrochim. Acta Part B 62 (2007) 63-68. -Due to the copyright transfer to the publisher, the online availability of the article will depend on your subscription to ScienceDirect. The article doi is 10.1016/j.sab.2006.12.002. The abstract should be available in any case. +Due to the copyright transfer to the publisher, the online availability of the article will depend on your subscription to ScienceDirect. The article `doi is 10.1016/j.sab.2006.12.002 <https://www.doi.org/10.1016/j.sab.2006.12.002>`_. The abstract should be available in any case. The current version features: @@ -36,6 +36,7 @@ Table of contents overview.rst install.rst tutorials.rst + recipes.rst changelog.rst license.rst faq.rst diff --git a/doc/source/license.rst b/doc/source/license.rst index b7261eb..a2b8857 100644 --- a/doc/source/license.rst +++ b/doc/source/license.rst @@ -1,7 +1,7 @@ License ======= -The source code of *silx* is licensed under the `MIT <https://opensource.org/licenses/MIT>`_ license: +The source code of *PyMca* is licensed under the `MIT <https://opensource.org/licenses/MIT>`_ license: .. include:: ../../LICENSE diff --git a/doc/source/recipes.rst b/doc/source/recipes.rst new file mode 100644 index 0000000..5f887b6 --- /dev/null +++ b/doc/source/recipes.rst @@ -0,0 +1,7 @@ +Recipes +======= + +.. toctree:: + + HOWTO Embed XRF fitting in a PyQt5 application <./recipes/xrfembedpyqt.rst> + HOWTO Write a stack of XRF spectra in HDF5 <./recipes/xrfhdf5stack.rst> diff --git a/doc/source/recipes/recipescode/GenerateHDF5Stack.py b/doc/source/recipes/recipescode/GenerateHDF5Stack.py new file mode 100644 index 0000000..6834f48 --- /dev/null +++ b/doc/source/recipes/recipescode/GenerateHDF5Stack.py @@ -0,0 +1,105 @@ +#/*########################################################################## +# +# Copyright (c) 2018 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__ = """ +Script writing a stack of XRF data with calibration and live_time information +""" + +import os +import numpy +import h5py + +# use a dummy 3D array generated using data supplied with PyMca +from PyMca5 import PyMcaDataDir +from PyMca5.PyMcaIO import specfilewrapper as specfile +from PyMca5.PyMcaIO import ConfigDict + +dataDir = PyMcaDataDir.PYMCA_DATA_DIR +spe = os.path.join(dataDir, "Steel.spe") +cfg = os.path.join(dataDir, "Steel.cfg") +sf = specfile.Specfile(spe) +y = counts = sf[0].mca(1) +x = channels = numpy.arange(y.size).astype(numpy.float) +configuration = ConfigDict.ConfigDict() +configuration.read(cfg) +calibration = configuration["detector"]["zero"], \ + configuration["detector"]["gain"], 0.0 +initialTime = configuration["concentrations"]["time"] + +# create the data +nRows = 5 +nColumns = 10 +nTimes = 3 +data = numpy.zeros((nRows, nColumns, counts.size), dtype = numpy.float) +live_time = numpy.zeros((nRows * nColumns), dtype=numpy.float) + +mcaIndex = 0 +for i in range(nRows): + for j in range(nColumns): + factor = (1 + mcaIndex % nTimes) + data[i, j] = counts * factor + live_time[i * nColumns + j] = initialTime * factor + mcaIndex += 1 + +# now we have a 3D array containing the spectra in data (mandatory) +# we have the channels (not mandatory) +# we have the associated calibration (not mandatory) +# we have the live_time (not mandatory) +# and we are going to create an HDF5 with that information +# +# Just writing those data as a dataset in an HDF5 file would be enough for +# using it in PyMca, but we can create a container group in order to associate +# additional information (channels, live_time, calibration) +# "instrument" can be replaced by, for instance, the beamline name +# "detector" can be replaced by, for instance, "mca_0" +# +h5File = "Steel.h5" +if os.path.exists(h5File): + os.remove(h5File) +h5 = h5py.File(h5File, "w") +h5["/entry/instrument/detector/calibration"] = calibration +h5["/entry/instrument/detector/channels"] = channels +h5["/entry/instrument/detector/data"] = data +h5["/entry/instrument/detector/live_time"] = live_time + +# add nexus conventions (not needed) +h5["/entry/title"] = u"Dummy generated map" +h5["/entry"].attrs["NX_class"] = u"NXentry" +h5["/entry/instrument"].attrs["NX_class"] = u"NXinstrument" +h5["/entry/instrument/detector/"].attrs["NX_class"] = u"NXdetector" +h5["/entry/instrument/detector/data"].attrs["interpretation"] = \ + u"spectrum" +# implement a default plot named measurement (not needed) +h5["/entry/measurement/data"] = \ + h5py.SoftLink("/entry/instrument/detector/data") +h5["/entry/measurement"].attrs["NX_class"] = u"NXdata" +h5["/entry/measurement"].attrs["signal"] = u"data" +h5["/entry"].attrs["default"] = u"measurement" + +h5.flush() +h5.close() +h5 = None diff --git a/doc/source/recipes/xrfembedpyqt.rst b/doc/source/recipes/xrfembedpyqt.rst new file mode 100644 index 0000000..ddc5166 --- /dev/null +++ b/doc/source/recipes/xrfembedpyqt.rst @@ -0,0 +1,15 @@ +Embedding PyMca XRF fitting +=========================== + +Besides providing ready-to-use applications, PyMca is very modular and it allows to be used as a library. + +Let's say you have your own way of displaying your data into a PyQt5 (or PyQt4, PySide or PySide2) +application. All you need to do to provide XRF fitting capabilities to it requires 4 lines of code. + +.. code-block:: python + + from PyMca5.PyMca import McaAdvancedFit + widget = McaAdvancedFit.McaAdvancedFit() + widget.setData(channels, counts) + widget.show() + diff --git a/doc/source/recipes/xrfhdf5stack.rst b/doc/source/recipes/xrfhdf5stack.rst new file mode 100644 index 0000000..8b7d512 --- /dev/null +++ b/doc/source/recipes/xrfhdf5stack.rst @@ -0,0 +1,87 @@ +HDF5 XRF Stack +============== + +There is a recurrent cuestion concerning how one should write the spectra associated to a raster +experiment to be compatible with PyMca. + +The solution is not unique, because PyMca can deal with (too) many data formats. For instance, if +you have a map of 20000 spectra corresponding to a map of 100 rows per 200 columns, 20000 single-column +ASCII files containing the measured counts would do the job. You would be "compatible" with PyMca but +you would be missing relevant information known at acquisition time like live_time and calibration +parameters. + +The most versatile file format supported by PyMca is without doubt HDF5. You can find information about it +at the `HDF Group web site <https://portal.hdfgroup.org/display/HDF5/HDF5>`_ + +Let's assume data is a 3-dimensional array or 20000 spectra corresponding to a raster scan of 100 rows per +200 columns. If each spectrum has 2048 channels, the shape of that array will be (following C-convention) +(100, 200, 2048). The simplest HDF5 file compatible with PyMca would contain a single 3-dimensional dataset +and it could be written using the code snipset shown below. + +.. code-block:: python + + import h5py + h5 = h5py.File("myfile.h5", "w") + h5["data"] = data + h5.flush() + h5.close() + +Obviously, besides a faster readout of the data by PyMca, one would not gain any information compared to +the use of single-column ASCII files. + +PyMca will automatically look for information associated to a dataset provided that information is +stored within the same container group in the file. + +If live_time is a one dimensional dataset with 20000 values corresponding to the actual measuring time +associated to each spectrum, the simplest way to allow PyMca to use that information is to put it at +the same level within the same group. + +If the channels associated to the data are different from 0,1,2,3, ..., 2046, 2047, they can be specified by +a one dimensional dataset named channels. + +The calibration can be specified as a dataset containing three values (corresponding to a, b and c in the +expression energy = a + b * ch + c * ch^2 and named calibration. + +.. code-block:: python + + import h5py + h5 = h5py.File("myfile.h5", "w") + h5["/mca_0/data"] = data + h5["/mca_0/channels"] = channels + h5["/mca_0/calibration"] = calibration + h5["/mca_0/live_time"] = live_time + h5.flush() + h5.close() + +Additional conventions can be applied to improve the user experience when using the PyMca graphical user +interface. + +The code below writes an HDF5 following NeXus conventions. Those conventions are attribute based, therefore +the actual names of the different groups are free. You can :download:`download a script <./recipescode/GenerateHDF5Stack.py>` generating a file using these +conventions. + +.. code-block:: python + + h5File = "myfile.h5" + if os.path.exists(h5File): + os.remove(h5File) + h5 = h5py.File(h5File, "w") + h5["/entry/instrument/detector/calibration"] = calibration + h5["/entry/instrument/detector/channels"] = channels + h5["/entry/instrument/detector/data"] = data + h5["/entry/instrument/detector/live_time"] = live_time + + # add nexus conventions (not needed) + h5["/entry/title"] = u"Dummy generated map" + h5["/entry"].attrs["NX_class"] = u"NXentry" + h5["/entry/instrument"].attrs["NX_class"] = u"NXinstrument" + h5["/entry/instrument/detector/"].attrs["NX_class"] = u"NXdetector" + h5["/entry/instrument/detector/data"].attrs["interpretation"] = u"spectrum" + # implement a default plot named measurement (not needed) + h5["/entry/measurement/data"] = h5py.SoftLink("/entry/instrument/detector/data") + h5["/entry/measurement"].attrs["NX_class"] = u"NXdata" + h5["/entry/measurement"].attrs["signal"] = u"data" + h5["/entry"].attrs["default"] = u"measurement" + + h5.flush() + h5.close() diff --git a/doc/source/training/matrix/img/matrix_01.png b/doc/source/training/matrix/img/matrix_01.png Binary files differnew file mode 100644 index 0000000..c84eec6 --- /dev/null +++ b/doc/source/training/matrix/img/matrix_01.png diff --git a/doc/source/training/matrix/img/matrix_02.png b/doc/source/training/matrix/img/matrix_02.png Binary files differnew file mode 100644 index 0000000..81a26e9 --- /dev/null +++ b/doc/source/training/matrix/img/matrix_02.png diff --git a/doc/source/training/matrix/img/matrix_03.png b/doc/source/training/matrix/img/matrix_03.png Binary files differnew file mode 100644 index 0000000..3b5e147 --- /dev/null +++ b/doc/source/training/matrix/img/matrix_03.png diff --git a/doc/source/training/matrix/img/matrix_04.png b/doc/source/training/matrix/img/matrix_04.png Binary files differnew file mode 100644 index 0000000..ec2268f --- /dev/null +++ b/doc/source/training/matrix/img/matrix_04.png diff --git a/doc/source/training/matrix/img/tertiary_01.png b/doc/source/training/matrix/img/tertiary_01.png Binary files differnew file mode 100644 index 0000000..fbf88d8 --- /dev/null +++ b/doc/source/training/matrix/img/tertiary_01.png diff --git a/doc/source/training/matrix/img/tertiary_03.png b/doc/source/training/matrix/img/tertiary_03.png Binary files differnew file mode 100644 index 0000000..5325dfc --- /dev/null +++ b/doc/source/training/matrix/img/tertiary_03.png diff --git a/doc/source/training/matrix/img/tertiary_04.png b/doc/source/training/matrix/img/tertiary_04.png Binary files differnew file mode 100644 index 0000000..69af2c7 --- /dev/null +++ b/doc/source/training/matrix/img/tertiary_04.png diff --git a/doc/source/training/matrix/index.rst b/doc/source/training/matrix/index.rst new file mode 100644 index 0000000..c84732f --- /dev/null +++ b/doc/source/training/matrix/index.rst @@ -0,0 +1,83 @@ +Automatic Sample Matrix Refinement +================================== + + +.. |img1| image:: ./img/matrix_01.png + :width: 400px + :align: middle + :alt: Fitted Steel Data + +.. |img2| image:: ./img/matrix_02.png + :width: 400px + :align: middle + :alt: The Single Layer Strategy Window + +.. |img3| image:: ./img/matrix_03.png + :width: 400px + :align: middle + :alt: Strategy Configured + +.. |img4| image:: ./img/matrix_04.png + :align: middle + :alt: Last Composition + +.. contents:: + :local: + +Introduction +------------ + +The characterization of the experimental setup (excitation beam, filters, attenuators, detector, geometry) and theoretical peak ratios are not sufficient to appropriately quantify X-ray fluorescence spectra. Unless we are dealing with very thin samples (that means samples where self-attenuation or multiple excitation effects can be neglected), the sample itself plays a critical role. However, the sample composition is exactly what we want to know. + +Versions of PyMca older than 5.0 required the user to manually modify the initial guess of the sample composition if the obtained composition was very different from the initial guess. + +Exercise +-------- + +The goal of the exercise it to show the user one way to instruct the program to automatically update the initial sample composition guess. + + +Preparation +........... + +This starting point of this exercise is the same as the one of the previous exercise `Accounting for higher order excitations <../tertiary/index.html>`_ + +In that exercise the reader was able to properly account for multiple order excitations in the sample. However, the actual composition of the sample was provided. A more realistic situation would be to know that we are dealing with a stainless steel sample of unknown composition. Despite the fact the spectrum itself tells us what elements are present, we are going to ask the program to perform a quantification starting from a pure Fe sample. + +As with the previous exercise, we have to make sure we have reached the situation shown below. + +|img1| + + +Configuring the Fit Strategy +............................ + +To properly configure the program we'll have to follow these steps: + +- As usual, enter the fit configuration widget via the Configure button. +- In the ATTENUATORS tab, enter (by typing) Fe as the Material of the Matrix. The program will automatically change the density. You can reset the value to the previous one if you want. It is a thick sample in any case and the results will not change. +- Make sure we have selected the Consider Tertiary Excitation checkbox of the CONCENTRATIONS tab. +- Move to the FIT tab of the configuration widget and select the Perform a fit using the selected strategy checkbox and click the SETUP button. + +You will be presented a screen similar to the screenshot below. + +|img2| + +Since you are there, please take a time to read the algorithm description. + +We are going to select a set of peak families and we are going to specify the chemical form in which they will be incorporated in the matrix. Since we are dealing with an alloy, we'll ask the program to incorporate the elements as pure metals. If we were dealing with a glass, we would have selected Si1O2 as completing material and incorporated the different elements in the form of oxides. + +We can directly select the peak families Cr K, Mn K, Fe Ka, Ni K. The idea is to select those peaks that give a high contribution to the spectrum and that we suspect they a present in the sample. For instance, if a experiment takes place in air, we can find an important contribution from Argon but the argon signal does not come from the sample and therefore should not be entered in the sample composition. You should manage to achieve the image below. + +|img3| + +Press OK to accept the strategy configuration and press OK again to finish the fit configuration. + +If you now carry out the fit you notice the fit takes longer due to the fit reconfiguration process associated to the matrix modifications. + +If you go to the CONCENTRATIONS tab, you will see that the obtained concentrations are quite acceptable despite about crude initial guess. + +If you are interested on knowing which was the last matrix composition used in the iterative process, you can obtain it by coming back to the ATTENUATORS tab of the fit configuration widget and selecting SingleLayerStrategyMaterial in the combo box of the Material Editor. + +|img4| + diff --git a/doc/source/training/quantification/img/quantification_01.png b/doc/source/training/quantification/img/quantification_01.png Binary files differnew file mode 100644 index 0000000..db0e316 --- /dev/null +++ b/doc/source/training/quantification/img/quantification_01.png diff --git a/doc/source/training/quantification/img/quantification_02.png b/doc/source/training/quantification/img/quantification_02.png Binary files differnew file mode 100644 index 0000000..8edbd1f --- /dev/null +++ b/doc/source/training/quantification/img/quantification_02.png diff --git a/doc/source/training/quantification/img/quantification_03.png b/doc/source/training/quantification/img/quantification_03.png Binary files differnew file mode 100644 index 0000000..88e0a8d --- /dev/null +++ b/doc/source/training/quantification/img/quantification_03.png diff --git a/doc/source/training/quantification/img/quantification_04.png b/doc/source/training/quantification/img/quantification_04.png Binary files differnew file mode 100644 index 0000000..76b8855 --- /dev/null +++ b/doc/source/training/quantification/img/quantification_04.png diff --git a/doc/source/training/quantification/img/quantification_05.png b/doc/source/training/quantification/img/quantification_05.png Binary files differnew file mode 100644 index 0000000..180f910 --- /dev/null +++ b/doc/source/training/quantification/img/quantification_05.png diff --git a/doc/source/training/quantification/img/quantification_06.png b/doc/source/training/quantification/img/quantification_06.png Binary files differnew file mode 100644 index 0000000..9657e2e --- /dev/null +++ b/doc/source/training/quantification/img/quantification_06.png diff --git a/doc/source/training/quantification/img/quantification_07.png b/doc/source/training/quantification/img/quantification_07.png Binary files differnew file mode 100644 index 0000000..0259224 --- /dev/null +++ b/doc/source/training/quantification/img/quantification_07.png diff --git a/doc/source/training/quantification/img/quantification_08.png b/doc/source/training/quantification/img/quantification_08.png Binary files differnew file mode 100644 index 0000000..134962b --- /dev/null +++ b/doc/source/training/quantification/img/quantification_08.png diff --git a/doc/source/training/quantification/img/quantification_09.png b/doc/source/training/quantification/img/quantification_09.png Binary files differnew file mode 100644 index 0000000..74aa531 --- /dev/null +++ b/doc/source/training/quantification/img/quantification_09.png diff --git a/doc/source/training/quantification/img/quantification_10.png b/doc/source/training/quantification/img/quantification_10.png Binary files differnew file mode 100644 index 0000000..7b1af7c --- /dev/null +++ b/doc/source/training/quantification/img/quantification_10.png diff --git a/doc/source/training/quantification/img/quantification_11.png b/doc/source/training/quantification/img/quantification_11.png Binary files differnew file mode 100644 index 0000000..6680735 --- /dev/null +++ b/doc/source/training/quantification/img/quantification_11.png diff --git a/doc/source/training/quantification/img/quantification_12.png b/doc/source/training/quantification/img/quantification_12.png Binary files differnew file mode 100644 index 0000000..05ebac3 --- /dev/null +++ b/doc/source/training/quantification/img/quantification_12.png diff --git a/doc/source/training/quantification/index.rst b/doc/source/training/quantification/index.rst new file mode 100644 index 0000000..1fc7f65 --- /dev/null +++ b/doc/source/training/quantification/index.rst @@ -0,0 +1,221 @@ +XRF Analysis +============ + +.. |img_01| image:: ./img/quantification_01.png + :width: 400px + :align: middle + :alt: XRF Spectrum + +.. |img_02| image:: ./img/quantification_02.png + :width: 400px + :align: middle + :alt: Calibration Widget + +.. |img_03| image:: ./img/quantification_03.png + :width: 400px + :align: middle + :alt: Calibrated Spectrum + +.. |img_04| image:: ./img/quantification_04.png + :width: 400px + :align: middle + :alt: Fit Region Selected + +.. |img_05| image:: ./img/quantification_05.png + :width: 400px + :align: middle + :alt: Fit Window + +.. |img_06| image:: ./img/quantification_06.png + :align: middle + :alt: Peak Identifier + +.. |img_07| image:: ./img/quantification_07.png + :width: 400px + :align: middle + :alt: Peak Family Selection + +.. |img_08| image:: ./img/quantification_08.png + :width: 400px + :align: middle + :alt: Initial Fit + +.. |img_09| image:: ./img/quantification_09.png + :width: 400px + :align: middle + :alt: Final Fit + +.. |img_10| image:: ./img/quantification_10.png + :width: 400px + :align: middle + :alt: Peaks Spectrum + +.. |img_11| image:: ./img/quantification_11.png + :width: 400px + :align: middle + :alt: Matrix Spectrum + +.. |img_12| image:: ./img/quantification_12.png + :width: 400px + :align: middle + :alt: One per cent Sc + +.. contents:: + :local: + +Introduction +------------ + +Many synchrotron users performing X-ray fluorescence experiments are interested in imaging the distribution of elements in their samples. However, they tend to show little interest in learning how to perform quantitative X-ray fluorescence (XRF) analysis and that can lead to the obtention of wrong elemental distribution maps. + +One has to take into account that even for pure imaging experiments one needs to calibrate the spectra, to identify the different elements in the sample and, most of the times, perform some fitting in order to resolve overlapping peaks of different elements. At this point users might not be aware that the relative peak areas they are extracting may be wrong because they did not take into account the modification of the database peak ratios by the conditions of the experiment. Simply introducing the experimental conditions and a guestimate of the sample composition is often enough to properly extract the signal from the different elements. + +The additional step to take to go from pure qualitative to quantitative analysis is a very small one if a minimum of care was taken (for instance recording the acquisition time and incoming flux and a known sample has been measured). + +Exercise +-------- + +The objective of this exercise is to get familiar with x-ray fluorescence analysis. For this, we are going to work with a spectrum from a thin film standard although we'll make some simplifications. + +Step 1: Loading the data +........................ + +The data required for this exercise are supplied with PyMca and can be loaded into the program via the main window File menu following the sequence File->Open->Load Training Data->XRF Analysis. + +The format associated to that spectrum is the simplest that PyMca can read. It is just a single column of numbers corresponding to the counts in the different channels. Under that situation, PyMca does not know if those data belong to an XRF experiment or to something else and offers two different visualization modes. One generic and one specific to XRF. + +Your first task is to achieve the situation shown in the figure below where the data are present in the MCA tab of the main window in a semilogarithmic plot. + +|img_01| + +Step 2: Calibrating the data +............................ + +If it is your first time with PyMca, you should take a look at the `Calibration tutorial <http://www.esrf.fr/computing/bliss/downloads/pymca/calibrationtutorial.htm>`_ + +The excitation energy was about 17.5 keV. Very often this is enough information for an initial calibration. However, this detector presented a very important offset and you will need an addition calibration point. Just imagine you have previously measured a cobalt sample and that you know that the peak around channel 1474 corresponds to the main emission line of Co. + +You may reach the situation illustrated below where the calibration window is shown. You have to press the OK button to validate the calibration. + +|img_02| + +At this point you should be back to the main window without any change respect to the previous situation. Prior to go any further, you should instruct PyMca about what calibration you intend to use. Unless you have changed the name of the calibration, choosing Internal in the calibration combo box should apply the just calculated one to the spectrum leading to the situation below. + +|img_03| + +Under the calibration combo box, following *Active curve uses*, you will see the calibration actually applied. It should be close to A=-0.5, B=-0.005 and C=0. (Hint: Make sure you have selected a first order calibration when calculating the calibration). If it is very different your calibration is wrong and you will experience a lot of difficulties later on. + + +Step 3: Select your fit region +.............................. + +We already have a calibrated spectrum. The rest of the exercise will use the McaAdvancedFit window. + +Prior to reach that window, we should select the region of the sample we'd like to analyze. For that, we have to zoom in that region by pressing and dragging the mouse. PyMca implements a zoom stack, you can go back by pressing the mouse right button or by pressing the reset zoom icon. + +At the very least, you should always leave the cut at the low energy side corresponding to the low-level discriminator of your acquisition system out of the fitting region. Something around 1.0 keV should be OK in this case. + +PyMca (still!) implements a very poor description of the scattering peaks. Unless you absolutely need it, you will obtain better results by limiting the high energy side of the region to the rail of the scattered peaks. Something like 16.3 keV should be a good upper limit. + +|img_04| + +At this point we are ready to access the McaAdvancedFit window by pressing the fit icon and selecting the *Advanced* option. + +Step 4: Using the Peak Identifier +................................. + +The first thing you will get is a message telling you that no peaks have been defined. PyMca has very good peak search routines and it could do a very good guess about the elements present. However, the author(s) consider that the responsibility should fall on the person carrying the analysis. + +In order to allow PyMca to give you some hints about what elements can be associated to a peak, you need to toggle the energy axis on. Your next target should be to obtain the image below. + +|img_05| + +If you now click on top of a peak, PyMca will show you the peaks that can be associated to that energy. If you click at around 6.9 keV. PyMca should show you the peak identifier. + +|img_06| + +You will be presented with a table of elements, peak families and rates within the family of all the elements emitting x-ray within the specified energy threshold around the selected energy. + +As a rule of thumb, you should aim at identifying the most intense peaks. Why? Because that can help you decide to what element they belong. For instance, the L3 lines are usually more intense than the L2 lines or L1 lines. If the program proposes you the L2 lines of one element and the L3 lines of other element, there are strong chances of having the element with the L3 lines because if it would be the element with the L2 lines there should be a stronger peak somewhere in the spectrum corresponding to the L3 lines of that element. Of course, that is to be considered as a hint. It may well happen that the intense L3 lines are hidden beneath the peak of another already identified element... + +Step 5: Fit Configuration +......................... + +In this example you could already start adding peaks families to be fitted immediately because the sample is relatively thin and matrix effects are small. However, you should aim at doing the things properly and enter as much information as possible into the fit configuration. + +The experimental conditions are excitation energy around 17.5 keV, Si detector 450 micron thickness and Be window of 8 micron thickness. For the sake of simplicity assume the sample is 100 micron water and contains 500 ppm of Co. Incident beam angle is 0.1 degrees and fluorescence beam angle is 90 degrees. There is an air path between sample and detector window of 2 mm. + +To will enter the fit configuration by pressing the Configure button. + +To enter the experimental setup you will need to use the ATTENUATORS tab and the MATRIX tab. The incident beam energy is set into the BEAM tab. + +Concerning the FIT tab, at this point just make sure the *Stripping* check box is selected in order to have some baseline to be applied to your fit. + +If you have done that and you select the PEAKS tab, you will see the excitation energy in red. Below you will see the selection of the Co K lines as peaks to be fitted. + +|img_07| + +Press OK to accept the changes. You will be back to the fit window and by pressing the Fit icon or the Fit again! button you should obtain a fit similar to the one displayed below. + +|img_08| + +As you see the background still needs some adjustment. You can do it via the corresponding SETUP button at the FIT tab of the fit configuration widget. + +You should spend some time going to the fit configuration to add peaks and back to the fit window to perform fits. WARNING: It is advisable to save your fit configuration from the fit configuration widget via the Save button. That can save you a lot of time in case of problems because you could restart form that point. + +You can take the image below as encouragement. + +|img_09| + +If you need to take a look at the individual contributions of the different elements to the fitted spectrum, you can do so by selecting the *Peaks Spectrum* button. + +|img_10| + +If you want to highlight a particular element contribution, you should make the legends widget appear by pressing on Options and selecting Legends. It is not shown here in order not to make the exercise too simple (remember *No pain, no gain*). + +Hint. You should not need more than 18 elements to achieve the same fit quality. + +Step 6: Concentrations +...................... + +The additional step to calculate concentrations is very simple. One either needs to know some details about the system (flux, acquisition live time, solid angle) or to use an internal standard. + +If we have set the sample is water with 500 ppm of Co, we can go back to the fit configuration and select in the CONCENTRATIONS tab the *From matrix composition* check box. You can also enter Co as *Matrix Reference Element* if you wish. + +To get the concentrations is as easy as selecting the CONCENTRATIONS tab of the +advanced fit window after performing a fit. + +Hint: If everything is OK, the concentrations of all the elements present in the sample should be in the vicinity of 500 ppm (0.0005 mass fraction). + +In real life you often do not have an internal standard. However, you could imagine that you have just measured a reference sample you have just prepared with a concentration of 500 ppm Co in water and that you want to calibrate your system. Then, obviously, the Co concentration given by the program is exactly 0.0005 because it is used as internal standard. To calibrate your system all what you have to do is to select the *From fundamental parameters* check box and modify the Active area, distance, time to match those of your experiment and finally play with the flux until the concentration of Co is back to 0.0005. From there on you will be ready to use your system without an internal standard. You would have removed the water-with-Co sample and measured our unknown sample. + +Step 7: Using the Matrix Spectrum +................................. + +PyMca can be used to calculate the expected measured spectrum given the experimental conditions and the sample composition. + +If you have performed the previous steps, you just have to perform a fit and press the Matrix Spectrum button. You can see something similar to the figure below where besides the spectrum and the fit there is a spectrum corresponding to the matrix (in this case is shown in magenta but the colors may vary). In our case it is just Co what is shown. + +|img_11| + +We can use this PyMca feature to measure the thickness of layers or to estimate confidence limits. Let's take a look at the later. + +We go back to the fit configuration and select the Sc K-line as element family of peaks to be fitted and we perform a fit. If we go to the CONCENTRATIONS tab we'll see that PyMca reports a concentration of the order of some ppms. The question is, can we trust that information? + +A simple exercise is to add Sc at different amounts to the sample composition and to ask the program to calculate the matrix spectrum. We can start with a fairly large amount like 1 % to visualize where the signal should appear. Then we just have to repeat the exercise lowering the concentration until we reach a point below which we would not trust anything. The figure below shows the matrix spectrum with 1 % of Scandium. + +|img_12| + +After performing the exercise, you will easily conclude that the concentration of Sc in the sample, if any, it is below the detection limits of our system under the exact conditions of our experiment (including sample!). + +Step 8: Final Comments +...................... + +If you want, you can also observe how the changes on the calculated concentrations when changing the attenuation conditions: + + - play with an air path between 1.0 mm and 100 mm (what happens at low energies?) + - play with a detector thickness between 10 micron and 1 mm (what happens with the concentrations at high energies?) + +The information to carry out this exercise is also available within PyMca. To access it, you just have to select the FILE tab of the widget appearing after a right click on the list shown after loading the file (right-mouse click on #S1 Unknown...) and *Show scan header* selection). + + diff --git a/doc/source/training/tertiary/img/tertiary_01.png b/doc/source/training/tertiary/img/tertiary_01.png Binary files differnew file mode 100644 index 0000000..fbf88d8 --- /dev/null +++ b/doc/source/training/tertiary/img/tertiary_01.png diff --git a/doc/source/training/tertiary/img/tertiary_02.png b/doc/source/training/tertiary/img/tertiary_02.png Binary files differnew file mode 100644 index 0000000..bfa82d7 --- /dev/null +++ b/doc/source/training/tertiary/img/tertiary_02.png diff --git a/doc/source/training/tertiary/img/tertiary_03.png b/doc/source/training/tertiary/img/tertiary_03.png Binary files differnew file mode 100644 index 0000000..5325dfc --- /dev/null +++ b/doc/source/training/tertiary/img/tertiary_03.png diff --git a/doc/source/training/tertiary/img/tertiary_04.png b/doc/source/training/tertiary/img/tertiary_04.png Binary files differnew file mode 100644 index 0000000..69af2c7 --- /dev/null +++ b/doc/source/training/tertiary/img/tertiary_04.png diff --git a/doc/source/training/tertiary/index.rst b/doc/source/training/tertiary/index.rst new file mode 100644 index 0000000..3ed19a7 --- /dev/null +++ b/doc/source/training/tertiary/index.rst @@ -0,0 +1,65 @@ +Accounting for higher order excitations +======================================= + +.. |img1| image:: ./img/tertiary_01.png + :width: 300px + :align: middle + :alt: Secondary Excitation Process + +.. |img2| image:: ./img/tertiary_02.png + :width: 400px + :align: middle + :alt: Fitted Steel Data + +.. |img3| image:: ./img/tertiary_03.png + :width: 400px + :align: middle + :alt: Analytical corrections + +.. |img4| image:: ./img/tertiary_04.png + :width: 400px + :align: middle + :alt: Monte Carlo corrections + +.. contents:: + :local: + +Introduction +------------ + +It can happen that the X-rays emitted by a sample element j excite other element i present in the sample up to a non-negligible extent as compared to the direct excitation process. The figure below illustrates the case for secondary excitation. + +|img1| + +Obviously it may happen that the element i excite in turn other element up to a non-negligible amount and so son. PyMca can account for these processes using two totally different approaches. One based on the use of analytical formulae and one based on the use of a Monte Carlo code. + +The analytical approach implemented in PyMca makes use of the `formulation of D.K.G. de Boer <https://doi.org/10.1002/xrs.1300190312>`_ that is well adapted for its use with multiple-layer samples. + +The Monte Carlo approach uses the `XMI-MSIM code <https://doi.org/10.1016/j.sab.2012.03.011>`_ mainly developed by Tom Schoonjans and Laszlo Vincze The code can be freely downloaded `here <http://lvserver.ugent.be/xmi-msim/>`_ + +Currently, PyMca simply evaluates the ratio between the expected measured signal considering primary excitation and the expected measured signal considering higher-order excitation. That ratio gives the correction to be applied to the concentrations calculated just considering primary excitation. + +Exercise +-------- + +We are going to learn how to account for secondary excitations using the spectrum from a stainless steel sample. + +The data are provided with PyMca. To access them, just start a new session of PyMca and, via the File menu, access the data following the sequence File -> Open -> Load Training Data -> Tertiary Excitation. At this point, PyMca will show you the spectrum in the MCA window. + +We are going to skip the usual procedure of calibrating the spectrum. Therefore we are going to press the fit icon and select the Advanced fit option to reach our usual McaAdvancedFit window where we perform our XRF analysis. + +A fit configuration file named Steel.cfg is also provided. As supplied, it only considers quantification following primary excitation. To load it, just press the Configure button, load the file from the file dialog that will appear after pressing the Load button and press OK to return to the McaAdvancedFit window. If you press the Fit Again! button or the fit icon, you should be able to obtain something similar to the figure below (please note the change to energy axis and logarithmic scale via the appropriate toolbar icons). + +|img2| + +At this point we can calculate the concentrations by selecting the CONCENTRATIONS tab. We can also select the DIAGNOSTICS tab. If we do so, we'll see that the program warns about secondary or tertiary excitation contributions when the correction is more than 1 %. You should see that neglecting secondary excitation would lead to overestimating the concentration of Cr in the sample by more than 60 % and that even tertiary excitation is not negligible. + +|img3| + +If XMI-MSIM is not installed, the MC Matrix Spectrum button will not be shown. If it is installed, you can also calculate the corrections using it. You just need to press the MC Matrix Spectrum button. You should get a window open where the output of the code will be shown. Under windows sometimes you need to use the 32-bit version of XMI-MSIM. The first time you run the XMI-MSIM code for a given detector and geometry it can take quite long. Subsequent runs are very fast for a Monte Carlo code. Besides showing the Monte Carlo calculated spectrum following 1 or 4 interactions in the sample, the logging window will show you the corrections we are interested on. + +|img4| + +Whether using the analytical formulas or the Monte Carlo approach, accounting for those corrections is as simple as selecting the appropriate option in the CONCENTRATIONS tab. You will see how the different concentrations are corrected following the selection of the appropriate checkbox to consider secondary, tertiary or Monte Carlo correction. If you know your samples require account for secondary or higher order excitations, you should select the appropriate checkbox of the CONCENTRATIONS tab of the fit configuration as part of the fir configuration itself. + + diff --git a/doc/source/training/xraydata/img/xraydata_01.png b/doc/source/training/xraydata/img/xraydata_01.png Binary files differnew file mode 100644 index 0000000..4601932 --- /dev/null +++ b/doc/source/training/xraydata/img/xraydata_01.png diff --git a/doc/source/training/xraydata/img/xraydata_02.png b/doc/source/training/xraydata/img/xraydata_02.png Binary files differnew file mode 100644 index 0000000..24dd553 --- /dev/null +++ b/doc/source/training/xraydata/img/xraydata_02.png diff --git a/doc/source/training/xraydata/index.rst b/doc/source/training/xraydata/index.rst new file mode 100644 index 0000000..4c6146c --- /dev/null +++ b/doc/source/training/xraydata/index.rst @@ -0,0 +1,71 @@ +Customizing X-Ray Data +====================== + +.. |img_01| image:: ./img/xraydata_01.png + :align: middle + :alt: Initial data + +.. |img_02| image:: ./img/xraydata_02.png + :align: middle + :alt: Final data + + +.. contents:: + :local: + +Introduction +------------ + +A program for X-ray fluorescence analysis needs: + +- A database +- A Physics engine +- A set of algorithms for spectrum deconvolution +- A user interface + +The developers of *PyMca* put the maximum degree of effort into correctness and transparency but they are not at the origin of the data used by the program. The theoretical data may not be reliable in some cases (in fact we know they are not!) and, since we do not have the time to perform a systematic study or to determine them ourselves, we have made sure the program can be modified by the user in order to adapt it to other data that the user may consider more reliable. + +Exercise +-------- + +The objective of this exercise is to teach you how to modify the theoretical data used by *PyMca*. For this, we are going to take the case of the L1-shell fluorescence yield of Pb and we are going to force *PyMca* to use a different value from the supplied default one. + +Step 1: Getting the data +........................ + +The L1-shell fluorescence yields are stored in the `LShellConstants.dat <https://github.com/vasole/fisx/blob/master/fisx_data/LShellConstants.dat>`_ file of the `fisx library <https://github.com/vasole/fisx>`_. + +As explained in the `Customizing PyMca section <../../customization/settings/index.html>`_, we have to download that file and put it into the data directory of the settings folder. You can do it by opening the `link <https://github.com/vasole/fisx/blob/master/fisx_data/LShellConstants.dat>`_ above in your browser, clicking on the button with the text Raw shown at the right side of the page and saving the file to the mentioned directory. + +Step 2: Initial verification +............................ + +In order to check the data *PyMca* is using, we can open the *Elements Info* tool available from the *Tools* option of the menu of the *PyMca* main window. + +We can click on the atomic symbol of Pb in order to display the information used by the program. + +|img_01| + +We should find a value of of 0.0932 for the lead L1-shell fluorescence yield. This value was obtained by interpolating the theoretical values in TABLE I from the article by M.H. Chen, B. Crasemann, H. Mark in *Widths and fluorescence yields of atomic L-shell vacancy states Physical Review A volume 24 number 1 (1981) 177-182* + +We are going to update that value with the value 0.128 recommended in Table 2 from the article by J.L. Campbell *Fluorescence yields and Coster-Kronig probabilities for the atomic L subshells. Part II: The L1 subshell revisited* with `doi:10.1016/j.adt.2008.08.002 <https://dx.doi.org/10.1016/j.adt.2008.08.002>`_ + +DISCLAIMER: The goal of this exercise is to show you how to update the data used by the application. We are not endorsing the use of the data provided by Campbell because we have not made an exhaustive study for all the elements. Despite that, we have to say that Campbell has performed such systematic studies and the value Campbell recommends for lead is indeed much closer to the value used in our own research than the theoretical one. You SHOULD verify yourself if, when calculating concentrations using the L1, L2 and L3 lines of Pb, you find a) a systematic discrepancy of the value of the L1-derived concentrations respect to the concentration values derived from the L2 and L3 shells and b) if you can correct the discrepancy by changing the L1-shell fluorescence yield of lead. + +Step 3: Modifying the data +.......................... + +You need an editor not modifying line endings. That is usually not a problem for Linux or MacOS users. For windows users `Notepad++ <https://notepad-plus-plus.org>`_ or `Vim for windows <https://www.vim.org>`_ could be good choices. + +We just have to get to the line 93 of the file, replace the value 0.0932 by the value 0.128 and save the changes. + +Step 4: Final verification +.......................... + +We close *PyMca* if we did not do it yet and we start it again. + +If we repeat the operation described in the step 2 above, we should get now the modified value. + +|img_02| + +Please keep in mind the DISCLAIMER above. diff --git a/doc/source/tutorials.rst b/doc/source/tutorials.rst index 0ed1b9c..bc536ab 100644 --- a/doc/source/tutorials.rst +++ b/doc/source/tutorials.rst @@ -1,5 +1,5 @@ -Tutorials and sample code -========================= +Tutorials and Exercises +======================= .. toctree:: :hidden: @@ -8,31 +8,50 @@ Tutorials and sample code ./xrf/strip-background/index.rst ./customization/index.rst ./hdf5/index.rst + ./training/quantification/index.rst + ./training/tertiary/index.rst + ./training/matrix/index.rst + ./training/xraydata/index.rst -There are several tutorials explaining different aspects of the program. +Things learned by practice usually require a greater effort than just reading or listening and tend to be better retained. Therefore we have prepared some `Exercises`_ to complement the usual set of `Tutorials`_ teaching different aspects of *PyMca*. + +Their combination should provide a good starting point to use the program. + +Tutorials +--------- The `Getting Started tutorial <http://www.esrf.fr/computing/bliss/downloads/pymca/PyMcaCHESS.pdf>`_ is a very old tutorial written by Darren Dale and initially tailored to `CHESS <http://www.chess.cornell.edu>`_ -users but usefull to everybody starting to use PyMca. In case the previous link is down, you can try +users but usefull to everybody starting to use *PyMca*. In case the previous link is down, you can try this `alternative link <https://wiki.utep.edu/display/MASE6402MME4501/PyMCA+support>`_. -:doc:`./hdf5/index` PyMca can deal with -HDF5 files since version 4.4.0. You should take a look at the -`HDF Group web site <https://portal.hdfgroup.org/display/HDF5/HDF5>`_ to know more about HDF. -`NeXus <http://www.nexusformat.org>`_ files are only supported when using the HDF5 backend. - `Calibration tutorial <http://www.esrf.fr/computing/bliss/downloads/pymca/calibrationtutorial.htm>`_. To be used if you still have some doubts about how to calibrate your spectra. :doc:`./xrf/material-definition/index`. This tutorial will show you how to define your own materials. +:doc:`./xrf/strip-background/index`. Description of the parameters defining your favorite background. + `ROI Imaging tutorial <http://www.esrf.fr/computing/bliss/downloads/pymca/roitooltutorial.htm>`_ . -Introduction to the stack imaging capabilities of PyMca +Introduction to the stack imaging capabilities of *PyMca* `Kinetics tutorial <http://www.esrf.fr/computing/bliss/downloads/pymca/kineticstutorial.htm>`_ . Illustration of the use of the ROI Imaging tool for kinetics studies. -:doc:`./xrf/strip-background/index`. Description of the parameters defining your favorite background. +:doc:`./hdf5/index` *PyMca* can deal with +HDF5 files since version 4.4.0. You should take a look at the +`HDF Group web site <https://portal.hdfgroup.org/display/HDF5/HDF5>`_ to know more about HDF. +`NeXus <http://www.nexusformat.org>`_ files are only supported when using the HDF5 backend. + +:doc:`./customization/index` Description about how to provide customized settings and add-ons to *PyMca*. + +Exercises +--------- + +:doc:`./training/quantification/index`. The classical exercise to learn how to carry out XRF analysis with *PyMca*. + +:doc:`./training/tertiary/index`. Press-button exercise to show how to deal with secondary and higher order excitations in X-ray fluorescence quantification problems. -:doc:`./customization/index` Description about how to add your own add-ons to PyMca. +:doc:`./training/matrix/index`. This exercise shows the user one way to tell the program how to automatically update the sample composition. +:doc:`./training/xraydata/index`. Exercise to teach the user how to modify the theoretical data used by *PyMca*. diff --git a/package/debian8/changelog b/package/debian8/changelog index bdc2a61..99ea4e2 100644 --- a/package/debian8/changelog +++ b/package/debian8/changelog @@ -1,3 +1,9 @@ +pymca (5.4.0-1) unstable; urgency=low + + * New release 5.4.0 + + -- Pierre Knobel <pierre.knobel@esrf.fr> Fri, 14 Sep 2018 10:01:17 +0200 + pymca (5.3.1-1) unstable; urgency=low * New release 5.3.1 diff --git a/package/debian8/control b/package/debian8/control index 886a215..adaa67a 100644 --- a/package/debian8/control +++ b/package/debian8/control @@ -6,27 +6,21 @@ Section: science Testsuite: autopkgtest Priority: extra Build-Depends: cython, - cython-dbg, cython3, - cython3-dbg, dh-python, debhelper (>= 9), libglu1-mesa-dev, libqhull-dev, - python-all-dbg, python-all-dev, python-numpy, - python-numpy-dbg, python-setuptools, python-sphinx, - python-fisx (>= 1.1.2), - python3-all-dbg, + python-fisx (>= 1.1.6), python3-all-dev, python3-numpy, - python3-numpy-dbg, python3-setuptools, python3-sphinx, - python3-fisx (>= 1.1.2), + python3-fisx (>= 1.1.6), Standards-Version: 3.9.8 Vcs-Browser: https://anonscm.debian.org/cgit/debian-science/packages/pymca.git Vcs-Git: https://anonscm.debian.org/git/debian-science/packages/pymca.git @@ -62,7 +56,7 @@ Package: python-pymca5 Architecture: any Section: python Depends: pymca-data (= ${source:Version}), - python-fisx (>= 1.1.2), + python-fisx (>= 1.1.6), python-matplotlib, python-h5py, python-silx, @@ -93,47 +87,11 @@ Description: Applications and toolkit for X-ray fluorescence analysis -- Python . This is the Python 2 version of the package. -Package: python-pymca5-dbg -Architecture: any -Section: debug -Depends: python-fisx-dbg (>= 1.1.2), - python-matplotlib-dbg, - python-h5py-dbg, - python-silx-dbg, - python-opengl, - python-pymca5 (= ${binary:Version}), - python-qt4-dbg | python-pyqt5-dbg, - python-qt4-gl-dbg | python-pyqt5.qtopengl-dbg, - ${misc:Depends}, - ${python:Depends}, - ${shlibs:Depends} -Recommends: python-dbg, - python-mdp -Description: Applications and toolkit for X-ray fluorescence analysis -- Python 2 debug - PyMca is set of applications and Python libraries for analysis of - X-ray fluorescence spectra. - . - The applications included in this package are: - . - * edfviewer - Display and inspection of data files in ESRF Data Format - * elementsinfo - Displays element specific X-ray data - * mca2edf - Converts files from SPEC MCA format to EDF - * peakidentifier - Displays X-ray fluorescence peaks in a given energy range - * pymcabatch - Batch fitting of spectra - * pymcapostbatch - Post-processing of batch fitting results - * pymca - Interactive data-analysis - * pymcaroitool - Region-of-interest (ROI) imaging tool - . - The PyMca toolkit can read data files in SPEC, ESRF data file (EDF), - OMNIC, HDF5, AIFIRA and SupaVisio formats. - . - This is the Python 2 debug version of the package. - Package: python3-pymca5 Architecture: any Section: python Depends: pymca-data (= ${source:Version}), - python3-fisx (>= 1.1.2), + python3-fisx (>= 1.1.6), python3-matplotlib, python3-h5py, python3-silx, @@ -164,42 +122,6 @@ Description: Applications and toolkit for X-ray fluorescence analysis -- Python . This is the Python 3 version of the package. -Package: python3-pymca5-dbg -Architecture: any -Section: debug -Depends: python3-fisx-dbg (>= 1.1.2), - python3-matplotlib-dbg, - python3-h5py-dbg, - python3-silx-dbg, - python3-opengl, - python3-pymca5 (= ${binary:Version}), - python3-pyqt4-dbg | python3-pyqt5-dbg, - python3-pyqt4.qtopengl-dbg | python3-pyqt5.qtopengl-dbg, - ${misc:Depends}, - ${python3:Depends}, - ${shlibs:Depends} -Recommends: python3-dbg, - python3-mdp -Description: Applications and toolkit for X-ray fluorescence analysis -- Python 3 debug - PyMca is set of applications and Python libraries for analysis of - X-ray fluorescence spectra. - . - The applications included in this package are: - . - * edfviewer - Display and inspection of data files in ESRF Data Format - * elementsinfo - Displays element specific X-ray data - * mca2edf - Converts files from SPEC MCA format to EDF - * peakidentifier - Displays X-ray fluorescence peaks in a given energy range - * pymcabatch - Batch fitting of spectra - * pymcapostbatch - Post-processing of batch fitting results - * pymca - Interactive data-analysis - * pymcaroitool - Region-of-interest (ROI) imaging tool - . - The PyMca toolkit can read data files in SPEC, ESRF data file (EDF), - OMNIC, HDF5, AIFIRA and SupaVisio formats. - . - This is the Python 3 debug version of the package. - Package: pymca-data Architecture: all Depends: ${misc:Depends} diff --git a/package/debian9/changelog b/package/debian9/changelog index bdc2a61..99ea4e2 100644 --- a/package/debian9/changelog +++ b/package/debian9/changelog @@ -1,3 +1,9 @@ +pymca (5.4.0-1) unstable; urgency=low + + * New release 5.4.0 + + -- Pierre Knobel <pierre.knobel@esrf.fr> Fri, 14 Sep 2018 10:01:17 +0200 + pymca (5.3.1-1) unstable; urgency=low * New release 5.3.1 diff --git a/package/debian9/control b/package/debian9/control index edccfe0..adaa67a 100644 --- a/package/debian9/control +++ b/package/debian9/control @@ -15,12 +15,12 @@ Build-Depends: cython, python-numpy, python-setuptools, python-sphinx, - python-fisx (>= 1.1.2), + python-fisx (>= 1.1.6), python3-all-dev, python3-numpy, python3-setuptools, python3-sphinx, - python3-fisx (>= 1.1.2), + python3-fisx (>= 1.1.6), Standards-Version: 3.9.8 Vcs-Browser: https://anonscm.debian.org/cgit/debian-science/packages/pymca.git Vcs-Git: https://anonscm.debian.org/git/debian-science/packages/pymca.git @@ -56,7 +56,7 @@ Package: python-pymca5 Architecture: any Section: python Depends: pymca-data (= ${source:Version}), - python-fisx (>= 1.1.2), + python-fisx (>= 1.1.6), python-matplotlib, python-h5py, python-silx, @@ -91,7 +91,7 @@ Package: python3-pymca5 Architecture: any Section: python Depends: pymca-data (= ${source:Version}), - python3-fisx (>= 1.1.2), + python3-fisx (>= 1.1.6), python3-matplotlib, python3-h5py, python3-silx, diff --git a/requirements.txt b/requirements.txt index 286cd04..e169738 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,12 +7,12 @@ --only-binary numpy,h5py,scipy,PyQt4,PyQt5,PySide numpy >= 1.8 -fisx >= 1.1.2 +fisx >= 1.1.5 PyOpenGL # For PyMca5.Object3D module h5py # For HDF5 file format support matplotlib > 1.0 # For visualization qtconsole # For interactive console plugin -silx >= 0.7 # For NeXus NXdata support and nicer icons +silx >= 0.8 # For NeXus NXdata support and nicer icons # PyQt4, PyQt5 or PySide # For PyMca5.PyMcaGui # Try to install a Qt binding from a wheel @@ -895,7 +895,7 @@ classifiers = ["Development Status :: 5 - Production/Stable", # install requires for non-GUI usage install_requires = ["numpy", "matplotlib>1.0", - "fisx>=1.1.4", + "fisx>=1.1.6", "h5py"] if use_gui(): # install requires with all easy-to-provide GUI functionality |